In UART communication, TX process and RX process are usually handled differently. When transmitting data, you first construct a full packet of data. Then you send it rather relaxed manner. You can let the DMA handle the transfer, or you can use blocking call outside of any time critical function. Most of SDK provide such functions by default.
When receiving data however, you do not know the size or the validity of the packet beforehand. So you have to check each byte and verify it against the protocol as it arrives. Thus it is best to handle the RX task in an UART interrupt callback function instead of using functions provided by SDK.
In STM32Cube framework, you first activate UART RX interrupt using LL.
412 /** Initialize SerialComm 413 */ 414 void SerialComm_Init() 415 { 416 LL_USART_EnableIT_RXNE(huart1.Instance); 417 }
Then write an interrupt handler for UART RX interrupt. In STM32Cube convention, a skeleton code for this interrupt handler is populated in stm32xxxx_it.c file.
126 void USART1_IRQHandler(void) 127 { 128 if(LL_USART_IsActiveFlag_RXNE(USART1) && LL_USART_IsEnabledIT_RXNE(USART1)) 129 { 130 SerialComm_RxRoutine(); 131 } 132 }
In acutual RX routine, each incoming byte is checked by the packet decoder, SerialComm_Decoder and loaded into one of the two pingpong buffers whenever a valid packet is received.
419 void SerialComm_RxRoutine() 420 { 421 pkt_status status; 422 423 status = SerialComm_Decoder(LL_USART_ReceiveData8(huart1.Instance), 424 UartIsrBuf); 425 426 if(status == PKT_RECEIVED) 427 { 428 // switch ping pong buffer 429 if(UartIsrBuf == UartRxBuf1) 430 { 431 UartIsrBuf = UartRxBuf2; 432 UartRxBuf = UartRxBuf1; 433 } 434 else 435 { 436 UartIsrBuf = UartRxBuf1; 437 UartRxBuf = UartRxBuf2; 438 } 439 // raise flag 440 bPktReceived = true; 441 } 442 }
Then you need another task that checks the contents of this pingpong buffer and generates events when new packet is loaded.
386 void UartRxTask() 387 { 388 int i; 389 uint8_t event[EVT_QWIDTH]; 390 391 // packet received 392 if(bPktReceived) 393 { 394 // event id 395 event[0] = EVT_UART_RXPKT; 396 // event data size 397 event[1] = UartRxBuf[1]; 398 399 // copy the payload 400 for(i = 0; i< UartRxBuf[1]; i++) 401 { 402 event[2+i] = UartRxBuf[2+i]; 403 } 404 405 // register the event 406 Evt_EnQueue(event); 407 // clear the flag 408 bPktReceived = 0; 409 } 410 }
This task runs as a software timer routine in regular interval, whose frequency should be higher than packet rate that the protocol defines.
107 // start a timer routine: 10msec period, perpetual 108 result = UsrTimer_Set(10, 0, UartRxTask);
This seemingly redundant UartRxTask is necessary to avoid race condition since EvtQueue is designed to be used by software timer callback for posting event. If Evt_EnQueue function is called from SerialComm_RxRoutine, a callback of the UART RX interrupt directly it can be disturbed by SysTick interrupt whose priority is higher.