uart_rx.h
/********************************************************************************* file uart_rx.h* brief 串口中断接收、环形缓冲区和简单二进制帧接收。** 本文件是团队自己写的代码放在 Core/Incuart_rx.c 放在 Core/Src。* CubeMX 重新生成代码不会覆盖这两个文件。** 使用前确认 CubeMX 已配置USARTx、USARTx global interrupt且中断服务函数中* 有 HAL_UART_IRQHandler(huartx)。本模块自己实现 HAL_UART_RxCpltCallback* 工程中不要再定义第二个同名回调函数。** --------------------------------------------------------------------------* 【串口助手发送多条可读命令回车换行结尾】** UART_Rx_ReadLine() 用于接收人能看懂的 ASCII 命令。发送端每一条命令都应以* 换行 \n 结束串口助手一般选择“发送新行”或“CRLF”即发送 \r\n。* 本模块会忽略 \r并把 \n 识别为一条命令结束。因此连续发送** LEDON\r\nPWM50\r\n12\r\n** 程序会依次得到三个独立字符串LEDON、PWM50、12。不能只使用 if* 取一次因为电脑可能一次就发送多条命令应在 while 内把已完整接收的行全部处理。** main.c 命令识别完整示例需要在 Includes 区加入 #include string.h* code* char line[32];** // 放在 while(1) 内。每轮会处理接收缓冲区中所有已收完整的命令。* while (UART_Rx_ReadLine(uart1_rx, line, sizeof(line)) UART_RX_STATUS_OK)* {* if (strcmp(line, LEDON) 0)* {* // strcmp 返回 0 表示两个字符串完全相同。* HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);* UART_Tx_SendLine(huart1, LED is ON, 100U);* }* else if (strcmp(line, LEDOFF) 0)* {* HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);* UART_Tx_SendLine(huart1, LED is OFF, 100U);* }* else if (strncmp(line, PWM, 4U) 0)* {* // strncmp(..., 4U)0 表示前四个字符正好是 P、W、M、。* // line[4] 是等号后的第一个字符例如 PWM50 中的 50。* uint32_t duty;* if ((UART_Rx_ParseU32(line[4], duty) ! 0U) (duty 100U))* {* // 这里按你的 PWM 驱动实际函数替换例如设置为 50% 占空比。* // PWM_Driver_SetDutyPercent(boost_pwm, (float)duty);* UART_Tx_SendLine(huart1, PWM updated, 100U);* }* else* {* UART_Tx_SendLine(huart1, PWM must be 0~100, 100U);* }* }* else* {* UART_Tx_SendLine(huart1, Unknown command, 100U);* }* }* endcode** 比赛时题目会规定命令字和参数格式只需要把 LEDON、PWM 等字符串及其* 对应处理动作替换成题目要求即可。若题目规定的是 AA 55 等二进制帧不使用本段* 的 ReadLine/strcmp 方法应使用后面的 ReadByte Protocol_Parser。* --------------------------------------------------------------------------** 二进制帧固定格式* HEADER[0] ... HEADER[N-1] LENGTH DATA[0] ... DATA[LENGTH-1] CHECKSUM** - HEADER帧头数组长度 N 可为 1~16 字节由题目决定。* - LENGTHDATA 区的字节数不包括帧头、长度字节和校验字节。* - CHECKSUMSUM8/XOR8 为 1 字节CRC16-Modbus 为 2 字节低字节在前。* - 校验范围默认是 LENGTH DATA不包含帧头和校验字节。** main.c 完整使用示例* code* // USER CODE BEGIN Includes* #include uart_rx.h** // USER CODE BEGIN PV* static UART_Rx_t uart1_rx;* static uint8_t uart1_rx_buffer[128]; // 中断收到的字节先暂存在这里* static Protocol_Parser_t protocol;* static Protocol_Frame_t received_frame;** // USER CODE BEGIN 2必须放在 MX_USART1_UART_Init() 之后* UART_Rx_Init(uart1_rx, huart1, uart1_rx_buffer, sizeof(uart1_rx_buffer));* UART_Rx_Start(uart1_rx); // 开始单字节中断接收** // 题目若定义帧AA 55 LEN DATA... SUM8则这样写* static const uint8_t frame_header[] {0xAAU, 0x55U};* Protocol_Parser_Init(protocol, frame_header, sizeof(frame_header),* PROTOCOL_CHECKSUM_SUM8);** // USER CODE BEGIN 3放进 while(1)* uint8_t one_byte;* while (UART_Rx_ReadByte(uart1_rx, one_byte) UART_RX_STATUS_OK)* {* if (Protocol_Parser_FeedByte(protocol, one_byte) PROTOCOL_STATUS_FRAME_READY)* {* Protocol_Parser_TakeFrame(protocol, received_frame);** // 例如收到AA 55 03 11 22 33 69* // received_frame.length 7U;* // received_frame.data[0..6] {0xAA,0x55,0x03,0x11,0x22,0x33,0x69};* // 之后直接按数组下标使用不需要关心命令、载荷等额外结构。* // 第一个数据的下标 帧头长度 1 个 LENGTH 字节。* uint8_t first_data received_frame.data[sizeof(frame_header) 1U]; // 0x11* (void)first_data;* }* }* endcode** 比赛时只改帧头数组内容、帧头数组长度和校验方式* code* static const uint8_t frame_header[] {0x55U, 0xAAU, 0x12U, 0x34U};* Protocol_Parser_Init(protocol, frame_header, sizeof(frame_header),* PROTOCOL_CHECKSUM_XOR8);* // 可选校验方式PROTOCOL_CHECKSUM_SUM8、PROTOCOL_CHECKSUM_XOR8、* // PROTOCOL_CHECKSUM_CRC16_MODBUS、PROTOCOL_CHECKSUM_NONE。* endcode*******************************************************************************/#ifndef UART_RX_H#define UART_RX_H#ifdef __cplusplusextern C {#endif#include usart.htypedef enum{UART_RX_STATUS_OK 0,UART_RX_STATUS_EMPTY,UART_RX_STATUS_INCOMPLETE,UART_RX_STATUS_INVALID_ARG,UART_RX_STATUS_NOT_INITIALIZED,UART_RX_STATUS_BUSY,UART_RX_STATUS_HAL_ERROR,UART_RX_STATUS_BUFFER_SMALL} UART_Rx_Status_t;/* UART 中断接收对象。应用层只需定义变量并传给 Init不要手动修改成员。 */typedef struct{UART_HandleTypeDef *huart;uint8_t *buffer;uint16_t capacity;volatile uint16_t read_index;volatile uint16_t write_index;uint8_t rx_byte;volatile uint32_t overflow_count;volatile uint32_t error_count;uint8_t initialized;uint8_t running;} UART_Rx_t;UART_Rx_Status_t UART_Rx_Init(UART_Rx_t *device, UART_HandleTypeDef *huart,uint8_t *buffer, uint16_t capacity);UART_Rx_Status_t UART_Rx_Start(UART_Rx_t *device);UART_Rx_Status_t UART_Rx_Stop(UART_Rx_t *device);uint16_t UART_Rx_Available(const UART_Rx_t *device);UART_Rx_Status_t UART_Rx_ReadByte(UART_Rx_t *device, uint8_t *byte);/*** brief 从串口环形缓冲区取出一整行 ASCII 文本并转换成 C 字符串。* param device 已 Init 且 Start 的 UART 接收对象例如 uart1_rx。* param line 用户提供的 char 数组用于保存文本例如 char line[32]。* param line_capacity line 数组总容量必须写 sizeof(line)不要手写数字。* return UART_RX_STATUS_OK已得到一行其他常见返回值见下方。** 本函数适用于“串口助手文本发送”不是十六进制二进制帧解析器。* 例如电脑发送 hello 并按回车串口收到的原始字节是* 0x68 0x65 0x6C 0x6C 0x6F 0x0D 0x0A* 本函数返回后 line 内容是 hello末尾的 \r 和 \n 被自动丢弃且自动补 \0。** 返回值含义* - UART_RX_STATUS_OK已经收到 \nline 内是一条完整字符串。* - UART_RX_STATUS_EMPTY目前没有收到任何字节。* - UART_RX_STATUS_INCOMPLETE收到了一部分文本但尚未收到 \n下次 while 循环再调用。* - UART_RX_STATUS_BUFFER_SMALL完整一行长度超过 line 数组容量该行被丢弃。** main.c 使用例子* code* char line[32];* if (UART_Rx_ReadLine(uart1_rx, line, sizeof(line)) UART_RX_STATUS_OK)* {* // 电脑文本发送 hello 回车line 为 hello。* // 电脑文本发送 12 回车line[0]1line[1]2不是数值 12。* UART_Tx_SendLine(huart1, line, 100U);* }* endcode** 若题目给的是 AA 55 LEN DATA CHECKSUM 这类二进制报文请使用* UART_Rx_ReadByte() Protocol_Parser_FeedByte()不要使用本函数。*/UART_Rx_Status_t UART_Rx_ReadLine(UART_Rx_t *device, char *line, uint16_t line_capacity);/*** brief 将一行纯十进制 ASCII 文本转换为可计算的 uint32_t 无符号整数。* param text 以 \0 结尾的字符串通常来自 UART_Rx_ReadLine()例如 12。* param value 转换成功后写入数值例如 12 转为 12U。* return 1转换成功0字符串为空、含非数字字符或数值超过 uint32_t 范围。** 此函数只接受十进制非负整数允许首尾空格和开头的 号* 12、 12 、12 均成功12.5、-12、hello 均失败。** main.c 使用例子* code* char line[32];* uint32_t number;* if (UART_Rx_ReadLine(uart1_rx, line, sizeof(line)) UART_RX_STATUS_OK)* {* if (UART_Rx_ParseU32(line, number) ! 0U)* {* number number 1U; // 电脑发 12number 变成 13* UART_Tx_SendU32(huart1, number, 100U);* }* }* endcode*/uint8_t UART_Rx_ParseU32(const char *text, uint32_t *value);/*** brief 将一行 ASCII 小数文本转换为可计算的 float 浮点数。* param text 以 \0 结尾的字符串通常来自 UART_Rx_ReadLine()例如 3.287。* param value 转换成功后写入浮点结果的变量地址例如 dac_pin_voltage。* return 1U转换成功0U字符串格式错误或参数为 NULLvalue 保持原值。** 支持首尾空格、可选的 / - 号、整数部分和小数部分例如 12、3.287、* -0.5、.25。不支持科学计数法例如 1e-3、逗号小数点3,2* 和单位3.3V。发送端每条数据仍须以 \r\n 结尾。** main.c 使用案例电脑发送 3.287 并勾选“发送新行CRLF”后变量保存为 3.287f。* code* char line[32];* float dac_pin_voltage;** if (UART_Rx_ReadLine(uart1_rx, line, sizeof(line)) UART_RX_STATUS_OK)* {* if (UART_Rx_ParseFloat(line, dac_pin_voltage) ! 0U)* {* // 例如3.287 1.0 4.287* dac_pin_voltage dac_pin_voltage 1.0f;** // 参数依次为串口、浮点值、小数位数、超时 ms。* UART_Tx_SendFloat(huart1, dac_pin_voltage, 3U, 100U);* UART_Tx_SendLine(huart1, , 100U);* }* else* {* UART_Tx_SendLine(huart1, Float format error, 100U);* }* }* endcode*/uint8_t UART_Rx_ParseFloat(const char *text, float *value);UART_Rx_Status_t UART_Rx_Clear(UART_Rx_t *device);uint32_t UART_Rx_GetOverflowCount(const UART_Rx_t *device);uint32_t UART_Rx_GetErrorCount(const UART_Rx_t *device);/* 单帧 DATA 最大长度。完整数组还需容纳帧头、长度和最多两个校验字节。 */#define PROTOCOL_MAX_HEADER_LENGTH 16U /* 题目帧头超过 16 字节时只改这个数值。 */#define PROTOCOL_MAX_DATA_LENGTH 128U#define PROTOCOL_MAX_FRAME_LENGTH (PROTOCOL_MAX_HEADER_LENGTH 1U PROTOCOL_MAX_DATA_LENGTH 2U)typedef enum{PROTOCOL_CHECKSUM_NONE 0, /* 无校验帧尾没有校验字节。 */PROTOCOL_CHECKSUM_SUM8, /* LENGTH 和 DATA 累加保留低 8 位。 */PROTOCOL_CHECKSUM_XOR8, /* LENGTH 和 DATA 逐字节异或。 */PROTOCOL_CHECKSUM_CRC16_MODBUS/* CRC 初值 FFFF低字节先到。 */} Protocol_Checksum_t;typedef enum{PROTOCOL_STATUS_OK 0,PROTOCOL_STATUS_FRAME_READY, /* 一帧已校验完成可调用 TakeFrame。 */PROTOCOL_STATUS_INVALID_ARGUMENT,PROTOCOL_STATUS_NOT_READY,PROTOCOL_STATUS_LENGTH_ERROR,PROTOCOL_STATUS_CHECKSUM_ERROR,PROTOCOL_STATUS_BUSY} Protocol_Status_t;/* 这就是给业务程序用的完整帧数组。data[0] 永远是帧头的第一个字节。 */typedef struct{uint16_t length; /* data[] 实际有效字节数。 */uint8_t data[PROTOCOL_MAX_FRAME_LENGTH];/* 完整帧帧头、长度、数据、校验。 */} Protocol_Frame_t;/* 解析器内部状态。只需定义变量和调用 API不需直接修改成员。 */typedef struct{uint8_t header[PROTOCOL_MAX_HEADER_LENGTH];uint8_t header_length;uint8_t header_index;Protocol_Checksum_t checksum_type;uint8_t state;uint8_t data_length;uint8_t received_data_count;uint16_t checksum;uint8_t received_crc_low;Protocol_Frame_t completed_frame;uint8_t frame_ready;uint8_t initialized;uint32_t frame_count;uint32_t length_error_count;uint32_t checksum_error_count;} Protocol_Parser_t;/* 设置帧头数组、帧头长度和校验方式格式始终为 HEADER LENGTH DATA CHECKSUM。 */Protocol_Status_t Protocol_Parser_Init(Protocol_Parser_t *parser,const uint8_t *header, uint8_t header_length,Protocol_Checksum_t checksum_type);/* 每从 UART 环形缓冲区读到一个字节就调用一次。 */Protocol_Status_t Protocol_Parser_FeedByte(Protocol_Parser_t *parser, uint8_t byte);uint8_t Protocol_Parser_IsFrameReady(const Protocol_Parser_t *parser);/* 把已完成的一帧复制到 frame-data[]并允许接收下一帧。 */Protocol_Status_t Protocol_Parser_TakeFrame(Protocol_Parser_t *parser,Protocol_Frame_t *frame);#ifdef __cplusplus}#endif#endif /* UART_RX_H */

