본문 바로가기
임베디드 관련/STM32

[STM32] DMA + IDLE + 링버퍼 실전

by minhyeok.lee 2025. 5. 2.
반응형

📦 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 라이브러리도 이 구조를 기본으로 추천

반응형

댓글