APM32에서 UART와 DMA를 활용한 핑퐁 버퍼링을 통한 대량 데이터 수신

UART-DMA 통합 데이터 수신 개요

UART는 임베디드 시스템에서 널리 사용되는 통신 인터페이스로, 대량 데이터 수신 시 전통적인 바이트 단위 인터럽트 방식은 CPU 부하 증가와 데이터 유실 문제를 초래합니다. APM32E103 시리즈(USART1 기준)의 DMA 컨트롤러를 활용하면 핑퐁 버퍼링 기법으로 이러한 문제를 해결할 수 있습니다.

핑퐁 버퍼링 구조 설계

이중 버퍼를 교대로 전환하는 핑퐁 방식은 데이터 수신과 처리의 병렬 실행을 가능하게 합니다. 버퍼 관리 구조체는 다음과 같이 정의됩니다:

#define BUFFER_CAPACITY 128

typedef struct {
  uint8_t dataBuffer[2][BUFFER_CAPACITY];
  uint32_t dataSize[2];
  uint8_t activeWriteIndex;
  uint8_t activeReadIndex;
} DoubleBufferManager;

DoubleBufferManager bufMgr;

버퍼 관리자 초기화 함수:

void bufferManagerInit(void) {
  bufMgr.activeWriteIndex = 0;
  bufMgr.activeReadIndex = 0;
  bufMgr.dataSize[0] = 0;
  bufMgr.dataSize[1] = 0;
}

버퍼 전환 로직 구현

데이터 수신 완료 시 버퍼 전환 함수:

uint8_t* switchBuffer(uint32_t receivedSize) {
  bufMgr.dataSize[bufMgr.activeWriteIndex] = receivedSize;
  bufMgr.activeReadIndex = bufMgr.activeWriteIndex;
  bufMgr.activeWriteIndex ^= 1;
  return bufMgr.dataBuffer[bufMgr.activeWriteIndex];
}

데이터 처리용 버퍼 획득 함수:

uint8_t* getProcessBuffer(uint32_t *outSize) {
  *outSize = bufMgr.dataSize[bufMgr.activeReadIndex];
  if(*outSize == 0) return NULL;
  
  uint8_t *target = bufMgr.dataBuffer[bufMgr.activeReadIndex];
  bufMgr.dataSize[bufMgr.activeReadIndex] = 0;
  return target;
}

UART-DMA 하드웨어 설정

USART1 및 DMA1 채널5 초기화:

void configureUartDma(void) {
  // GPIO 및 USART1 설정 생략 (표준 구성)
  
  DMA_Config_T dmaConfig;
  dmaConfig.peripheralBaseAddr = (uint32_t)(USART1_BASE + 0x04);
  dmaConfig.memoryBaseAddr    = (uint32_t)bufMgr.dataBuffer[0];
  dmaConfig.bufferSize        = BUFFER_CAPACITY;
  dmaConfig.peripheralInc     = DMA_PERIPHERAL_INC_DISABLE;
  dmaConfig.memoryInc         = DMA_MEMORY_INC_ENABLE;
  dmaConfig.loopMode          = DMA_MODE_NORMAL;
  dmaConfig.dir               = DMA_DIR_PERIPHERAL_SRC;
  DMA_Config(DMA1_Channel5, &dmaConfig);
  
  USART_EnableDMA(USART1, USART_DMA_RX);
  DMA_Enable(DMA1_Channel5);
  DMA_EnableInterrupt(DMA1_Channel5, DMA_INT_TC);
  USART_EnableInterrupt(USART1, USART_INT_IDLE);
}

인터럽트 핸들러 구현

UART 유휴 상태 인터럽트 처리:

void USART1_IRQHandler(void) {
  if(USART_ReadStatusFlag(USART1, USART_FLAG_IDLE)) {
    USART_ClearIntFlag(USART1, USART_INT_IDLE);
    USART_RxData(USART1);  // 플래그 클리어
    
    DMA_Disable(DMA1_Channel5);
    uint16_t bytesReceived = BUFFER_CAPACITY - DMA1_Channel5->CHNDATA;
    uint8_t* nextBuf = switchBuffer(bytesReceived);
    
    DMA1_Channel5->CHMADDR = (uint32_t)nextBuf;
    DMA1_Channel5->CHNDATA = BUFFER_CAPACITY;
    DMA_Enable(DMA1_Channel5);
  }
}

DMA 전송 완료 인터럽트 처리:

void DMA1_Channel5_IRQHandler(void) {
  if(DMA_ReadIntFlag(DMA1_INT_FLAG_TC5)) {
    DMA_ClearIntFlag(DMA1_INT_FLAG_TC5);
    
    DMA_Disable(DMA1_Channel5);
    uint16_t bytesReceived = BUFFER_CAPACITY - DMA1_Channel5->CHNDATA;
    uint8_t* nextBuf = switchBuffer(bytesReceived);
    
    DMA1_Channel5->CHMADDR = (uint32_t)nextBuf;
    DMA1_Channel5->CHNDATA = BUFFER_CAPACITY;
    DMA_Enable(DMA1_Channel5);
  }
}

데이터 처리 및 검증

메인 루프에서 데이터 처리:

void processReceivedData(void) {
  static uint32_t totalBytes = 0;
  uint32_t currentSize;
  uint8_t* data = getProcessBuffer(¤tSize);
  
  if(data) {
    totalBytes += currentSize;
    // 데이터 처리 로직 구현
  }
}

64KB 파일 전송 테스트 시 정확한 데이터 수신 검증 가능

태그: APM32 UART DMA 핑퐁버퍼링 임베디드시스템

7월 5일 21:29에 게시됨