USART 정의
USART(또는 UART)는 MCU가 다른 장치와 데이터를 주고받는 기본 통신 장치
USART = Universal Synchronous / Asynchronous Receiver / Transmitter > 범용 동기/비동기 송수신 장치
"시리얼(Serial)" 통신?
데이터를 “한 비트씩” 순차적으로 보내는 방식 (하나의 데이터 선, 장거리 안정)
"병렬(Parallel)" 통신?
여러 개의 데이터 선을 동시에 사용해서 여러 비트를 한 번에 전송하는 방식 (여러 물리적인 선이 필요)
동기 vs 비동기
정의
| 구분 | 의미 |
| 동기(Synchronous) | 송신측과 수신측이 같은 클럭 신호(clock) 를 공유해서 통신함 |
| 비동기(Asynchronous) | 클럭 없이, 양쪽이 약속된 속도(baud rate) 로만 타이밍을 맞춰 통신함 , 시작과 끝 비트 존재 |
주로 비동기 방식을 사용한다?
반대로 동기(Synchronous)는 왜 잘 안 쓰이냐?
| 단점 | 설명 |
| 🧵 클럭선이 추가로 필요함 | TX, RX 외에 CLK선 1개 더 필요 |
| ⚡ 거리가 짧을수록 유리 | 클럭선이 멀리 가면 신호 지연/잡음 커짐 |
| 🧩 대체 기술이 이미 존재함 | 고속 동기 통신은 대부분 SPI, I²C 같은 전용 방식으로 해결 |
| 🧮 구현 복잡도 증가 | 클럭 방향 설정, 마스터/슬레이브 설정 필요 |
비동기 방식의 통신 방법
USART DATA FRAME

ST : Start(시작비트) 항상 Low 이며 데이터 시작을 알림
0~8 (데이터 비트) : 수신측은 보레이트 (baud rate) 로 정해진 갯수를 비트를 순서대로 읽음
P : Parity(패리티 비트) : 오류 검출 기능 , 데이터의 1의 개수가 홀수 인지 짝수인지
Sp1 : Stop (정지 비트) : 항상 High 데이터 끝을 알림
#궁금점 CAN 통신은 그럼 뭐지 자동차와 산업 현장에서 많이 쓴다고 함
여러 개의 ECU(Engine Control Unit, ABS, 에어백, 계기판 등)가
하나의 두 가닥 선(CAN_H, CAN_L) 을 공유하면서 데이터를 주고받는 통신 규격
| 구분 | UART | CAN |
| 통신 형태 | 1:1 | 1:N (멀티노드) |
| 선 수 | TX, RX (2선) | CANH, CANL (2선) |
| 속도 | 수백 kbps~수 Mbps | 125 kbps~1 Mbps |
| 신뢰성 | 비교적 약함 (소프트웨어 재전송) | 매우 높음 (하드웨어 오류 복구) |
| 충돌 처리 | 없음 (1:1이므로) | 있음 → ID 우선순위로 자동 중재 |
| 주요 사용처 | PC 연결, 디버깅, 모듈 | 자동차, 산업제어, 로봇 |
| 노이즈 내성 | 약함 | 강함 (차동 신호) |
PC와 Atmega328p가 통신하기 위해서는 UART 신호로 변환을 해줘야 함 (Atmega328p 는 UART 기능만 존재)
ATMega16U2 칩이 이를 수행

UART 통신 실습
복습

1. MCU 안에는 “기능 블록(Peripherals)”이 많다
예시
| 종류 기능 | 기능 |
| GPIO | 디지털 입출력 (PORTA/B/C 등) |
| Timer/Counter | 시간 측정, PWM 생성 |
| USART (UART) | 직렬 통신 |
| SPI / I2C | 다른 칩과 통신 |
| ADC | 아날로그 → 디지털 변환 |
| EEPROM Controller | 비휘발성 메모리 제어 |
| Watchdog Timer | 시스템 오류 감시 및 리셋 |
2. 이 장치들은 전부 레지스터(Register) 로 제어된다
모든 장치를 제어할 수 있는 스위치나 버튼이 “레지스터”로 존재해.
즉, CPU가 하드웨어에 직접 “명령”을 내리는 수단이 레지스터 조작이다.
결론 : MCU 제어 = 레지스터를 읽고/쓰기
그 레지스터의 3종류가
| 구분 | 역할 | 예시 |
| Control Register | 기능 설정 | 송신/수신 활성화, 비트 길이, 패리티 등 |
| Status Register | 상태 확인 | 전송 완료 플래그, 버퍼 비었는지 등 |
| Data Register | 실제 데이터 읽기/쓰기 | 송신할 데이터(UDR), 수신된 데이터 |
USART뿐 아니라 Timer, ADC, SPI 등 전부 이런 식으로 구성
그래서 UART 통신을 위한 레지스터를 분석해보자

Control : USCRnA , USCRnB , USCRnC (통신 방식을 설정) , UBRRN[H:L] (통신 길이를 설정)
Data : UDRn
Status : USCRnA 안에 수신 완료, 수신 가능 비트가 포함되어 있다
이것들을 차근차근 설정해보자
Step1 : 송신기와 수신기를 킨다. USCRnB에 존재

RXEN0 , TXEN0 => 1
USCRnA 는 주로 Status 로 상태 역할을 주로 한다

RXCn : 수신을 했는지 안했는지
UDREn : 통신이 가능한지 안한지
Step 2 : 통신 형식 결정 > 데이터 크기, 통신모드 , 패리티 ,Stop 비트


클럭이 없는 비동기방식으로 할 것 : 0 0
패리티 설정 x : 0
정지비트 1비트 : 0

