UART is indeed a pretty typical case because many applications require that some processing is done in response to command/date received via the serial port. If the application is architectured around an infinite processing loop, as it is often the case, one good way is to use DMA to transfer received bytes into a small buffer and process this buffer at each loop iteration. The following example code illustrates this:
#define BUFFER_SIZE 1000
uint8_t inputBuffer[BUFFER_SIZE];
uint16_t inputBufferPosition = 0;
// setup DMA reception USART2 RX => DMA1, Stream 6, Channel 4
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
DMA_InitTypeDef dmaInit;
DMA_StructInit(&dmaInit);
dmaInit.DMA_Channel = DMA_Channel_4;
dmaInit.DMA_PeripheralBaseAddr = ((uint32_t) USART2 + 0x04);
dmaInit.DMA_Memory0BaseAddr = (uint32_t) inputBuffer;
dmaInit.DMA_DIR = DMA_DIR_PeripheralToMemory;
dmaInit.DMA_BufferSize = BUFFER_SIZE;
dmaInit.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dmaInit.DMA_MemoryInc = DMA_MemoryInc_Enable;
dmaInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
dmaInit.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
dmaInit.DMA_Mode = DMA_Mode_Circular;
dmaInit.DMA_Priority = DMA_Priority_Medium;
dmaInit.DMA_FIFOMode = DMA_FIFOMode_Disable;
dmaInit.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dmaInit.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream5, &dmaInit);
USART_DMACmd(port, USART_DMAReq_Rx, ENABLE);
// loop infinitely
while(true)
{
// read out from the DMA buffer
uint16_t dataCounter = DMA_GetCurrDataCounter(DMA1_Stream5);
uint16_t bufferPos = BUFFER_SIZE - dataCounter;
// if we wrapped, we consume everything to the end of the buffer
if (bufferPos < inputBufferPosition)
{
while (inputBufferPosition < BUFFER_SIZE)
processByte(inputBuffer[inputBufferPosition++]);
inputBufferPosition = 0;
}
// consume the beginning of the buffer
while (inputBufferPosition < bufferPos)
processByte(inputBuffer[inputBufferPosition++]);
// do other things...
}
What this code does it to first setup a DMA channel to read from USART2. The correct DMA controller, stream and channel is dependant on which USART you use (check the STM32 reference manual to figure out which combination is needed for a given USART port). Then the code enters the main infinite loop. At each loop, the code checks whether something has been written (through DMA) in inputBuffer
. If so, this data is processed by processByte
, which you should implement in a way that is similar to your original IRQ handler.
What's nice in this setup is that there is no interrupt code -- everything runs synchronously. Thanks to DMA, received data just "magically" appears in inputBuffer
. The size of inputBuffer
should be carefully determined though. It should be large enough to contain all the data you can possibly receive during a loop iteration. For example, with a baud rate of 115200 (about 11KB/s) and a maximum loop time of 50 ms, the buffer size should be at least 11KB/s * 50 ms = 550 bytes.