云计算百科
云计算领域专业知识百科平台

嵌入式通信的本质:从“翻 GPIO“到“写驱动“,UART/I2C/SPI 到底在干什么?

目录

  • 一、引子:为什么"软件能模拟 I2C"?
  • 二、通信的本质:电平 + 时间 = 协议
    • 2.1 一根线只有 0 和 1,意义来自"何时变"
    • 2.2 同步与异步:有没有那根时钟线
    • 2.3 推挽、开漏、三态:电气层的三种"性格"
    • 2.4 我们看到的字符,和线上那串 0/1 是怎么对应起来的?
    • 2.5 一张图看懂:字符 → 二进制 → 线上电平
  • 三、三大协议一句话定性
    • 3.1 UART:异步、点对点、两根线
    • 3.2 I2C:同步、半双工、多设备、两根线
    • 3.3 SPI:同步、全双工、四根线、最快
    • 3.4 对比一览表
    • 3.5 三大协议一图对比
  • 四、硬件外设 vs 软件模拟:波形由谁产生?
    • 4.1 硬件外设:专用电路自动发波形
    • 4.2 软件模拟(Bit-Banging):CPU 手工翻 GPIO
    • 4.3 为什么 I2C/SPI 适合模拟,UART 不适合
    • 4.4 工程选型:能用硬件就别模拟
    • 4.5 硬件外设 vs 软件模拟
  • 五、驱动的本质:把"我要数据"翻译成"线上怎么动"
    • 5.1 驱动 = 分层翻译器
    • 5.2 以 BMP280 双传感器为例的四层驱动
    • 5.3 好的驱动让上层"忘了"底层是硬件还是软件
    • 5.4 四层驱动一图看懂
  • 六、CubeMX 工程配置实战(STM32 为例)
    • 6.1 UART 配置(以 USART3 接 ESP8266 为例)
    • 6.2 I2C 配置(以 I2C1 接双 BMP280 为例)
    • 6.3 SPI 配置(以 SPI 接 W25Q128 为例)
  • 七、应用场景与选型准则
  • 八、高频排坑指南(血泪经验)
    • 8.1 I2C 读不到器件
    • 8.2 I2C 地址冲突
    • 8.3 UART 乱码
    • 8.4 SPI 不响应
    • 8.5 软件模拟 I2C 偶发失败
    • 8.6 EEPROM 写后立刻读
    • 8.7 CubeMX 生成代码后引脚复用冲突

摘要

通信的本质,是在约定好的时间把一根线拉高或拉低。UART、I2C、SPI 看似各异,剥到最底层都是"电平+时间"的游戏。本文从"为什么软件能模拟 I2C"这个朴素问题出发,讲透通信协议的本质、硬件外设与软件模拟的取舍、驱动的分层本质,并结合 STM32CubeMX 给出三大协议的工程配置清单与高频排坑指南。

关键词:嵌入式通信、UART、I2C、SPI、CubeMX、驱动分层、开漏上拉、波特率、CPOL/CPHA


一、引子:为什么"软件能模拟 I2C"?

通信,是这个时代最容易被忽略、却又最不可或缺的基础设施。

早上你扫码进地铁,闸机在零点几秒内完成了与后台服务器的握手;手机刷短视频,每一帧画面都经过基站、光纤、CDN 的层层接力;就连你手里这块开发板,也要靠 UART 把调试信息吐到电脑屏幕上。可以说,没有通信,就没有现代生活,更没有现代工业。

而在嵌入式世界里,通信的重要性被放得更大。一个智能家居网关,要同时跟温湿度传感器、门锁、摄像头打交道;一台医疗设备,要把采集到的生理信号实时传给上位机;一辆汽车里,几十个 ECU 通过 CAN 总线协同工作。通信协议,就是这些设备之间唯一的"语言"——协议选得对不对、驱动写得好不好,直接决定整个系统能不能稳定跑起来。

也正因为如此,理解通信的底层原理,才不是一个"可选项",而是嵌入式工程师的必修课。今天这篇文章,我们就从最朴素的问题出发,把通信这件事彻底讲透。


先讲一个我亲眼见过的场景。

新来的同事接手一个老项目,翻到 bsp_iic.c,看到里面写着:

void MyI2C_Start(void)
{
SDA_Write(1);
SCL_Write(1);
delay_us(10);
SDA_Write(0);
delay_us(10);
SCL_Write(0);
}

他愣了半天,转头问我:“这也叫 I2C?I2C 不是 STM32 片上的一个外设吗?”

这个问题问得特别好。它戳中了很多嵌入式工程师心里那个模糊的角落:协议到底是硬件,还是一份说明书?

答案是:协议不是硬件,协议是一份"电平怎么变、变多快"的说明书。CPU 翻 GPIO 能拼出来,专用电路也能拼出来。两者只是"谁来执行这份说明书"的区别。

这篇文章,我们就从"电平 + 时间"这个最朴素的角度,把 UART、I2C、SPI 三大协议、硬件外设与软件模拟的取舍、驱动的分层本质一次讲透,最后落到 STM32CubeMX 的工程配置与血泪排坑。


二、通信的本质:电平 + 时间 = 协议

2.1 一根线只有 0 和 1,意义来自"何时变"

一根导线,在数字世界里只有两种状态:高电平(1)和低电平(0)。单看某一瞬间,这根线什么信息都表达不了——它只是一个电平。

信息的含义,来自时间轴上的排列。

