news 2026/5/4 1:17:31

从零实现hal_uart_transmit数据发送完整示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零实现hal_uart_transmit数据发送完整示例

从零实现HAL_UART_Transmit数据发送:一个真正能跑的完整示例

你有没有遇到过这种情况——明明代码写完了,引脚也配了,时钟也开了,但串口助手就是“收不到一个字”?
别急,这大概率不是硬件坏了,而是你对HAL_UART_Transmit的理解还停留在“抄个例子就完事”的层面。

今天我们就来彻底搞懂这个函数是怎么工作的,并手把手带你写出一个可移植、可复用、真正可靠的 UART 发送程序。不讲虚的,只讲你在开发板上能跑出来的实战细节。


为什么你的HAL_UART_Transmit没有输出?

先别急着看代码。我们先解决那个最让人抓狂的问题:为什么我调了函数,TX 引脚却一点波形都没有?

答案往往藏在三个地方:

  1. GPIO 没设成复用功能
  2. UART 时钟没开
  3. huart 句柄压根没初始化成功

这些问题都不会直接报错,但结果就是——静悄悄,啥也没有。

所以今天我们不仅要写“能运行”的代码,更要让你知道每一步背后到底发生了什么


HAL_UART_Transmit到底做了啥?

这个函数名字听着挺高级,其实干的事很朴实:把一串数据一个字节一个字节地塞进 UART 的发送寄存器里,直到发完为止。

