Skip to main content
toby2
Senior
November 9, 2016
Question

printf with HAL_UART_Transmit_IT?

  • November 9, 2016
  • 10 replies
  • 10026 views
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
This topic has been closed for replies.

10 replies

Walid FTITI_O
Visitor II
November 9, 2016
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
toby2Author
Senior
November 9, 2016
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......

Michael Ruppe
Associate
December 10, 2016
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.
toby2
toby2Author
Senior
December 15, 2016
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 III
December 15, 2016
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.

toby2
toby2Author
Senior
December 15, 2016
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.

Alan Chambers
Associate III
December 15, 2016
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.
shingadaddy
Senior
December 16, 2016
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..

T J
Senior III
February 4, 2017
Posted on February 04, 2017 at 02:13

now I'm hungry

Geoff Espin
Visitor II
December 22, 2016
Posted on December 22, 2016 at 16:51

Can't wait for the discussion about HAL_UART_Receive().  Instead of returning the length of bytes read, you get an HAL_OK or TIMEOUT, BUSY...

Mark Shoe
Associate III
February 3, 2017
Posted on February 03, 2017 at 17:48

The routine printing to a que like the example is just what i need. After filling the que i want to start the printing with:

SET_BIT(UartHandle.Instance->CR1, USART_CR1_TXEIE);

To start the HAL_UART_TxCpltCallback routine. However after the 

SET_BIT my program locks up until watchdog comes in.

Any idea?

Mark Shoe
Associate III
March 30, 2017
Posted on March 30, 2017 at 13:04

It seems to be difficult to make a non blocking serial send and receive routine. Even the simple way with blocking printing will not work. If chars are received during transmitting the receive irq will not appear anymore. A command interface simply made in microchip seems to be difficult using HAL drivers. Can someone share a command interface? 

toby2
toby2Author
Senior
March 30, 2017
Posted on March 30, 2017 at 13:55

I am not sure what problem you are having. Any of the techniques discussed in this thread will work, depending on whether you can accept the main code blocking when it writes.

Transmitting should not block the receive interrupt so I think you have a fundamental bug you need to find but without much more detail I am not sure I can help with that.

T J
Senior III
March 30, 2017
Posted on March 30, 2017 at 14:14

to make it totally nonblockable and an interrupt on every char, echoing all Rx to Tx, at 460800, is that what you want ?

to make that work, i had to buffer the string into a 2k TX DMA buffer then Tx DMA in normal mode and set a circular RxDMA of just 2 bytes.

Both of the RxDMAcallbacks load into a 1k circular buffer automatically, the foreground can poll the counters when it wants too.

ie.

while (console::io.U1RxBufferPtrIN != console::io.U1RxBufferPtr_OUT) { // we have char(s)

// Process chars

}

I should make the TxDMA circular too, but its works so well, I left good enough alone.

extern 'C' void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
 //HAL_UART_RxCpltFlag = true; //not needed // testing flag
 uint8_t Rxbyte; // save the char, update the buffer flags
 
 Rxbyte = console::io.Usart1RxDMABuffer[1]; // tricky, no counters needed... // circular 2 byte DMA buffer: Byte 2
 
 console::io.Usart1RxBuffer [ console::io.U1RxBufferPtrIN ++] = Rxbyte;
 if (console::io.U1RxBufferPtrIN >= 1024) console::io.U1RxBufferPtrIN =0; // circular 1k Rx buffer
 
}
 
extern 'C' void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart){
 //HAL_UART_RxHalfCpltFlag = true; // not needed // testing flag
 uint8_t Rxbyte; 
 Rxbyte = console::io.Usart1RxDMABuffer[0]; // tricky, no counters needed...// circular 2 byte DMA buffer: Byte 1
 
 console::io.Usart1RxBuffer [ console::io.U1RxBufferPtrIN ++] = Rxbyte;
 if (console::io.U1RxBufferPtrIN >= 1024) console::io.U1RxBufferPtrIN =0; // circular 1k Rx buffer
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Mark Shoe
Associate III
March 30, 2017
Posted on March 30, 2017 at 14:40

Well this is what i have to use printf:

void _write(int file, char *ptr, int len)

{

int i;

for (i = 0; i < len; i++)

{

HAL_UART_Transmit(&huart2, (uint8_t*) &ptr[i], 1, 10);

}

}

And here to receive

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

uint8_t i;

if (huart->Instance == USART1) //UART USB debug

{

HAL_UART_Receive_IT(&huart1, Rx_data1, 1); //activate UART receive interrupt every time

}

}

and getting a char:

char getch_1(void) // USB

{

char a;

a = Rx_data1[0];

Rx_data1[0] = 0;

return a;

}

when printing a new char directly 

poll this in main:

ch = getch_1();

if (ch)

{

printf('%c', ch);

...

Will work from keyboard but if the cars are too fast, like from ctrl-V or gps receiver it will not work for long.

So now i want to send and receive using send HAL_UART_Transmit_IT and a receive circular buffer. Once the transmit has done 1 char it must interrups as long there are chars.

So changed the _write to:

static void vprint(const char *fmt, va_list argp)

{

char string[200];

char ch;

if (0 < vsprintf(string, fmt, argp)) // build string

{

int i;

for (i = 0; i < strlen(string); i++)

{

byteq_put(&g_handles[SCI_CH1]->tx_queue, string[i]);

}

SET_BIT(UartHandle.Instance->CR1, USART_CR1_TXEIE);

}

}

Now i aspect an irq transmit buffer empty and send the chars 1 by 1 unit the byteq is empty

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)

{

char ch;

if (huart->Instance == USART1) //UART USB

{

if (byteq_get(&g_handles[SCI_CH1]->tx_queue, &ch))

HAL_UART_Transmit_IT(&huart1, ch, 1); // send message via UART

}

}

