从有线到无线——蓝牙来了
上篇博客我们学完了 DMA + 空闲中断,串口收发效率达到了极致。但一直是有线连接
这篇,我们把线拔掉——用蓝牙实现无线通信。
先回顾一下过去几篇的通信方式演进:
1 | 串口轮询: STM32 ←──USB线──→ 电脑串口助手 CPU全程搬运 |
蓝牙基础
经典蓝牙 vs BLE
蓝牙分为两种:
| 经典蓝牙 (Classic) | 低功耗蓝牙 (BLE) | |
|---|---|---|
| 典型设备 | 蓝牙耳机、音箱 | 运动手环、体测设备 |
| 数据传输方式 | 持续传输(音频流) | 间歇性同步(传感器数据) |
| 功耗 | 较高 | 极低(纽扣电池可用数月) |
| 我们的模块 | — | BT24(BLE串口透传模块) |
主机与从机
蓝牙通信分两端:
1 | ┌─────────────┐ ┌─────────────────┐ |
蓝牙串口透传模块——BT24
蓝牙协议本身很复杂(GAP 广播、GATT 服务、Service/Characteristic 等),但keysking的学习套件里有一枚 BT24 蓝牙串口透传模块。
“透传” 的意思很简单:
1 | 手机 app 发送 "R1" |
模块把复杂的蓝牙协议全部封装起来,对外只暴露一个串口。对 STM32 来说,操作蓝牙模块 = 操作串口——前面学的所有串口知识(轮询、中断、DMA、不定长接收)全部可以直接复用。
硬件连接
学习板上专门为蓝牙模块引出了 USART3 的接口:
1 | 学习板 BT24 蓝牙模块 |
使用学习套件附赠的 4pin 杜邦线,一端接学习板的 USART3 接口,另一端接蓝牙模块。务必注意线序:VCC ↔ 5V,GND ↔ GND,TX ↔ RX(交叉),RX ↔ TX(交叉)。
CubeMX 配置
新建工程
芯片选 STM32F103C8T6,工程名设为 Ble。
配置调试接口
System Core → SYS → Debug: Serial Wire(老规矩)。
配置 USART3——蓝牙通信通道
在 Connectivity → USART3 中,Mode 设为 Asynchronous:
| 参数 | 值 | 说明 |
|---|---|---|
| Baud Rate | 9600 | ⚠️ BT24 模块默认波特率是 9600,不是 115200! |
| Word Length | 8 Bits | 标准配置 |
| Parity | None | 无校验 |
| Stop Bits | 1 | 1位停止位 |
为什么是 9600 而不是 115200? BT24 蓝牙模块出厂默认波特率是 9600。蓝牙无线传输速率有限,9600 已经足够。如果设成 115200,模块和 STM32 的波特率不匹配,收到的将全是乱码。
开启 USART3 中断 + DMA
在 NVIC Settings 中勾选 USART3 global interrupt。
在 DMA Settings 中,点击 Add 添加两个 DMA 通道:
USART3_TX(发送):
| 参数 | 值 |
|---|---|
| Direction | Memory To Peripheral |
| Priority | Low |
| Mode | Normal |
| Data Width | Byte |
USART3_RX(接收):
| 参数 | 值 |
|---|---|
| Direction | Peripheral To Memory |
| Priority | Low |
| Mode | Normal |
| Data Width | Byte |
CubeMX 会自动分配 DMA1 的通道 2 给 USART3_RX,通道 3 给 USART3_TX。
配置 LED 引脚
三个 LED 和之前完全一样:
| 引脚 | User Label |
|---|---|
| PB0 | LED_RED |
| PA7 | LED_GREEN |
| PA6 | LED_BLUE |
均设为 GPIO_Output(推挽输出、无上下拉、低速)。
生成代码
Project Manager 中 Toolchain 选 CMake,点击 Generate Code。
VS Code 中编写核心代码
和上篇(DMA+空闲中断)代码的对比
这篇的代码和上篇很多一样——只是串口从 USART2 换成了 USART3,波特率从 115200 换成了 9600,然后再加上了数据包解析逻辑。
| 变化点 | 第10集 (DMA串口) | 本篇 (蓝牙) |
|---|---|---|
| 使用的串口 | USART2 | USART3 |
| 波特率 | 115200 | 9600(BT24 默认) |
| 接收函数 | HAL_UARTEx_ReceiveToIdle_DMA |
相同 |
| 回调函数 | HAL_UARTEx_RxEventCallback |
相同 |
| 新增 | — | 数据包解析(包头+长度+校验和) |
main.c 完整代码
1 | /* USER CODE BEGIN PV */ |
代码讲解
用到的 HAL 函数:
| 函数 | 作用 |
|---|---|
HAL_UARTEx_ReceiveToIdle_DMA(&huart3, buf, maxlen) |
启动 USART3 的不定长 DMA 接收(空闲中断触发回调) |
HAL_UART_Transmit_DMA(&huart3, buf, len) |
用 DMA 从 USART3 发送数据(回显给蓝牙模块) |
__HAL_DMA_DISABLE_IT(&hdma_usart3_rx, DMA_IT_HT) |
关闭 DMA 接收过半中断(防重复回调) |
HAL_GPIO_WritePin(...) |
老朋友——控制 LED |
逻辑流程(按回调函数中的执行顺序):
1 | 蓝牙数据到达 USART3 |
通信协议设计
本次的数据包格式比之前(”R1、G0”文本协议)更正规——采用了十六进制二进制协议:
数据包结构
1 | ┌────────┬────────┬──────────┬──···──┬────────┐ |
| 位置 | 字段 | 说明 |
|---|---|---|
receiveData[0] |
包头 (Header) | 固定 0xAA,标识一帧数据的开始 |
receiveData[1] |
数据包长度 | 包括包头、长度位、数据和校验和的总字节数 |
receiveData[2] ~ [Size-2] |
数据段 (Payload) | 每2字节一组:[灯号, 状态],可有多组 |
receiveData[Size-1] |
校验和 (Checksum) | 前 Size-1 字节的累加和,取低8位 |
数据段编码
| 灯号 | 含义 |
|---|---|
0x01 |
🔴 红色 LED |
0x02 |
🟢 绿色 LED |
0x03 |
🔵 蓝色 LED |
| 状态 | 含义 |
|---|---|
0xFF |
亮起 |
0x00 |
熄灭 |
示例
让红灯亮起:
1 | 0xAA 0x05 0x01 0xFF 0xAF |
让红灯熄灭、绿灯亮起:
1 | 0xAA 0x07 0x01 0x00 0x02 0xFF 0x9D |
同时控制红蓝两灯(红灯亮、蓝灯灭):
1 | 0xAA 0x07 0x01 0xFF 0x03 0x00 0xAD |
校验和(Checksum)的原理
“校验和”是一种最简单也最常用的数据完整性校验方法:
1 | 发送方: |
蓝牙调试——波特律动串口助手的蓝牙模式
这次不用插 USB 线了!
电脑端(蓝牙串口助手)
keysking 在波特律动串口助手上专门添加了蓝牙调试功能:
- 确保电脑蓝牙已开启
- 打开波特律动串口助手
- 点击蓝牙图标 → 从普通串口模式切换为蓝牙串口模式
- 点击选择蓝牙 → 扫描附近蓝牙设备
- 找到 BT24 → 点击配对
- 配对成功后,就可以像用普通串口一样收发数据了!
如果扫描不到 BT24,可以按一下蓝牙模块上的复位按钮再试。
自动计算校验和
手动算校验和太痛苦。波特律动串口助手支持自动计算校验和:
- 点击校验算法选择 → 选择 校验和 (Checksum)
- 每次发送数据时自动计算并附加校验字节
- 再也不用打开计算器了!
手机端(蓝牙调试助手)
也可以用手机上的 BLE 调试 app:
- 先在电脑端断开蓝牙连接(或按模块复位键)
- 手机开启蓝牙 + 授予 app 精确定位权限
- 配套资料包中找到
BT24 蓝牙调试助手.apk - 安装后选择透传模式 → 搜索 BLE → 找到 BT24 → 连接
- 输入十六进制数据即可发送
效果测试
测试1:电脑蓝牙 → 让红灯亮起
在波特律动串口助手(蓝牙模式)的发送区输入 Hex:
1 | AA 05 01 FF AF |
点击发送 → 🔴 红灯亮起!同时收到回显 AA 05 01 FF AF。
测试2:自动校验和 → 红灯灭 + 绿灯亮
输入 Hex(注意长度改为 7):
1 | AA 07 01 00 02 FF |
选择校验和算法后,助手自动在末尾补上 9D → 发送 → 🔴 红灯灭,🟢 绿灯亮!
测试3:手机蓝牙 → 全灯控制
手机上输入 Hex AA 07 01 FF 02 FF 03 FF + 自动校验和 → 三灯全亮!✅
| 发送内容 | 效果 |
|---|---|
AA 05 01 FF + 校验和 |
🔴 红灯亮 |
AA 05 01 00 + 校验和 |
🔴 红灯灭 |
AA 05 02 FF + 校验和 |
🟢 绿灯亮 |
AA 05 03 FF + 校验和 |
🔵 蓝灯亮 |
AA 07 01 FF 02 00 + 校验和 |
🔴 红灯亮 + 🟢 绿灯灭 |
AA 07 01 FF 02 FF 03 FF + 校验和 |
三灯全亮 |
效果展示视频:
https://www.bilibili.com/video/BV1gaGc6WEYQ/?vd_source=5a111e8d49f70e532d36ddb73be3d0aa
解读初始化代码
MX_USART3_UART_Init —— 蓝牙串口初始化
1 | static void MX_USART3_UART_Init(void) |
| 行 | 代码 | 和上篇的区别 |
|---|---|---|
| (1) | .Instance = USART3 |
🔴 USART2 → USART3(蓝牙模块接在 USART3 上) |
| (2) | .BaudRate = 9600 |
🔴 115200 → 9600(BT24 模块默认波特率) |
| 其余 | 数据位8、停止位1、无校验…… | ✅ 与之前完全相同 |
⚠️ 波特率必须匹配——BT24 默认 9600,如果把 STM32 的 USART3 设成 115200,两边波特率不同,通信会完全失败。
MX_DMA_Init —— DMA 初始化
1 | static void MX_DMA_Init(void) |
| 对比 | 上篇(USART2) | 本篇(USART3) |
|---|---|---|
| RX 通道 | DMA1_Channel6 | DMA1_Channel2 |
| TX 通道 | DMA1_Channel7 | DMA1_Channel3 |
每个 USART 外设的 DMA 请求绑定到不同的 DMA 通道。USART3_RX 对应 DMA1 的通道 2,USART3_TX 对应通道 3——这是 STM32 硬件设计固定的。
USART3 的引脚映射
USART3 的 TX 和 RX 分别使用 PB10 和 PB11(不像 USART2 用 PA2/PA3)。在 HAL_UART_MspInit 中可以看到:
1 | // 在 stm32f1xx_hal_msp.c 中 |
代码结构总览
1 | 全局变量区: |
和上一篇代码的三大变化
| # | 变化 | 第10集 | 本篇 |
|---|---|---|---|
| 1 | 串口号 | USART2 | USART3(蓝牙模块接在这里) |
| 2 | 波特率 | 115200 | 9600(BT24 模块默认) |
| 3 | 数据解析 | 无(直接回显) | 新增:包头验证 + 长度验证 + 校验和 + 多组指令解析 |
核心的 DMA+空闲中断接收机制 —— HAL_UARTEx_ReceiveToIdle_DMA + HAL_UARTEx_RxEventCallback —— 完全一样。这说明一通百通,掌握了串口通信的底层机制后,换一个外设(蓝牙模块)只是换一个串口号和波特率而已。
关于数据包解析的补充说明
本篇把数据包的解析直接放在了中断回调函数里。这种方式简单直接,适合以下场景:
- 数据包发送频率不高
- 每包数据量不大
- 没有复杂的粘包/拆包问题
但如果数据包发送很快、两包间隔极短,或者在实际产品中需要处理更复杂的情况,就需要:
- 建立循环缓冲区(Ring Buffer)缓存收到的数据
- 把数据解析逻辑搬到
main()的 while 循环中 - 考虑粘包(多包粘在一起)、断包(一包分两次收到)等情况
keysking 提到了配套资料包中的 UART_Command 例程展示了这种更健壮的做法,有详细注释,需要的小伙伴可以参考。
总结
这篇博客实现了三个跨越:
- 从有线到无线 —— 通过 BT24 蓝牙透传模块,STM32 可以和手机/电脑无线通信
- 从文本协议到二进制协议 —— 从 “R1”, “G0” 字符串进化为
0xAA + 长度 + 数据 + 校验和的正规二进制协议 - 从简单到健壮 —— 增加了包头验证、长度验证、校验和验证三重数据完整性保障
| 新知识点 | 说明 |
|---|---|
| 蓝牙基础 | 经典蓝牙 vs BLE、主机/从机、广播/扫描/连接 |
| 蓝牙串口透传 | BT24 模块将蓝牙协议封装为串口,STM32 操作串口=操作蓝牙 |
| USART3 配置 | 学习板第三个串口,PB10(TX)/PB11(RX),9600波特率 |
| 二进制通信协议 | 包头(0xAA) + 长度 + 变长数据段 + 校验和 |
| 校验和 (Checksum) | 累加取低8位,发送方/接收方各自计算后比对 |
| 波特律动蓝牙模式 | 串口助手可直接连蓝牙模块,支持自动校验和 |
| 中断回调中解析数据 | 简易方案,适合教学场景;生产环境建议用循环缓冲区 |
和上一篇的关系:除了串口号从 USART2 变成 USART3、波特率从 115200 变成 9600、以及新增了数据包解析逻辑外,DMA+空闲中断的核心机制完全一样。这说明真正掌握了前面的知识后,新外设的接入变得非常简单。