相关新闻

放大器非线性失真研究装置:从原理到实践的硬件设计与算法实现

放大器非线性失真研究装置:从原理到实践的硬件设计与算法实现

1. 项目概述与核心价值放大器,这个在电子世界里无处不在的“能量搬运工”,我们总希望它是个完美的线性系统——输入一个正弦波,输出一个等比例放大的、纯净的正弦波。但现实很骨感,任何实际的放大器,无论是我们DIY的运…

2026/7/30 4:23:38阅读更多 →
OpenRouter统一图像生成API:解决多模型集成碎片化难题

OpenRouter统一图像生成API:解决多模型集成碎片化难题

上周在调试一个图像生成项目时,我遇到了一个典型问题:项目需要调用多个图像生成模型,但每个模型都有不同的 API 格式、认证方式和参数规范。光是处理不同厂商的 API 差异就花了大半天时间,更不用说后续的异常处理和批量调度了。就…

2026/7/30 4:23:38阅读更多 →
网络安全入门:从零构建攻防实战框架

网络安全入门:从零构建攻防实战框架

1. 网络安全入门:从零开始的认知框架第一次接触网络安全时,我被各种术语和概念搞得晕头转向。防火墙、加密算法、渗透测试...这些词听起来都很高大上,但究竟从哪里开始学?经过三年实战,我发现建立正确的认知框架比盲目…

