news 2026/4/18 10:06:27

从零实现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/4/18 4:30:01

Hitboxer终极指南:键盘玩家的SOCD冲突解决方案

Hitboxer终极指南&#xff1a;键盘玩家的SOCD冲突解决方案 【免费下载链接】socd SOCD cleaner tool for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 在激烈的游戏对抗中&#xff0c;你是否曾经因为按键冲突而错失良机&#xff1f;当同时按下左右方…

作者头像 李华
网站建设 2026/4/18 0:45:53

Poppler-Windows终极指南:快速掌握免费PDF处理工具

Poppler-Windows终极指南&#xff1a;快速掌握免费PDF处理工具 【免费下载链接】poppler-windows Download Poppler binaries packaged for Windows with dependencies 项目地址: https://gitcode.com/gh_mirrors/po/poppler-windows 想要在Windows系统上轻松处理PDF文件…

作者头像 李华
网站建设 2026/4/16 19:01:07

SSH连接超时怎么办?调整Miniconda容器KeepAlive设置

SSH连接超时怎么办&#xff1f;调整Miniconda容器KeepAlive设置 在远程开发日益普及的今天&#xff0c;尤其是在使用云服务器运行基于Miniconda的AI训练环境时&#xff0c;一个看似微小却极其烦人的问题频频出现&#xff1a;SSH连接莫名其妙断开。你正盯着Jupyter Notebook跑模…

作者头像 李华
网站建设 2026/4/18 8:38:31

Anaconda下载太慢?换用Miniconda-Python3.11+清华源

Anaconda下载太慢&#xff1f;换用Miniconda-Python3.11清华源 在数据科学和人工智能开发中&#xff0c;一个稳定高效的 Python 环境是项目成功的基础。然而&#xff0c;许多开发者都曾经历过这样的场景&#xff1a;点击“下载 Anaconda”按钮后&#xff0c;进度条以 KB/s 的速…

作者头像 李华
网站建设 2026/4/14 9:47:22

DeTikZify:科研绘图革命,让LaTeX图表制作变得如此简单

DeTikZify&#xff1a;科研绘图革命&#xff0c;让LaTeX图表制作变得如此简单 【免费下载链接】DeTikZify Synthesizing Graphics Programs for Scientific Figures and Sketches with TikZ 项目地址: https://gitcode.com/gh_mirrors/de/DeTikZify 在科研工作中&#x…

作者头像 李华
网站建设 2026/4/18 8:33:26

KeymouseGo自动化神器:跨平台部署终极实战指南

KeymouseGo自动化神器&#xff1a;跨平台部署终极实战指南 【免费下载链接】KeymouseGo 类似按键精灵的鼠标键盘录制和自动化操作 模拟点击和键入 | automate mouse clicks and keyboard input 项目地址: https://gitcode.com/gh_mirrors/ke/KeymouseGo KeymouseGo作为一…

作者头像 李华