cancel
Showing results for 
Search instead for 
Did you mean: 

printf with HAL_UART_Transmit_IT?

toby2
Senior
Posted on November 09, 2016 at 10:34

I am new to the hal libraries (but not stm32 devices) and struggle to see howHAL_UART_Transmit_IT is expected to be used for something simple like printf over uart.

Take a blocking example:

void PRINT_MESG_UART(const char * format, ... )
{
va_list ap;
uint8_t buffer [128];
int n;
va_start(ap, format);
n = vsnprintf ((char*)buffer, 128, format, ap);
va_end(ap);
//notify_uart(buffer, n);
if(HAL_UART_Transmit(&UartHandle, (uint8_t*)buffer, n, 300) != HAL_OK) {
Error_Handler();
}
}

That works fine. But what if I want it non blocking under interrupts? What I have come up with is as follows:

uint8_t buffer [128];
void PRINT_MESG_UART(const char * format, ... )
{
va_list ap;
int n;
while (UartHandle.gState != HAL_UART_STATE_READY)
; // will overwrite the buffer otherwise, (needs a timeout and error handling)
va_start(ap, format);
n = vsnprintf ((char*)buffer, 128, format, ap);
va_end(ap);
HAL_UART_Transmit_IT(&UartHandle, (uint8_t*)buffer, n) == HAL_BUSY) ;
}

This works, but is pretty poor as it still blocks if the buffer is already in use. Is there a better way? The obvious way is to use the traditional method of a circular buffer. eg

bool u_put_byte(uint8_t data)
{
bool ok = FALSE;
if ((uart_buffer.txin + 1) % S_BUF_SIZE != uart_buffer.txout)
{
uart_buffer.txbuf[uart_buffer.txin] = data;
uart_buffer.txin = (uart_buffer.txin + 1) % S_BUF_SIZE;
ok = TRUE;
}
SET_BIT(UartHandle.Instance->CR1, USART_CR1_TXEIE);
return ok;
}
void PRINT_MESG_UART(const char * format, ... )
{
va_list ap;
uint8_t buffer[S_BUF_SIZE];
int n;
va_start(ap, format);
n = vsnprintf ((char*)buffer, S_BUF_SIZE, format, ap);
va_end(ap);
int i = 0;
while ((buffer[i] != 0) && (i < S_BUF_SIZE) && (i < n))
{
while (!u_put_byte(buffer[i])) // keep attempting until it succeeds
; // timeout and error handling here.
i++;
}
}

However the HAL uart library functions do not appear to support this sort of buffer. Am I correct? #nucleo #stm32 #hal #uart
30 REPLIES 30
Walid FTITI_O
Senior II
Posted on November 09, 2016 at 17:19

HiToby

AS shown in ''UART_Hyperterminal_IT'' example in STM32Cube package , you should add a wait statement for the end of transfer.Before starting a new communication transfer, you need to check the current state of the peripheral; if it’s busy you need to wait for the end of current transfer before starting a new one. For simplicity reasons, this is just waiting till the end of thetransfer, but application may perform other tasks while transfer operation is ongoing.

while (HAL_UART_GetState(&UartHandle) != HAL_UART_STATE_READY)
{
}

-Hannibal-
toby2
Senior
Posted on November 09, 2016 at 18:39

Thanks Hannibal but I have already done this. 7th line down on my HAL example (2nd block of code) waits the the uart to be ready before trying to send anything else.

But that not very useful, I don't want to wait, I want to queue more data immediately, the only time I would want to block on queuing data would be if the buffer was full. This is all handled automatically by the standard circular buffer (as per my last code block) but I cannot find a easy way to do using HAL.

What am I missing? I cannot believe HAL cannot support this as it is standard use of a uart from printing debug info......

Posted on December 10, 2016 at 06:02

Hi Toby,

Did you ever figure this out? I have the same problem - I haven't figured out how to just set up a buffer that I can write arbitrary length strings into and have an interrupt dequeue them out the UART.

My own thread is

https://community.st.com/0D50X00009XkYahSAF

, but I think yours has better information.
Posted on December 15, 2016 at 15:13

No, I came to the conclusion that the hal libraries are not written with this sort of use in mind.

I just used my 'traditional' circular buffer approach which I have used many times before without problems.

Alan Chambers
Associate II
Posted on December 15, 2016 at 16:25

I don't use HAL, but would implementing HAL_USART_TxCpltCallback() do the trick? You are going to need a buffer whatever you do, to decouple the asynchronous accesses to the data. Create a queue of pointer + length structures which point at the data to be written. In your implementation of the callback, kick off the next call to HAL_UART_Transmit_IT().