2026/7/30 4:23:38阅读更多 →
Electron应用菜单开发实战:从模板构建到跨平台状态管理

Electron应用菜单开发实战:从模板构建到跨平台状态管理

1. 从零开始:为什么Electron应用需要一个好菜单如果你刚开始接触Electron,可能会觉得菜单栏是个“锦上添花”的东西,先把窗口和功能做出来更重要。但很快你就会发现,事情没那么简单。一个设计得当的菜单,远不止是窗口顶…

2026/7/30 5:43:55阅读更多 →
UE5像素流送音频问题全解析:从AudioMixer配置到网页播放的完整解决方案

UE5像素流送音频问题全解析:从AudioMixer配置到网页播放的完整解决方案

1. 项目概述:UE5像素流送音频问题的根源与解决思路如果你正在用虚幻引擎5(UE5)做像素流送,并且发现打包出来的应用在网页端死活没声音,那你绝对不是一个人。这个问题困扰了太多开发者,从独立游戏制作者到企…

2026/7/30 5:43:55阅读更多 →
Harris角点检测原理与Matlab实现:从数学基础到工程实践

Harris角点检测原理与Matlab实现:从数学基础到工程实践

如果你正在做图像处理相关的项目,比如目标跟踪、图像配准或者三维重建,那么"角点检测"这个技术名词一定不会陌生。但很多人可能只是听说过Harris角点检测,却不太清楚它到底解决了什么问题,为什么在OpenCV等成熟库已经提…