举个最小例子。我跟你约定:

线拉低 10μs,再拉高,表示"START"。

那么当我在某个时刻把线拉低 10μs 再拉高,你就知道:哦,通信开始了。

再约定:

拉低 20μs 表示"0",拉高 20μs 表示"1"。

于是这根线就能传数据了。

你看,所谓协议,本质就是时序 + 电平的排列规则。0 和 1 本身没有意义,意义来自它们"什么时候变、变多快、按什么顺序变"。

💡 这一句话,是理解全文的钥匙。后面所有协议、所有驱动、所有排坑,都是这句话的展开。

2.2 同步与异步:有没有那根时钟线

通信双方要读懂"何时变",就必须有一个共同的时间基准。根据这个基准从哪来,通信分成两大类:

  • 同步通信:发送方额外拉一根时钟线(SCL / SCLK),告诉接收方"每个时钟沿,你采样一次"。I2C、SPI 都属于这一类。
  • 异步通信:没有时钟线,双方各自按约定的波特率(每秒多少位)硬对齐。UART 属于这一类。

这个区别极其关键,它直接决定了"为什么 UART 难模拟、I2C/SPI 好模拟"——我们到第四章细讲。

2.3 推挽、开漏、三态:电气层的三种"性格"

协议跑在哪根线上,还取决于这根线的电气特性。三种最常见的输出结构:

类型特点典型应用
推挽(Push-Pull) 能主动拉高、也能主动拉低,驱动能力强 UART 的 TX、SPI 的 MOSI/MISO/SCLK
开漏(Open-Drain) 只能主动拉低,拉高靠外部上拉电阻 I2C 的 SCL/SDA(必须上拉!)
三态(Tri-state) 可输出高/低,也可释放为高阻 SPI 多从机共享 MISO 时,未选中的从机输出高阻

⚠️ 开漏 + 无上拉 = 总线永远拉不高。这是 I2C 读不到器件的第一大元凶,第八章排坑会重点讲。


2.4 我们看到的字符,和线上那串 0/1 是怎么对应起来的?

前面讲了"电平 + 时间 = 协议",但你可能还有一个更朴素的疑问:我在电脑上敲一个字母 A,它怎么就成了线上的一串 0 和 1?这中间到底经历了什么?

答案是:靠一张"翻译表"——字符编码表(Character Encoding)。它规定了"哪个字符对应哪个二进制数"。

2.4.1 字符编码表:字符 ↔ 二进制数的"字典"

最经典的是 ASCII 码(American Standard Code for Information Interchange,美国信息交换标准代码)。它用 7 位二进制(0~127)给 128 个字符编号:

字符十进制二进制(7位)十六进制
A 65 1000001 0x41
B 66 1000010 0x42
a 97 1100001 0x61
0 48 0110000 0x30
\\n(换行) 10 0001010 0x0A

所以,当你在串口助手里发送字符 A 时,它先被查表翻译成二进制 01000001(实际传输常补足 8 位),然后这 8 个 bit 才被 UART 按波特率一位一位地变成 TX 引脚上的电平变化。

2.4.2 这张表定义在哪里?由谁完成翻译?
  • 定义:ASCII 表是国际标准,由美国国家标准学会(ANSI)制定,后来被国际标准化组织(ISO)采纳为 ISO/IEC 646 标准。它不依赖任何硬件厂商,是全世界通用的"字典"。
  • 翻译执行者:翻译动作发生在软件层,由你的应用程序 + 操作系统 + 编译器/库共同完成。比如你在 C 语言里写 char c = 'A';,编译器把字符字面量 'A' 直接编译成数值 65;你在串口助手里输入 A,是上位机软件查表把它转成 0x41。CPU 和 UART 外设本身完全不认识字符,它们只认二进制数。
2.4.3 从字符到线上波形的完整链路

以 UART 发送一个字符 A 为例,完整流程是:

你在电脑上敲 'A'

① 上位机软件查 ASCII 表:'A' → 0x41(二进制 01000001)

② 软件把 0x41 写入 UART 外设的发送数据寄存器(TDR)

③ UART 外设硬件自动加上起始位(0)、可选的校验位、停止位(1)

④ 外设按波特率把每一位依次输出到 TX 引脚:低电平=0,高电平=1

线上出现一串电平变化:起始位 + 01000001 + 停止位

接收端则完全反过来:RX 引脚收到电平变化 → UART 外设按波特率采样、去掉起始/停止位、拼回 0x41 → 存入接收寄存器 → 软件读出 0x41 → 查 ASCII 表翻译回字符 A → 显示在屏幕上。

2.4.4 中文怎么办?——ASCII 不够用,还有 UTF-8

ASCII 只有 128 个字符,连中文都装不下。所以后来又有了 GB2312/GBK(简体中文编码)、UTF-8(Unicode 的可变长编码)等。UTF-8 用 1~4 个字节表示一个字符,比如汉字"通"的 UTF-8 编码是 E9 80 9A(三个字节)。编码表不同,同一个字符对应的二进制串就不同——这就是为什么串口收到中文乱码时,往往是发送端和接收端用的编码表不一致。

💡 一句话总结:字符编码表就是"字符 ↔ 二进制"的字典,定义在标准里,翻译由软件完成,最终由硬件外设把二进制变成线上电平。 协议管的是"二进制怎么在线上传",编码表管的是"字符怎么变成二进制",两者各司其职。

