반응형
📦 DMA, IDLE, 링버퍼를 사용한 실전 예제
📌 HAL_UART_Receive_DMA + IDLE 인터럽트 조합 링버퍼 방식
✨ DMA + IDLE + 링버퍼 완성 예제
#include "main.h"
// 수신 버퍼 (DMA용)
#define UART_RX_BUFFER_SIZE 256
uint8_t uart_rx_dma_buffer[UART_RX_BUFFER_SIZE];
// 링버퍼 (수신 데이터 임시 저장용)
#define UART_RING_BUFFER_SIZE 1024
uint8_t uart_ring_buffer[UART_RING_BUFFER_SIZE];
volatile uint16_t ring_head = 0;
volatile uint16_t ring_tail = 0;
extern UART_HandleTypeDef huart3;
// 링버퍼에 한 바이트 추가
void ring_buffer_push(uint8_t data) {
uint16_t next = (ring_head + 1) % UART_RING_BUFFER_SIZE;
if (next != ring_tail) { // 오버플로우 방지
uart_ring_buffer[ring_head] = data;
ring_head = next;
}
}
// 링버퍼에서 한 바이트 꺼내기
int ring_buffer_pop(uint8_t *data) {
if (ring_head == ring_tail) {
return 0; // 비어있음
}
*data = uart_ring_buffer[ring_tail];
ring_tail = (ring_tail + 1) % UART_RING_BUFFER_SIZE;
return 1;
}
// DMA 수신 초기화
void uart_dma_start() {
HAL_UART_Receive_DMA(&huart3, uart_rx_dma_buffer, UART_RX_BUFFER_SIZE);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE); // IDLE 인터럽트 활성화
}
// UART 인터럽트 핸들러
void USART3_IRQHandler(void) {
HAL_UART_IRQHandler(&huart3);
if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
uint16_t remaining = __HAL_DMA_GET_COUNTER(huart3.hdmarx);
uint16_t received = UART_RX_BUFFER_SIZE - remaining;
for (uint16_t i = 0; i < received; i++) {
ring_buffer_push(uart_rx_dma_buffer[i]);
}
HAL_UART_Receive_DMA(&huart3, uart_rx_dma_buffer, UART_RX_BUFFER_SIZE); // DMA 재시작
}
}
// 메인 루프 예시
void loop() {
uint8_t c;
while (ring_buffer_pop(&c)) {
// 수신한 데이터 처리
process_uart_byte(c);
}
}
📋 이번 코드 특징
항목 | 설명 |
DMA 버퍼 | 256바이트 버퍼 |
링버퍼 (소프트웨어) | 1024바이트, CPU가 직접 읽기 |
수신 이벤트 트리거 | IDLE 인터럽트 발생 시 |
데이터 처리 흐름 | DMA → IDLE → 복사 → 링버퍼에 저장 → 메인루프에서 읽기 |
장점 | 대량 수신에도 안전, 메인루프가 느려도 데이터 손실 없음 |
📚 개념 정리 예제 vs 실전 예제 차이점
구분 | 개념 정리 예제 | 실전 예제 |
수신 데이터 처리 | uart_process_data() 라는 별도 함수에서 처리 |
직접 링버퍼에 밀어넣기 (직결) |
데이터 누락 대응 | 안 언급했음 | 링버퍼 크기 체크해서 오버플로 방지 |
메인 루프 연계 | 따로 설명 없었음 | loop() 에서 pop해서 처리하는 구조 추가 |
수신 후 처리 방법 | 간단 처리 위주 | 실제 운영 가능한 안정화 구조 |
링버퍼 구현 | 없음 (언급만) | 직접 코드로 작성 |
🧠 정리
- DMA는 버퍼를 채워줌
- IDLE 인터럽트는 "이제 읽어라"를 알려줌
- 링버퍼는 "데이터가 와도 천천히 처리할 수 있게" 버퍼링 함
1. 이 조합이 실제 STM32 UART 대량 수신에서의 거의 표준 공식처럼 쓰임
2. HAL 라이브러리도 이 구조를 기본으로 추천
반응형
'임베디드 관련 > STM32' 카테고리의 다른 글
STM32 용어/약어 정리 (0) | 2025.05.08 |
---|---|
[STM32] 링버퍼 코드 연동 UART 수신 처리 시스템 (0) | 2025.05.03 |
[STM32] DMA + 인터럽트 + 링버퍼 수신 방식의 개념 (0) | 2025.05.01 |
[STM32] DMA vs 인터럽트 (0) | 2025.04.30 |
[STM32] DMA(Direct Memory Access) (0) | 2025.04.29 |
댓글