STM32 零基础可移植教程 09:串口收一行命令,用 led on 控制 LED上一篇我们把 USART RX 中断跑通了。电脑发一个字符,STM32 能收到,并且能打印:RX: A, hex: 0x41这一步说明串口接收链路没问题。但真实项目里,我们很少只收一个字符。更常见的是在串口助手里输入一行命令:led on led off led toggle然后让板子执行对应动作。这一篇就把单字节接收往前推一步:用 USART RX 中断接收一行命令,然后解析命令控制 LED本篇还是不讲 DMA、不讲 IDLE、不讲复杂协议。先把“收一行字符串”这件事跑通。本篇目标最终现象:串口助手输入:led onSTM32 返回:led on OK: LED on同时 LED 点亮。输入:led offSTM32 返回:led off OK: LED off同时 LED 熄灭。输入:helpSTM32 返回命令列表。本篇跑通标准:Keil 编译通过;串口能打印启动信息;串口助手输入help后能看到命令列表;输入led on后 LED 点亮;输入led off后 LED 熄灭;输入led toggle后 LED 翻转;能说清楚一行命令是怎么从一个个字节拼出来的;能说清楚换 USART、换 LED 引脚时要改哪里。准备工作建议从上一篇工程复制一份,改名为:09_usart_line_command上一篇已经完成:USART 基本配置;USART global interrupt;HAL_UART_Receive_IT()单字节中断接收;printf()串口输出;串口助手接线和参数设置。这一篇在它的基础上加两个东西:还需要第 02 篇里的 LED 应用层代码:Core/Inc/app_led.h Core/Src/app_led.c如果你的工程里还没有这两个文件,本篇后面也会给出。硬件连接本篇用到两个硬件功能:USART LEDUSART 接线仍然是:|STM32|USB 转 TTL|| — | — ||USART_TX|RXD||USART_RX|TXD||GND|GND|LED 如果是板载 LED,先看原理图确认接到哪个 GPIO。如果是外接 LED,注意串联限流电阻。本系列仍然建议把 LED 引脚的 CubeMXUser Label设置为:LED这样 CubeMX 会在main.h里生成:#define LED_Pin ...#define LED_GPIO_Port ...应用层代码就能直接用LED_Pin和LED_GPIO_Port。先理解一行命令怎么收上一章我们每次只收 1 个字节。比如电脑发送:led onSTM32 实际收到的不是“一整句”,而是一个个字节:l e d 空格 o n 回车 换行所以我们要做一件事:每收到1个字节,就先放进缓冲区 遇到\r 或\n,说明一行结束 然后把这一行交给命令解析函数led on这行命令在缓冲区里大概是这样:['l']['e']['d'][' ']['o']['n']['\0']最后这个\0很重要。C 语言字符串必须以\0结束,否则strcmp()、printf("%s")这些函数不知道字符串到哪里结束。本篇的处理方式是:app_uart 只负责接收一行字符串 app_command 只负责解析这一行命令 app_led 只负责控制 LED main.c 只负责把几个模块串起来不要把所有逻辑都堆在main.c。这样后面扩展beep、adc read、pwm 50这些命令时,也比较清楚。CubeMX 配置步骤1. USART 保持上一篇配置USART 仍然配置为:Mode: Asynchronous Baud Rate:115200Word Length:8Bits Parity: None Stop Bits:1Hardware Flow Control: None也就是:115200, 8N1并且要打开:USARTx global interrupt如果这一项忘了勾,串口打印可能正常,但接收回调不会进。2. LED 引脚保持 GPIO OutputLED 引脚配置为:GPIO_OutputGPIO 参数仍然建议:|配置项|推荐值|| — | — ||GPIO output level|默认关闭 LED 的电平||GPIO mode|Output Push Pull||GPIO Pull-up/Pull-down|No pull-up and no pull-down||Maximum output speed|Low||User Label|LED|LED 是高电平亮还是低电平亮,由app_led.c里的宏处理。3. 生成 Keil 工程配置完成后点击:GENERATE CODE打开 Keil 后先编译一次,确认 CubeMX 生成代码没问题。完整代码本篇一共有 3 个应用模块:Core/Inc/app_uart.h Core/Src/app_uart.c Core/Inc/app_command.h Core/Src/app_command.c Core/Inc/app_led.h Core/Src/app_led.capp_uart负责串口收一行。app_command负责解析命令。app_led负责控制 LED。1.Core/Inc/app_uart.h#ifndef APP_UART_H#define APP_UART_H#include "main.h"#include stdint.h#ifndef APP_UART_LINE_MAX#define APP_UART_LINE_MAX 64u#endifvoid App_UART_Init(void);void App_UART_Print(const char *text);void App_UART_PrintBytes(const uint8_t *data, uint16_t len);HAL_StatusTypeDef App_UART_StartReceiveIT(void);void App_UART_OnRxCplt(UART_HandleTypeDef *huart);uint8_t App_UART_ReadLine(char *line, uint16_t size);uint8_t App_UART_TakeOverflow(void);#endif这里的:#define APP_UART_LINE_MAX 64u表示一行命令最多 63 个有效字符,最后 1 个位置留给\0。新手阶段够用了。2.Core/Src/app_uart.c#include "app_uart.h"#include stdio.h#include string.h#ifndef APP_UART_HANDLE#define APP_UART_HANDLE huart1#endifextern UART_HandleTypeDef APP_UART_HANDLE;static uint8_t s_rx_it_byte=0;static char s_build_line[APP_UART_LINE_MAX];static char s_ready_line[APP_UART_LINE_MAX];static uint16_t s_build_len=0;static uint8_t s_drop_line=0;static volatile uint8_t s_line_ready=0;static volatile uint8_t s_line_overflow=0;static void App_UART_SaveReadyLine(void){uint16_t i;if((s_build_len==0u)||(s_line_ready!=0u)){s_build_len