cancel
Showing results for 
Search instead for 
Did you mean: 

Serial UART design for various size transfers

David Pekin
Senior

Hello,

I'm writing an application on the STM32F303 Nucleo board that uses the serial UART to receive commands and send responses. I believe example code from the standard peripherals library shows either single byte transfers or fixed size buffer transfers. That is, the UART receive interrupt is fired only after N bytes are received. But N is fixed... This doesn't work for the case where you may be receiving a variable number of characters in your input stream to make up a command.

I've got a system limping along asking for and receiving a single character interrupt at a time but this is less than a reasonable solution and it fails randomly with a HAL_UART_ErrorCallback as other things are going on in the Nucleo code. The error occurs much more frequently when I am simultaneously reading/writing the I2C bus.

Is there a way to peek into the RX UART handle and see how many characters are in the RX buffer and grab them? Polling the number of characters received at the top of the main loop and pulling them into my own circular buffer would probably work more reliably than single character interrupts.

Any thoughts on the best solution?

Thanks,

Dave

10 REPLIES 10

The UART on the F3 fire the interrupt for every byte, there is no FIFO, HAL might call the callback at a decimated rates, but is super inflexible.

You could peer inside the HAL structures at the interrupt, I just chose to s**tcan the HAL code for this, and use proper ring buffers like I do everywhere else.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Trevor Jones
Senior

using the DMA buffer hardware;

I use this to "Peek" into the buffer: where U1RxBufferPtrOUT points to the last byte read.

char readableU1(void) {

   U1RxBufferPtrIN = U1RxBufSize - huart1.hdmarx->Instance->CNDTR;

   return U1RxBufferPtrIN - U1RxBufferPtrOUT;

}

Thanks Clive. I would like to use a simple ring buffer and get rid of HAL (at least for rx). Are you aware of any public or sample code that demonstrates how to do this?

Thanks again.

Dave

Thanks Trevor. I'm trying not to rewrite the whole serial UART HAL implementation. I'm currently using the interrupt driven HAL UART dode which is giving me the problem. The DMA implementation has the same drawback where you need to know the number of bytes you're going to be receiving.

I wonder if I could tell the system that the incoming serial read is a large N bytes. Where N is larger than the biggest packet I'm expecting. In the main loop I could monitor how many bytes the incoming DMA buffer has and when I'm not receiving any more data, set the DMA requested size to what I've already received, thereby causing a DMA RX interrupt to get the data. I know this is a lot of hand waving and I'm not sure if the DMA controller would allow that sort of peeking and poking while it's running. Or maybe this same type of kluge using the interrupt based code. What do you think?

I'm somewhat loath to open Pandora's box and start rewriting the HAL UART code from scratch. That's not really in the project timeline....

Dave

Hi Clive.

Does the F3 fire an interrupt for every character in DMA mode? Maybe there's a kluge work around to let the DMA collect the buffer and then poke the DMA controller expected count register to the current received character count, thereby generating the DMA interrupt and xfer. Not ideal and maybe even not possible... Rewriting the UART code doesn't sound that appealing either...

To minimize the rewrite I guess I could let HAL setup the UART but not connect the interrupt. Then I could check the UART for a received character every time through the main loop. That sounds possible I guess. Any thoughts on that approach?

Dave

DMA can interrupt at HT/TC (Half/Complete Transfer), for DMA I'd use a large circular buffer, use 16-bit, and mark the buffer as I pulled out data, ie write an 0xFFFF pattern that will never occur from the USART DMA, where the active portion of the register is only 9-bit wide. Then have a circular pointer, and sweep the buffer in the data consumption task.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..

Will data be put in to the circular buffer only upon one of the DMA interrupts or as it is received? So, I 'm thinking I'd setup a circular buffer of N bytes and then setup the DMA for a data size of N/2. Then just ping pong the sides of the buffer every Complete Transfer interrupt and I process the buffer in my consumption task.

Unless the data gets placed in the circular buffer real-timeish (not at the interrupt) we still have the case where the serial stream terminates shortly after a TC but before a HT and we don't get it, right? But, if it gets put in as it is received we're golden.

Thanks!

DMA request is equivalent to RXNE IRQ, instead of the CPU servicing, the DMA reads USARTx->RDR, clearing the request. One DMA hit for each byte received. Dozens of CPU cycles avoided.

You wouldn't even need to use the HT/TC interrupts in the scheme I'm using. It is basically a ring buffer, the fill side done in HW, the empty side done in SW. The size of the ring buffer depends on the baud rate and service interval. For 115200 baud, you might see 12 bytes on a 1KHz/1ms tick.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Trevor Jones
Senior

its super simple...

you dont need to rewrite HAL, just use it...