However 

SET_BIT(UartHandle.Instance->CR1, USART_CR1_TXEIE); will lock the program, watchdog timeout.

aaron239955_stm1_st
Senior
March 30, 2017
Posted on March 30, 2017 at 15:05

Why are you working so hard to output one character at a time?  The interrupt driven (as well as the blocking) HAL calls are happy to accept a message length parameter and manage the transfer for you.  The original discussion in this thread discusses options if you would like to feed this with an additional buffer so that you do not block if you have another message to send before all of the characters of an outgoing message have been sent.  But you do not need to handle the individual character interrupts.  That's what the HAL call does when you pass it a buffer longer than 1 character.

On the input side, you should define an input buffer with two index pointers.  One is incremented when a character is received in the ISR, the other incremented when a character is taken out of the buffer by your _read() or getch() types of functions.  When these pointers are equal the buffer is empty.  Both pointers should wrap at the limit of the buffer to make it circular.

Not sure why xmit activity is causing a problem with your Rx process, but you might want to disable the Tx Interrupt (or all interrupts) while you re-initialize the Rx process with the HAL call in your Rx ISR.

S.Ma
Principal
March 30, 2017
Posted on March 30, 2017 at 18:25

My 2 cents on the UART printing strings. If the baudrate is very high, the printf analyse the string and build the message to spit out character by character. Polling TX might be good enough: While the char is sent, I'm preparing the next.

If the baudrate is low, or sending big string taking significant amount of time, I've used a circular buffer with a FIFO mecanics between the one that fills characters and the one that pops it. Happens that you only need 2 events in this plumbing: Fifo gets empty event (you disable the UART interrupt), the fifo is no longer empty (you enable the uart interrupt).

Fifo mecanics are nice to echange info between interrupts or between main loop and interrupts.

Here is the inspirational code which I used to code differently than on a typical project. This one is generic, a list of pointers to elements to queue up like a snake (in a rolling pointer buffer). You can add/cut elements from the head or tail. Inspired from Clojure function styles. For the initiated. Optimizing it for bytes, words, dwords should be easy.

Have fun! Feedback welcome.

________________

Attachments :

StuffsArtery.c.zip : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006HyjG&d=%2Fa%2F0X0000000bBe%2FtrBUOEh2p9IfRVaOmj4VteIdz9A9zdtnc3fonDqT3_c&asPdf=false

StuffsArtery.h.zip : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006HyjL&d=%2Fa%2F0X0000000bBd%2FIXPgOgEBJfPuB3xbCdgtqh8VRGihr.TZrtaF3XTDNoc&asPdf=false
Mark Shoe
Associate III
March 30, 2017
Posted on March 30, 2017 at 21:13

Here my CubeMX generated project. The receive using que does work. The print with printf uses the blocking routine this works. The sci1_printf uses the que this does not work.

When entering chars on uart1 it prints them directly on uart1. CTRL-V lots of chars and no more receiving irq's. So i made a byteq to print using irq's

You can type 'send' to direct send aHAL_UART_Transmit_IT or 'send2' to fill the byteq but i cannot start that!

Problem is how the start the printing see usart.c line 419

The idea is filling the buffer using sci1_printf, enable the tx buffer empty IRQ and when the char is printed an irq gets the next char and so on.

The project runs on my homemade pcb STM32f103 but the routine should work on every STM32

________________

Attachments :

Test_Uart_103.zip : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006Hye0&d=%2Fa%2F0X0000000bBm%2FN.7INZed4I7pUVLP.qeG4nrUjI3GnS6_UTaHc2bmc38&asPdf=false
toby2
toby2Author
Senior
March 31, 2017
Posted on March 31, 2017 at 10:58

I am afraid that, like Nick, I don't have time to try and understand your code properly. I have had a quick look though and it does appear to buffer both the rx and tx so I don't understand what we were talking about above.

I have also noticed a couple of issues though:

1) in start_uart() you activate the RX before setting up the queues. That doesn't sound the right order to me although I don't think it is the problem in this case.

2) I think your rx callback is incorrect, you appear to be reading the uart DR. This has already been done by the UART irq routine which will have put the data in Rx_data1 which you don't appear to use. 

I think you need to read the HAL description again but basically the sequence for receiving is:

- start the RX with  HAL_UART_Receive_IT(&huart1, Rx_data1, 1); 

- the irq routine puts any data in Rx_data1 and calls the callback when it has received enough data (1 byte in this case)

- your callback routine should take the data from Rx_data1 and do something with it. (in this case put it in the queue?)

Still not sure if this the root of your problem but it does need sorting. Given this error I would also look more carefully at the tx stuff.

T J
Senior III
March 30, 2017
Posted on March 31, 2017 at 01:29

I am sorry that I don't have time to load your code and try to fix, but I can give you hints from my set;

I had to poll the HAL progress in the foreground for non-blocking Tx, this works from 1 byte to 1024 bytes, without issue.

 void IO::CheckTxDMABufferProgress(void) {
if (TxDMABufHasData) {
char uartState = HAL_UART_GetState(&huart1);
if ((uartState == HAL_UART_STATE_READY) || (uartState == HAL_UART_STATE_BUSY_RX)) {
TxDMABufHasData = false;// sending now
if (HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Usart1TxDMABuffer + U1TxBufferPtrOUT, U1TxBufferPtrIN - U1TxBufferPtrOUT) == HAL_OK) {
HAL_UART_Transmit_DMA_Status = UartDMAsuccess;
U1TxBufferPtrOUT = U1TxBufferPtrIN;
}
else {
Error_Handler();/* Transfer error in transmission process */
HAL_UART_Transmit_DMA_Status = UartDMAfailed;
}
}
}
 }�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

hope this helps.