본문 바로가기
Atmega328p

{Atmega328p}-UART 통신

by syyy1 2025. 10. 24.

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

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 통신 실습

 

 

복습

MCU.장치를 이해하기 위한 레지스터 3요소

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 통신을 위한 레지스터를 분석해보자

USART(UART) 하드웨어 블록

Control : USCRnA , USCRnB , USCRnC (통신 방식을 설정) , UBRRN[H:L] (통신 길이를 설정)

Data : UDRn

Status : USCRnA 안에 수신 완료, 수신 가능 비트가 포함되어 있다

이것들을 차근차근 설정해보자

 

Step1 : 송신기와 수신기를 킨다. USCRnB에 존재

Atmega328p datasheet

RXEN0 , TXEN0 => 1

 

USCRnA 는 주로 Status 로 상태 역할을 주로 한다

Atmega328p datasheet

RXCn : 수신을 했는지 안했는지

UDREn : 통신이 가능한지 안한지

 

 

Step 2 : 통신 형식 결정 >  데이터 크기, 통신모드 , 패리티 ,Stop 비트

Atmega328p datasheet

클럭이 없는 비동기방식으로 할 것 : 0 0

패리티 설정 x : 0

정지비트 1비트 : 0

ASCII 문자 보내기 위한 8비트 : 0 1 1

UCPOLn : Rising 이냐 Falling (0에서 1일때 샘플링 하냐 1에서 0일때 샘플링하냐) 기본값

 

 

Step 3 : UBRRnL 설정을 통해 Baud Rate(통신 속도) 결정

UBRR0L
UBRR0H
Date Sheet 16MHz

 

공식은 참고

fosc : 시스템 오실레이터 클록 주파수 , MCU가 1초에 몇 번 명령을 처리할 수 있는지 아두이노 Uno R3 기본값 16MHz

Baud Rate : 자주 사용되는 9600bps

따라서 설정은 UBRRn UBRRN

 

U2Xn 은 뭔가요 2x 배속 모드?

 

따라서 설정 방법은

UBRR012비트짜리 레지스터
하지만 AVR에서는 한 번에 8비트만 접근 가능하니까,
이걸 상위 4비트(UBRR0H) + 하위 8비트(UBRR0L) 로 나누어 관리한다는 뜻이야.

즉:

 
UBRR0 = [UBRR0H(상위 4비트)] + [UBRR0L(하위 8비트)]

예를 들어 UBRR0 = 103 (0x0067) 라면:

  • UBRR0H = 0x00
  • UBRR0L = 0x67
UBRR0H = (103 >> 8);   // 0x00
UBRR0L = (uint8_t)103; // 0x67

 

 

UART 설정 완료

실제 데이터가 어떻게 어디서 주고 받는지

Data register

UDRn 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 UART 통신

 

'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