
🎬 渡水无言 :个人主页渡水无言
❄专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》
❄专栏传送门 : 《freertos专栏》 《STM32 HAL库专栏》
⭐️流水不争先,争的是滔滔不绝
📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生
| 省级优秀毕业生获得者 | csdn新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生
在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连
目录
[1.1 物理层连接规则](#1.1 物理层连接规则)
[1.2 数据帧格式](#1.2 数据帧格式)
[1.3 波特率](#1.3 波特率)
[2.1 准备工具](#2.1 准备工具)
[2.2 STM32CubeMX 配置步骤](#2.2 STM32CubeMX 配置步骤)
[2.5、Keil 代码编写](#2.5、Keil 代码编写)
[2.5.2、STM32 接收串口数据的核心函数](#2.5.2、STM32 接收串口数据的核心函数)
前言
通过前两期的HAL库专栏博客我们讲解了STM32 的 GPIO工作模式的8种工作模式,并完成了从 CubeMX 配置到代码编写的完整 LED 闪烁实验,掌握了 HAL 库中 GPIO 控制与延时函数的使用方法、以及使用hal库完成常用的按键输入实验。本期博客我们开始学习stm32中常用的一些通信协议,这次博客我们首先来学习一下串口通信。
一、串口通信核心基础知识
1.1 物理层连接规则
串口(UART/USART)通信是单片机与外设、电脑通信最常用的方式,核心是通过两根线(Tx/Rx)实现异步数据传输:
Tx(Transmit):发送引脚,负责向外输出数据;
Rx(Receive):接收引脚,负责接收外部数据;
设备 A 的 Tx ↔ 设备 B 的 Rx,设备 A 的 Rx ↔ 设备 B 的 Tx(必须交叉,否则数据收不到)。
具体如下图所示:

1.2 数据帧格式
串口传输的数据不是裸数据,而是按固定格式打包的 "帧",如下图所示:

常用的四种数据帧格式(打绿勾的更常用):

起始位:1 位低电平,标记数据开始;
数据位:8/9 位(8 位最常用),实际要传输的有效数据;
校验位(可选):奇校验 / 偶校验 / 无校验,用于验证数据传输是否出错;
奇校验:整个帧中 "1" 的个数必须是奇数(不足则校验位补 1);
偶校验:整个帧中 "1" 的个数必须是偶数;
示例:传输10101010(4 个 1),奇校验会自动补 1,使总 1 的个数为 5(奇数);若传输中数据变为10111010(5 个 1),校验位仍为 1,总个数 6(偶数),校验失败,判定数据出错。
1.3 波特率
波特率越高,数据的传输速度越快
波特率 = 每秒传输的二进制位数 如下图所示:

二、实战:单片机向电脑发送数据
目标:通过 STM32 的 USART1 向电脑串口调试助手发送 16 进制和 ASCII 字符数据,验证串口通信基本功能。
2.1 准备工具
硬件:STM32 开发板、USB 转 TTL 模块(CH340/PL2303)、杜邦线;
软件:STM32CubeMX、Keil5、串口调试助手(SSCOM / 安信可串口助手)。
2.2 STM32CubeMX 配置步骤
选择芯片------选择调试串口------选择左侧的"Connectivity"(连接性),
可以看到单片机有三个串口USART1、USART2、USART3:

Mode表示工作模式,我们需要为串口选择工作模式:

选择
Asynchronous(异步模式,其他模式不用管)。选择异步模式之后
cubeMX自动为我们分配好了USART的引脚。

配置后 CubeMX 自动分配引脚:USART1_TX→PA9,USART1_RX→PA10;

此时还可以看到,在界面的中底部,出现了一些参数:
串口参数(默认即可,无需修改):
波特率:115200 Bits/s;
数据位:8 Bits;
校验位:None;
停止位:1 Bit;
收发模式:TX and RX(双向)。


可以发现,CubeMX已经为我们自动配好了引脚参数

这里我们按照默认设置,不做调整。点击Project Manager,输入名字、位置、工具链:

2.3、硬件连接
硬件连接如下图所示:

2.4、串口调试助手设置
串口收发双方的数据帧格式应该一致,并打开串口:

2.5、Keil 代码编写
2.5.1、总代码如下
在main.c中添加接收和 LED 控制逻辑:
/* USER CODE BEGIN PV */
uint8_t recv_data; // 接收数据缓冲区
/* USER CODE END PV */
/* USER CODE BEGIN WHILE */
while (1)
{
/* 接收1字节数据,超时时间100ms */
if(HAL_UART_Receive(&huart1, &recv_data, 1, 100) == HAL_OK)
{
/* 根据接收的数据控制LED */
if(recv_data == '1') // 收到字符'1',点亮LED(PC13低电平点亮)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
// 回传确认信息
uint8_t ack_on[] = "LED ON\r\n";
HAL_UART_Transmit(&huart1, ack_on, sizeof(ack_on)-1, 100);
}
else if(recv_data == '0') // 收到字符'0',熄灭LED
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
// 回传确认信息
uint8_t ack_off[] = "LED OFF\r\n";
HAL_UART_Transmit(&huart1, ack_off, sizeof(ack_off)-1, 100);
}
else // 收到无效指令
{
uint8_t ack_err[] = "Invalid Command\r\n";
HAL_UART_Transmit(&huart1, ack_err, sizeof(ack_err)-1, 100);
}
}
HAL_Delay(100); // 短延时,降低CPU占用
/* USER CODE END WHILE */
}
2.5.2、STM32 接收串口数据的核心函数
/*
* @brief 串口接收数据(阻塞式)
* @param huart: 串口句柄
* @param pData: 接收数据缓冲区
* @param Size: 接收数据长度
* @param Timeout: 超时时间
* @retval HAL状态
*/
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
阻塞式接收:若超时时间内未收到数据,函数返回超时状态,程序继续执行。
2.5.3、STM32串口发送数据核心函数
HAL_StatusTypeDef HAL_UART_Transmit(
UART_HandleTypeDef *huart, // 串口句柄指针,如 &huart1
uint8_t *pData, // 要发送的数据缓冲区指针
uint16_t Size, // 要发送的字节数
uint32_t Timeout // 超时时间(ms),HAL_MAX_DELAY 表示无限等待
);
通过串口向外发送数据。
三、测试验证
- 重新编译代码,下载到开发板;
- 串口调试助手配置不变,打开串口;
- 发送指令:
- 发送
1→ LED 点亮,串口助手收到LED ON; - 发送
0→ LED 熄灭,串口助手收到LED OFF; - 发送其他字符 → 收到
Invalid Command。
- 发送
总结
本次博客完成了从串口基础原理到实际实战。