Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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()?

like image 912
guagay_wk Avatar asked May 20 '16 01:05

guagay_wk


1 Answers

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.

like image 187
imbearr Avatar answered Nov 02 '22 13:11

imbearr