2.5 一张图看懂:字符 → 二进制 → 线上电平

#mermaid-svg-kcwRx6dPsNuJ090I{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kcwRx6dPsNuJ090I .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kcwRx6dPsNuJ090I .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kcwRx6dPsNuJ090I .error-icon{fill:#552222;}#mermaid-svg-kcwRx6dPsNuJ090I .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kcwRx6dPsNuJ090I .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kcwRx6dPsNuJ090I .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kcwRx6dPsNuJ090I .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kcwRx6dPsNuJ090I .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kcwRx6dPsNuJ090I .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kcwRx6dPsNuJ090I .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kcwRx6dPsNuJ090I .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kcwRx6dPsNuJ090I .marker.cross{stroke:#333333;}#mermaid-svg-kcwRx6dPsNuJ090I svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kcwRx6dPsNuJ090I p{margin:0;}#mermaid-svg-kcwRx6dPsNuJ090I .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-kcwRx6dPsNuJ090I .cluster-label text{fill:#333;}#mermaid-svg-kcwRx6dPsNuJ090I .cluster-label span{color:#333;}#mermaid-svg-kcwRx6dPsNuJ090I .cluster-label span p{background-color:transparent;}#mermaid-svg-kcwRx6dPsNuJ090I .label text,#mermaid-svg-kcwRx6dPsNuJ090I span{fill:#333;color:#333;}#mermaid-svg-kcwRx6dPsNuJ090I .node rect,#mermaid-svg-kcwRx6dPsNuJ090I .node circle,#mermaid-svg-kcwRx6dPsNuJ090I .node ellipse,#mermaid-svg-kcwRx6dPsNuJ090I .node polygon,#mermaid-svg-kcwRx6dPsNuJ090I .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kcwRx6dPsNuJ090I .rough-node .label text,#mermaid-svg-kcwRx6dPsNuJ090I .node .label text,#mermaid-svg-kcwRx6dPsNuJ090I .image-shape .label,#mermaid-svg-kcwRx6dPsNuJ090I .icon-shape .label{text-anchor:middle;}#mermaid-svg-kcwRx6dPsNuJ090I .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kcwRx6dPsNuJ090I .rough-node .label,#mermaid-svg-kcwRx6dPsNuJ090I .node .label,#mermaid-svg-kcwRx6dPsNuJ090I .image-shape .label,#mermaid-svg-kcwRx6dPsNuJ090I .icon-shape .label{text-align:center;}#mermaid-svg-kcwRx6dPsNuJ090I .node.clickable{cursor:pointer;}#mermaid-svg-kcwRx6dPsNuJ090I .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kcwRx6dPsNuJ090I .arrowheadPath{fill:#333333;}#mermaid-svg-kcwRx6dPsNuJ090I .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kcwRx6dPsNuJ090I .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kcwRx6dPsNuJ090I .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kcwRx6dPsNuJ090I .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kcwRx6dPsNuJ090I .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kcwRx6dPsNuJ090I .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kcwRx6dPsNuJ090I .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kcwRx6dPsNuJ090I .cluster text{fill:#333;}#mermaid-svg-kcwRx6dPsNuJ090I .cluster span{color:#333;}#mermaid-svg-kcwRx6dPsNuJ090I div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kcwRx6dPsNuJ090I .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kcwRx6dPsNuJ090I rect.text{fill:none;stroke-width:0;}#mermaid-svg-kcwRx6dPsNuJ090I .icon-shape,#mermaid-svg-kcwRx6dPsNuJ090I .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kcwRx6dPsNuJ090I .icon-shape p,#mermaid-svg-kcwRx6dPsNuJ090I .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kcwRx6dPsNuJ090I .icon-shape .label rect,#mermaid-svg-kcwRx6dPsNuJ090I .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kcwRx6dPsNuJ090I .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kcwRx6dPsNuJ090I .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kcwRx6dPsNuJ090I :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

字符 'A'

查 ASCII 表

0x41 = 01000001

UART 外设加起始/停止位

TX 引脚电平变化

三、三大协议一句话定性

3.1 UART:异步、点对点、两根线

  • 两根线:TX(发送)、RX(接收)
  • 异步,无时钟线,靠波特率对齐
  • 全双工:TX/RX 同时收发
  • 无寻址:点对点,一根线只能接一个对端
  • 典型速率:110 ~ 921600 baud
  • 典型应用:调试打印、GPS、蓝牙透传、WiFi AT 指令(ESP8266)

3.2 I2C:同步、半双工、多设备、两根线

  • 两根线:SCL(时钟)、SDA(数据)
  • 同步,有时钟线
  • 半双工:同一时刻只能一个方向
  • 7-bit / 10-bit 地址寻址,一条总线可挂多个设备
  • 典型速率:100kHz(标准)~ 3.4MHz(高速)
  • 典型应用:BMP280 气压传感器、SHT30 温湿度、AT24C02 EEPROM、OLED 屏

3.3 SPI:同步、全双工、四根线、最快

  • 四根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)
  • 同步,有时钟线
  • 全双工:MOSI/MISO 同时收发
  • CS 片选寻址:每个从机一根 CS,谁被拉低谁应答
  • 典型速率:可达 100M+,三大协议中最快
  • 典型应用:W25Q128 字库 Flash、ILI9341 LCD、SD 卡、高速 ADC

3.4 对比一览表

