Why does UART transmit interrupt fail to work in this case?

23,421

First:

As has been described in answers to your previous question null timeout just exclude wait for flag state. If you open HAL_UART_Transmit code - you will see that when you send 1 byte without timeout no any blocking state will!

Second:

It's not true method to send/receive one byte from a huge HAL's functions and their callbacks. I guess: next your question will "how i must implement parse there?". And I hope you will not insert you parse function in IRQ callback!

So generally you need buffers. And it is good idea to use cyclic buffer.

mxconstants.h:

/* USER CODE BEGIN Private defines */

/* Buffer's length must be select according to real messages frequency */
#define RXBUF_LEN            128 // must be power of 2
#define TXBUF_LEN            128 // must be power of 2
#define RXBUF_MSK            (RXBUF_LEN-1)
#define TXBUF_MSK            (TXBUF_LEN-1)

/* USER CODE END Private defines */

main.c:

uint8_t rx_buf[RXBUF_LEN], tx_buf[TXBUF_LEN];
/* xx_i - counter of input bytes (tx - pushed for transmit, rx - received)
   xx_o - counter of output bytes (tx - transmitted, rx - parsed)
   xx_e - counter of echoed bytes */
volatile uint16_t rx_i = 0, tx_o = 0;
uint16_t rx_o = 0, rx_e = 0, tx_i = 0;
volatile uint8_t tx_busy = 0;

void transmit(uint8_t byte) 
{
    tx_buf[TXBUF_MSK & tx_i] = byte;
    tx_i++;
    tx_busy = 1;
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);
}

void main(void)
{
    /* Initialization code */
    /* ... */
    /* Enable usart 1 receive IRQ */
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
    for (;;) {
        /* Main cycle */
        while (rx_i != rx_e) {
            /* echo here */
            transmit(rx_buf[RXBUF_MSK & rx_e]);
            rx_e++;
        }
        while (rx_i != rx_o) {
            /* parse here */
            /* ... */
            rx_o++;
        }
        /* Power save 
        while (tx_busy);
        HAL_UART_DeInit(&huart1);
        */
    }
}

stm32f0xx_it.c:

extern uint8_t rx_buf[RXBUF_LEN], tx_buf[TXBUF_LEN];
extern volatile uint16_t rx_i, tx_o;
extern uint16_t rx_o, rx_e, tx_i;
extern volatile uint8_t tx_busy;

void USART1_IRQHandler(void)
{
    /* USER CODE BEGIN USART1_IRQn 0 */
    if((__HAL_UART_GET_IT(&huart1, UART_IT_RXNE) != RESET) && 
       (__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_RXNE) != RESET))
    {
        rx_buf[rx_i & RXBUF_MSK] = (uint8_t)(huart1.Instance->RDR & 0x00FF);
        rx_i++;
        /* Clear RXNE interrupt flag */
        __HAL_UART_SEND_REQ(&huart1, UART_RXDATA_FLUSH_REQUEST);
    }
    if((__HAL_UART_GET_IT(&huart1, UART_IT_TXE) != RESET) &&
       (__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TXE) != RESET))
    {
        if (tx_i == tx_o) {
            __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);
            __HAL_UART_ENABLE_IT(&huart1, UART_IT_TC);
        } else {
            huart1.Instance->TDR = (uint8_t)(tx_buf[TXBUF_MSK & tx_o] & (uint8_t)0xFF);
            tx_o++;
        }
    }
    if((__HAL_UART_GET_IT(&huart1, UART_IT_TC) != RESET) &&
       (__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TC) != RESET))
    {
        tx_busy = 0;
        __HAL_UART_DISABLE_IT(&huart1, UART_IT_TC);
    }
    /* And never call default handler */
    return;
    /* USER CODE END USART1_IRQn 0 */

    HAL_UART_IRQHandler(&huart1);

    /* USER CODE BEGIN USART1_IRQn 1 */
    /* USER CODE END USART1_IRQn 1 */
}

And third!!!

And about this:

Why HAL_UART_Transmit_IT not help/work?

Because it's too slow! And if you try to count HAL_BUSY results:

uint8_t Rx_data[5]; 
uint32_t tx_timeout = 0;
//Interrupt callback routine
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    static uint32_t hal_busy_counter = 0;
    if (huart->Instance == USART1)  //current UART
    {
        if (HAL_UART_Transmit_IT(&huart1, &Rx_data[0], 1) == HAL_BUSY) {
            hal_busy_counter++;
        }        
        HAL_UART_Receive_IT(&huart1, Rx_data, 1);   //activate UART receive interrupt every time on receiving 1 byte
    }
}

