引脚定义
在 STM32F429IGT6 这块开发板中:
- USART1_TX 与 PA9 复用,USART1_RX 与 PA10 复用。
- USART2_TX 与 PA2 复用,USART2_RX 与 PA3 复用。
- USART3_TX 与 PB10 复用,USART3_RX 与 PB11 复用。
HAL 库串口发送重要函数
阻塞式发送函数(新手推荐使用)
阻塞式发送函数是指在向设备发送数据时,函数会一直阻塞(即一直等待)直到数据发送完毕后才返回。在这种发送方式下,发送函数会一直等待直到发送缓冲区中的数据全部被发送出去,才会返回函数执行结果。
1
2
3
4HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, unit32_t Timeout);
/* 参数解释 */
句柄(哪个外设),指针,数据长度,超时时间非阻塞式发送函数(不推荐使用)
非阻塞式发送函数是指在向设备发送数据时,不会一直等待数据全部发送完毕后才返回,而是在发送数据时,将数据放入发送缓冲区中,然后立即返回函数执行结果,继续执行后续代码。这种方式下,发送函数不会阻塞当前线程或任务,可以提高系统的实时性和响应能力。但是,需要在发送函数中添加相应的错误处理机制,以避免因为发送过程中出现错误导致数据未发送完成的问题。
1
2
3HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
/* 可以看到这个函数里面多了 IT(interrupt 中断),且没有了 Timeout(超时)参数 */发送完毕中断回调函数
发送完毕中断回调函数指的是当使用 UART 或者其他通信方式向外部设备发送数据时,当数据全部发送完毕后,会产生一个发送完成中断(或称为发送完毕中断)。这个中断是外部设备向处理器发送的一种通知,用于告诉处理器数据已经全部发送完成,可以进行其他操作了。当发送完成中断触发时,可以通过调用对应的中断回调函数来处理这个中断事件。中断回调函数是在中断服务程序之后执行的一种特殊函数,它负责处理中断服务程序中未处理完的任务。
1
2void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
举个🌰1
要求:使用(非)阻塞式的串口发送函数,将发送缓存数组 dat_Txd 中的前 5 个数据发送到 USART1,在数据发送完成后,翻转 PB1(LED0)引脚的输出电平。
代码:
1 | /* 使用非阻塞式串口 1 发送函数 */ |
HAL 库串口接收重要函数
阻塞式接收函数(不推荐使用)
阻塞式接收函数是指在从设备接收数据时,函数会一直阻塞(即一直等待)直到接收到完整的数据后才返回。在这种接收方式下,接收函数会一直等待直到接收缓冲区中的数据长度达到预定长度,或者接收超时时间到达后才返回函数执行结果。如果接收数据长度过短或者接收速率过慢,会导致阻塞时间较长,从而影响系统的实时性和响应性能。
1
2
3
4HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, unit32_t Timeout);
/* 参数解释 */
句柄(哪个外设),指针,数据长度,超时时间非阻塞式接收函数(推荐使用)
非阻塞式接收函数是指在从设备接收数据时,不会一直等待数据接收完成后才返回,而是在接收数据时,将接收到的数据存入接收缓冲区中,然后立即返回函数执行结果,继续执行后续代码。这种方式下,接收函数不会阻塞当前线程或任务,可以提高系统的实时性和响应能力。但是,需要在接收函数中添加相应的错误处理机制,以避免因为接收过程中出现错误导致数据未接收完成的问题。
1
2
3HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
/* 可以看到这个函数里面多了 IT(interrupt 中断),且没有了 Timeout(超时)参数 */接收完毕中断回调函数
接收完毕中断回调函数指的是当使用UART或者其他通信方式接收到完整的数据后,会产生一个接收完成中断(或称为接收完毕中断)。这个中断是外部设备向处理器发送的一种通知,用于告诉处理器数据已经接收完成,可以进行其他操作了。当接收完成中断触发时,可以通过调用对应的中断回调函数来处理这个中断事件。中断回调函数是在中断服务程序之后执行的一种特殊函数,它负责处理中断服务程序中未处理完的任务。
1
2void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
举个🌰2
要求:使用非阻塞式的串口接收函数,接收USART1中的一个字节,将其保存在 dat_Rxd 变量中,在数据发送完成后,若该字节为 0x5A,则翻转 PB0(LED1) 引脚的输出电平。
代码:
1 | /* 使用非阻塞式串口 1 发送函数 */ |
例程 1:通过串口通信开关灯
要求:在 STM32F429IGT6 中进行 STM32 应用开发,完成以下功能。
- 开机后,向串口 1 发送”Hello World!”。
- 串口 1 收到字节指令”0xA1”,打开 LED0(PB1),发送”LED0 Opened!”。
- 串口 1 收到字节指令”0xA2”,关闭 LED0(PB0),发送”LED0 Closed!”。
- 在串口发送过程中,打开 LED1 作为发送数据指示灯。
步骤:
- 打开 STM32CubeMX 软件,按照
STM32CubeMX 通用配置
[1]配置完成后。将 PB0、PB1 引脚分别设置为GPIO_Out
。接着配置 USART1 的模式、参数及使能中断,如图所示:
进行
STM32CubeMX 通用配置
[1]的第 5 步:生成代码。打开工程文件。按照
Keil5 MDK 通用配置
[1]完成后开始编写代码。打开
usart1.c
文件,可以看到 huart1 的配置。图 4.3 USART1 的代码初始化 打开
main.c
文件。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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72-----------------------
↓↓↓注:main() 函数外↓↓↓
-----------------------
/* USER CODE BEGIN 0 */
/* 宏定义 LED0、LED1 亮灭 */
/* 首先定义了无符号 8 位整数型(unsigned 8-bit integer)数组的语句,常用于表示一串 ASCII 字符。其中,Tx_str1 是数组的名称,方括号中没有指定数组长度,因此该数组长度将根据初始化时赋值的元素个数进行确定 */
uint8_t Tx_str1[] = "Hello World!\r\n"; // \r:回车,\n:换行
uint8_t Tx_str2[] = "LED1 Opened!\r\n";
uint8_t Tx_str3[] = "LED1 Closed!\r\n";
/* 定义了一个无符号8位整数型(unsigned 8-bit integer)变量,通常用于存储通过UART接收到的单个字节数据,每次接收完成后将数据存储到该变量中 */
uint8_t Rx_dat = 0;
/* 中断回调函数, 在这里面实现接收到指令后的功能*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
if(Rx_dat == 0xa1)
{
LED0_ON();
/* LED1 作为指示灯,先设置高电平灭掉,然后发送数据,再点亮 */
LED1_OFF();
HAL_Delay(200);
HAL_UART_Transmit(&huart1, Tx_str2, sizeof(Tx_str2), 10000);
HAL_Delay(200);
LED1_ON();
HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
}
else if(Rx_dat == 0xa2)
{
LED0_OFF();
/* LED1 作为指示灯,先设置高电平灭掉,然后发送数据,再点亮 */
LED1_OFF();
HAL_Delay(200);
HAL_UART_Transmit(&huart1, Tx_str3, sizeof(Tx_str3), 10000);
HAL_Delay(200);
LED1_ON();
HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
}
}
}
/* USER CODE END 0 */
-----------------------
↓↓↓注:main() 函数内↓↓↓
-----------------------
/* USER CODE BEGIN 2 */
/* 功能 1:开机后向串口 1 发送 “Hello World!” */
/* LED1 作为指示灯,先设置高电平灭掉,然后发送数据,再点亮 */
LED1_OFF();
HAL_Delay(200); //加入延时函数,以便肉眼观察得到
HAL_UART_Transmit(&huart1, Tx_str1, sizeof(Tx_str1), 10000);
HAL_Delay(200); //加入延时函数,以便肉眼观察得到
LED1_ON();
/* 功能 2、3:接收串口发来的数据” */
/* &huart1 表示指向 UART1 外设的指针,&Rx_dat 表示指向存储接收数据的缓冲区的指针, 1 表示要接收的数据长度 */
HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
/* USER CODE END 2 */串口调试助手操作要点
- MicroUSB 接口要接到板子的左下角第二个口,写着 USB_232。才会显示
COM4:USB-SERIAL
。COM3
不能用于串口调试。 - 波特率要和 CubeMX 中设置的一致。
- 在发送框中输入程序中所写的字符串,然后选择
16 进制发送
。
- MicroUSB 接口要接到板子的左下角第二个口,写着 USB_232。才会显示
例程 2:定时器与串口综合训练
要求:在 STM32F429IGT6 中进行 STM32 应用开发,完成以下的功能。
开机后,LED0 与 LED1 依次点亮,然后熄灭,进行灯光检测。
系统通过串口 1 向上位机发送一个字符串”STM32F429 欢迎您!”。
LED0 作为一个秒闪灯,系统向上位机发送完字符串后,开始亮 0.5 秒,灭 0.5 秒……循环闪烁,并开始启动系统运行时间的记录,其时分秒格式为”XX:XX:XX”。
上位机通过一个由 3 个字节组成的命令帧控制 LED1 灯的开关。该命令帧的格式为”0xBF 控制字 OxFB”。0xBF 为帧头,0xFB 为帧尾,控制字的定义如下:
0xA1:打开 LED1,返回信息”XX:XX:XX LED1 打开”。
0xA2:关闭 LED1,返回信息”XX:XX:XX LED1 关闭”。
其他:返回信息”XX:XX:XX 这是一个错误指令!”。
步骤:
- 打开 STM32CubeMX 软件,按照
STM32CubeMX 通用配置
[1]配置完成后。将 PB0、PB1 引脚分别设置为GPIO_Out
。接着配置定时器 TIM2、串口 USART1,最后中断使能,如图所示:
进行
STM32CubeMX 通用配置
[1]的第 5 步:生成代码。打开工程文件。按照
Keil5 MDK 通用配置
[1]完成后开始编写代码。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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127-----------------------
↓↓↓注:main() 函数外↓↓↓
-----------------------
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
/* 第一步:宏定义 LED 灯,方便后续代码易读 */
/* 第二步:定义所要用到的字符串 */
uint8_t str1[] = "= = = = = = = Welcome to Xiaoma's codes = = = = = = =\r\n"; //开机显示
uint8_t hh = 0, mm = 0, ss = 0, ss05 = 0; //定义时分秒,以及 0.5s
uint8_t str_buff[64]; //定义一个字符串的缓冲数组,64 个字节
uint8_t Rx_dat[16]; //定义一个串口接收的数组
/* 第三步-1:功能函数:灯光检测 */
/* 跑马灯,LED0、LED1 轮流灭亮 */
void Check_LED()
{
HAL_Delay(1000);
LED0_OFF();
HAL_Delay(500);
LED1_OFF();
HAL_Delay(500);
LED0_ON();
HAL_Delay(500);
LED1_ON();
HAL_Delay(500);
}
/* 第五步-2:重写定时器 TIM2 的中断回调函数,使 LED0 按 0.5s 间隔闪烁,0.5 是在 CubeMX 中设置好的 500ms 产生一次中断 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
LED0_TOG();
/* 把时间变化记录到字符串时分秒中 */
/* 逻辑:当记录两次 ss05,则 ss05 清零,记录 1s;当记录 60 次 ss,则 ss 清零,记录 1min;当记录 60 次 1min,则 mm 清零,记录 1h。依此循环*/
ss05++;
if(ss05 == 2)
{
ss05 = 0;
ss++;
if(ss == 60)
{
ss = 0;
mm++;
if(mm == 60)
{
mm = 0;
hh++;
}
}
}
}
/* 第六步-2:重写非阻塞式接收字符串的中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* 判断串口是否为 USART1 */
if(huart->Instance == USART1)
{
/* 判断第一个字符是否为 BF 并且第三个字符是否为 FB */
if(Rx_dat[0] == 0xBF && Rx_dat[2] == 0xFB)
{
/* 使用 switch:case/break 来判断第二个字符是什么,共有三组判断 */
/* 此处也可以使用 if/else 来判断,但使用 switch 使代码更整洁 */
switch(Rx_dat[1])
{
/* 接收到 a1 时,则 LED1 关闭 */
case 0xa1:
LED1_OFF();
/* 要想使用 sprintf() 函数,需引入头文件 #include "stdio.h" */
/* %d 是占位符,在双引号的后面写对应的参数 */
/* 开头定义 str_buff 为无符号 8 位整型(uint8_t),此处使用 (char *) 转化为字符型指针,因为 sprintf() 函数需要的参数是字符型指针*/
sprintf((char *)str_buff, "%d:%d:%d LED1 关闭!\r\n", hh, mm, ss);
break;
/* 接收到 a1 时,则 LED1 关闭 */
case 0xa2:
LED1_ON();
sprintf((char *)str_buff, "%d:%d:%d LED2 打开!\r\n", hh, mm, ss);
break;
default:
sprintf((char *)str_buff, "%d:%d:%d 这是一个错误的命令!\r\n", hh, mm, ss);
break;
}
/* 向串口发送缓冲区 str_buff 字符串 */
HAL_UART_Transmit(&huart1, str_buff, sizeof(str_buff), 10000);
/* 补一个接收中断函数,因为还要继续接收串口调试助手发来的字符串,同例程 1*/
HAL_UART_Receive_IT(&huart1, Rx_dat, 3);
}
}
}
/* USER CODE END 0 */
-----------------------
↓↓↓注:main() 函数外↓↓↓
-----------------------
/* USER CODE BEGIN 2 */
/* 第三步-2:灯光检测功能运行 */
Check_LED();
/* 第四步:发送 str1 到串口调试助手*/
HAL_UART_Transmit(&huart1, str1, sizeof(str1), 10000); //阻塞式发送
/* 第五步-1:启动定时器 TIM2 中断 */
/* 此函数在 main.c 关联的 stm32f4xx_hal_tim.h 文件的最下面可以找到 */
HAL_TIM_Base_Start_IT(&htim2);
/* 第六步-1:采用非阻塞式接收字符串 */
/* 接收到的字节放到 Rx_dat,当接收到完整的三个字节后,进入串口接收完成中断,然后调用它的回调函数 */
HAL_UART_Receive_IT(&huart1, Rx_dat, 3);
/* USER CODE END 2 */
参考
- 本文标题:STM32 HAL 库实现串口通信
- 创建时间:2023-04-10 10:59:03
- 本文链接:2023/04/10/056-STM32-HAL-库实现串口通信/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!