协议线数同步双工寻址速率典型应用
UART 2(TX/RX) 异步 全双工 无(点对点) 110~921600 baud 调试、GPS、蓝牙、WiFi AT
I2C 2(SCL/SDA) 同步 半双工 7/10-bit 地址 100k~3.4M 传感器、EEPROM、OLED
SPI 4(SCLK/MOSI/MISO/CS) 同步 全双工 CS 片选 可达 100M+ Flash、LCD、SD 卡、ADC

3.5 三大协议一图对比

#mermaid-svg-g4e26FzIYUA8otuU{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-g4e26FzIYUA8otuU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-g4e26FzIYUA8otuU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-g4e26FzIYUA8otuU .error-icon{fill:#552222;}#mermaid-svg-g4e26FzIYUA8otuU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-g4e26FzIYUA8otuU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-g4e26FzIYUA8otuU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-g4e26FzIYUA8otuU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-g4e26FzIYUA8otuU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-g4e26FzIYUA8otuU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-g4e26FzIYUA8otuU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-g4e26FzIYUA8otuU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-g4e26FzIYUA8otuU .marker.cross{stroke:#333333;}#mermaid-svg-g4e26FzIYUA8otuU svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-g4e26FzIYUA8otuU p{margin:0;}#mermaid-svg-g4e26FzIYUA8otuU .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-g4e26FzIYUA8otuU .cluster-label text{fill:#333;}#mermaid-svg-g4e26FzIYUA8otuU .cluster-label span{color:#333;}#mermaid-svg-g4e26FzIYUA8otuU .cluster-label span p{background-color:transparent;}#mermaid-svg-g4e26FzIYUA8otuU .label text,#mermaid-svg-g4e26FzIYUA8otuU span{fill:#333;color:#333;}#mermaid-svg-g4e26FzIYUA8otuU .node rect,#mermaid-svg-g4e26FzIYUA8otuU .node circle,#mermaid-svg-g4e26FzIYUA8otuU .node ellipse,#mermaid-svg-g4e26FzIYUA8otuU .node polygon,#mermaid-svg-g4e26FzIYUA8otuU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-g4e26FzIYUA8otuU .rough-node .label text,#mermaid-svg-g4e26FzIYUA8otuU .node .label text,#mermaid-svg-g4e26FzIYUA8otuU .image-shape .label,#mermaid-svg-g4e26FzIYUA8otuU .icon-shape .label{text-anchor:middle;}#mermaid-svg-g4e26FzIYUA8otuU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-g4e26FzIYUA8otuU .rough-node .label,#mermaid-svg-g4e26FzIYUA8otuU .node .label,#mermaid-svg-g4e26FzIYUA8otuU .image-shape .label,#mermaid-svg-g4e26FzIYUA8otuU .icon-shape .label{text-align:center;}#mermaid-svg-g4e26FzIYUA8otuU .node.clickable{cursor:pointer;}#mermaid-svg-g4e26FzIYUA8otuU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-g4e26FzIYUA8otuU .arrowheadPath{fill:#333333;}#mermaid-svg-g4e26FzIYUA8otuU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-g4e26FzIYUA8otuU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-g4e26FzIYUA8otuU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-g4e26FzIYUA8otuU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-g4e26FzIYUA8otuU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-g4e26FzIYUA8otuU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-g4e26FzIYUA8otuU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-g4e26FzIYUA8otuU .cluster text{fill:#333;}#mermaid-svg-g4e26FzIYUA8otuU .cluster span{color:#333;}#mermaid-svg-g4e26FzIYUA8otuU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-g4e26FzIYUA8otuU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-g4e26FzIYUA8otuU rect.text{fill:none;stroke-width:0;}#mermaid-svg-g4e26FzIYUA8otuU .icon-shape,#mermaid-svg-g4e26FzIYUA8otuU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-g4e26FzIYUA8otuU .icon-shape p,#mermaid-svg-g4e26FzIYUA8otuU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-g4e26FzIYUA8otuU .icon-shape .label rect,#mermaid-svg-g4e26FzIYUA8otuU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-g4e26FzIYUA8otuU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-g4e26FzIYUA8otuU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-g4e26FzIYUA8otuU :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

SPI

SCLK/MOSI/MISO/CS 四根线

同步 · 全双工 · 最快

I2C

SCL/SDA 两根线

同步 · 多设备

UART

TX/RX 两根线

异步 · 点对点

四、硬件外设 vs 软件模拟:波形由谁产生?

这是本文的差异化重点。同样一份"电平+时间"的说明书,可以由两种"执行者"来跑。

4.1 硬件外设:专用电路自动发波形

以 STM32 的 I2C1 为例。你调用:

HAL_I2C_Mem_Read(&hi2c1, 0x76 << 1, 0xF7, 1, data, 3, 100);

这一条命令背后,START、从机地址、寄存器地址、重启、读数据、ACK、STOP——全部由 I2C 外设的硬件状态机自动在 PB6/PB7 上发出。CPU 只负责发起命令,然后等完成标志,期间可以去干别的(配合中断/DMA 更是如此)。

4.2 软件模拟(Bit-Banging):CPU 手工翻 GPIO

而同事的 MyI2C_Start() 呢?它是用 GPIO_WriteBit + delay_us(10) 手工拼波形:

void MyI2C_Start(void)
{
SDA_Write(1);
SCL_Write(1);
delay_us(10);
SDA_Write(0);
delay_us(10);
SCL_Write(0);
}