I've implemented my own debug output as a queue of DMA transfers. Each transfer points to a contiguous block in a buffer. The transfer compete interrupt fetches the next item off the queue, if any, and kicks off the next transfer. Each call to printf() copies data into the buffer and adds a transfer (or two if the data wraps) to the queue. I have not tried, but assume I could implement something similar with HAL. If not, that seems kind of flawed.

Posted on December 15, 2016 at 17:38

I am not sure I follow you fully. So in your DMA example you have a queue that points to a block of data in a buffer? Is that buffer a circular one? So you need an head and tail pointer to manage the buffer plus the queue with pointer/length elements to tell the dma routines what to send? 

Yes, that would work with HAL and interrupts and I can see it being a good way to do it with DMA.

However if just using interrupts I think it highlights the problem I have with HAL. It over-complicates things as normally you would not need the queue of pointers, just the circular buffer (and the routines take less time to write than understanding HAL!).

I guess I am being a bit unfair though, it is not that you cannot use the HAL libs for this sort of thing, just that they are not really suited to doing it efficiently.

Posted on December 15, 2016 at 18:17

I have a large circular buffer in which to store data before it is actually written. I append data in chunks - generally a single chunk for a single call to MyUARTWrite(data, len), but this is split into two if the data wraps in the ring buffer. Every chunk is a contiguous block of memory - ideal for DMA. I have a separate queue which contains the address and length of each chunk. Appending a chunk kicks off a DMA transfer if the stream is idle. Transfer complete updates the tail for the big buffer and the queue, and starts a transfer for the next chunk, if any. Naturally I have to copy data into the buffer, but after that, there is very little time spent by the core to get the data out through the peripheral. It has proved very effective over the years.

With interrupts, the problem with the HAL seems to be that you don't implement the ISR yourself, so you can't manage every byte (I think - perhaps there is a callback for this too). Rather, you tell it to go away and perform an asynchronous transfer of N bytes, and possibly tell you when it is complete (the callback). This is rather like a DMA transfer. Curiously, the same callback function appears to be called at the end of DMA!

The problem with using interrupts is that the processor core has to do work for every single byte. DMA is more efficient. My question for you is: since the HAL supports DMA, why not just use that instead?

As I said, I don't use HAL myself. I have tried to evaluate it, and found it lacking. The API offers apparently easy to use functions, but they seem rather limiiting. Perhaps I am being unfair. I much prefer SPL which does not get in the way. The main benefit seems to be the Cube GUI which generates the initialisation code. Since that's a small fraction of the effort on most projects, it seems like a minor gain.
Posted on December 15, 2016 at 20:52

Understood, and yes, that sounds a good way of doing it with DMA.

My question for you is: since the HAL supports DMA, why not just use that instead?

A good question! To be honest, only because I have done it with interrupts and circular buffers for longer than I care to remember and it works so hadn't even thought of using DMA for this sort of stuff until I started looking at the HAL stuff. 

I'd have to check but I suspect a simple circular buffer with interrupts would be slightly less code that the DMA system, but (as you said) DMA would be more efficient processing wise so probably worth it overall. I will have a play with that on my next project!

As I said, I don't use HAL myself. I have tried to evaluate it, and found it lacking. The API offers apparently easy to use functions, but they seem rather limiiting. Perhaps I am being unfair. I much prefer SPL which does not get in the way. The main benefit seems to be the Cube GUI which generates the initialisation code. Since that's a small fraction of the effort on most projects, it seems like a minor gain.

And that is really my problem I think. SPL is easy to use, not too much code bloat and gets the job done. CubeMX is good if you are trying to do something for the 1st time but after that it just gets in the way and reduces code readability. Plus the bloat of HAL means it is not a sensible choice for any project with a tight memory or processing speed budget.

I can see the advantage to HAL for those that are just starting out (ie no code base) and those that come from a higher level programming background (so like standard APIs and high levels of abstraction) but I think it has lost sight of the fact it is supposed to be for low level microcontroller programming where reading the reference manual and understanding what the hardware is doing is generally fairly important.

shingadaddy
Senior
Posted on December 16, 2016 at 20:11

Gosh thats the smooooothest interpretation of HAL/SPL/Roll Your Own I've seen on here since I've been here.

Then again I do try to look at it from ST's point of view which I imaginge is something like having to get an ENORMOUS amount of information in a very portable/re-usable manner to an audience that is wide and varied. So they are effectively the software supermarket. They have all the ingredients scattered across the whole store. Trouble is, from my viewpoint, I want a relatively small bowl of soup..All prepared and gathered together in one relatively small pot. St tries to do small pots too but all the ingrediants are WHOLE PIECES!. A whole chunk of meat, a collection of whole potatoes, a collection of whole carrots, a collection of whole cellary plants....

It might smell and even taste the same but it sure doesn't LOOK the same, and it's harder to eat. Not to mention it doesn't fit in the *small* bowl too well either.

D a r n.    Now I've made myself hungry..