第一步:STM32CubeIDE 配置 (.ioc)
我们要开启串口的中断“开关”:
- 打开 Connectivity -> USART1。
- 点击 NVIC Settings 选项卡。
- 勾选 USART1 global interrupt 后的 Enabled 框。
- 保存(Ctrl + S)生成代码。
第二步:编写接收逻辑 (main.c)
中断接收的逻辑是:你先告诉单片机“你听着,收到一个字节就叫我”,然后单片机去跑 while(1)。当电脑发来数据,单片机自动跳进“回调函数”执行任务。
1. 定义接收缓存
在 main.c 的 USER CODE BEGIN PV (Private Variables) 区域定义一个变量来存放收到的字符:
|
|
2. 开启“听力”模式
在 main 函数的 while(1) 之前,调用一次中断接收函数:
|
|
3. 编写“收到命令后做什么” (回调函数)
在 main.c 最下方的 USER CODE BEGIN 4 区域,重写回调函数。我们要实现:电脑发 ‘1’ 亮灯,发 ‘0’ 灭灯。
|
|
注意这里使用了 printf,需要查看 [[ZZZ-14-串口通讯UART]] 中关于 printf 的设置
第三步:原理图解 (中断流程)
第四步:串口调试助手测试
- 烧录程序。
- 打开串口调试助手,波特率 115200。
- 在发送框输入字符
1(注意是字符模式,不是十六进制),点击发送。- 现象:板子上的 PC13 灯亮了,助手屏幕显示
LED ON!。
- 现象:板子上的 PC13 灯亮了,助手屏幕显示
- 在发送框输入字符
0,点击发送。- 现象:板子上的 PC13 灯灭了,助手屏幕显示
LED OFF!。
- 现象:板子上的 PC13 灯灭了,助手屏幕显示
避坑指南:为什么我的单片机只听一次话?
在代码里会看到反复强调最后一行 HAL_UART_Receive_IT。
- 原因:HAL 库的中断接收是“单次任务制”。当你收完指定的 1 个字节后,中断就会自动关闭。
- 解决:必须在回调函数结束前,再次调用该函数,相当于给单片机续一份“听力合规”,它才能持续监听。
进阶挑战:如何接收一个“单词”
刚才实现的例子是“一个字节”触发一次中断。但现实中,我们经常需要接收一串字符,比如发送 "ON" 开启,发送 "OFF" 关闭。作为初学者,处理字符串接收有两种最常用的思路:
方案 A:字节拼接法(最基础,锻炼逻辑)
- 原理:依然是 1 个字节触发一次中断。
- 逻辑:在单片机里准备一个“小篮子”(数组)。每收到一个字节,就把它丢进篮子,直到收到了回车符
\n,说明一句话说完了。然后用strcmp函数比较这个篮子里装的是不是"ON"。
方案 B:空闲中断法(最优雅,专业用法)
这是处理不定长数据(比如一条指令有时长,有时短)最优雅、最工业级的方案。
- 原理:利用 STM32 串口的一个高级特性——IDLE(空闲中断)。
- 逻辑:单片机会盯着串口线,如果发现超过 1 个字节的时间没有新数据进来了,它就认为“这串话发完了”,然后一次性把整个数组交给你处理。
下面开始方案 B 空闲中断法的学习
1. 为什么它最“优雅”?
- 传统中断:像“强迫症”,必须数够 10 个字节才告诉你。如果对方只发了 5 个,你就永远在死等。
- 空闲中断:像“观察员”。它不管对方发了多少,它只盯着信号线。只要线上一段时间没有波动了(空闲了),它就认为“这串话发完了”,立刻叫醒 CPU。
最强组合:UART IDLE + DMA 通常我们会配合 DMA(直接存储器访问)使用。DMA 就像一个搬运工,数据一到就自动搬到内存里,不占用 CPU 资源。只有当一串话全说完了(触发 IDLE),CPU 才出来看一眼。
2、核心原理图
3. STM32CubeIDE 配置步骤
我们要用到 HAL 库中一个非常强大的“新”函数:HAL_UARTEx_ReceiveToIdle_DMA(这里的 Ex 代表 Extended 扩展功能)。
- 开启 DMA:
- 在
.ioc文件中,点击 Connectivity -> USART1。 - 点击 DMA Settings 选项卡 -> 点击 Add。
- 选择 USART1_RX。
- Priority 选 Medium(中等)。
- Mode 选 Normal(普通模式,接收完处理后再重开)。
- 在
- 确认 NVIC:
- 在 NVIC Settings 选项卡中,确保 USART1 global interrupt 已勾选(DMA 配合空闲中断也需要开启串口总中断)。
- 生成代码:
Ctrl + S。
4. 编写代码 (main.c)
第一步:定义缓冲区
|
|
第二步:启动接收(在 main 函数循环前)
|
|
第三步:编写回调函数
注意!空闲中断的回调函数和之前的 RxCpltCallback 不同,它叫 RxEventCallback。
|
|
5. 这种方案的“职业美感”体现在哪?
- Size 变量:这是神来之笔。如果你发
"Hello",Size就是 5;如果你发"How are you",Size就是 11。你再也不用自己去写循环判断\n了。 - 极低 CPU 占用:在数据传输过程中,CPU 甚至可以去“睡觉”(进入低功耗模式)。只有整句话说完了,CPU 才醒过来处理一下。
- 高可靠性:不再会因为数据发得太快而导致丢字节(DMA 硬件级搬运)。
常见避坑
- 重载函数名:一定要写对
HAL_UARTEx_RxEventCallback,拼错一个字母它都不会运行。 - 缓冲区溢出:如果你定义的
rx_buf是 100,但对方一次性发了 150 个字节,DMA 会在满 100 的时候强制触发一次中断。所以缓冲区要设得比最长的指令稍微大一点。
现在你可以试试发送 "Hello STM32"。你会发现,无论你发多长或多短,单片机都能精准地识别出长度并回复你。并且发送 ON,灯会亮、发送 OFF,灯会灭