引脚随便选(PB10/PB11 也行),灵活性极高。但代价是:CPU 全程被占用,而且最怕中断打断——一旦 delay_us 中间被中断插一脚,SCL 低电平被拉长,从机就认为超时了。

4.3 为什么 I2C/SPI 适合模拟,UART 不适合

关键就在第二章说的同步/异步之分:

  • I2C/SPI 是同步协议,有时钟线掌控节奏。SCL 拉高多久、拉低多久,只要从机能采到沿,延时长短不致命。CPU 慢一点、快一点,从机都跟着时钟走。
  • UART 是异步协议,没有时钟线。接收方靠波特率硬对齐,每一位的时间精度要求极高。CPU 手工翻 GPIO 模拟 UART,稍有抖动就乱码,几乎不可用。

所以你会看到:软件模拟 I2C、软件模拟 SPI 在工程里很常见,但几乎没人软件模拟 UART。

4.4 工程选型:能用硬件就别模拟

什么情况才考虑软件模拟?

  • 引脚被占用,硬件外设的固定引脚不够用;
  • 芯片上没有对应的硬件外设;
  • 需要灵活换脚、快速验证。

除此之外,一律用硬件外设。硬件外设不占 CPU、时序稳定、有中断/DMA 加持,是工程首选。

⚠️ 特别提醒:网上很多软件模拟 I2C 的代码是 SPL 标准库写的(GPIO_WriteBit + delay_us),用的是 PB10/PB11。在你的 HAL 工程里不能直接拷——库不兼容、引脚冲突、SysTick 打架,三座大山等着你。


4.5 硬件外设 vs 软件模拟

#mermaid-svg-cxygQ62h3Ty5N87x{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cxygQ62h3Ty5N87x .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cxygQ62h3Ty5N87x .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cxygQ62h3Ty5N87x .error-icon{fill:#552222;}#mermaid-svg-cxygQ62h3Ty5N87x .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cxygQ62h3Ty5N87x .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cxygQ62h3Ty5N87x .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cxygQ62h3Ty5N87x .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cxygQ62h3Ty5N87x .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cxygQ62h3Ty5N87x .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cxygQ62h3Ty5N87x .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cxygQ62h3Ty5N87x .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cxygQ62h3Ty5N87x .marker.cross{stroke:#333333;}#mermaid-svg-cxygQ62h3Ty5N87x svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cxygQ62h3Ty5N87x p{margin:0;}#mermaid-svg-cxygQ62h3Ty5N87x .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-cxygQ62h3Ty5N87x .cluster-label text{fill:#333;}#mermaid-svg-cxygQ62h3Ty5N87x .cluster-label span{color:#333;}#mermaid-svg-cxygQ62h3Ty5N87x .cluster-label span p{background-color:transparent;}#mermaid-svg-cxygQ62h3Ty5N87x .label text,#mermaid-svg-cxygQ62h3Ty5N87x span{fill:#333;color:#333;}#mermaid-svg-cxygQ62h3Ty5N87x .node rect,#mermaid-svg-cxygQ62h3Ty5N87x .node circle,#mermaid-svg-cxygQ62h3Ty5N87x .node ellipse,#mermaid-svg-cxygQ62h3Ty5N87x .node polygon,#mermaid-svg-cxygQ62h3Ty5N87x .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cxygQ62h3Ty5N87x .rough-node .label text,#mermaid-svg-cxygQ62h3Ty5N87x .node .label text,#mermaid-svg-cxygQ62h3Ty5N87x .image-shape .label,#mermaid-svg-cxygQ62h3Ty5N87x .icon-shape .label{text-anchor:middle;}#mermaid-svg-cxygQ62h3Ty5N87x .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cxygQ62h3Ty5N87x .rough-node .label,#mermaid-svg-cxygQ62h3Ty5N87x .node .label,#mermaid-svg-cxygQ62h3Ty5N87x .image-shape .label,#mermaid-svg-cxygQ62h3Ty5N87x .icon-shape .label{text-align:center;}#mermaid-svg-cxygQ62h3Ty5N87x .node.clickable{cursor:pointer;}#mermaid-svg-cxygQ62h3Ty5N87x .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cxygQ62h3Ty5N87x .arrowheadPath{fill:#333333;}#mermaid-svg-cxygQ62h3Ty5N87x .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cxygQ62h3Ty5N87x .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cxygQ62h3Ty5N87x .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cxygQ62h3Ty5N87x .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cxygQ62h3Ty5N87x .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cxygQ62h3Ty5N87x .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cxygQ62h3Ty5N87x .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cxygQ62h3Ty5N87x .cluster text{fill:#333;}#mermaid-svg-cxygQ62h3Ty5N87x .cluster span{color:#333;}#mermaid-svg-cxygQ62h3Ty5N87x div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cxygQ62h3Ty5N87x .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cxygQ62h3Ty5N87x rect.text{fill:none;stroke-width:0;}#mermaid-svg-cxygQ62h3Ty5N87x .icon-shape,#mermaid-svg-cxygQ62h3Ty5N87x .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cxygQ62h3Ty5N87x .icon-shape p,#mermaid-svg-cxygQ62h3Ty5N87x .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cxygQ62h3Ty5N87x .icon-shape .label rect,#mermaid-svg-cxygQ62h3Ty5N87x .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cxygQ62h3Ty5N87x .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cxygQ62h3Ty5N87x .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cxygQ62h3Ty5N87x :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

