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 파일 전송 테스트 시 정확한 데이터 수신 검증 가능