串口通信代码的一些解释

串口(UART/USART)是单片机内部 的一个通信硬件模块,通过 TX 和 RX 引脚让设备之间以字节为单位交换数据。

UART 是一种:

  • 异步(无时钟);字符串式(按字节发送);全双工(收发独立);基于电平高低的通信协议

串口通信是"双向的",你可以同时:

  • 用 TX 把调试数据打印到电脑

  • 用 RX 让电脑发送命令给 STM32(如电脑发送:"LEDON",STM32 收到后点灯)

常见格式:起始位:1 bit(低电平) ;数据位:8 bit; 校验位:可选; 停止位:1 bit

波特率:9600 / 115200 等

串口发送

一.对比串口通信中Serial_SendXXX和Serial_Printf

串口(UART)只能发送"字节数据",不能直接发送"数字本身"。

1.串口发送的本质:只能发送 0~255 的字节

UART 的发送寄存器(USARTx->DR)每次只能发送1 个字节(8 bit)。

例如:Serial_SendByte(0x41);

串口只知道:我要发一个字节:0x41,它 不知道什么是整数 123、浮点数 3.14,它只会发出一个又一个字节。

2.C 语言里"数字"不是 ASCII 字符

例如数字:123(int 类型),它在内存里的存储是 二进制整数:0x00 0x00 0x00 0x7B

串口如果直接发这些字节,电脑会收到乱码。

但我们希望电脑串口助手看到的是:123 ,也就是字符 '1' '2' '3'

ASCII 对应是:'1' → 0x31 '2' → 0x32 '3' → 0x33

因此必须先把数字变成字符(字符串)。

Serial_SendNumber(123, 3) 会把整数 123:

1>分解成每个数字:1、2、3

2>把每个数字+0x30 转成 ASCII

3>再通过串口一个一个发出去

最终电脑看到:123

3.如果不转换成字符串,会发生什么?

例如直接发 int 类型:int a = 123;

Serial_SendByte(a); // 错误示例

实际发送的是 0x7B(也就是 123 的低 8 位),终端只会看到乱码或一个不可打印字符。

函数名 作用 是否支持格式化 典型用途
Serial_SendByte 发送 1 个字节 ❌ 不支持 发送单字符或控制字节
Serial_SendArray 发送字节数组 ❌ 不支持 发送二进制数据、ASCII 字节流
Serial_SendString 发送字符串(以 \0 结尾) ❌ 不支持 发送固定内容字符串
Serial_SendNumber 将数字转为字符串并发送 ❌(仅数字) 显示整数、计数等
Serial_Printf 发送格式化字符串 ✅ 支持格式化 功能类似 printf,用最强大最灵活

1.所有 Serial_SendXXX 函数 只能发送已经存在的内容:

  • 要么是固定字符串 "Hello"

  • 要么是数字 123

  • 要么是字节数组 {0x41, 0x42,...}

它们不能自动组合字符串、不能插入变量、不能格式化内容。

2.Serial_Printf 能做更多:支持格式化、变参、字符串构造

比如:Serial_Printf("温度=%.2f C, 湿度=%d%%\r\n", temp, hum);

它会自动:格式化字符串(构造最终字符串)把 "温度=21.53 C, 湿度=45%" 通过串口发出去

相当于**:** sprintf(buffer, ...); Serial_SendString(buffer);

但开发者不用自己写缓冲区,因为已经封装好了。

3.使用场景对比

🔹 使用 Serial_SendXXX 的场景

  • 发送固定内容(例如指令、固定格式)

  • 调试简单输出

  • 发送二进制数据(例如传输结构体、传感器原始数据)

  • 节省程序空间(不需要 printf 库)

🔹 使用 Serial_Printf 的场景

  • 输出调试信息

  • 输出变量值

  • 输出格式化数据(浮点、整型、字符串混合等)

  • 需要像 printf 一样灵活的输出


4.总结:一句话区分它们

Serial_SendXXX:直接发已有内容,不格式化

("我给你什么,你就发什么")

Serial_Printf:先拼接格式化字符串,再发送

("我告诉你模板,你自动把内容拼好再发")

二.三种常见在嵌入式系统中输出格式化字符串的做法

方法1:直接重定向printf,但printf函数只有一个,此方法不能在多处使用

printf("\r\nNum2=%d", 222);

串口发送printf打印的格式化字符串,需要重定向fputc函数,并在工程选项里勾选Use MicroLIB

解释:

1>你用 printf() 想把内容输出到串口,但printf 默认输出到"标准输出 stdout**"。** 在电脑上,stdout = 屏幕,在 STM32 上,stdout = 没有实际设备(默认什么都不输出),所以如果你直接写:printf("Hello"); STM32 不知道"要送到哪里",所以 不会显示、也不会自动发到串口。printf 发送每个字符时,调用 fputc() 函数输出单个字符。所以你要告诉 fputc:"每次 printf 输出字符,都帮我通过串口发送"。这样做,所有 printf() 输出内容 → 自动发送到串口助手。如 printf("Num=%d\r\n", 123); 会通过 USART(串口)打印到电脑。