2026/7/30 5:43:55阅读更多 →
2026年最新!找北京靠谱机器狗销售厂家必看的完整名单

2026年最新!找北京靠谱机器狗销售厂家必看的完整名单

我做机器狗领域内容5年,最近至少有30个北京的粉丝私信我,要本地靠谱的机器狗供货方名单。 特意整理了我实测过、跟进过落地的品牌,重点拆解大家最关心的巡检场景适配、售后保障、算法落地的坑,帮大家避我之前踩过的雷。选北京本地…

2026/7/30 5:43:55阅读更多 →
Wireshark网络抓包实战:从TCP三次握手到HTTPS解密

Wireshark网络抓包实战:从TCP三次握手到HTTPS解密

1. Wireshark抓包核心价值与应用场景Wireshark作为网络协议分析领域的瑞士军刀,其核心价值在于将抽象的网络通信转化为可视化的数据流。我在实际网络排障中发现,90%的复杂网络问题都能通过抓包分析定位到具体协议层。不同于其他工具仅显示原始数据&#…

2026/7/30 5:43:55阅读更多 →
校园问卷调查与数据分析平台的设计与实现

校园问卷调查与数据分析平台的设计与实现

校园问卷调查与数据分析平台的设计与实现实训 目的1.掌握前后端分离架构设计思想:理解 SpringBoot 3 Vue 3 前后端分离架构的分层原则与模块划分方法,掌握 B/S 模式下表现层、接入层、应用层、数据访问层和基础设施层的协同工作机制。 …

