Search code examples
armstm32uartcmsisstm32f0

STM32f091rc UART Receive function returning only the last byte of the packet instead of the full data packet


I have been working on STM32f091rc board, trying to get UART1 and UART2 work. I tried sending 8 bytes of packet from a controller to the STM board. Due to some reasons, my function is just displaying the last byte of the packet. My Receiving function is given below:-

uint8_t rxd[10];
void getChar (void) {

while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) { // Check RXNE to 
//see if there is data
    for(j=0; j<8; j++) { 
        rxd[i] = (0xFF & (USART1->RDR));
    }

What am i doing wrong? Can anyone please point me in the right direction? Thanks for your time.


Solution

  • The UART->RDR register has no buffers, it holds only the last fully received byte. If another byte is received, it will be overwritten.

    You should ensure that the value in RDR is read out every time after a byte arrives, and before the next one is received. There are 3 basic ways to do it.

    • Polling

    Check the RXNE flag regularly, and read RDR exactly once when it's set. Repeat until you have the whole data packet. Reading a byte from RDR clears the RXNE flag, wait until it's set again before you read the next byte.

    • Interrupt

    Set the RXNEIE bit in CR1 and enable the interrupt corresponding to the UART in NVIC. The interrupt handler will be called every time a byte is received. The handler can be very simple at first, just reading RDR and storing it in a buffer. Later you can add error checking and recovery. Don't forget to declare every variable the interrupt handler touches as volatile.

    • DMA

    Set up the DMA channel first (USART1 is mapped to DMA1_Channel3 by default, can be remapped, check the reference manual for others):

    DMA1_Channel3->CPAR = (uint32_t)&USART1->RDR;
    DMA1_Channel3->CMAR = (uint32_t)rxd;            // start of receive array
    DMA1_Channel3->CNDTR = 8;                       // 8 bytes to receive
    DMA1_Channel3->CCR = DMA_CCR_MINC | DMA_CCR_EN; // Memory increment, enable
    

    then set up the serial port, and enable DMA receive in USART1->CR3. The end of transfer is signaled in the DMA1->ISR register, you can check it regularly in the main program, or enable an interrupt in DMA1_Channel3->CCR (and in NVIC). You should clear the interrupt flag through DMA1->IFCR, otherwise you'll get endless interrupts when enabled. To start another transfer, set the CNDTR register again. Declare all variables touched by DMA or the interrupt handler as volatile.