STM32CubeIDE新建项目过程记录备忘(五)中断方式的USART串口通信
我的野火开发板上有CH340串口芯片,CH340的RXD和TXD与STM32的PA9、PA10已经连接。
-
定义GPIO管脚和设置串口参数
在STM32CubeIDE新建项目过程记录备忘(一) 创建一个基础的模板-CSDN博客里面,我已经将PA9和PA10配置为USART1的TX和RX,并完成了串口参数的设置。
-
设置中断
-
编写代码
-
生成代码
黄齿轮生成代码。
打开main.c,可以看到usart1的初始化函数,串口的参数全部在里面:
以及,在stm32f1xx_it.c中的串口中断服务函数:
-
引入必要的头文件
在main.c中引入stdio.h和string.h
#include <stdio.h> /*比如printf()等常用函数在这里*/
#include <string.h> /*字符串处理*/
-
设置缓冲区和完成标志
uint8_t rx_buffer[100]; // 接收缓冲区uint8_t rx_data; // 接收数据变量uint8_t tx_buffer[100]; // 发送缓冲区volatile uint8_t rx_complete = 0; // 接收完成标志
-
启动接收中断
-
用一条字符串测试一下发送
sprintf((char*)tx_buffer, "STM32 USART Communication Demo"); //将需要发送的字符格式化并写入字符数组(缓冲区)
HAL_UART_Transmit_IT(&huart1, tx_buffer, strlen((char*)tx_buffer)); //发送字符
仿真软件接收到了发送的字符串:
这里注意:波特率之前设置的115200,不能正常通信,改成19200后正常,这在工程中很常见,如果不能正常通信就降低波特率试试。
-
测试一下接收
在main.c的发送代码后加一条接收的代码:
发送和接收的这段完整代码:
/* USER CODE BEGIN 2 */// 启动第一个接收中断HAL_UART_Receive_IT(&huart1, &rx_data, 1);//发送一条字符串sprintf((char*)tx_buffer, "STM32 USART Communication Demo"); //将需要发送的字符格式化并写入字符数组(缓冲区)HAL_UART_Transmit_IT(&huart1, tx_buffer, strlen((char*)tx_buffer)); //使用中断发送字符HAL_UART_Receive_IT(&huart1, rx_buffer, sizeof(rx_buffer)); //使用中断接收字符/* USER CODE END 2 */
接收函数的解释:
当接收完成后,会调用HAL_UART_RxCpltCallback()这个回调函数,函数的原型在stm32f1xx_hal_uart.c内,看得出它是个弱定义函数,需要重新定义后才能使用。
重新定义的回调函数一般放在文件stm32f1xx_it.c内:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //usart接收中断回调函数
{if(huart->Instance == USART1){HAL_UART_Transmit_IT(&huart1,rx_buffer, sizeof(rx_buffer)); //将接收到的内容原样发送出去HAL_UART_Receive_IT(&huart1, rx_buffer, sizeof(rx_buffer)); //使用中断接收字符}
}
看得出,在HAL_UART_RxCpltCallback()这个回调函数内又运行了一次接收指令:HAL_UART_Receive_IT(&huart1, rx_buffer, sizeof(rx_buffer)),目的是开启新的接收任务。
总结一下本例接收的过程:
接收函数HAL_UART_Receive_IT()-->当接收到的数据达到预设字节数,产生中断,调用中断服务函数HAL_UART_RxCpltCallback()-->在中断服务函数内执行特定操作,本例的操作是将接收到的内容又发送出去,当然也可以是任何其他,比如计算、存储、操作GPIO等-->在中断服务函数内开启下一次接收任务。
需要注意的是:要外部声明一下接收缓冲区,不然会报错。
extern uint8_t rx_buffer[100]; //usart1接收缓冲区
运行结果:
在串口仿真软件XCOM中不停点击“发送”,每次发送一个字符串“abcdefghij”,当字符总数够100个后,stn32将接收到的100个字符发送给XCOM。
当然了,在工程中,接收到的每帧数据不一定总是确定长度的,使用本例的代码就不能满足所有的场景需求,这就是下一篇的任务:接收不定长数据。