为什么UART发送中断在这种情况下无法工作?
Why does UART transmit interrupt fail to work in this case?
我用的是stm32f0单片机
我有一个简单的 UART 回显代码,其中接收到的每个字节都会被发送出去。我测试过它有效。在这里;
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
}
}
我对代码感到不舒服,即使它可以工作。首先,tx_timeout
是 0,大多数代码示例都是非零的。我不知道副作用。其次,HAL_UART_Transmit()
是阻塞调用,不建议在中断内使用阻塞调用。所以,我决定使用中断进行 uart 传输 HAL_UART_Transmit_IT()
而不是阻塞调用。这是修改后的代码;
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
}
}
但是,它没有按预期工作。我的电脑将 ASCII 12345678 传输到 stm32。如果事情按预期进行,PC 应该会收到 12345678。但是,PC 收到的却是 1357。使用 HAL_UART_Transmit_IT()
的代码有什么问题?
第一个:
如您的 previous question 空超时的答案中所述,仅排除等待标志状态。如果你打开 HAL_UART_Transmit
代码 - 你会看到当你发送 1 个字节而没有超时时,不会有任何阻塞状态!
第二个:
从巨大的 HAL 函数及其回调中 send/receive 一个字节不是真正的方法。我猜:接下来你的问题将 "how i must implement parse there?"。我希望你不要在 IRQ 回调中插入你的解析函数!
所以通常你需要缓冲区。使用循环缓冲区是个好主意。
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 */
}
第三个!!!
关于这个:
Why HAL_UART_Transmit_IT not help/work?
因为太慢了!如果您尝试计算 HAL_BUSY
个结果:
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
}
}
当您在数据交换后在调试器中暂停 MCU 时 - 您会感到惊讶:它将等于丢失的字符数。
我用的是stm32f0单片机
我有一个简单的 UART 回显代码,其中接收到的每个字节都会被发送出去。我测试过它有效。在这里;
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
}
}
我对代码感到不舒服,即使它可以工作。首先,tx_timeout
是 0,大多数代码示例都是非零的。我不知道副作用。其次,HAL_UART_Transmit()
是阻塞调用,不建议在中断内使用阻塞调用。所以,我决定使用中断进行 uart 传输 HAL_UART_Transmit_IT()
而不是阻塞调用。这是修改后的代码;
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
}
}
但是,它没有按预期工作。我的电脑将 ASCII 12345678 传输到 stm32。如果事情按预期进行,PC 应该会收到 12345678。但是,PC 收到的却是 1357。使用 HAL_UART_Transmit_IT()
的代码有什么问题?
第一个:
如您的 previous question 空超时的答案中所述,仅排除等待标志状态。如果你打开 HAL_UART_Transmit
代码 - 你会看到当你发送 1 个字节而没有超时时,不会有任何阻塞状态!
第二个:
从巨大的 HAL 函数及其回调中 send/receive 一个字节不是真正的方法。我猜:接下来你的问题将 "how i must implement parse there?"。我希望你不要在 IRQ 回调中插入你的解析函数!
所以通常你需要缓冲区。使用循环缓冲区是个好主意。
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 */
}
第三个!!!
关于这个:
Why HAL_UART_Transmit_IT not help/work?
因为太慢了!如果您尝试计算 HAL_BUSY
个结果:
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
}
}
当您在数据交换后在调试器中暂停 MCU 时 - 您会感到惊讶:它将等于丢失的字符数。