同一份协议说明书

硬件外设

软件模拟

专用电路自动发波形

CPU 手工翻 GPIO

不占 CPU · 时序稳定

占 CPU · 怕中断

五、驱动的本质:把"我要数据"翻译成"线上怎么动"

5.1 驱动 = 分层翻译器

驱动是什么?一句话:驱动 = 把"我要数据"翻译成"线上怎么动"的分层翻译器。

上层应用说"我要室内外压差",底层硬件只听得懂"PB6 拉低、PB7 拉高"。中间这一层层翻译,就是驱动。

5.2 以 BMP280 双传感器为例的四层驱动

我做过一个"双 BMP280 测压差"的项目,正好是四层驱动的典型:

应用层(negpress.c) "我要室内外压差"

芯片驱动层(BMP280.c) 分别读 0x76/0x77,先温度后气压刷新 t_fine

总线驱动层(bsp_iic.c) HAL_I2C_Mem_Read 封装

硬件层(I2C1 外设) PB6/PB7 上真实波形

  • 应用层:只关心业务,比如"算压差、判断负压是否达标"。
  • 芯片驱动层:知道 BMP280 的寄存器地址、校准系数、读时序。它调用总线层,不关心底层是硬件 I2C 还是软件模拟。
  • 总线驱动层:把 HAL_I2C_Mem_Read 这类操作封装成 I2C_ReadReg(addr, reg, buf, len)。
  • 硬件层:真正的电平翻转。

5.3 好的驱动让上层"忘了"底层是硬件还是软件

分层的威力在于:换底层时,上层代码零改动。

今天我用硬件 I2C1(PB6/PB7),明天我改成软件模拟(PB10/PB11),只要总线驱动层对外接口不变,BMP280.c 和 negpress.c 一行都不用动。这就是"好的驱动让上层忘了底层"。

📌 特别点出 BMP280 的经典坑:读气压前必须先读温度刷新 t_fine(温度补偿系数)。双传感器时,t_fine 必须存在各自的结构体里,否则室内温度会去补偿室外气压——差分结果全错。这个坑我踩过,血泪。

5.4 四层驱动一图看懂

#mermaid-svg-xfTFZojX5KpG9Z2o{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xfTFZojX5KpG9Z2o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xfTFZojX5KpG9Z2o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xfTFZojX5KpG9Z2o .error-icon{fill:#552222;}#mermaid-svg-xfTFZojX5KpG9Z2o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xfTFZojX5KpG9Z2o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xfTFZojX5KpG9Z2o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xfTFZojX5KpG9Z2o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xfTFZojX5KpG9Z2o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xfTFZojX5KpG9Z2o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xfTFZojX5KpG9Z2o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xfTFZojX5KpG9Z2o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xfTFZojX5KpG9Z2o .marker.cross{stroke:#333333;}#mermaid-svg-xfTFZojX5KpG9Z2o svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xfTFZojX5KpG9Z2o p{margin:0;}#mermaid-svg-xfTFZojX5KpG9Z2o .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-xfTFZojX5KpG9Z2o .cluster-label text{fill:#333;}#mermaid-svg-xfTFZojX5KpG9Z2o .cluster-label span{color:#333;}#mermaid-svg-xfTFZojX5KpG9Z2o .cluster-label span p{background-color:transparent;}#mermaid-svg-xfTFZojX5KpG9Z2o .label text,#mermaid-svg-xfTFZojX5KpG9Z2o span{fill:#333;color:#333;}#mermaid-svg-xfTFZojX5KpG9Z2o .node rect,#mermaid-svg-xfTFZojX5KpG9Z2o .node circle,#mermaid-svg-xfTFZojX5KpG9Z2o .node ellipse,#mermaid-svg-xfTFZojX5KpG9Z2o .node polygon,#mermaid-svg-xfTFZojX5KpG9Z2o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xfTFZojX5KpG9Z2o .rough-node .label text,#mermaid-svg-xfTFZojX5KpG9Z2o .node .label text,#mermaid-svg-xfTFZojX5KpG9Z2o .image-shape .label,#mermaid-svg-xfTFZojX5KpG9Z2o .icon-shape .label{text-anchor:middle;}#mermaid-svg-xfTFZojX5KpG9Z2o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xfTFZojX5KpG9Z2o .rough-node .label,#mermaid-svg-xfTFZojX5KpG9Z2o .node .label,#mermaid-svg-xfTFZojX5KpG9Z2o .image-shape .label,#mermaid-svg-xfTFZojX5KpG9Z2o .icon-shape .label{text-align:center;}#mermaid-svg-xfTFZojX5KpG9Z2o .node.clickable{cursor:pointer;}#mermaid-svg-xfTFZojX5KpG9Z2o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xfTFZojX5KpG9Z2o .arrowheadPath{fill:#333333;}#mermaid-svg-xfTFZojX5KpG9Z2o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xfTFZojX5KpG9Z2o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xfTFZojX5KpG9Z2o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xfTFZojX5KpG9Z2o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xfTFZojX5KpG9Z2o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xfTFZojX5KpG9Z2o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xfTFZojX5KpG9Z2o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xfTFZojX5KpG9Z2o .cluster text{fill:#333;}#mermaid-svg-xfTFZojX5KpG9Z2o .cluster span{color:#333;}#mermaid-svg-xfTFZojX5KpG9Z2o div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xfTFZojX5KpG9Z2o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xfTFZojX5KpG9Z2o rect.text{fill:none;stroke-width:0;}#mermaid-svg-xfTFZojX5KpG9Z2o .icon-shape,#mermaid-svg-xfTFZojX5KpG9Z2o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xfTFZojX5KpG9Z2o .icon-shape p,#mermaid-svg-xfTFZojX5KpG9Z2o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xfTFZojX5KpG9Z2o .icon-shape .label rect,#mermaid-svg-xfTFZojX5KpG9Z2o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xfTFZojX5KpG9Z2o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xfTFZojX5KpG9Z2o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xfTFZojX5KpG9Z2o :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