它的原型是这样的:

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  • huart:指向你配置好的 UART 句柄(比如&huart2
  • pData:你要发的数据起始地址
  • Size:发多少个字节
  • Timeout:最多等多久(毫秒),超时就放弃

它是一个阻塞函数——调用之后 CPU 就卡在那里轮询状态位,直到发完或超时才返回。

听起来简单?确实简单。但正因为太简单,很多人忽略了它背后的陷阱。


真正完整的发送流程:从启动到点亮 LED

下面这段代码,是在 STM32F407VE(或其他常见 F4 芯片)上验证通过的真实可运行示例。假设你使用的是USART2,PA2 为 TX 引脚,波特率 115200,8-N-1 格式。

✅ 完整 main.c 示例

#include "main.h" // 要发送的字符串(注意不含自动补上的 '\0') uint8_t txBuffer[] = "Hello from HAL_UART_Transmit!\r\n"; int main(void) { // 初始化 HAL 库(必须第一步) HAL_Init(); // 配置系统时钟为 168MHz(F4 标准配置) SystemClock_Config(); // 初始化 GPIO 和 USART2 MX_GPIO_Init(); MX_USART2_UART_Init(); // 主循环 while (1) { // 发送数据,设置 100ms 超时 if (HAL_UART_Transmit(&huart2, txBuffer, sizeof(txBuffer) - 1, 100) == HAL_OK) { // 成功用 PB0 控制的 LED 闪烁一下 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); HAL_Delay(100); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); } else { // 失败?至少让我知道出错了(可用调试器断点) __NOP(); } // 每秒发一次 HAL_Delay(900); // 加上发送时间,接近 1s } }

是不是和你以前写的差不多?但关键在于——每个初始化函数都得自己写清楚,不能只依赖 CubeMX 自动生成而不知其所以然


关键组件拆解:缺一不可

1. UART 初始化 ——MX_USART2_UART_Init()

这是整个通信的基础。参数错了,发出来就是乱码。

UART_HandleTypeDef huart2; void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX; // 注意:这里可以只开 TX! huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } }

🔍 特别提醒:Mode设为UART_MODE_TX即可,不需要一定要 RX/TX 同时启用。


2. GPIO 配置 —— 别再让 PA2 当普通 IO!

很多初学者在这里翻车:他们以为只要包含usart.h就万事大吉,殊不知引脚功能映射才是关键

void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; // 开启 GPIOA 和 GPIOB 时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); // 配置 USART2_TX -> PA2 GPIO_InitStruct.Pin = GPIO_PIN_2; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出 GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART2; // 必须匹配 AF7! HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 配置 LED 引脚 PB0 GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); }

❗ 错误示例:如果把Alternate写成了GPIO_AF0_USART1,或者根本没设AF_PP模式,那 TX 引脚永远发不出信号!


3. 时钟使能 —— 最容易被忽略的一环

即使你写了上面的代码,如果忘了这一句:

__HAL_RCC_USART2_CLK_ENABLE();

那么 USART2 外设就像没通电一样,怎么折腾都没反应

通常我们在MX_USART2_UART_Init()中加入:

__HAL_RCC_USART2_CLK_ENABLE(); // 在配置前打开时钟

否则HAL_UART_Init()会失败,但如果你没检查返回值,就会一路“静默崩溃”。


常见坑点与避坑指南

问题现象原因分析解决方法
📉 完全无输出GPIO未配置为复用 / 时钟未开 / huart未初始化检查AlternateMode__HAL_RCC_xxx_CLK_ENABLE()
💬 输出乱码波特率不一致 / 采样频率偏差大双方统一为 115200,确认 HSE/LSE 准确
⏳ 发送卡死超时时间设为HAL_MAX_DELAY改为具体值如100ms
🔁 数据重复/丢失sizeof(buf)包含\0导致多发一字节使用sizeof(buf)-1排除终止符
🧱 中断中调用导致死机阻塞函数在中断中执行改用HAL_UART_Transmit_IT()

✅ 经验法则:不要在中断服务函数中调用HAL_UART_Transmit,因为它内部会等待标志位,可能导致主程序无法响应其他中断。


如何让它更好用?封装成日志函数

既然每次都写HAL_UART_Transmit(...)很麻烦,为什么不把它变成像printf一样好用呢?

方法一:重定向_write实现 printf 输出

#include <stdio.h> int _write(int fd, char *ptr, int len) { HAL_UART_Transmit(&huart2, (uint8_t*)ptr, len, 100); return len; }

然后你就可以这么用了:

printf("System running... Tick: %d\r\n", HAL_GetTick());

⚠️ 注意:确保工程已启用 “Use MicroLIB” 或链接了半主机支持库,否则_write不会被调用。


性能对比:什么时候该换方式?

虽然HAL_UART_Transmit简单易用,但它不适合所有场景。

方式是否阻塞CPU占用适用场景
HAL_UART_Transmit调试打印、小包命令
HAL_UART_Transmit_IT中小数据、需并发处理
HAL_UART_Transmit_DMA极低大批量数据传输

📌 建议:
- < 64 字节 → 用Transmit
- 64 ~ 1024 字节 → 用IT
- > 1KB → 上DMA


实战建议:写出健壮的发送逻辑

对于实际项目,别只是“发一次”,要考虑失败重试:

uint8_t reliable_transmit(UART_HandleTypeDef *huart, uint8_t *data, uint16_t len) { uint8_t retry = 3; while (retry--) { if (HAL_UART_Transmit(huart, data, len, 50) == HAL_OK) { return HAL_OK; } HAL_Delay(10); // 短暂恢复时间 } return HAL_ERROR; }

这样即使线路干扰或短暂异常,也能提高通信成功率。


结尾彩蛋:如何用逻辑分析仪验证?

想知道你真的发出去了吗?接个逻辑分析仪看看!

  • 抓取 PA2 引脚波形
  • 设置协议解析为 UART,波特率 115200
  • 观察是否有标准 8-N-1 帧结构
  • 检查起始位、数据位、停止位是否完整

如果能看到清晰的“Hello…”帧,说明你已经完全掌控了这条发送链路。


掌握了HAL_UART_Transmit,你就迈出了嵌入式通信的第一步。它看似只是一个简单的发送函数,实则是连接你与硬件世界的桥梁。

下次当你按下复位键,看到串口助手里跳出第一行“Hello”,你会明白:这不是魔法,是每一个配置、每一行代码共同作用的结果。

如果你在实现过程中遇到了其他挑战,欢迎在评论区分享讨论。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 8:51:09

PyTorch安装后无法检测到CUDA?排查流程图

PyTorch安装后无法检测到CUDA&#xff1f;排查流程图 在搭建深度学习开发环境时&#xff0c;你是否也遇到过这样的场景&#xff1a;满怀期待地装好PyTorch&#xff0c;运行 torch.cuda.is_available() 却返回 False&#xff1f;明明有NVIDIA显卡、驱动也更新了&#xff0c;为什…

作者头像 李华
网站建设 2026/4/29 0:03:23

Vue.js文档预览终极指南:从零开始打造专业文档展示系统

Vue.js文档预览终极指南&#xff1a;从零开始打造专业文档展示系统 【免费下载链接】vue-office 项目地址: https://gitcode.com/gh_mirrors/vu/vue-office 还在为Vue项目中如何优雅地展示Word、Excel、PDF文档而烦恼吗&#xff1f;vue-office组件库为你提供了完美的解…

作者头像 李华
网站建设 2026/4/29 0:01:08

SMUDebugTool技术文章仿写Prompt

SMUDebugTool技术文章仿写Prompt 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gitcode.com/gh_mirrors/smu/SMUDeb…

作者头像 李华
网站建设 2026/5/2 8:42:04

Conda环境导入导出跨平台迁移注意事项

Conda环境导入导出跨平台迁移注意事项 在人工智能和数据科学项目中&#xff0c;一个让人头疼的常见场景是&#xff1a;同事发来一段“完美运行”的代码&#xff0c;你兴冲冲地拉下来准备复现结果&#xff0c;却卡在了第一步——环境依赖报错。ModuleNotFoundError、版本冲突、…

作者头像 李华
网站建设 2026/5/1 22:16:38

Windows下Miniconda命令在VS Code中不识别的解决

Windows下Miniconda命令在VS Code中不识别的解决 在日常Python开发中&#xff0c;尤其是从事数据科学、机器学习或AI项目时&#xff0c;你是否曾遇到过这样的尴尬&#xff1a;明明在系统终端里能正常使用conda命令&#xff0c;一打开VS Code的集成终端&#xff0c;敲下conda -…

作者头像 李华
网站建设 2026/5/3 9:30:33

PyWencai终极指南:快速获取同花顺问财金融数据的完整方案

PyWencai终极指南&#xff1a;快速获取同花顺问财金融数据的完整方案 【免费下载链接】pywencai 获取同花顺问财数据 项目地址: https://gitcode.com/gh_mirrors/py/pywencai PyWencai是一个专为金融数据获取设计的Python工具&#xff0c;能够将复杂的同花顺问财平台查询…

作者头像 李华