stm32中断收发

最近在中断收发的原理与使用中还是模糊,本文主要是对中断收发的阶段性学习成果予以陈述。

之前我看的是江协写的文档,但是那边的函数实现有些怪:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
uint8_t Serial_RxFlag;
uint8_t ByteRecv;

void hhSerialSendByte(uint8_t Byte)
{
	HAL_UART_Transmit(&huart1, &Byte, 1, HAL_MAX_DELAY);
}

// 中断接收
// 复位
uint8_t Serial_getRxFlag(void){
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

// 接收中断函数
void HAL_UART_CpltCallback(UART_HandleTypeDef *huart){
	if (huart == &huart1){
		Serial_RxFlag = 1;
		HAL_UART_Receive_IT(&huart1, &ByteRecv, 1);
	}
}

它的主要问题在于还是使用阻塞式发送 HAL_UART_Transmit() 而且用了一个很奇怪的 Serial_getRxFlag() 并且这个函数在主循环中一直被调用,不太符合中断调用的目的。使用一个 Flag 太奇怪了。

此外,我在整个 main 中想要将发送也采用中断但最后走向失败。

中断式接收

中断式接收关键在于自己实现 HAL_UART_RxCpltCallback() 。因为原本的该函数是没东西的。

1
2
3
4
5
6
7
8
__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);
  /* NOTE: This function should not be modified, when the callback is needed,
           the HAL_UART_RxCpltCallback could be implemented in the user file
   */
}

该函数是接收完成后触发的回调函数。我们的实现如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
uint8_t ByteRecv;
// 接收完成后触发的中断
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	// 检查传入的 UART 句柄对应是哪个硬件外设
	if(huart->Instance == USART1)
	{
		// 接收后就发送
		HAL_UART_Transmit_IT(&huart1, &ByteRecv, 1);
		// 在这里重启接收要等待发送完成回调中在重启
	}
}

也就是在接收后检查 UART 句柄确认后就通过中断发送到串口。

使用 HAL_UART_Transmit_IT 需要 HAL_UART_TxCpltCallback() 这这里用于在发送完成后重启中断接收

1
2
3
4
5
6
7
8
9
// 发送完成后触发的中断
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART1)
	{
		// 重启接收
		HAL_UART_Receive_IT(&huart1, &ByteRecv, 1);
	}
}

实际我们在 main() 函数内放置的只有以下函数

1
	HAL_UART_Receive_IT(&huart1, &ByteRecv, 1);

这个可以启动串口接收,同时在接收到指定大小的数据时触发中断。需要注意的是这里是一次性中断,在中断后不会重新打开中断也就是接收不到新的数据了。

在这两个回调函数中都有对 huart->Instance 进行判断,这边需要做些补充说明:

这里的 Instance 存储的是 UART 寄存器的基地址具体还需要更多深入的对 UART 研究才能更好的理解。

其实这边几乎等价于江协的 huart == &huart1

以上代码基本上可以实现串口发送接收使用中断来完成了。

HAL_UART_RxCpltCallback()HAL_UART_TxCpltCallback()

这两个都是回调函数,用于在串口接收/发送完成后由系统或库进行调用。也就是说,在每次接收/发送完成后,不需要我们在主循环中调用,系统就会自动调用这个函数完成一些东西。而且,这个函数是需要我们自己去实现的。也就是说我们通过自己的实现来告诉 stm32 在接收/发送完成后需要做些什么。

在最初主函数内通过 HAL_UART_Receive_IT 函数启动串口接收,并在最后触发中断调用 HAL_UART_RxCpltCallback 回调函数。

比如说,我们要在串口接收到信息后就发送回信息,那么上文的 HAL_UART_RxCpltCallback() 就能做到这一点。只是在接收完成后调用 HAL_UART_Transmit_IT(&huart1, &ByteRecv, 1); 发送字节。上文我们提到 HAL_UART_Receive_IT 的中断是一次性的。所以我们还需要在发送完成后重新打开中断,也就是说,我们要在 **HAL_UART_TxCpltCallback** 中重新执行 HAL_UART_Receive_IT`

参考资料

  1. STM32 HAL库函数——HAL_UART_RxCpltCallback()详解
萌ICP备20241614号