Skip to main content
Rami Rosenbaum (old)
Associate III
February 19, 2017
Question

USART - reading 1 byte at a time

  • February 19, 2017
  • 4 replies
  • 4609 views
Posted on February 19, 2017 at 13:39

Hi,

I need to parse incoming data from UART, with unknown length.

The packet length is sent within the packet, so I need to parse the packet as it arrives.

So - I need to read 1 byte at a time, and parse the packet while receiving.

I'm using CubeMX generated code and HAL libraries, and will probably work with interrupts.

Should I be calling HAL_UART_Receive_IT(&huart1, &nextByte, 1) within my HAL_UART_TxCpltCallback() method, and move the received byte to the foreground thread' for parse?

#hal #usart
This topic has been closed for replies.

4 replies

Tesla DeLorean
Guru
February 19, 2017
Posted on February 19, 2017 at 18:21

Depends on how burdensome your parsing is. If you can process with a state-machine, or similar, within one byte time you could do in the interrupt. For more complex stuff you could buffer, and process in foreground or worker task.

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
Rami Rosenbaum (old)
Associate III
February 19, 2017
Posted on February 19, 2017 at 19:53

Yes, I'll process in foreground, but - is this the correct usage of the HAL API?

Tesla DeLorean
Guru
February 19, 2017
Posted on February 19, 2017 at 22:13

I believe so, but there are examples under the Cube install directory. 

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
S.Ma
Principal
February 19, 2017
Posted on February 19, 2017 at 18:55

Use interrupt scheme in RX mode and do the 'start/end' detection of the packet of bytes for treatment.

Use a DMA Memory to Memory to push the slice out and get the interrupt ready for the next batch. For Transmit, usually the message is constructed byte by byte, sometime, if it is text, it would be generated from a form of printf. As one char is pushed to USART, the printf char by char processing can be done in the dead time, and for RS232, there is no 'urgency' as it is typically asynchroneous. 

T J
Senior III
February 19, 2017
Posted on February 19, 2017 at 23:30

set the Uart receive DMA circular buffer to two bytes length

then the halfcallback and full callback will interrupt you for each individual byte

I have done this exactly, it works extremely well at 460800 baud.

Rami Rosenbaum (old)
Associate III
February 20, 2017
Posted on February 20, 2017 at 12:27

'way too much overhead' - that's exactly what I thought.

The 2-byte circular buffer looks like what I need.

I won't find it imposing, if you attach a demo-code... feel free, on my behalf :)

T J
Senior III
February 20, 2017
Posted on February 20, 2017 at 12:50

void MX_USART1_UART_Init(void){
 huart1.Instance = USART1; huart1.Init.BaudRate = 460800; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); }
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle){
 GPIO_InitTypeDef GPIO_InitStruct; if(uartHandle->Instance==USART1) { /* USER CODE BEGIN USART1_MspInit 0 */
 /* USER CODE END USART1_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_USART1_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF1_USART1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 /* Peripheral DMA init*/ hdma_usart1_tx.Instance = DMA1_Channel7; hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_tx.Init.Mode = DMA_NORMAL; hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW; if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK) { Error_Handler(); }
 __HAL_DMA1_REMAP(HAL_DMA1_CH7_USART1_TX);
 __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);
 hdma_usart1_rx.Instance = DMA1_Channel6; hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_rx.Init.Mode = DMA_CIRCULAR; hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW; if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK) { Error_Handler(); }
 __HAL_DMA1_REMAP(HAL_DMA1_CH6_USART1_RX);
 __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);
 /* Peripheral interrupt init */ HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspInit 1 */
 //hdma_usart1_tx.Init.BufferSize = ARRAYSIZE;
 /* USER CODE END USART1_MspInit 1 */ }}

void initUart1RxDMABuffer(void){ //##-3- Put UART peripheral in reception process ###########################
 if (HAL_UART_Receive_DMA(&huart1,(uint8_t *)Usart1RxDMABuffer, 2 != HAL_OK) { // Transfer error in reception process Error_Handler(); }}

extern 'C' void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
 HAL_UART_RxCpltFlag = true;
 uint8_t Rxbyte; // save the char, update the buffer flags Rxbyte = Usart1RxDMABuffer[1]; // tricky, no counters needed... Usart1RxBuffer [ U1RxBufferPtrIN ++] = Rxbyte; if (U1RxBufferPtrIN >= 1024) U1RxBufferPtrIN =0; } extern 'C' void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart){
 HAL_UART_RxHalfCpltFlag = true;
 uint8_t Rxbyte; Rxbyte = Usart1RxDMABuffer[0]; // tricky, no counters needed... Usart1RxBuffer [ U1RxBufferPtrIN ++] = Rxbyte; if (U1RxBufferPtrIN >= 1024) U1RxBufferPtrIN =0;
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Alan Chambers
Associate III
February 20, 2017
Posted on February 20, 2017 at 17:02

I can't really comment on the correct HAL approach to this but, if it helps:

I use DMA with a circular buffer for RX. I don't bother with DMA interrupts but poll NDTR on a timer to see if any new data has arrived. I do this because the packets are variable length and might be received intermittently: I don't want to hang around mid-packet waiting for more data to arrive to trigger a DMA interrupt. The buffer need not be very large: a bit over twice the maximum possible bytes per timer interval should do it. I usually opt for every 1ms, but this is arbitrary. I typically run the UART at 115200 or 230400 baud. New data is copied into a buffer and processed through a packet finding FSM (actually it's copied into a small structure which is appended to a queue for handling in application land). I would much rather do this than interrupt for every byte at 20kHz or whatever.

I have no idea whether something like this is possible using HAL. I assume so.