应用层 negpress.c

芯片驱动层 BMP280.c

总线驱动层 bsp_iic.c

硬件层 I2C1 外设

六、CubeMX 工程配置实战(STM32 为例)

6.1 UART 配置(以 USART3 接 ESP8266 为例)

  • Mode:Asynchronous
  • 波特率:115200
  • 数据位:8,校验:None,停止位:1(即 8N1)
  • 引脚:TX + RX
  • NVIC:开启全局中断(接收"续杯"必须)
  • 高速数据流可选 DMA

关键点:

  • TX/RX 必须交叉接:STM32 的 TX 接 ESP8266 的 RX,反之亦然。
  • 3.3V 与 5V 直连会烧 IO,注意电平匹配。
  • 波特率误差要 < 2%,否则长帧必乱码。

6.2 I2C 配置(以 I2C1 接双 BMP280 为例)

  • Mode:I2C
  • Speed Mode:Standard Mode,100kHz
  • 寻址:7-bit
  • PB6/PB7 自动分配为 SCL/SDA,开漏输出
  • 硬件电路必须外接 4.7kΩ~10kΩ 上拉到 VCC(ST 官方配置界面也提示 enable pull-ups)

关键点:

  • 读不到器件,先量 SCL/SDA 空闲是不是高电平。
  • 双设备地址 0x76/0x77 靠 SDO 引脚电平区分:SDO 接 GND → 0x76,SDO 接 VCC → 0x77。

6.3 SPI 配置(以 SPI 接 W25Q128 为例)

  • CPOL/CPHA 组合成 4 种 Mode,必须与从设备 datasheet 严格匹配(Flash 多为 Mode 0)
  • CS 引脚每从机一个,未选中必须高阻
  • 关键点:Mode 错 → 设备完全不响应;CS 时序错 → 数据错乱

📌 三大协议 CubeMX 配置清单速查表:

协议模式关键参数必查项
UART Asynchronous 115200 8N1 TX/RX 交叉、共地
I2C I2C Standard 100kHz 7-bit 上拉电阻、空闲高电平
SPI SPI Mode 0 CPOL=0 CPHA=0 CS 拉低、Mode 匹配

七、应用场景与选型准则

  • 传感器多、IO 紧 → I2C:一条总线挂 BMP280、SHT30、OLED,只占两根线。
  • 屏幕、Flash、高速数据 → SPI:ILI9341 LCD、W25Q128、SD 卡,要的就是速度。
  • 调试打印、模块通信、简单透传 → UART:ESP8266、语音模块、GPS,点对点最省心。

选型没有绝对的对错,核心是匹配需求:要省 IO 选 I2C,要速度选 SPI,要简单选 UART。

选型一图流

#mermaid-svg-VcPzusbZWuy4pqtN{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VcPzusbZWuy4pqtN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VcPzusbZWuy4pqtN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VcPzusbZWuy4pqtN .error-icon{fill:#552222;}#mermaid-svg-VcPzusbZWuy4pqtN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VcPzusbZWuy4pqtN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VcPzusbZWuy4pqtN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VcPzusbZWuy4pqtN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VcPzusbZWuy4pqtN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VcPzusbZWuy4pqtN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VcPzusbZWuy4pqtN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VcPzusbZWuy4pqtN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VcPzusbZWuy4pqtN .marker.cross{stroke:#333333;}#mermaid-svg-VcPzusbZWuy4pqtN svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VcPzusbZWuy4pqtN p{margin:0;}#mermaid-svg-VcPzusbZWuy4pqtN .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-VcPzusbZWuy4pqtN .cluster-label text{fill:#333;}#mermaid-svg-VcPzusbZWuy4pqtN .cluster-label span{color:#333;}#mermaid-svg-VcPzusbZWuy4pqtN .cluster-label span p{background-color:transparent;}#mermaid-svg-VcPzusbZWuy4pqtN .label text,#mermaid-svg-VcPzusbZWuy4pqtN span{fill:#333;color:#333;}#mermaid-svg-VcPzusbZWuy4pqtN .node rect,#mermaid-svg-VcPzusbZWuy4pqtN .node circle,#mermaid-svg-VcPzusbZWuy4pqtN .node ellipse,#mermaid-svg-VcPzusbZWuy4pqtN .node polygon,#mermaid-svg-VcPzusbZWuy4pqtN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VcPzusbZWuy4pqtN .rough-node .label text,#mermaid-svg-VcPzusbZWuy4pqtN .node .label text,#mermaid-svg-VcPzusbZWuy4pqtN .image-shape .label,#mermaid-svg-VcPzusbZWuy4pqtN .icon-shape .label{text-anchor:middle;}#mermaid-svg-VcPzusbZWuy4pqtN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VcPzusbZWuy4pqtN .rough-node .label,#mermaid-svg-VcPzusbZWuy4pqtN .node .label,#mermaid-svg-VcPzusbZWuy4pqtN .image-shape .label,#mermaid-svg-VcPzusbZWuy4pqtN .icon-shape .label{text-align:center;}#mermaid-svg-VcPzusbZWuy4pqtN .node.clickable{cursor:pointer;}#mermaid-svg-VcPzusbZWuy4pqtN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VcPzusbZWuy4pqtN .arrowheadPath{fill:#333333;}#mermaid-svg-VcPzusbZWuy4pqtN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VcPzusbZWuy4pqtN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VcPzusbZWuy4pqtN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VcPzusbZWuy4pqtN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VcPzusbZWuy4pqtN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VcPzusbZWuy4pqtN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VcPzusbZWuy4pqtN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VcPzusbZWuy4pqtN .cluster text{fill:#333;}#mermaid-svg-VcPzusbZWuy4pqtN .cluster span{color:#333;}#mermaid-svg-VcPzusbZWuy4pqtN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VcPzusbZWuy4pqtN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VcPzusbZWuy4pqtN rect.text{fill:none;stroke-width:0;}#mermaid-svg-VcPzusbZWuy4pqtN .icon-shape,#mermaid-svg-VcPzusbZWuy4pqtN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VcPzusbZWuy4pqtN .icon-shape p,#mermaid-svg-VcPzusbZWuy4pqtN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VcPzusbZWuy4pqtN .icon-shape .label rect,#mermaid-svg-VcPzusbZWuy4pqtN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VcPzusbZWuy4pqtN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VcPzusbZWuy4pqtN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VcPzusbZWuy4pqtN :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

