cancel
Showing results for 
Search instead for 
Did you mean: 

Modifying STM32FCubeMX HAL Drivers for UART RX (IT mode) with unknown size data size

pdu-fr
Associate II
Posted on December 30, 2014 at 12:07

Hello,

I am programming a shell based on HAL UART and I experienced difficulties with the HAL UART API provided with CUBEMX. I was using the non-blocking UART Reveice IO function (IT mode).

I have modified the CUBEMX source code (stm32f4xxx_hal_uart.h and.c) to provided theses functionalities :

- New IO functions from Arduino world : Available(), GetChar()

- UART Receive IT handles a circular buffer to store each received byte.

- Receiving a byte does not disable UART Rx IT.

You will find the source code in the next posts of this thread.

Let me know if you know a better solution. Any feedback is appreciated.

Patrick.

#uart-hal-it-byte
18 REPLIES 18
Posted on January 12, 2015 at 16:08

Hi Patrick,

If you want to handle a circular buffer to store each received byte via the UART circular mode, we recommend to use the UART DMA Process so that can benefit for the Pause, Resume and Stop functionalities: 

  • HAL_UART_DMAPause(UART_HandleTypeDef *huart)
  • HAL_UART_DMAResume(UART_HandleTypeDef *huart): 
  • HAL_UART_DMAStop(UART_HandleTypeDef *huart): 
Note: The UART circular mode is not supported by the IT process 

Regards,

Heisenberg.

pdu-fr
Associate II
Posted on January 13, 2015 at 20:48

Thank you very much. I will have a look at DMA.

I hope that the Transmit DMA does not cause the same probleme (ORE due to delay waiting TC) as Transmit IT.

Bye.

Patrick.

pdu-fr
Associate II
Posted on January 13, 2015 at 22:40

How do I know the offset of the last byte transfered from UART to MEMORY using DMA in circular mode ? Do you have to use the following macro :

/**

  * @brief  Returns the number of remaining data units in the current DMAy Streamx transfer.

  * @param  __HANDLE__: DMA handle

  *

  * @retval The number of remaining data units in the current DMA Stream transfer.

  */

#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->NDTR)

pdu-fr
Associate II
Posted on January 14, 2015 at 23:27

The UART+DMA combination in circular mode for RX (and normal for TX) seems to work very well. Thank you, Eisenberg.

Bye,

Patrick.
Posted on January 16, 2015 at 01:43

Hi there,

I'm seeing something where the last 4 characters of a transfer are regularly wrong, but seem to be duplicates of valid characters previously received.

Would it be possible that NDTR is being decremented before the transfer of the last 32 bits (4 bytes) is done between the peripheral and the memory?

If I rely on the half and full interrupts, the data is correct, but if I use partial buffer DMA with timeouts the last 32 bits are sometimes wrong.

Does HAL_UART_DMAPause force that last transfer?

Thanks,

Andrei

Posted on January 16, 2015 at 21:02

Got it. Yes, the final transfer from the UART to memory was not happening due to buffering.

DMA_SxFCR.DMDIS = 0

By turning off FIFO mode and using DMA Direct the incoming characters are placed in the memory buffer immediately. There is a loss in efficiency, but now I can read characters without the overruns of polled mode, or the final 3 character corruption of interrupt mode.

pdu-fr
Associate II
Posted on January 17, 2015 at 15:38

Good ! DMA solved all my problems about UART overrun, too, and now I can send and receive data at 921.600bauds in non bloking mode reliably.

Patrick.

vincent2
Associate II
Posted on January 26, 2015 at 18:03

Hi Patrick,

Would you mind to share your DMA non blocking mode code ?

Thanks,

BR.

Vince.
pdu-fr
Associate II
Posted on January 28, 2015 at 22:19

Here is the code produced by CUBEMX for the UART

/* USART3 init function */
void MX_USART3_UART_Init(void)
{
huart3.Instance = USART3;
huart3.Init.BaudRate = 460800;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart3);
}

Here is the code produced by CUBEMX for the DMA associated to the UART en RX :

/* Peripheral DMA init*/
hdma_usart3_rx.Instance = DMA1_Stream1;
hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4;
hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart3_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
hdma_usart3_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
hdma_usart3_rx.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_usart3_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
HAL_DMA_Init(&hdma_usart3_rx);
__HAL_LINKDMA(huart,hdmarx,hdma_usart3_rx);

In reception, I have got a few variables :

#define size_of_rx_circular_buffer 128
uint8_t rx_circular_buffer[size_of_rx_circular_buffer];
uint8_t const * rx_tail_ptr;

Then, I start DMA RX at the begining of my application :

HAL_UART_Receive_DMA(huart, rx_circular_buffer, size_of_rx_circular_buffer);
rx_tail_ptr = rx_circular_buffer;

Every time I want to know how many characters I have received :

uint8_t const * head = rx_circular_buffer + size_of_rx_circular_buffer - __HAL_DMA_GET_COUNTER(huart->hdmarx);
uint8_t const * tail = rx_tail_ptr;
if( head>=tail )
return head-tail;
else
return head-tail+size_of_rx_circular_buffer;

Every time I read a char in the circular buffer, I inc the tail ptr.

if(head!=tail)
{
char c = *rx_tail_ptr++;
if(rx_tail_ptr>=rx_circular_buffer + size_of_rx_circular_buffer)
rx_tail_ptr-=size_of_rx_circular_buffer;
return c;
}

Hope that will help you ! 🙂 Bye, Patrick.