1

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.

3 Answers3

1

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.

  • Thanks for this valuable information. So now i conclude that the RXNE flag is set only when a byte is received and reading the RDR register clears the flag. Now i have changed the logic to this :- uint8_t getChar (void) { if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) { // Check RXNE to see if there is data return ((USART1->RDR) & (0xFF)); } else { return 0xFF; } } -- This is my function getchar() and if i run this continuously in while(1), on returning the value, it should clear the flag RXNE right? – Gaurav Sharma Mar 30 '18 at 07:42
  • Then i think i have to store the byte before returning the value? – Gaurav Sharma Mar 30 '18 at 07:43
  • Either store the value in the function and return an error/success code, or use an integer return type in the function, and return a negative value when nothing is received. The latter would be analogous to the `getchar()` library function. – followed Monica to Codidact Mar 30 '18 at 09:28
  • According to what you had explained, i intend to use polling method in order to receive 8 bytes, this is my logic which does not seem to work:- char rxdata[2]; for (k=0; k<8; k++){ //checking the flag regularly till 8 bytes of data is obtained if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) { // Check RXNE to see if there is data rxdata[0]=((USART1->RDR)&(0x00FF)); // putting the RDR value in the rxdata[0], reading exactly once } else{ //if the flag is not set UartTransmitPacket("flag reset", sizeof("flag reset")); //printing that the flag is reset } } – Gaurav Sharma Mar 31 '18 at 07:50
  • I have tried printing the the value of rxdata[0] after every iteration. still not getting it. – Gaurav Sharma Mar 31 '18 at 08:11
  • Your code checks the flag only 8 times. It should check the flag continuously until it is set. – followed Monica to Codidact Mar 31 '18 at 08:25
  • for (k=0; k<8; k++){ data = getChar(); if (data!=0xFF) { //putChar(data); //if (data == 0x04){ //GPIO_SetBits(GPIOA, GPIO_Pin_5); if (data == 0x01){ GPIO_SetBits(GPIOA, GPIO_Pin_5); } if (data == 0x08){ GPIO_ResetBits(GPIOA, GPIO_Pin_5); } rxdata[k]=data; } } I am getting a blink so i know now that i am getting the whole packet. But now i see that the delay that i am applying in the while condition is affecting this getchar function. – Gaurav Sharma Mar 31 '18 at 08:56
  • I will try to strengthen the integrity of the packet by checking if the first byte is 0x01 or not. In Polling i will have to take care of the delays i guess. i will also try the interrupt method which can help me in improving the output. – Gaurav Sharma Mar 31 '18 at 08:57
0

Here:

for(j=0; j<8; j++) { 
  rxd[i] = (0xFF & (USART1->RDR));
}

you use j as a loop counter but you write to rxd under index i, not j. You overwrite the same byte 8 times.

Another thing is that you wait for the USART_FLAG_RXNE flag to be set, then you read from the RDR register 8 times. This flag is set when the first byte is received, then you read it 8 times - you very likely read much faster than the speed at which you send the data. If you want to do it by polling - which seems to be your intention - you should wait for the USART_FLAG_RXNE flag being set after reading each byte separately, as USART peripheral in this MCU has no FIFO and can only hold a single received byte for you to read (mentioned RDR register).

J_S
  • 2,985
  • 3
  • 15
  • 38
0

Your logic is completely wrong.

for(j=0; j<8; j++) 
{
    while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != SET); // wait for the data
    rxd[i] = (0xFF & (USART1->RDR));
}
0___________
  • 60,014
  • 4
  • 34
  • 74