要省 IO

I2C

要速度

SPI

要简单

UART

八、高频排坑指南(血泪经验)

平时总听说"终于把某某通信调好了"——这句话背后,是无数个深夜对着示波器发呆的工程师。你可能会问:协议不是一套明明白白、白纸黑字写好的方案吗?为什么这么"确定"的东西,还是这么容易出错?

答案恰恰藏在前面几章里:协议是确定的,但"执行协议"的每一环都不确定。

  • 电气层不确定:开漏没上拉、电平不匹配、共地没做好、线太长有干扰——协议写得再清楚,线上电平不对,一切白搭。
  • 时序层不确定:波特率误差、时钟抖动、中断打断延时、上电时序不对——协议规定了"何时变",但"变"的时机被各种因素扰动。
  • 配置层不确定:CPOL/CPHA 配错、地址引脚接错、引脚复用冲突——协议没错,是"人"把参数配错了。
  • 理解层不确定:datasheet 没读透、寄存器地址记错、编码表不一致——协议没错,是"人"理解错了。

所以"调通信"调的不是协议本身,而是把协议之外的所有不确定因素,一个个排查、对齐、消除。协议是那条"应该走的路",但路上有坑、有岔路、有信号灯失灵,工程师的工作就是把这些坑一个个填平。下面这些血泪经验,就是最常见的坑。

八、高频排坑指南(血泪经验)

8.1 I2C 读不到器件

  • 现象:HAL_I2C_Mem_Read 返回 HAL_BUSY 或 HAL_TIMEOUT。
  • 原因:开漏输出 + 无上拉 = 总线永远拉不高。
  • 解决:先别改代码!用示波器/万用表量 SCL/SDA 空闲是不是高电平。不是高电平,先补 4.7kΩ~10kΩ 上拉。

8.2 I2C 地址冲突

  • 现象:明明地址对,就是读不到。
  • 原因:A0/A1/A2 哪怕接了一个高,地址就从 0xA0 变 0xA2。
  • 解决:核对硬件地址引脚,确认实际地址。

8.3 UART 乱码

  • 现象:收到一堆乱码。
  • 原因:波特率不匹配、TX/RX 未交叉、未共地、3.3V 直连 5V。
  • 解决:逐一排查——先量波特率,再查交叉,再查共地。

8.4 SPI 不响应

  • 现象:设备完全不响应,或数据错乱。
  • 原因:CPOL/CPHA Mode 配错、CS 没拉低、片选引脚搞混。
  • 解决:对照 datasheet 核对 Mode,确认 CS 时序。

8.5 软件模拟 I2C 偶发失败

  • 现象:时好时坏,偶发失败。
  • 原因:中断打断了 delay_us 时序,SCL 低电平被拉长,从机认为超时。
  • 解决:模拟期间关中断,或改用硬件 I2C。

8.6 EEPROM 写后立刻读

  • 现象:写完立刻读,读出来是旧数据。
  • 原因:AT24C02 内部写周期需 5ms 以上,跨页写入会回卷。
  • 解决:写后延时 5ms+,注意页边界。

8.7 CubeMX 生成代码后引脚复用冲突

  • 现象:下载一次后芯片锁死,再也连不上。
  • 原因:SYS 的 SWD 调试口忘选,被复用成普通 IO。
  • 解决:CubeMX 里 SYS → Debug


感谢您的阅读!本号长期深耕电子信息、通信与嵌入式领域的技术分享,如果这篇文章对您有所启发,欢迎点赞、收藏并关注,后续将持续输出更多硬核干货,与您一同成长!

赞(0)
未经允许不得转载:网硕互联帮助中心 » 嵌入式通信的本质:从“翻 GPIO“到“写驱动“,UART/I2C/SPI 到底在干什么?
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!