※ 주의사항 ※
본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다.
본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다!
감사합니다😁
안녕하세요!!
오늘은 아래와 같은 내용을 확인해보겠습니다.
UART 통신 이란?
UART 관련 레지스터
UART로 문자열 송수신
1. UART 통신 이란?
1-1. 병렬 전송과 직렬 전송
1) 병렬 전송 : 8개의 핀을 통해 1번에 1바이트(8비트) 데이터 전송
- 연결이 복잡해진다.
- 핀수가 제한된 마이크로컨트롤러에서는 핀 부족 현상이 발생할 수 있다.
2) 직렬 전송 : 1개의 핀을 통해 8번에 나누어 1바이트 데이터 전송
- UART 통신은 시리얼(직렬) 통신의 한 종류이다.
- 그외 I2C, SPI 통신 등이 있다.
1-2. UART 통신 이란?
1) UART = 비동기 통신 방식
- 데이터를 위한 별도의 클록을 사용하지 않는다.
- 약속된 속도로 송수신을 수행한다.
- 통신 속도의 단위로 보율(Baud Rate)를 사용한다.
※ Baud Rate와 bps는 최근들어 다른 개념을 가지고 있지만 지금은 생략하겠습니다.
2) 데이터 전송 규칙
- 동일한 속도로 데이터를 주고 받는다고 해도 항상 데이터를 주고 받는것이 아니기때문에 데이터의 시작과 끝이 언제인지를 알아낼수가 있어야 한다.
- 그래서 데이터 비트의 시작과 끝에 시작비트 '0', 정지비트 '1'을 추가하여 총 10비트의 데이터를 전송한다.
※ 추가적으로 데이터의 오류를 찾아내기 위한 '패리티비트'를 데이터 비트 뒤에 사용하는 경우도 있는데 이는 선택적인 부분이라서 생략하겠습니다..😄
- UART 통신은 전이중 방식 통신으로 송신과 수신을 동시에 진행할 수 있으며 이를 위해 2개의 핀을 필요로 한다.
- ATmega128A 에서는 UART0은 PE0, PE1핀을 UART1은 PD2와 PD3핀을 사용한다.
2. UART 관련 레지스터
※ ATmega128A는 UART 포트가 2개 존재하기 때문에 아래의 레지스터 또는 비트 이름에 적혀있는 n(또는 m)은 0이나 1에 해당 된다.
- UART 관련 레지스터는 총 약 6개의 레지스터가 존재하는데 각 레지스터에서 사용도가 높은 비트만 대략적으로 설명하고 넘어가겠습니다!!!
2-1. UCSRnA(USART Control and Status Register n A) 레지스터
UART 시리얼 통신의 상태를 확인하고 통신 과정을 제어하기 위한 레지스터 중 하나이다.
1) U2Xn(1번 비트)
- 비동기 전송모드에서만 사용되며 해당 비트가 1이면 2배속 모드, 0이면 1배속 모드
※ UCSRnA 레지스터에서 U2Xn 비트만 1로 설정하기 위한 프로그래밍 방법은 아래의 4가지가 있다.
(1) UCSR1A = UCSR1A | (1 << 1);
(2) UCSR1A = UCSR1A | (1 << U2X1);
(3) UCSR1A |= (1 << U2X1);
(4) UCSR1A |= _BV(U2X1);
모두 똑같이 U2Xn 비트만 1로 세트한다는 코드이다. (취향에 맞게 사용하길..)
2) UDReN(5번 비트)
- 데이터를 저장하는 버퍼(UDRn)이 비어 데이터가 받을 준비가 되면 1, 버퍼 안에 데이터가 있으면 0
- UDRe 레지스터와 이름이 비슷해서 햇갈릴수 있지만 주의할 것!
UDRe 레지스터는 데이터를 저장하는 8비트 레지스터에 해당된다.(비트가 아니다!)
3) RXCn(7번 비트)
- 데이터 버퍼(UDRn)에 읽지 않은 문자가 있으면 1, 버퍼 안에 데이터가 없으면 0
※ 5번 비트와 초기값이 다르므로 주의해야 한다.
4) TXCn(6번 비트)
- 5번 비트와 비슷한 기능을 한다. 단, 디폴트 값이 0으로 별도의 설정 없이는 데이터 전송이 불가능하다.
- TXCn 비트는 전송 시프트 레지스터에서 데이터를 모두 전송하고 UDRn 레지스터에도 데이터가 없는경우 세트 된다.
- UDReN 비트는 UDRn 레지스터에 데이터가 없는 경우에 세트된다.
- 즉, TXCn 비트를 사용하면 더 번거롭고 시간이 오래걸린다. (지금은 이정도로만 기억하고 PASS 😥)
2-2. UCSRnB & UCSRnC 레지스터
1) USCRnB 레지스터
UCSRnB 레지스터는 데이터 송수신을 가능하도록 설정하기 위해 사용된다.
(1) TXENm (3번 비트)
- UART 송신기의 송신 기능을 활성화 한다. (디폴트 값은 '0', UART 통신 금지 상태)
(2) RXENm (4번 비트)
- UART 수신기의 수신 기능을 활성화 한다. (디폴트 값은 '0', UART 통신 금지 상태)
(3) UCSZm2 (2번 비트)
- UCSRnC 레지스터의 1번, 2번 비트와 함께 전송할 데이터 비트 수를 결정한다.
(자세한 내용은 아래 UCSRnC 레지스터에서 설명하겠습니다.)
2) UCSRnC 레지스터
(1) UCSZm0, UCSZm1 (1번, 2번 비트)
- UCSRnB 레지스터의 2번 비트와 함께 전송할 데이터 비트 수를 결정한다.
- 3개의 비트의 값에 따라 전송할 데이터 비트수는 다음 표와 같다.
(2) USBSn (3번 비트)
- 정지 비트의 수를 지정하기 위해 사용된다. (0 : 1비트, 1 : 2비트)
(3) UPMn0, UPMn1 (4번, 5번 비트)
- 비트 설정에 따라 패리티 비트 의 모드 설정이 가능하다.
(패리티 비트 없음, 홀수 패리티, 짝수 패리티)
(4) UMSELn (6번 비트)
- 0 : 비동기 USART 설정, 1: 동기 USART 설정
2-3. UBBRnH & UBBRnL 레지스터
- 2개의 레지스터중 16비트를 조합해서 Baud Rate를 설정하는 레지스터이다.
- 16Mhz, 비동기 전송 모드에서 보율에 따른 UBRRn 값은 아래의 표를 보면 된다.
BAUD | UBRRn 계산값 | UBRRn 사용값 | ||
1배속 | 2배속 | 1배속 | 2배속 | |
2,400 | 415.67 | 832.33 | 416 | 832 |
4,800 | 207.33 | 415.67 | 207 | 416 |
9,600 | 103.17 | 207.33 | 103 | 207 |
14,400 | 68.44 | 137.89 | 68 | 138 |
19,200 | 51.08 | 103.17 | 51 | 103 |
28,800 | 33.72 | 68.44 | 34 | 68 |
38,400 | 25.04 | 51.08 | 25 | 51 |
57,600 | 16.36 | 33.72 | 16 | 34 |
2-4. UDRn 레지스터
- 송수신된 데이터가 저장되는 버퍼 레지스터이다.
- 전송을 위한 TXB 레지스터와 데이터 수신을 위한 RXB 레지스터로 구성되어 있다.
- UDRn 레지스터에 데이터를 Write 하면 TXB 레지스터에 데이터가 기록되고,
UDRn 레지스터에 데이터를 Read 하면 RXBn 레지스터의 데이터가 읽혀진다.
2-5. UART 통신 기본 코드
1) 코드 설명
(1) UART0 통신 초기화 함수부(void UART0_init)
- Line 11 ~ 12 : Baud Rate를 9600으로 설정
- Line 13 : UART 통신을 2배속 모드로 설정 (UART 비동기 통신, 패리티 비트 없음, 정지비트 1비트)
- Line 14 : 데이터 비트 수는 8비트로 설정
- Line 15 ~ 16 : UART 송신기와 수신기의 기능 활성화
(2) UART0 데이터 송신 함수부(void UART0_transmit)
- Line 21 : 데이터를 저장하는 송신버퍼(UDR0)가 비어 있으면 while 문을 탈출한다.
- Line 22 : 데이터를 저장하는 버퍼(UDR0)에 데이터가 수신되면 데이터를 전송한다.
(3) UART0 데이터 수신 함수부(void UART0_receive)
- Line 28 : 데이터를 저장하는 수신 버퍼(UDR0)에 읽지 않은 데이터가 있으면 while 문을 탈출한다.
- Line 29 : 데이터를 저장하는 수신 버퍼(UDR0)에 읽지 않은 데이터가 없으면 UDR0 값을 리턴한다.
2) 동작 설명
- AVR에 빌드 및 프로그래밍 후 PuTTY를 통해 컴퓨터와 UART 통신한 결과,
받은 데이터(내가 입력한 데이터)를 UART를 통해 다시 내보내는 것을 알 수 있다.
3. UART로 문자열 송수신
3-1. UART로 문자열 출력
1) 문자열을 출력하는 함수 (UART0_print_string)
- 함수를 호출하는 부분에서 사용하는 인자, 즉 문자열을 매개변수로 받아서 NULL값이 나올때까지 한글자씩 출력하는 함수이다.
- Line 34 : for문 안에 조건식에 문자열(str[i])이 들어간다면 NULL값이 나올때까지 i를 반복한다 라고 생각하면 된다.
(평소에 정수형태로만 사용하던 for문하고는 다른형태라 햇갈리지만, 그냥 단순히 암기하면 될 것 같습니다..ㅠ🤣)
2) 1바이트(256) 크기의 정수 출력 함수 (UART0_print_1_byte_number)
- Line 38 : 매개변수로 사용된 uint8_t n 은 unsigned char의 형태를 가지는 n이란 변수라 생각하면 된다.
- Line 46 : 1바이트 크기의 정수를 1의자리, 10자리, 100의 자리로 나누기 위한 문장으로써 연산에 대한 결과값은 아스키코드 값으로 배열에 저장된다.
'0'을 더하는 이유는 각 자릿수에 해당하는 숫자를 정수가 아닌 문자로 저장하기 위해 아스키코드 값을 구하기 위함이다. 따라서 문자 1~9에 해당하는 아스키 값을 구하기 위해서는 n + '0' 즉 n + 48을 해주면 해당하는 숫자의 아스키코드 값을 구할 수 있다. ( ex. '2'의 아스키코드 값은 2 + 48 = 50, 즉 십진수로 50에 해당 된다.)
3-2. UART로 문자열 수신
3-1에서 문자열이나 정수를 UART 통신을 통해 송신을 했다면, 반대로 컴퓨터(Putty를 통해)에서 UART를 통해 ATmega128A로 문자열을 수신할 수도 있다.
1) 새로운 함수 사용 : strcmp(char a, char b)
- 헤더파일 <stirng.h>
- 매개변수로 들어온 두개의 문자열(a, b)를 비교하여 문자열이 완전히 같다면 '0'을 반환한다.
문자열이 다르다면 그 문자의 아스키 코드 값을 비교해서 음수 또는 양수를 반환한다.
2) 코드 설명
- Line 58 : UART 통신으로 통해 들어온 데이터 값(PUTTY를 통해 사용자가 입력한 값)을 data 변수에 저장합니다.
- Line 59 ~ 61 : 입력한 데이터 값에서 줄바꿈(\r)이 수행되면 문자열을 저장하는 변수에 NULL값을 저장한다.
- Line 64 ~ 65 : 줄바꿈이 입력 안되면 데이터를 계속 문자열 변수에 저장한다.
- Line 67 : 데이터 입력이 완료(줄바꿈 입력)되면 if문을 수행한다.
- Line 68 ~ 73 : 입력한 데이터 값이 "DOWN" 인지 strcmp 함수를 통해 비교하고 맞으면 카운터 변수값을 1개 감소시키고 출력한다.
- Line 74 ~ 79 : 입력한 데이터 값이 "UP" 인지 strcmp 함수를 통해 비교하고 맞으면 카운터 변수값을 1개 증가시키고 출력한다.
※ 주의사항
(1) strcmp 함수는 대소문자를 구별한다. (strcasecmp 함수는 구별하지 않는다.)
(2) PuTTY에서 엔터키가 눌러진 경우 문자열 끝에 \r을 추가하므로 문자열의 끝을 나타내는 종료 문자로 정의하여 사용함.
3-3. printf & scanf 함수 사용
3-1, 3-2를 통해 문자열의 송신과 수신 방법에 대해 배웠는데, 이는 옛날 방법이고 잘 사용하지 않는 방법이다.
더 쉽고 간단한 방법으론 printf, scanf 함수를 사용해서 문자열을 송수신 할 수 있다.
하지만, printf와 scanf 함수를 사용하면 실행파일의 크기가 커지므로 감안해서 사용하여야 한다.
아래 코드는 위의 3-2 코드와 동일하게 동작한다.
(단, 3-2 코드는 대소문자를 구별해서 사용해야 한다.)
1) 코드 설명 :
- Line 100 : printf, scanf 함수를 사용하기 위한 헤더파일(stdio.h, STanDard Input Output)
- Line 102 ~ 105 : 표준 스트림 할당 하는 함수부인데 초보자는 이해하기 어려운 개념이라 생각됩니다..ㅠ
(그냥 printf, scanf 를 사용하기 위한 사전 함수부로 단순히 암기하고 넘어가는게 편할 것 같습니다..🤣)
- Line 112 ~ 113 : 표준 입출력장치와 위에서 정의한 입출렬 객체들을 연결하기 위한 함수부..
(이부분도 동일하게 단순히 암기하고 넘어갑시다..ㅠ)
- Line 123 : 3-2에서 사용한 코드에서 Line 56 ~ 67 까지를 단 한줄로 코딩한 부분.
scanf 함수를 통해 PuTTY에서 사용자가 입력한 데이터를 buffer에 저장한다.
- Line 125 : 사용자가 입력한 문자가 "DOWN" 이면 if문을 수행한다.
(starcasecmp는 starcmp와 다르게 대문자와 소문자를 구별하지 않는다.)
댓글