When you pause MCU in debugger after data exchange - you will be suprised: it will be equal to count of missed chars.

Share:
23,421
guagay_wk
Author by

guagay_wk

Updated on May 30, 2020

Comments

  • guagay_wk
    guagay_wk about 4 years

    I am using stm32f0 MCU.

    I have a simple UART echo code in which every byte received will be transmitted out. I tested that it works. Here it is;

    uint8_t Rx_data[5]; 
    uint32_t tx_timeout = 0;
    //Interrupt callback routine
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
        if (huart->Instance == USART1)  //current UART
        {
            HAL_UART_Transmit(&huart1, &Rx_data[0], 1, tx_timeout);        
            HAL_UART_Receive_IT(&huart1, Rx_data, 1);   //activate UART receive interrupt every time on receiving 1 byte
        }
    }
    

    I do not feel comfortable with the code even though it works. Firstly, tx_timeout is 0 and most code examples are non-zero. I do not know the side effect. Secondly, HAL_UART_Transmit() is a blocking call and it is not advisable to use blocking calls inside an interrupt. So, I decided to use an interrupt for uart transmission HAL_UART_Transmit_IT()instead of a blocking call. Here is the modified code;

    uint8_t Rx_data[5]; 
    uint32_t tx_timeout = 0;
    //Interrupt callback routine
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
        if (huart->Instance == USART1)  //current UART
        {
            HAL_UART_Transmit_IT(&huart1, &Rx_data[0], 1);        
            HAL_UART_Receive_IT(&huart1, Rx_data, 1);   //activate UART receive interrupt every time on receiving 1 byte
        }
    }
    

    However, it does not work as expected. My PC transmits ASCII 12345678 to stm32. If things work as expected, the PC should be receiving 12345678 back. However, the PC receives 1357 instead. What is wrong with this code that uses HAL_UART_Transmit_IT()?

  • imbearr
    imbearr about 8 years
    Buffer, input and output counters (ex. uint8_t rx_buf[RXBUF_LEN]; volatile uint16_t rx_i; uint16_t rx_o;) - is implementation of queue mechanism.
  • guagay_wk
    guagay_wk about 8 years
    Wonderful answer. Upvoted. I would like to confirm something with you. Using HAL_UART_Transmit() with zero timeout means HAL_UART_Transmit() becomes non-blocking? Any possible side effect? If so, I think that solution would be good enough.
  • imbearr
    imbearr about 8 years
    No side effects at current version of HAL. But this IRQ handler is too huge still. When you will use this solution in big project where USART IRQ may stay while another IRQ handler work - you may have missed bytes again!
  • Admin
    Admin about 8 years
    @imbearr, I was directed to your answer from stackoverflow.com/questions/37370687/… Does your code somehow takes away the need for re-enabling RX interrupt? Which part of it? Is it in USART1_IRQHandler()?
  • imbearr
    imbearr about 8 years
    This code __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); enable USART RX IRQ and if you don't use default handler - so you don't need to reeanble it. Be shure that USART IRQs enabled in your STM32CubeMx project.
  • imbearr
    imbearr about 8 years
    @LitAiy, when you enable IRQ in STM32CubeMx project - it add NVIC initialize to generated USART init functions. So you just need to enable IRQ with ready to process handler.
  • Admin
    Admin about 8 years
    @imbearr, do you mean if I run __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); in main(), I do not need to reenable rx interrupt and call HAL_UART_Receive_IT(&huart1, Rx_data, 1); inside HAL_UART_RxCpltCallback()? Assume that CubeMX settings are correct. Checking to see if I understand you correctly.
  • imbearr
    imbearr about 8 years
    @LitAiy, no it's not all. For use this solution you must change stm32f0xx_it.c where based real IRQ handler and add code that implement your own IRQ handle and exclude HAL handler calling. Thus you can't use HAL receive/transmit functions and their callbacks.
  • imbearr
    imbearr about 8 years
    @LitAiy, generally all changes described in answer above it is parts of complex solution. I have wrote this answer with expectation that reader understand IRQ mechanism and HAL drivers implementation. If you newbie with stm32 and HAL, so I can recomend you to see STM32 datasheets, HAL user guide and examples of HAL usage for your MCU.