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
Posted on December 22, 2016 at 11:56

I do use HAL all the time and I think Alan has the right answer.  It should be fairly straightforward.  HAL_UART_TxCpltCallback() gets called automatically by the HAL Interrupt transfer mechanism when the last character of the transfer is being sent out.  So, in that routine you can put a check to see if there is new data available in your big buffer to be sent out, and if so it starts a new transfer using HAL_UART_Transmit_IT().  If not, it simply returns.  The other modification you will need is that in your main print routine (PRINT_MESG_UART()) needs to first check if the UART is busy.  If it is, the routine puts the new data into the buffer and returns, if it isn't busy, it needs to start a transfer itself with HAL_UART_Transmint_IT() which will then trigger the end-of-xfer callback when it's done.    Also, you may want to disable the UART interrupt while you check if it is busy, and load the buffer, so that you avoid the reace condition where the interrupt gets triggered on the last char going out while you are updating the buffer. 

Geoff Espin
Associate II
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
Senior
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?

Posted on February 04, 2017 at 02:13

now I'm hungry

Mark Shoe
Senior
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? 

Mark Shoe
Senior
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.

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.

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
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

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
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