ASCII 문자 보내기 위한 8비트 : 0 1 1
UCPOLn : Rising 이냐 Falling (0에서 1일때 샘플링 하냐 1에서 0일때 샘플링하냐) 기본값
Step 3 : UBRRnL 설정을 통해 Baud Rate(통신 속도) 결정




fosc : 시스템 오실레이터 클록 주파수 , MCU가 1초에 몇 번 명령을 처리할 수 있는지 아두이노 Uno R3 기본값 16MHz
Baud Rate : 자주 사용되는 9600bps
따라서 설정은 UBRRn UBRRN
U2Xn 은 뭔가요 2x 배속 모드?
따라서 설정 방법은
➡ UBRR0는 12비트짜리 레지스터
하지만 AVR에서는 한 번에 8비트만 접근 가능하니까,
이걸 상위 4비트(UBRR0H) + 하위 8비트(UBRR0L) 로 나누어 관리한다는 뜻이야.
즉:
예를 들어 UBRR0 = 103 (0x0067) 라면:
- UBRR0H = 0x00
- UBRR0L = 0x67
UBRR0H = (103 >> 8); // 0x00
UBRR0L = (uint8_t)103; // 0x67
UART 설정 완료
실제 데이터가 어떻게 어디서 주고 받는지
Data register

USART 의 송수신 데이터 버퍼 기능을 수행하는 레지스터
송신 TXB 에 데이터 저장 ()
수신 : RXB에 데이터 저장 (쓰는 즉시 전송 됨)
Code
#include <avr/io.h>
#include <stdint.h> // uint8_t, uint16_t 등을 사용하기 위함
// ATmega328P의 클럭 주파수를 16MHz로 가정
#define F_CPU 16000000UL
// 통신 속도 정의
#define BAUD 9600
// UBRR 값 계산 : 103 으로 계산될 예정
#define UBRR_VALUE ((F_CPU / (16UL * BAUD)) - 1)
void USART_Init(void);
void USART_Transmit(uint8_t data);
uint8_t USART_Receive(void);
int main(void){
USART_Init();
while(1){
char received = USART_Receive();
USART_Transmit(received);
}
}
/**
* @brief USART 통신 초기화 함수 (9600 bps)
* * - 통신 속도 설정: 9600 bps
* - 송신(TX) 및 수신(RX) 활성화
* - 프레임 형식: 비동기(Asynchronous), 패리티 없음(No Parity), 8비트 데이터, 1 스톱 비트
*/
void USART_Init(void) {
// 1. UBRR 레지스터에 통신 속도 값 설정
// UBRR_VALUE는 16비트 값이므로, 상위 8비트는 UBRRH에, 하위 8비트는 UBRRL에 설정
UBRR0H = (uint8_t)(UBRR_VALUE >> 8); //UBRR0H = 0;
UBRR0L = (uint8_t)UBRR_VALUE; //UBRR0L = 103;
// 2. UCSR0B 레지스터 설정
// RXEN0 (수신 활성화) 및 TXEN0 (송신 활성화) 비트 설정
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
// 3. UCSR0C 레지스터 설정 (프레임 형식)
// - UMSEL01/00: 00 (비동기 USART)
// - UPM01/00: 00 (패리티 없음)
// - USBS0: 0 (1 스톱 비트)
// - UCSZ01/00: 11 (8비트 데이터) -> UCSR0C의 UCSZ01/00과 UCSR0B의 UCSZ02를 조합
UCSR0C = (0 << UMSEL00) | (0 << UPM00) | (0 << USBS0) | (1 << UCSZ01) | (1 << UCSZ00);
// 참고: 8비트 데이터는 UCSZ01(UCSR0C), UCSZ00(UCSR0C)을 1로, UCSZ02(UCSR0B)를 0으로 설정하여 구현됨
}
/**
* @brief 1 바이트 데이터를 USART로 송신하는 함수
* * @param data 송신할 8비트 데이터
*/
void USART_Transmit(uint8_t data) {
// UDRE0 (송신 데이터 레지스터 Empty) 플래그가 설정될 때까지 기다림
// UDRE0 = 0 아직 전송 중 → 기다려야 함
// UDRE0 = 1 버퍼 비었음 → 새 데이터 넣어도 됨
// 설정 = 송신 버퍼가 비어있고, 다음 데이터를 받을 준비가 됨
while (!(UCSR0A & (1 << UDRE0))) {
// 대기
}
// 송신 버퍼에 데이터 로드
//“UDR0 송신 버퍼에 data를 써라”
// (그러면 USART가 알아서 Shift Register에 옮겨서 TX 핀으로 내보냄)
UDR0 = data;
}
/**
* @brief 1 바이트 데이터를 USART로부터 수신하는 함수
* * @return 수신된 8비트 데이터
*/
uint8_t USART_Receive(void) {
// RXC0 (수신 완료) 플래그가 설정될 때까지 기다림
// 설정 = 수신 버퍼에 읽을 수 있는 데이터가 있음
//RXC0 플래그 = 1 이 될 때까지 대기
while (!(UCSR0A & (1 << RXC0))) {
// 대기
}
// 수신 버퍼에서 데이터 반환
return UDR0;
}
송신한 문자열 데이터를 echo하는 코드

'Atmega328p' 카테고리의 다른 글
| {Atmega328p}-Timer , Counter (0) | 2025.10.29 |
|---|---|
| {Atmega328p}-ADC (0) | 2025.10.26 |
| {Atmega328p}-ISR (0) | 2025.10.26 |
| {Atmega328p}-페리페럴 제어 GPIO , 레지스터 구조체 (0) | 2025.10.22 |
| {임베디드}-임베디드 시스템 (0) | 2025.10.21 |