2>在工程选项里勾选 Use MicroLIB"是什么意思?

这是 Keil MDK 的一个选项,MicroLIB = 微型 C 标准库**,**勾上后:含有更小的 printf库,代码更小,适合 Flash 很小的 MCU更好支持用户重定向 printf

Keil → Options for Target →Target→ Use MicroLIB ✔

方法2:使用sprintf打印到字符数组,再用串口发送字符数组,此方法打印到字符数组,之后想怎么处理都可以,可在多处使用

char String[100];

sprintf(String, "\r\nNum3=%d", 333);

Serial_SendString(String);

定义字符数组,使用sprintf,把格式化字符串打印到字符数组串口发送字符数组(字符串)

**方法3:**将sprintf函数封装起来,实现专用的printf,此方法就是把方法2封装起来,更加简洁实用,可在多处使用

Serial_Printf("\r\nNum4=%d", 444); //串口打印字符串,使用自己封装的函数实现printf的效果

Serial_Printf("\r\n");

三.串口打印实现过程

1.Serial_Printf("Num=%d\r\n", 444);

会让单片机做以下事情:

① 把字符串格式化为 "Num=444\r\n"

② 一个字节一个字节写入串口的发送寄存器 USARTx->DR

③ 串口硬件把这些字节调制成 UART 信号

④ 通过 TX 引脚(如 PA9)发送出去

2.电脑并不能直接接收 UART 信号所以需要一个 USB 转串口(TTL)模块,如 CH340、CP2102。

STM32 TX ------> USB串口模块 RX

STM32 RX ------> USB串口模块 TX(如果需要接收)

STM32 GND ------> USB串口模块 GND

模块通过 USB 线连接电脑。

  1. 电脑串口助手(Serial Assistant)显示数据

电脑打开串口助手,当串口助手检测到数据从 USB 来,就显示出来。

最终你会看到:Num=444

四. 数据流的全过程

Serial_Printf()

格式化字符串

Serial_SendByte()

USART1->DR(数据寄存器)

串口硬件把数据从 TX 引脚送出去

USB 转串口模块(CH340等)

USB 电缆

电脑驱动(虚拟串口 COMx)

串口助手显示出来

总结:串口打印的数据永远是从 STM32 单片机发出来的,最后显示在电脑的串口助手软件中。

在 STM32 芯片内部,大概是这样:

DR 寄存器在 STM32 内部, 数据从 DR → TX 引脚,然后通过 USB 转串口模块 → 电脑 → 串口助手

串口助手显示的数据来自STM32 内部 USART 外设(DR 寄存器)发送的数据

字符 → 写入 STM32 内部 DR → 移位寄存器 → TX 引脚 → USB 转串口 → 电脑串口助手

烧录程序:CPU把"程序代码"写入 STM32 的 Flash(程序存储器)里。

串口发送:把"数据"写入 STM32 内部的 USART 寄存器 DR,再从 TX 引脚发出去。

写 Flash = 烧录程序,写寄存器 = 运行时操作(通信)

串口接收

一.串口接收的过程

TX 发出的电压波形 → MCU RX 引脚接收到

RX 引脚 → 串口硬件 → 移位寄存器 → DR寄存器

触发接收中断 → 程序读数据

电脑发送数据----波形来到 STM32 的 RX 引脚----串口硬件自动"采样并解码"----串口硬件把字节放进接收寄存器 DR----硬件置位 RXNE(接收非空标志,RXNE = 1 ,意味着 DR里有数据可读)----如果开了接收中断,会进入 USART1_IRQHandler----应用层使用收到的数据电脑 → 发送 "1"(如STM32 → 控制LED亮)

相关推荐
怀旧,1 小时前
【Linux系统编程】8. 进程的概念(下)
linux·运维·服务器
路人甲ing..1 小时前
Ubuntu怎么安装tar.gz (android-studio为例)
linux·ubuntu·kotlin·android studio
福尔摩斯张1 小时前
二维数组详解:定义、初始化与实战
linux·开发语言·数据结构·c++·算法·排序算法
阿沁QWQ1 小时前
Reactor反应堆模式
linux·运维·服务器
No.Ada1 小时前
头歌-基于 socket 的网络编程
linux·服务器·网络
w***15311 小时前
若依部署Nginx和Tomcat
运维·nginx·tomcat
都小事儿2 小时前
VSCODE:保存文件时删除行尾空格
linux·vscode
wdfk_prog2 小时前
[Linux]学习笔记系列 -- [kernel]sysctl
linux·笔记·学习
Xiaomostream2 小时前
实现TCP服务器:一请求一线程 | epoll
服务器·网络协议·tcp/ip