2026/7/30 5:41:55阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/29 9:47:45阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/29 7:00:19阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/29 7:58:51阅读更多 →
3分钟解锁iOS应用自由:TrollInstallerX让你的iPhone摆脱安装限制 [特殊字符]

3分钟解锁iOS应用自由:TrollInstallerX让你的iPhone摆脱安装限制 [特殊字符]

3分钟解锁iOS应用自由:TrollInstallerX让你的iPhone摆脱安装限制 🚀 【免费下载链接】TrollInstallerX A TrollStore installer for iOS 14.0 - 16.6.1 项目地址: https://gitcode.com/gh_mirrors/tr/TrollInstallerX 你是否曾经因为iOS系统的严格…

2026/7/30 0:00:58阅读更多 →
[GESP202606 四级] 扫雷

[GESP202606 四级] 扫雷

B4557 [GESP202606 四级] 扫雷 https://www.luogu.com.cn/problem/B4557 中国计算机学会(CCF)2026年6月C四级讲解——扫雷 https://www.bilibili.com/video/BV1MCMg6AEXR/ B4557 [GESP202606 四级] 扫雷 https://www.bilibili.com/video/BV1ZKTj6ZEVh/ 2…

2026/7/30 0:00:58阅读更多 →
Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼?是否遇到过设…

2026/7/30 0:00:58阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/30 0:27:26阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/30 4:47:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/29 14:26:42阅读更多 →