UART

Table of contents

핵심 요약

UART는 별도 클록선 없이 약속한 baudrate·data bits·parity·stop bits로 데이터를 주고받는 비동기 직렬 통신이다. 프레임 경계를 애플리케이션이 정의해야 하며, UART 전기 레벨과 RS-232/RS-485 물리 계층을 구분한다.

S32K312 LPUART 설정 순서

  • 회로도와 패키지에서 사용할 LPUART 인스턴스·TX/RX pin mux·입력 경로를 확인한다. TX↔RX와 공통 GND를 연결하고 USB-UART 전압을 맞춘다. RS-232 전압 신호를 MCU 핀에 직접 연결하지 않는다.
  • 실제 LPUART functional clock을 확인한다. 설치 RTD의 Lpuart_Uart IP 또는 해당 프로젝트 UART 드라이버 예제를 기준으로 초기화한다. S32K1 SDK API를 이름만 바꾸어 사용하지 않는다.
  • 초기 값은 상대 장치와 합의한 115200, 8 data bits, no parity, 1 stop bit(8N1) 등으로 맞춘다. hardware flow control 지원·배선이 없으면 RTS/CTS를 임의 활성화하지 않는다.
  • 처음에는 짧은 polling TX와 고정 길이 RX를 검증한다. 이후 IRQ·DMA·ring buffer·parser를 추가한다. timeout 단위와 nonblocking API의 완료 조건을 확인한다.

baudrate 계산과 오차

UART 8N1, 115200 baud, 0x55를 LSB first로 송신한 설명용 파형이다. 한 비트는 약 8.68 μs, start부터 stop까지 10비트는 약 86.8 μs다. 실제 측정 파형이 아니다.
UART 8N1, 115200 baud, 0x55를 LSB first로 송신한 설명용 파형이다. 한 비트는 약 8.68 μs, start부터 stop까지 10비트는 약 86.8 μs다. 실제 측정 파형이 아니다.

💡

그림 읽기와 실측 확인: Logic-level TX/RX를 측정하고 start edge부터 각 비트 중앙의 값과 stop High를 확인한다. RS-232의 전압 극성과 혼동하지 않는다.

일반적인 LPUART 계산 모델:
baud_actual = f_functional / (OSR_actual × SBR)
OSR_actual = OSR_field + 1인 구현은 필드값과 실제값을 구분
오차[%] = 100 × (baud_actual - baud_target) / baud_target

가정: f_functional = 80 MHz, OSR_actual=16, SBR=43
baud_actual ≈ 116279.07 bit/s
115200 대비 오차 ≈ +0.937%

이 후보가 최적 또는 해당 보드에서 검증된 설정이라는 뜻은 아니다.
다른 OSR/SBR 조합과 해당 IP의 허용 범위를 함께 탐색한다.

낮은 OSR에서 추가 sampling 옵션이 필요한지 매뉴얼을 확인한다. 허용 오차는 송수신 양쪽의 발진기 오차·프레임 길이·sampling 방식·온도 조건을 합쳐 평가한다. 통신 한 번 성공했다고 전 범위에서 안정적이라고 판단하지 않는다. 8N1은 payload 1바이트당 일반적으로 10비트 시간을 사용하므로 115200에서 이론상 약 11520 bytes/s다.

수신 buffer와 메시지 framing

UART는 byte stream이다. RX callback 한 번이 애플리케이션 메시지 하나와 일치한다고 가정하지 않는다. ISR/DMA 완료에서 bytes를 ring buffer에 넣고 task에서 parser를 실행한다. buffer full 정책과 overrun 카운터를 정의한다. producer/consumer 공유 인덱스에는 플랫폼에 맞는 원자성·동기화가 필요하다.

#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define LINE_MAX 64U
typedef struct { uint8_t data[LINE_MAX]; size_t used; bool dropping; } Parser;
/* callback는 동기 호출된다. data 포인터를 저장하지 말고 필요하면 복사한다. */
typedef void (*LineFn)(const uint8_t *, size_t, void *);
static void feed(Parser *p, const uint8_t *bytes, size_t size, LineFn fn, void *ctx) {
 for (size_t i=0; i<size; ++i) {
  uint8_t v=bytes[i];
  if (v == '\n') {
   if (!p->dropping) {
    size_t n=p->used;
    if (n && p->data[n-1] == '\r') --n;
    fn(p->data,n,ctx); /* 데이터는 null 종료 문자열이 아니다. */
   }
   p->used=0; p->dropping=false;
  } else if (!p->dropping) {
   if (p->used == LINE_MAX) { p->used=0; p->dropping=true; }
   else p->data[p->used++]=v;
  }
 }
}
static void check(const uint8_t *data, size_t n, void *ctx) {
 unsigned *count=ctx;
 const char *expected=(*count == 0) ? "PING" : "OK";
 assert(n == strlen(expected) && memcmp(data,expected,n)==0);
 ++*count;
}
int main(void) {
 Parser p={0}; unsigned count=0;
 feed(&p,(const uint8_t *)"PI",2,check,&count); assert(count==0);
 feed(&p,(const uint8_t *)"NG\r\nOK\n",7,check,&count); assert(count==2);
 uint8_t overflow[LINE_MAX+1]; memset(overflow,'X',sizeof overflow);
 feed(&p,overflow,sizeof overflow,check,&count);
 feed(&p,(const uint8_t *)"\nOK\n",4,check,&count); assert(count==3);
 return 0;
}

바이너리 프로토콜 설계

바이너리 데이터는 SOF·길이·명령·sequence·payload·CRC 등으로 계약을 정의한다. 길이 상한·partial frame timeout·CRC 실패 후 재동기화를 구현한다. SOF가 payload에 들어갈 수 있다면 escape 또는 COBS 같은 framing을 선택한다. UART parity는 프레임 CRC나 애플리케이션 ACK를 대체하지 않는다.

송신 완료와 RS-485

TX buffer 수명이 완료까지 유지되어야 한다. DMA 완료나 TX FIFO empty와 마지막 stop bit 전송 완료는 다를 수 있다. RS-485의 DE를 내릴 때는 실제 transmission complete 조건을 확인한다. 반이중 방향 전환·응답 대기·버스 bias·종단은 사용 트랜시버와 네트워크 설계에 따른다.

오류 처리와 검증

  • 깨진 문자: baudrate·data bits·parity·stop bits·전압·GND를 확인한다. 보이는 문자가 없으면 TX 파형과 RX input mux를 따로 점검한다.
  • 연속 수신에서 누락: overrun·ISR 지연·ring buffer 크기·DMA 재설정 공백을 확인한다. callback에서 printf나 blocking 송신을 하지 않는다.
  • framing/parity/noise/overrun flag를 구분해 기록하고 설치 드라이버의 flag clear·재수신 절차를 따른다.
  • 내부/외부 loopback → PC terminal → 분할·연속 메시지 → 최대 길이·overflow·timeout·잘못된 CRC → 장시간 부하 순으로 검증한다. terminal의 local echo와 개행 자동 변환은 끄거나 명확히 설정한다.