通信协议与接口全解析,包含C语言代码详细解释,UART,I2C,CAN,SPI,RS422,RS232,RS485

通信协议与接口详解

一、基础知识

1. 基本概念

1.1 串行通信与并行通信
  • 串行通信:数据逐位传输,传输线少,长距离传输时成本低,但数据的传输控制较复杂。按照实现数据同步的方式,可分为同步串行和异步串行两种。
  • 并行通信:多位数据同时传输,传输控制简单,传输速度快,但在长距离传输时硬件成本较高。
1.2 单工、半双工、全双工
  • 单工:数据传输仅能沿一个方向,不能实现反向传输,只有一条通信线路。
  • 半双工:数据传输可以沿两个方向,但需要分时进行,且只有一条通信线路。
  • 全双工:数据可以同时进行双向传输,具有两条通信线路。典型实例:UART。
1.3 同步与异步
  • 同步通信:双方使用频率一致的时钟,在时钟信号的作用下进行发送与接收。
  • 异步通信:收发双方可以有各自的时钟。收发双方要规定数据位、停止位、校验位、波特率等。
1.4 差分

两根信号线上的信号振幅相同,相位相反,它们的电势差构成了差分信号。

1.5 比特率、波特率、传输速率
  • 比特率:每秒传输的二进制位数。1 字节(Byte)等于 8 比特(bit)。单位 bps。
  • 波特率:每秒传输的码元个数。波特(Baud)即调制速率,指有效数据信号调制载波的速率,即单位时间内载波调制状态变化的次数。一个码元符号上可负载多个 bit 位信息。单位 baud。
  • 换算关系:比特率 = 波特率 × 单个调制状态对应的二进制位数,即\(R_{bit} = R_{baud}× log_2M\)(其中 M 是信号的编码级数)。

2. UART

全双工异步通信,有两根线:发送线与接收线。1 帧共 10 位,结构为:空闲→起始位→数据位(8 位,LSB 先传,MSB 后传)→停止位→空闲。

2.1 通信过程

一开始为高电平,然后拉低表示起始位,接着传输 8 个数据位,之后是校验位,最后拉高表示停止位,并进入空闲状态,等待下一次数据传输。

  • 起始位:先发出逻辑 0 的信号,表示传输开始。
  • 数据位:支持 4、5、6、7、8 位等,需提前约定才能正确传输。
  • 校验位:数据校验方式为奇偶校验。
  • 停止位:一个字符结束的标志。
2.2 特点
  • 传输距离:抗干扰能力差,通信距离短。
  • 时序:总线空闲时信号线为高电平(逻辑 1)。
  • 波特率:表示每秒传输二进制数据的位数。

3. I2C

串行、半双工、近距离、一主多从通信协议,可连接多个从设备,扩展性好。包含 SDA(串行数据线)和 SCL(串行时钟线),总线空闲时 SCL 和 SDA 由上拉电阻拉高保持高电平状态。

3.1 速率
  • 标准模式:100kbit/s
  • 快速模式:400kbit/s
  • 高速模式:3.4Mbit/s
  • 换算关系:\(1MB/s = 8Mbps = 8Mbit/s\),即\(1Mbps = 0.125MB/s\)
3.2 起始与终止信号
  • 起始信号:SCL 为高期间,SDA 由高到低跳变。
  • 终止信号:SCL 为高期间,SDA 由低到高跳变。
  • ACK(从机应答):SDA 拉低代表收到数据。
3.3 通信时序
  • 写时序
STARTDeviceID(8bits)ACKW_ADDR(8bits)ACKW_DATA(8bits)ACKSTOP
起始位从器件地址从机应答寄存器地址从机应答写数据从机应答停止位
  • 读时序
STARTDeviceID(8bits)ACKR_ADDR(8bits)ACKStartDeviceID(8bits)ACKR_DATA(8bits)NACKSTOP
起始位从器件地址从机应答寄存器地址从机应答起始位从器件地址从机应答读数据主机不应答停止位
3.4 特点

I2C 通信需要输出高电平的能力。开漏输出无法直接输出高电平,需在漏极接上拉电阻才能实现 “线与” 功能。总线具有 “与” 逻辑功能:只要有一个设备发送低电平,总线上就为低电平;所有设备都发送高电平时,总线才为高电平。

4. SPI

高速、全双工、同步通信总线,由一个主模块和一个或多个从模块组成。包含 MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)和 CS/SS(片选)引脚。SPI 的时钟极性(CPOL)和时钟相位(CPHA)可设置 4 种不同的通信操作模式。

4.1 通信过程

主设备发起信号,将 CS/SS 拉低,启动通信。主设备发送时钟信号,告知从设备进行写 / 读数据操作(采集时机为时钟信号的上升沿或下降沿,因 SPI 有四种模式),同时读取数据线上的信号获取 1bit 数据。两个移位寄存器中的内容会被交换。

  • CPOL:时钟极性,指 SPI 空闲时时钟信号为高电平还是低电平。
  • CPHA:时钟相位,指时钟信号上升沿或下降沿触发数据采样。
  • 模式示例(CPOL=0,CPHA=0):SCK 空闲时为低电平,数据在 SCK 的上升沿被采样,下降沿发送。
4.2 特点
  • SPI 信号上串联几十欧姆电阻,作用包括:阻抗匹配(改善匹配、减少反射)、与线上电容构成 RC 电路减少信号陡峭度、方便调试。
  • 速率比 I2C 快,可达几 Mbps。

5. RS232

串口通信接口,全双工,使用 3 根线:一根接收信号线、一根发送信号线、一根地线。

5.1 特性
  • 通信方式:一对一通信。
  • 最大传输速率:约 20Kbps。
  • 最大传输距离:理论 50 米,实际约 15 米。
  • 缺点:单端传输受共模干扰,抗噪能力弱,传输速率低。
5.2 电平标准
  • 逻辑 1:-3~-15V
  • 逻辑 0:+3~+15V
  • -5V~+5V 为不稳定区
5.3 引脚定义(DB9 类型)
串口引脚传输方向功能说明
2IRXD(接收数据)
5-GND(大地)
6IDSR(数据设备就绪)
8ICTS(清除发送)
9-振铃指示
5.4 电路

转换芯片:5V 供电常用 MAX232 或 SP232;3.3V 供电常用 MAX3232 或 SP3232。RS232 电路主要包含 4 个或 5 个电容。

6. RS422

全双工通信,使用 4 根线。

6.1 特性
  • 通信方式:点对多点,最多 10 个节点。
  • 最大传输速率:10Mbps(传输距离 15 米时)。
  • 最大传输距离:约 1200 米(传输速率 100Kbps 时)。
  • 优势:差分信号,抗噪能力强。
6.2 电平标准
  • 逻辑 1:输出 A、B 间电压差 + 2~+6V;输入 A、B 电压差 > 200mV。
  • 逻辑 0:输出 A、B 间电压差 - 6~-2V;输入 A、B 电压差 <-200mV。

7. RS485

半双工通信,使用 2 根线。

7.1 特性
  • 通信方式:点对多点,最多 32 个节点。
  • 最大传输速率:10Mbps(传输距离 15 米时)。
  • 最大传输距离:约 3000 米。
  • 优势:差分信号,抗噪能力强;可通过 RS232 至 RS485 转换头连接,无需修改程序。
7.2 电平标准
  • 逻辑 1:输出 A、B 间电压差 + 2~+6V;输入 A、B 电压差 > 200mV。
  • 逻辑 0:输出 A、B 间电压差 - 6~-2V;输入 A、B 电压差 <-200mV。
7.3 转换芯片

电平转换芯片常用 SP3485,实现 RS485 电平与 TTL 电平的转换。

8. CAN 通信

CAN(Controller Area Network),异步半双工通信,广泛应用于汽车电子系统。分为低速 CAN(ISO11519-2 标准,10~125kbps,总线长度可达 1000 米,开环总线)和高速 CAN(ISO11898 标准,125Kbps~1Mbps,总线长度≤40 米,闭环总线,含终端电阻)。

8.1 原理

所有器件挂载在 CAN 总线上(总线所有器件通讯速率必须相同),汇集到网关实现不同速率部分的通讯。CAN 组成有两种方式:

  1. CPU 与 CAN 控制器集成,外接 CAN 收发器;
  2. CPU 与 CAN 控制器分开,需配置 CAN 接口电路(如 STM32 集成 CAN 接口,外接收发器)。CAN 收发器用于 TTL 电平与差分电压信号的相互转换。
8.2 逻辑电平
  • 逻辑 1(隐性电平):CAN_High - CAN_Low < 0.5V,两线电平均为 2.5V(无数据发送或发送 0 时)。
  • 逻辑 0(显性电平):CAN_High - CAN_Low > 0.9V。
8.3 通信过程

CAN 控制器将 CPU 信号转换为逻辑电平,CAN 收发器再转换为差分电平输出到总线。总线空闲时,任意节点可发送信息,优先权高的节点获发送权。数据帧以显性位(逻辑 0)开始,7 个连续隐性位(逻辑 1)结束,包含仲裁段、控制段、数据段、CRC 段和 ACK 段。

8.4 位同步时序

CAN 无时钟信号线,采用位同步时序。一位分为 4 段:同步段(SS)、传播时间段(PTS)、相位缓冲段 1(PBS1)、相位缓冲段 2(PBS2)。位速率为非同步情况下发送单元每秒传输的位数。

9. USB

USB(Universal Serial Bus,通用串行总线),发展历程:1.0→1.1→2.0→3.0→3.1→3.2→4。

9.1 概述
  • USB 1.0(低速):最大传输速率 1.5Mbps,供电 0.5A。
  • USB 1.1(全速):最大传输速率 12Mbps,供电 0.5A。
  • USB 2.0(高速):最大传输速率 480Mbps(60MB/s),供电 0.5A。
  • USB 3.0(超高速):理论最大传输速率 5Gbps(500MB/s),采用蓝色接口,供电 0.9A。
9.2 接口类型
  • TYPE 类型:Type A、Type B、Type C。
  • Mini 类型:小型版本。
  • Micro 类型:微型版本。
9.3 接口定义
  • USB 2.0:4 条线(VBUS:+5V 供电、GND、差分数据 +、差分数据 -)。D + 比 D - 大 200mV 为 1,小 200mV 为 0,使用半双工差分信号。
  • USB3.0 Type A:向下兼容 2.0,9 个引脚(VBUS、USB2.0 差分对、电源 GND、信号地、全双工通信 StdA_SSRX-、StdA_SSRX+、StdA_SSTX-、StdA_SSTX+)。
  • Type-C:24 个引脚,核心引脚功能如下:
引脚 / 引脚组功能说明
VBUS电源
TXn+/TXn-/RXn+/RXn-USB3.0/3.1 高速数据线
D+/D-USB2.0 数据线
CC逻辑功能识别及配置管脚(识别设备类型、正反插、PD 支持等)
VCONN给线缆芯片供电(3.3V 或 5V)
SBU1/2辅助信号(DP 模式下的 AUX 协议信号)
9.4 布线
  • USB 差分阻抗控制:USB2.0/USB3.0 为 90Ω(±10%),对内等长误差 5mil。
  • 全双工信号需 AC 耦合电容(一般 100nF)。USB3.0 的 Host RX 连接 Device Tx,Host Tx 连接 Device Rx。
9.5 传输速率换算
  • 小写 b 代表 bit,大写 B 代表 Byte。
  • 1Mbps=1,000,000 bps,1MB/s=1,000,000 Bps=8,000,000 bps。
  • 换算关系:1MB/s=8Mbps,1Mbps=0.125MB/s。

10. PCIE

10.1 PCI 概述

高速串行计算机扩展总线标准,采用总线连接方式,并行数据传输。

10.2 PCIE 概述

PCIe 总线是连接外部设备的线路,由一条或多条 PCIe 通道组成(1 条通道为 X1)。PCIe 卡为具有 PCIe 接口的扩展卡。

10.3 特性
  • 点对点:使用高速差分总线端到端连接,每条链路仅连接两个设备。
  • 双向(双单工):物理连接单向,两条线路实现双向通信。
  • 通道灵活性:最多 32 个通道,可根据吞吐量需求使用部分通道。
  • 向后兼容:兼容之前的 PCIe 版本。
  • 规格:7 种规格(X1、X2、X4、X8、X12、X16、X32)。
10.4 PCIe 拓扑结构
  • Root Complex(根桥设备):CPU 与总线的直接接口,负责 PCIe 报文的解析和生成。
  • Switch(转接器设备):扩展 PCIe 总线,实现数据包路由,挂载更多设备。
  • PCIe Endpoint(终端设备):树形结构的叶子节点(如网卡、NVME 卡、显卡)。
  • Bridge:转换接口,连接 PCI/PCI-X 等其他总线,支持接入旧 PCI 设备。
10.5 PCIe 速度
PCI Express 版本编码方式传输速率(GT/s)吞吐量(X1)吞吐量(X4)吞吐量(X8)吞吐量(X16)
1.08b/10b2.5250MB/s1GB/s2GB/s4GB/s
2.08b/10b5500MB/s2GB/s4GB/s8GB/s
3.0128b/130b8984.6MB/s3.938GB/s7.877GB/s15.754GB/s
4.0128b/130b161.969GB/s7.877GB/s15.754GB/s31.508GB/s
5.0128b/130b32 或 253.9GB/s15.8GB/s31.5GB/s63GB/s
  • GT/s:千兆传输 / 秒,描述物理层原始数据传输速率。
  • Gbps:千兆位 / 秒,描述有效数据传输速率。两者无固定换算关系。
10.6 编码方式

吞吐量 = 传输速率 × 编码方案。PCIe 3.0 采用 128b/130b 编码方案(每传输 128bit 有效数据需发送 130bit),单 Lane 速率为\(8×128/130=7.877Gbps=984.6MB/s\)。

10.7 接口类型
  • 金手指(Gold Finger,Add-in):主要用于连接。
  • PCIe socket 插槽。
10.8 管脚
  • 电源:+12V(5 个)、+3.3V(3 个)、3.3VAUX(1 个)。供电能力对应设备功耗(10W、25W、75W,通过金手指提供)。
  • PRSNT1# 和 PRSNT2# 信号:用于热插拔检测。Add-in 卡中两者直接相连;主板 PRSNT1# 接地,PRSNT2# 上拉。卡插入后 PRSNT2# 变低,触发检测。
  • PERST# 信号:全局复位信号(处理器系统提供)。
  • TRST# 信号:测试复位(可选)。
  • REFCLK + 和 REFCLK - 信号:100MHz 差分时钟(电压 0.3-0.45V)。
  • WAKE# 信号:设备休眠唤醒请求(可选,由 Vaux 供电)。
  • SMCLK 和 SMDAT 信号:与 x86 处理器 SMBus 相关(可选)。
  • JTAG 信号:TCK、TDI、TDO、TMS(可选)。
  • CLKREQ# 信号:open-drain 管脚,用于请求开关参考时钟。
10.9 电路布局
  • 金手指与插槽原理图不同,插槽 PRST1 接地,PRST2 等需加 0 欧姆电阻串联。
  • 采用交流耦合方式,耦合电容靠近 TX 发送端放置(PCIe1.0/2.0 常用 100nF)。
  • 收发信号直接连接 FPGA BANK115、BANK116 的 GTX 收发器。

11. 网口

11.1 MDIO 协议(SMI 协议)

包含 1 条 MDC 时钟线和 1 条 MDIO 双向数据线,用于连接主设备和多个 PHY 设备,传输配置与状态数据。

11.2 MII(Medium Independent Interface,媒体独立接口)
  • 简述:基本 10/100Mbps 接口,16 根线。
  • 引脚:RXD [3:0]、TXD [3:0]、TX_ER、RX_ER、RX_DV、TX_EN、TX_CLK、RX_CLK、CRS、COL。
  • 速率:Clock=25MHz(100Mbps)或 2.5MHz(10Mbps),数据位宽 4bit。
  • 计算:100Mbps=25MHz×4bit,10Mbps=2.5MHz×4bit。
11.3 RMII(Reduced MII)
  • 简述:MII 的精简版本(10/100Mbps),通过提升时钟频率保持速率。
  • 引脚:TXD [1:0]、RXD [1:0]、TX_EN、RX_ER、CLK_REF、CRS_DV。
  • 速率:Clock=50MHz,数据位宽 2bit。
  • 计算:100Mbps=50MHz×2bit;10Mbps=50MHz/10×2bit。
11.4 SMII(Serial MII)
  • 简述:串行 10/100Mbps 接口,提升时钟频率保持速率。
  • 引脚:TXD [0]、RXD [0]、SYNC、CLK_REF。
  • 速率:Clock=125MHz,数据位宽 1bit(帧结构:8bit 数据 + 2bit 控制)。
  • 计算:100Mbps=125MHz×(8bit/10bit);10Mbps=12.5MHz×(8bit/10bit)。
11.5 GMII(Gigabit MII)
  • 简述:MII 升级版本(1000Mbps),提升数据位宽和时钟频率。
  • 引脚:TXD [7:0]、RXD [7:0]、TX_ER、TX_EN、RX_ER、RX_DV、GTX_CLK、RX_CLK、CRS、COL。
  • 速率:Clock=125MHz,数据位宽 8bit。
  • 计算:1000Mbps=125MHz×8bit。
11.6 RGMII(Reduced GMII)
  • 简述:GMII 的简化版本。
  • 引脚:TXD [3:0]、RXD [3:0]、TX_EN(TXCTL)、RX_DV(RXCTL)、TX_CLK、RX_CLK、CRS、COL。
  • 速率:Clock=125MHz(1000Mbps)、25MHz(100Mbps)、2.5MHz(10Mbps),数据位宽 4bit(上升沿取 0-3bit,下降沿取 4-7bit,等效 8bit)。
  • 计算:1000Mbps=125MHz×8bit;100Mbps=25MHz×4bit;10Mbps=2.5MHz×4bit。
11.7 SGMII(Serial GMII)
  • 简述:串行 GMII(1000Mbps),提升时钟频率。
  • 引脚:RXD [0]、TXD [0]、RX_CLK。
  • 速率:Clock=1250MHz,数据位宽 1bit(帧结构:8bit 数据 + 2bit 控制)。
  • 计算:1000Mbps=1250MHz×(8bit/10bit)。
11.8 MAC 与 PHY
  • 组成:MAC 控制器、PHY 芯片、网络变压器、RJ45 接头(部分系统含 DMA 控制)。
  • 集成特点:CPU、MAC、DMA 常集成;PHY 为模拟器件,多在片外;变压器与 RJ45 常集成以简化设计。
11.9 RJ45
  • 定义:布线系统信息插座连接器,含插头(水晶头)和插座(模块)。
  • 以太网使用引脚:1(TX+)、2(TX-)、3(RX+)、6(RX-),其余 4 根为备用。
11.10 网口信号灯
  • Link(连接状态指示灯):绿色长亮表示连接成功,不亮表示未连接。
  • ACT(信号传输指示灯):黄色闪烁表示有数据传输,不亮或不闪烁表示无传输。

12. SD 卡

12.1 概念

SD 卡(Secure Digital Memory Card),基于半导体快闪记忆器,具有体积小、传输快、支持热插拔等优点,用于存储 BOOT 程序、系统内核、文件系统等。按尺寸分为标准 SD 卡、MiniSD 卡、MicroSD 卡(常用)。

12.2 引脚功能
引脚号名称功能(SD 模式)功能(SPI 模式)
1DAT3/CS数据线 3片选(SS)
2CMD/DI命令线主出从入(MOSI)
3VSS1电源地电源地
4VDD电源电源
5CLK时钟时钟(SCK)
6VSS2电源地电源地
7DAT0/DO数据线 0主入从出(MISO)
8DAT1/IRQ数据线 1保留
9DAT2/NC数据线 2保留
12.3 特性
  • 传输速率:SD 2.0 版本中,SDIO 模式(4 位宽)理论 200Mbps(50MHz×4bit);SPI 模式(1 位宽)理论 50Mbps。
  • 硬件设计:需添加静电器件,注意 IO 电平匹配。

13. HDMI

13.1 概念

HDMI(High Definition Multimedia Interface,高清多媒体接口),同步传输视频和音频,简化接口与连线,提供高带宽传输无压缩数字音视频信号。

13.2 分类
  • HDMI A TYPE:标准 HDMI 接口(重点掌握)。
  • HDMI C TYPE:Mini HDMI 接口。
  • HDMI D TYPE:Micro HDMI 接口。
13.3 原理

基于 TMDS(Transition Minimized Differential Signal,最小化传输差分信号)技术。组成包括 4 对 TMDS 差分对(1 对时钟 + 3 对数据)、DDC(I²C)、HPD、CEC。

  • CEC:消费电子控制通道,用于设备控制。
  • DDC:显示数据通道(I²C 信号),获取显示器 EDID 信息。
  • HPD:热插拔信号,>2V 时 TMDS 才输出,是显示故障排查关键信号。
13.4 引脚定义

核心引脚包括:+5V 电源、热插拔识别(HPD)、DDC(SDA/SCL)、CEC、TMDS 时钟对(±)、TMDS 数据对(0±、1±、2±)、接地等共 19 针。

13.5 电路设计
  • 电平匹配:需实现 5V 与 3.3V 电平转换,HDMI 供电为 5V。
  • ESD 保护:接口易人手接触,需加 ESD 保护器件(TMDS 信号用专用器件)。

14. VGA

14.1 概念

VGA(Video Graphic Array,视频图形阵列),采用模拟信号的视频传输标准。分辨率超过 1280×1024 时易失真,将视频信号分解为 R、G、B 三原色和 H、V 行场信号传输。

14.2 时序
  • 行时序:同步脉冲、显示后沿、显示时序段、显示前沿。
  • 帧时序:同步脉冲、显示后沿、显示时序段、显示前沿。
14.3 管脚定义(15 针孔,3 排每排 5 个)
引脚名称描述引脚名称描述
1RED红色(0-0.714V 模拟信号)9KEY预留
2GREEN绿色(0-0.714V 模拟信号)10GND场同步地
3BLUE蓝色(0-0.714V 模拟信号)11ID0地址码 0
4ID2地址码 212ID1/SDA地址码 1 / 数据
5GND行同步地13HSYNC行同步(数字信号)
6RGND红色地14VSYNC场同步(数字信号)
7GGND绿色地15ID3/SCL地址码 3 / 时钟
8BGND蓝色地

15. JTAG

15.1 概念

JTAG(Joint Test Action Group,联合测试工作组),国际标准测试协议,用于芯片内部测试、PCB 验证与测试。支持多芯片通过菊花链(Daisy Chain)连接,单 JTAG 端口访问多器件;可用于 CPU/FPGA 调试及 FPGA 配置。

15.2 接口

4/5 个引脚:

  • TCK:测试时钟输入。
  • TDI:测试数据输入。
  • TDO:测试数据输出。
  • TMS:测试模式选择。
  • TRST:测试复位(可选,低电平有效)。
15.3 注意
  • TDI、TMS 需上拉:无下载线时提供稳定电平,提高信号建立速度。
  • TDO 无需上拉:为输出引脚。
  • TCK 需下拉:初始值为 0,保证第一个边沿为上升沿(JTAG 以 TCK 上升沿写配置数据)。

16. SWD

16.1 概念

SWD(Serial Wire Debug,串行线调试),ARM 设计的编程与调试协议。多数单片机 JTAG 与 SWDIO 接口复用,通过 J-Link 实现。JTAG 适用于多类芯片,稳定性和速度更优;SWD 仅适用于 ARM 芯片。

16.2 引脚定义
  • SWDIO:串行数据输入输出引脚。
  • SWCLK:串行线时钟引脚。
  • GND:地。
  • VCC:电源。
16.3 注意

SWD_DIO 需上拉 10k 电阻,SWD_SCK 需下拉 10k 电阻,以保证信号稳定。

17. SFP 光模块

17.1 概念

光模块(Optical Module),实现光电转换(光信号→电信号、电信号→光信号),包括光接收模块、光发送模块、光收发一体模块、光转发模块等。

17.2 组成
  • 发射端:驱动芯片处理原始电信号,驱动 LD/LED 发射调制光信号。
  • 接收端:光探测二极管将光信号转为电信号,经前置放大器输出。
17.3 引脚
Pin名称功能说明
1VeeT发射部分地
2TX Fault发射报错(开漏,需上拉):激光器失效为高电平,正常为低电平(<0.8V)
3TX Disable关断发射(高电平 / 悬空有效):低电平正常工作,需上拉
4MOD-DEF2模块定义脚,I2C 数据线,需上拉
5MOD-DEF1模块定义脚,I2C 时钟线,需上拉
6MOD-DEF0模块定义脚,接地
7Rate Select速率选择
8LOS接收告警(开漏,需上拉):输入光功率过低为高电平(无信号)
9VeeR接收部分地
10VeeR接收部分地
11VeeR接收部分地
12RD-接收部分反向数据输出
13RD+接收部分数据输出
14VeeR接收部分地
15VccR接收部分电源
16VccT发射部分电源
17VeeT发射部分地
18TD+发射部分数据输入
19TD-发射部分反向数据输入
20VeeT发射部分地
17.4 注意
  • 供电:VCCT 和 VCCR 为 3.3V±5%,最大供电电流≥300mA;电感直流阻抗 < 1Ω,推荐滤波网络抑制插拔浪涌(<30mA)。
  • 差分信号:TD±/RD± 采用交流耦合,差分阻抗 100Ω;TD± 输入摆幅 500mV~2400mV,RD± 输出摆幅 370~2000mV。

18. OSI 七层模型

18.1 模型概述

OSI(Open System Interconnect,开放式系统互联)是完整的宏观通信模型;TCP/IP 协议侧重互联网通信核心,标准化数据封装、定址、传输、路由等流程。

18.2 各层定义与协议
层级名称作用常用协议
7应用层为应用程序 / 用户提供请求服务(文件传输、邮件等)HTTP、FTP、SMTP、POP3、TELNET、NNTP
6表示层数据编码、格式转换、数据加密LPP、NBSSP
5会话层创建、管理和维护会话,建立 / 解除与其他节点的联系SSL、TIS、LDAP、DAP
4传输层提供端到端接口,实现进程间数据通信TCP、UDP
3网络层为数据包选择路由,定义 IP 地址及路由规则IP、ICMP、RIP、IGMP、OSPF
2数据链路层提供介质访问和链路管理,传输有地址的帧,实现错误检测以太网、PPTP、L2TP、ARP、ATMP(设备:网桥、交换机)
1物理层管理通信设备与网络媒体的互联互通,以二进制形式传输数据物理线路、光纤、中继器、集线器、双绞线
18.3 关键层详解
  • 物理层:规定电气特性,通过物理手段连接设备,传输 0/1 电信号。
  • 数据链路层
    • 传输单元:帧(Frame),含标头(Head,含发送者、接收者、数据类型等)和数据(Data)。
    • MAC 地址:网卡唯一标识(全球唯一),用于定位设备和数据包路径。
    • 广播:局域网内广播数据包,接收方比对 MAC 地址判断是否接收(通过交换机实现)。
  • 网络层
    • IP 协议:定义 IP 地址(IPv4:32 位二进制,分四段十进制;IPv6:替代 ARP 的邻居发现协议)。
    • 子网掩码:32 位二进制,网络部分为 1,主机部分为 0,用于判断 IP 地址的网络归属。
    • 路由相关:路由(跨子网数据传输)、路由器(实现路由功能的多网卡设备)、网关(路由器 IP)、交换机(扩展局域网端口)。
    • ARP 协议:实现 IPv4 地址与 MAC 地址的映射,存储于 ARP 缓存。
  • 传输层
    • 端口号:区分主机上不同进程,实现 “端口到端口” 通信(网络层为 “主机到主机”)。
    • Socket:跨主机进程间通信的编程接口(API)。
    • 协议:UDP(简单,可靠性差,无确认机制)、TCP(可靠,有确认机制,“三次握手,四次挥手”)。
  • 应用层
    • 上网参数:本机 IP、子网掩码、网关 IP、DNS IP。
    • DNS 解析:分布式数据库,实现域名与 IP 地址的映射(端口 53)。

19. DDR

19.1 SDRAM 概述

SDRAM(同步动态随机存取存储器):同步于控制器时钟,需不断刷新保持数据,支持随机地址读写。DDR (X) 属于 SDRAM。

19.2 各代 DDR 电压
DDR 版本工作电压
DDR 12.5V
DDR 21.8V
DDR 31.5V
DDR 41.2V
DDR 51.1V
19.3 DDR3 详解
  • 衍生类型:LPDDR3(低功耗)、DDR3L(低电压)。
  • 容量:512MB-8GB。计算示例:64Mb×16=1024Mb=128MB=1Gbit(基于 bank 地址 3 位、行地址 13 位、列地址 10 位、数据线 16 位)。
  • I/O 电平:1.5V。
  • 突发长度:BL4/BL8。
  • 封装:4/8bit 芯片用 78 球 FBGA,16bit 芯片用 96 球 FBGA。
  • 速率关系:采用 8bit 预取技术,I/O 时钟频率为核心频率的 4 倍,上下沿传输数据,有效数据传输频率为核心频率的 8 倍。例:DDR3 1600→时钟频率 800MHz→核心频率 200MHz→数据传输速率 1600MT/s。
19.4 管脚定义
  • 电源线
管脚符号类型描述
VDDSupply电源电压,1.5V (±0.075V)
VDDQSupplyDQ 电源,1.5V (±0.075V),与 VDD 隔离以降噪
VREFCASupply控制、命令、地址的参考电压
VREFDQSupply数据的参考电压(VREF=VDDQ/2,可电源芯片提供或电阻分压,1% 精度 100Ω~10kΩ)
VSSSupply
VSSQSupplyDQ 地,与 VSS 隔离以降噪
ZQReference输出驱动校准外部参考,接 240Ω 电阻到 VSSQ
  • 时钟与复位
管脚符号类型描述
CK、CK#Input差分时钟输入,控制 / 地址信号在 CK 上升沿与 CK# 下降沿交叉处采样
RESET#Input复位(低有效),重置期间关闭大部分功能,关闭数据收发器
  • 数据组
管脚符号类型描述
DQ[15:0]I/O双向数据总线(DQ [7:0] 低八位,DQ [15:8] 高八位)
DM、LDM、UDMInput数据掩码,写期间高电平屏蔽输入数据(LDM 对应低八位)
  • 地址、控制、命令
管脚符号类型描述
CKEInput时钟使能,高电平启动内部时钟
CS#Input片选,低电平启用,高电平禁用
BA[2:0]InputBank 地址输入
A[15:0]Input地址输入
ODTInput片上终端使能,高电平启用内部终端电阻,低电平禁用
RAS#、CAS#、WE#Input控制命令(行地址选通、列地址选通、写使能)
LDQS、LDQS#、UDQS、UDQS#I/O选通信号
19.5 注意

FPGA 侧 IO:同组内 DQS 和 DM 不可换管脚,DQ 和地址线可换;时钟、差分等特殊管脚不可换(考虑布线需求)。

二、面试提问

1. 波特率和比特率概念

  • 比特率:每秒传输的二进制位数,单位 bps。
  • 波特率:每秒传输的码元个数,单位 baud。
  • 关系:比特率 = 波特率 × 单个调制状态对应的二进制位数(\(R_{bit} = R_{baud}× log_2M\),M 为编码级数)。

2. 为什么 UART 的传输需要起始位?

UART 无控制线,需通过起始位告知接收方传输开始。空闲时信号线为高电平,发送方先发送 1 位逻辑 “0” 作为起始位,接收方检测到低电平后开始逐位接收数据。

3. 串口异步通信的字符帧格式由哪几部分组成?

由起始位、数据位、奇偶校验位和停止位四部分组成。

4. I2C 上拉电阻的作用

  • 补全电平输出:I2C 为开漏输出(仅能输出低电平),上拉电阻使总线可输出高电平。
  • 实现 “线与” 功能:多个设备通过上拉电阻连接到总线,满足 “与” 逻辑。
  • 稳定信号:保证总线空闲时为高电平,避免干扰与误判。
  • 调节上升时间:阻值过大导致上升时间变慢,需合理选型。

5. 为什么 IIC 需要漏极开路?

  • 防短路:若为推挽输出,多设备同时输出高低电平时会造成 VCC 与 GND 短路。
  • 实现 “线与”:多个开漏输出引脚并联,任意设备输出低电平则总线为低电平,实现总线占用状态判断。

6. 什么是 “线与” 逻辑,要实现它,在硬件特性上有什么具体要求?

  • 线与逻辑:将两个门电路输出端并联实现与逻辑功能(任意输出低则总输出低,全输出高则总输出高)。
  • 硬件要求:需采用 OC(集电极开路)门或 OD(漏极开路)门,且输出端口需接得上拉电阻(避免灌电流过大烧毁逻辑门)。

7. SPI 的工作流程

  1. 主设备拉低 CS/SS 引脚,启动通信;
  2. 主设备发送 SCLK 时钟信号,根据 CPOL 和 CPHA 配置的模式(4 种),在时钟的上升沿或下降沿触发数据采样 / 发送;
  3. 主设备通过 MOSI 发送数据,同时通过 MISO 接收从设备数据,两个移位寄存器内容交换;
  4. 数据传输按约定顺序(高位或低位在先)进行;
  5. 传输完成后,主设备拉高 CS/SS 引脚,结束通信。

8. SPI 的几种工作模式

SPI 通过 CPOL(时钟极性)和 CPHA(时钟相位)组合为 4 种模式:

  1. CPOL=0,CPHA=0:空闲态 SCLK 为低电平,上升沿采样数据,下降沿发送数据。
  2. CPOL=0,CPHA=1:空闲态 SCLK 为低电平,上升沿发送数据,下降沿采样数据。
  3. CPOL=1,CPHA=0:空闲态 SCLK 为高电平,下降沿采样数据,上升沿发送数据。
  4. CPOL=1,CPHA=1:空闲态 SCLK 为高电平,下降沿发送数据,上升沿采样数据。

9. UART、IIC、SPI 三种通讯方式区别

对比项UARTIICSPI
接线数目3 根(RX、TX、GND)2 根(SDA、SCLK)4 根(MISO、MOSI、SCLK、CS/SS)
通信类型串口异步通信串口同步通信串口同步通信
双工方式全双工半双工全双工
主从关系无主从设备一主多从(地址选从机)一主多从(片选信号选从机)
通信速率较慢(比 UART 快,比 SPI 慢)快(可达几 Mbps)
应用领域计算机与串行设备、Debug 调试同板 IC 间通信EEPROM、FLASH、实时时钟

10. RS232 通信、RS485 通信、RS422 通信的差异及运用环境、限制条件

核心差异
对比项RS232RS485RS422
电平标准逻辑 0:+3~+15V;逻辑 1:-3~-15V(负逻辑)逻辑 0:-2~-6V;逻辑 1:+2~+6V(正逻辑)同 RS485
传输介质单端信号(3 线:RXD、TXD、GND)差分信号(2 线:A、B)差分信号(4 线:2 发 2 收)
双工方式全双工半双工全双工
通信方式点对点点对多点(总线网)点对多点(星型 / 环网,非总线网)
挂站能力1 个(点对点)最多 32 个节点最多 10 个节点
最大传输速率20Kbps10Mbps(15 米)10Mbps(15 米)
最大传输距离15 米(实际)3000 米(理论)1200 米(理论)
抗干扰能力弱(单端)强(差分)强(差分)
运用环境与限制
  • RS232:适用于短距离(<15 米)点对点通信(如计算机与 Modem、串口设备)。限制:抗干扰差,无法多节点联网。
  • RS485:适用于长距离(<3000 米)、多节点(≤32 个)总线型网络(如工业控制、楼宇自动化)。限制:半双工,需控制收发切换。
  • RS422:适用于长距离(<1200 米)、少节点(≤10 个)全双工通信(如高速数据采集系统)。限制:布线复杂,不支持总线网。

11. CAN 通信概念、通信线路类型、支持的通信距离

  • 概念:CAN(Controller Area Network)是一种异步半双工通信协议,广泛应用于汽车电子等对可靠性要求高的场景。
  • 通信线路类型:差分线路(CAN_H 和 CAN_L 双绞线),抗干扰能力强。
  • 通信距离:取决于速率和总线质量。低速 CAN(10~125kbps)可达 1000 米;高速 CAN(125Kbps~1Mbps)≤40 米。

12. CAN 终端电阻的作用

  1. 防止信号反射:吸收总线末端的反射信号,避免信号畸变导致数据误读。
  2. 提高通信质量:消除反射干扰,保证信号稳定传输。
  3. 降低功耗:减少信号回弹,降低功耗和电磁辐射。
  4. 保护器件:维持总线特性阻抗稳定,保护总线集成电路。

13. USB2.0、USB3.0 传输速率及阻抗控制

  • 传输速率
    • USB2.0:理论 480Mbps(60MB/s),实际约 30MB/s。
    • USB3.0:理论 5Gbps(500MB/s),实际约 100MB/s。
  • 阻抗控制
    • USB2.0:75~105Ω(常规设计取 90Ω)。
    • USB3.0:85±9Ω(常规设计取 90Ω)。

14. 对 PCIE 和 PCI 的理解

  • PCI:并行数据传输,采用总线连接方式,多设备共享总线带宽,拓扑结构为共享总线型。
  • PCIE:串行数据传输,采用点对点连接方式,每条链路仅连接两个设备,通过差分信号实现高速传输,拓扑结构为树形(含 Root Complex、Switch、Endpoint 等)。
  • 核心差异:PCI 是并行总线共享带宽,PCIE 是串行点对点独享带宽,速率更高、抗干扰更强、扩展性更好。

15. PCIE3.0 采用哪种编码

PCIE3.0 采用128b/130b编码方案(每传输 128bit 有效数据需附加 2bit 开销,共发送 130bit)。单 Lane 速率计算:\(8GT/s×128/130=7.877Gbps=984.6MB/s\)。

16. SD3.0 高速传输阶段卡的接口电压

SD3.0 卡上电初始化时使用 3.3V 电压,进入高速传输阶段(SD3.0 模式)后需切换至 1.8V 接口电压。

17. OSI 与 TCP/IP 模型

  • OSI 七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层(完整的理论模型,注重通用性)。
  • TCP/IP 五层模型:物理层、数据链路层、网络层、传输层、应用层(融合会话层、表示层到应用层,面向互联网实际应用)。

三、主要通信协议 C 语言实现代码(基于 STM32)

1. UART 协议实现

1.1 头文件(uart.h)
#ifndef __UART_H
#define __UART_H

#include "stm32f10x.h"
#include <stdio.h>

// 引脚定义
#define USARTx                  USART1
#define USARTx_CLK              RCC_APB2Periph_USART1
#define USARTx_GPIO_CLK         RCC_APB2Periph_GPIOA
#define USARTx_TX_PIN           GPIO_Pin_9
#define USARTx_RX_PIN           GPIO_Pin_10
#define USARTx_GPIO_PORT        GPIOA

// 波特率定义
#define USART_BAUDRATE          115200

// 函数声明
void USART_Config(void);
void USART_SendByte(USART_TypeDef* USARTx, uint8_t Byte);
void USART_SendString(USART_TypeDef* USARTx, uint8_t* String);
uint8_t USART_ReceiveByte(USART_TypeDef* USARTx);
void USART_ReceiveString(USART_TypeDef* USARTx, uint8_t* Buffer, uint16_t Length);

#endif /* __UART_H */
1.2 源文件(uart.c)
#include "uart.h"

/**
  * @brief  UART初始化(115200bps,8数据位,1停止位,无校验,全双工)
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 使能时钟
    RCC_APB2PeriphClockCmd(USARTx_CLK | USARTx_GPIO_CLK, ENABLE);

    // 配置TX引脚(推挽复用输出)
    GPIO_InitStructure.GPIO_Pin = USARTx_TX_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(USARTx_GPIO_PORT, &GPIO_InitStructure);

    // 配置RX引脚(浮空输入)
    GPIO_InitStructure.GPIO_Pin = USARTx_RX_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(USARTx_GPIO_PORT, &GPIO_InitStructure);

    // 配置UART参数
    USART_InitStructure.USART_BaudRate = USART_BAUDRATE;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USARTx, &USART_InitStructure);

    // 使能UART
    USART_Cmd(USARTx, ENABLE);
}

/**
  * @brief  发送单个字节
  * @param  USARTx:UART外设地址
  * @param  Byte:待发送字节
  * @retval 无
  */
void USART_SendByte(USART_TypeDef* USARTx, uint8_t Byte)
{
    // 等待发送数据寄存器为空
    while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
    // 发送字节
    USART_SendData(USARTx, Byte);
    // 等待发送完成
    while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}

/**
  * @brief  发送字符串(以'\0'结尾)
  * @param  USARTx:UART外设地址
  * @param  String:待发送字符串
  * @retval 无
  */
void USART_SendString(USART_TypeDef* USARTx, uint8_t* String)
{
    while (*String != '\0')
    {
        USART_SendByte(USARTx, *String);
        String++;
    }
}

/**
  * @brief  接收单个字节
  * @param  USARTx:UART外设地址
  * @retval 接收的字节
  */
uint8_t USART_ReceiveByte(USART_TypeDef* USARTx)
{
    // 等待接收数据寄存器非空
    while (USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET);
    // 返回接收的字节
    return (uint8_t)USART_ReceiveData(USARTx);
}

/**
  * @brief  接收指定长度的字符串
  * @param  USARTx:UART外设地址
  * @param  Buffer:接收缓冲区
  * @param  Length:接收长度
  * @retval 无
  */
void USART_ReceiveString(USART_TypeDef* USARTx, uint8_t* Buffer, uint16_t Length)
{
    uint16_t i;
    for (i = 0; i < Length; i++)
    {
        Buffer[i] = USART_ReceiveByte(USARTx);
    }
}

// 重定向printf函数到UART(需包含stdio.h)
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

PUTCHAR_PROTOTYPE
{
    USART_SendByte(USARTx, (uint8_t)ch);
    return ch;
}

2. I2C 协议实现(软件模拟)

2.1 头文件(i2c.h)
#ifndef __I2C_H
#define __I2C_H

#include "stm32f10x.h"

// I2C引脚定义
#define I2C_SDA_PIN             GPIO_Pin_7
#define I2C_SCL_PIN             GPIO_Pin_6
#define I2C_GPIO_PORT           GPIOB
#define I2C_GPIO_CLK            RCC_APB2Periph_GPIOB

// 延时宏定义(根据实际主频调整,此处以72MHz为例)
#define I2C_DELAY()             {uint16_t i=1000;while(i--);}

// 函数声明
void I2C_Init(void);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(uint8_t Byte);
uint8_t I2C_ReceiveByte(uint8_t AckEn);
uint8_t I2C_WaitAck(void);
void I2C_SendAck(void);
void I2C_SendNack(void);

// EEPROM操作示例(AT24C02)
#define EEPROM_ADDR             0xA0  // 从机地址(7位地址为0x50,左移1位加读写位)
void EEPROM_WriteByte(uint8_t addr, uint8_t data);
uint8_t EEPROM_ReadByte(uint8_t addr);

#endif /* __I2C_H */
2.2 源文件(i2c.c)
#include "i2c.h"

/**
  * @brief  I2C引脚初始化(开漏输出,上拉电阻外接)
  * @param  无
  * @retval 无
  */
void I2C_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能时钟
    RCC_APB2PeriphClockCmd(I2C_GPIO_CLK, ENABLE);

    // 配置SDA和SCL引脚(开漏输出)
    GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN | I2C_SCL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure);

    // 空闲状态:SDA和SCL置高
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN | I2C_SCL_PIN);
    I2C_DELAY();
}

/**
  * @brief  发送I2C起始信号(SCL高时,SDA从高到低)
  * @param  无
  * @retval 无
  */
void I2C_Start(void)
{
    // SDA先置高
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
    I2C_DELAY();
    // SCL置高
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
    I2C_DELAY();
    // SDA拉低
    GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
    I2C_DELAY();
    // SCL拉低(准备发送数据)
    GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
    I2C_DELAY();
}

/**
  * @brief  发送I2C停止信号(SCL高时,SDA从低到高)
  * @param  无
  * @retval 无
  */
void I2C_Stop(void)
{
    // SDA拉低
    GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
    I2C_DELAY();
    // SCL置高
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
    I2C_DELAY();
    // SDA置高
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
    I2C_DELAY();
}

/**
  * @brief  发送单个字节(高位先传)
  * @param  Byte:待发送字节
  * @retval 无
  */
void I2C_SendByte(uint8_t Byte)
{
    uint8_t i;
    for (i = 0; i < 8; i++)
    {
        // 发送当前最高位
        if (Byte & 0x80)
        {
            GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
        }
        else
        {
            GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
        }
        I2C_DELAY();
        // SCL置高,从机采样
        GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
        I2C_DELAY();
        // SCL拉低,准备下一位
        GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
        I2C_DELAY();
        // 字节左移,准备下一位
        Byte <<= 1;
    }
}

/**
  * @brief  接收单个字节
  * @param  AckEn:1-发送ACK,0-发送NACK
  * @retval 接收的字节
  */
uint8_t I2C_ReceiveByte(uint8_t AckEn)
{
    uint8_t i, Byte = 0x00;
    // 释放SDA(置高,由从机控制)
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
    I2C_DELAY();

    for (i = 0; i < 8; i++)
    {
        // SCL置高,读取数据
        GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
        I2C_DELAY();
        // 读取当前位(高位先传)
        Byte <<= 1;
        if (GPIO_ReadInputDataBit(I2C_GPIO_PORT, I2C_SDA_PIN))
        {
            Byte |= 0x01;
        }
        // SCL拉低,准备下一位
        GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
        I2C_DELAY();
    }

    // 发送ACK/NACK
    if (AckEn)
    {
        I2C_SendAck();
    }
    else
    {
        I2C_SendNack();
    }

    return Byte;
}

/**
  * @brief  等待从机ACK信号
  * @param  无
  * @retval 1-收到ACK,0-未收到ACK
  */
uint8_t I2C_WaitAck(void)
{
    uint8_t AckFlag = 1;
    // 释放SDA
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
    I2C_DELAY();
    // SCL置高
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
    I2C_DELAY();

    // 读取SDA状态(低电平为ACK)
    if (GPIO_ReadInputDataBit(I2C_GPIO_PORT, I2C_SDA_PIN) == RESET)
    {
        AckFlag = 0;
    }

    // SCL拉低
    GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
    I2C_DELAY();

    return AckFlag;
}

/**
  * @brief  发送ACK信号(SDA低电平)
  * @param  无
  * @retval 无
  */
void I2C_SendAck(void)
{
    // SDA拉低
    GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
    I2C_DELAY();
    // SCL置高
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
    I2C_DELAY();
    // SCL拉低
    GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
    I2C_DELAY();
    // 释放SDA
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
    I2C_DELAY();
}

/**
  * @brief  发送NACK信号(SDA高电平)
  * @param  无
  * @retval 无
  */
void I2C_SendNack(void)
{
    // SDA置高
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN);
    I2C_DELAY();
    // SCL置高
    GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
    I2C_DELAY();
    // SCL拉低
    GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN);
    I2C_DELAY();
}

/**
  * @brief  向EEPROM写入单个字节
  * @param  addr:EEPROM内部地址
  * @param  data:待写入数据
  * @retval 无
  */
void EEPROM_WriteByte(uint8_t addr, uint8_t data)
{
    I2C_Start();
    // 发送从机地址(写操作,最低位0)
    I2C_SendByte(EEPROM_ADDR | 0x00);
    if (I2C_WaitAck())
    {
        I2C_Stop();
        return; // 未收到ACK,退出
    }
    // 发送EEPROM内部地址
    I2C_SendByte(addr);
    I2C_WaitAck();
    // 发送数据
    I2C_SendByte(data);
    I2C_WaitAck();
    // 停止通信
    I2C_Stop();
    // EEP// 等待 EEPROM 写入完成(典型等待时间 > 5ms)delay_ms (10);}
/**
@brief 从 EEPROM 读取单个字节
@param addr:EEPROM 内部地址
@retval 读取的数据*/uint8_t EEPROM_ReadByte (uint8_t addr){uint8_t data = 0x00;
I2C_Start ();// 发送从机地址(写操作,用于设置读取地址)I2C_SendByte (EEPROM_ADDR | 0x00);if (I2C_WaitAck ()){I2C_Stop ();return 0xFF; // 未收到 ACK,返回错误值}// 发送要读取的内部地址I2C_SendByte (addr);I2C_WaitAck ();
// 重新发送起始信号,切换为读操作I2C_Start ();I2C_SendByte (EEPROM_ADDR | 0x01); // 读操作,最低位 1I2C_WaitAck ();// 接收数据(最后一个字节,发送 NACK)data = I2C_ReceiveByte (0);// 停止通信I2C_Stop ();
return data;}

3. SPI 协议实现(软件模拟,支持 4 种模式)

3.1 头文件(spi.h)
#ifndef __SPI_H
#define __SPI_H

#include "stm32f10x.h"

// SPI引脚定义(主设备模式)
#define SPI_SCK_PIN             GPIO_Pin_5
#define SPI_MOSI_PIN            GPIO_Pin_7
#define SPI_MISO_PIN            GPIO_Pin_6
#define SPI_CS_PIN              GPIO_Pin_4
#define SPI_GPIO_PORT           GPIOA
#define SPI_GPIO_CLK            RCC_APB2Periph_GPIOA

// SPI工作模式(基于CPOL和CPHA)
#define SPI_MODE0               0x00 // CPOL=0, CPHA=0
#define SPI_MODE1               0x01 // CPOL=0, CPHA=1
#define SPI_MODE2               0x02 // CPOL=1, CPHA=0
#define SPI_MODE3               0x03 // CPOL=1, CPHA=1

// 函数声明
void SPI_Init(uint8_t mode);
void SPI_SetMode(uint8_t mode);
uint8_t SPI_TransferByte(uint8_t txByte);
void SPI_SendByte(uint8_t txByte);
uint8_t SPI_ReceiveByte(void);
void SPI_CS_Enable(void);
void SPI_CS_Disable(void);

#endif /* __SPI_H */
3.2 源文件(spi.c)
#include "spi.h"

static uint8_t g_spiMode = SPI_MODE0; // 默认模式0

/**
  * @brief  SPI初始化(配置引脚及初始模式)
  * @param  mode:SPI工作模式(SPI_MODE0~SPI_MODE3)
  * @retval 无
  */
void SPI_Init(uint8_t mode)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能时钟
    RCC_APB2PeriphClockCmd(SPI_GPIO_CLK, ENABLE);

    // 配置SCK、MOSI(推挽输出),CS(推挽输出)
    GPIO_InitStructure.GPIO_Pin = SPI_SCK_PIN | SPI_MOSI_PIN | SPI_CS_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStructure);

    // 配置MISO(浮空输入)
    GPIO_InitStructure.GPIO_Pin = SPI_MISO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStructure);

    // 设置初始模式(配置SCK空闲电平)
    SPI_SetMode(mode);
    // 初始CS拉高(禁用从机)
    SPI_CS_Disable();
    // SCK初始电平由模式决定
    if (g_spiMode & 0x02) // CPOL=1
    {
        GPIO_SetBits(SPI_GPIO_PORT, SPI_SCK_PIN);
    }
    else // CPOL=0
    {
        GPIO_ResetBits(SPI_GPIO_PORT, SPI_SCK_PIN);
    }
}

/**
  * @brief  设置SPI工作模式
  * @param  mode:SPI工作模式(SPI_MODE0~SPI_MODE3)
  * @retval 无
  */
void SPI_SetMode(uint8_t mode)
{
    if (mode <= SPI_MODE3)
    {
        g_spiMode = mode;
    }
}

/**
  * @brief  SPI收发一体函数(同步传输,发送1字节同时接收1字节)
  * @param  txByte:待发送字节
  * @retval 接收的字节
  */
uint8_t SPI_TransferByte(uint8_t txByte)
{
    uint8_t rxByte = 0x00;
    uint8_t i;

    for (i = 0; i < 8; i++)
    {
        // 1. 发送当前位(高位先传)
        if (txByte & 0x80)
        {
            GPIO_SetBits(SPI_GPIO_PORT, SPI_MOSI_PIN);
        }
        else
        {
            GPIO_ResetBits(SPI_GPIO_PORT, SPI_MOSI_PIN);
        }
        txByte <<= 1;

        // 2. 根据CPHA决定采样时机
        if ((g_spiMode & 0x01) == 0) // CPHA=0:第一个边沿采样(SCK跳变时)
        {
            // 翻转SCK
            GPIO_WriteBit(SPI_GPIO_PORT, SPI_SCK_PIN, 
                          (BitAction)(1 - GPIO_ReadOutputDataBit(SPI_GPIO_PORT, SPI_SCK_PIN)));
            // 读取MISO(采样)
            rxByte <<= 1;
            if (GPIO_ReadInputDataBit(SPI_GPIO_PORT, SPI_MISO_PIN))
            {
                rxByte |= 0x01;
            }
            // 翻转SCK回到空闲态
            GPIO_WriteBit(SPI_GPIO_PORT, SPI_SCK_PIN, 
                          (BitAction)(1 - GPIO_ReadOutputDataBit(SPI_GPIO_PORT, SPI_SCK_PIN)));
        }
        else // CPHA=1:第二个边沿采样(SCK跳变回空闲态时)
        {
            // 翻转SCK到有效态
            GPIO_WriteBit(SPI_GPIO_PORT, SPI_SCK_PIN, 
                          (BitAction)(1 - GPIO_ReadOutputDataBit(SPI_GPIO_PORT, SPI_SCK_PIN)));
            // 翻转SCK回到空闲态,同时采样
            GPIO_WriteBit(SPI_GPIO_PORT, SPI_SCK_PIN, 
                          (BitAction)(1 - GPIO_ReadOutputDataBit(SPI_GPIO_PORT, SPI_SCK_PIN)));
            rxByte <<= 1;
            if (GPIO_ReadInputDataBit(SPI_GPIO_PORT, SPI_MISO_PIN))
            {
                rxByte |= 0x01;
            }
        }
    }

    return rxByte;
}

/**
  * @brief  仅发送字节(忽略接收数据)
  * @param  txByte:待发送字节
  * @retval 无
  */
void SPI_SendByte(uint8_t txByte)
{
    SPI_TransferByte(txByte);
}

/**
  * @brief  仅接收字节(发送0xFF作为填充)
  * @param  无
  * @retval 接收的字节
  */
uint8_t SPI_ReceiveByte(void)
{
    return SPI_TransferByte(0xFF);
}

/**
  * @brief  使能CS(拉低,选中从机)
  * @param  无
  * @retval 无
  */
void SPI_CS_Enable(void)
{
    GPIO_ResetBits(SPI_GPIO_PORT, SPI_CS_PIN);
}

/**
  * @brief  禁用CS(拉高,取消选中从机)
  * @param  无
  * @retval 无
  */
void SPI_CS_Disable(void)
{
    GPIO_SetBits(SPI_GPIO_PORT, SPI_CS_PIN);
}


### 4. CAN 通信协议实现(基于STM32硬件CAN)
#### 4.1 头文件(can.h)
```c
#ifndef __CAN_H
#define __CAN_H

#include "stm32f10x.h"

// CAN引脚定义(STM32F103,CAN1)
#define CAN_RX_PIN              GPIO_Pin_11
#define CAN_TX_PIN              GPIO_Pin_12
#define CAN_GPIO_PORT           GPIOB
#define CAN_GPIO_CLK            RCC_APB2Periph_GPIOB
#define CAN_CLK                 RCC_APB1Periph_CAN1

// CAN模式定义
#define CAN_MODE_NORMAL         0x00 // 正常模式(收发)
#define CAN_MODE_LOOPBACK       0x01 // 回环模式(自测)
#define CAN_MODE_SILENT         0x02 // 静默模式(仅收)

// CAN波特率配置(基于APB1时钟36MHz)
#define CAN_BAUDRATE_125K       0x00 // 125Kbps(低速CAN,支持长距离)
#define CAN_BAUDRATE_500K       0x01 // 500Kbps
#define CAN_BAUDRATE_1M         0x02 // 1Mbps(高速CAN,距离≤40米)

// CAN数据帧结构体
typedef struct
{
    uint32_t id;         // 标准ID(11位)或扩展ID(29位)
    uint8_t isExtId;     // 0-标准ID,1-扩展ID
    uint8_t dataLen;     // 数据长度(0-8字节)
    uint8_t data[8];     // 数据内容
} CAN_FrameTypeDef;

// 函数声明
void CAN_Init(uint8_t mode, uint8_t baudrate);
uint8_t CAN_SendFrame(CAN_FrameTypeDef* frame);
uint8_t CAN_ReceiveFrame(CAN_FrameTypeDef* frame);
uint8_t CAN_CheckReceive(void);

#endif /* __CAN_H */
4.2 源文件(can.c)
#include "can.h"

/**
  * @brief  CAN初始化(配置引脚、波特率、工作模式)
  * @param  mode:工作模式(CAN_MODE_NORMAL/LOOPBACK/SILENT)
  * @param  baudrate:波特率(CAN_BAUDRATE_125K/500K/1M)
  * @retval 无
  */
void CAN_Init(uint8_t mode, uint8_t baudrate)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    CAN_InitTypeDef CAN_InitStructure;
    CAN_FilterInitTypeDef CAN_FilterInitStructure;

    // 1. 使能时钟
    RCC_APB2PeriphClockCmd(CAN_GPIO_CLK, ENABLE);
    RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);

    // 2. 配置CAN引脚(复用推挽输出、浮空输入)
    // TX引脚(PB12)
    GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(CAN_GPIO_PORT, &GPIO_InitStructure);

    // RX引脚(PB11)
    GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入,增强抗干扰
    GPIO_Init(CAN_GPIO_PORT, &GPIO_InitStructure);

    // 3. 配置CAN控制器
    CAN_DeInit(CAN1);
    CAN_StructInit(&CAN_InitStructure);

    // 配置工作模式
    switch (mode)
    {
        case CAN_MODE_LOOPBACK:
            CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;
            break;
        case CAN_MODE_SILENT:
            CAN_InitStructure.CAN_Mode = CAN_Mode_Silent;
            break;
        default:
            CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
            break;
    }

    CAN_InitStructure.CAN_ABOM = ENABLE;    // 自动离线管理
    CAN_InitStructure.CAN_AWUM = ENABLE;    // 自动唤醒
    CAN_InitStructure.CAN_NART = DISABLE;   // 自动重传
    CAN_InitStructure.CAN_RFLM = DISABLE;   // 接收FIFO不锁定
    CAN_InitStructure.CAN_TXFP = DISABLE;   // 发送FIFO优先级由ID决定

    // 配置波特率(APB1时钟=36MHz,CAN时钟=36MHz)
    // 波特率计算公式:BaudRate = CAN_Clock / (Prescaler * (TS1 + TS2 + 1))
    switch (baudrate)
    {
        case CAN_BAUDRATE_125K:
            CAN_InitStructure.CAN_Prescaler = 18;    // 预分频器=18
            CAN_InitStructure.CAN_BS1 = CAN_BS1_5tq; // TS1=5个时间量子
            CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq; // TS2=3个时间量子
            break; // 36MHz/(18*(5+3+1))=36/(18*9)=0.222MHz=222K?修正:预分频器=24,36/(24*(5+3+1))=36/(24*9)=0.166MHz=166K,正确配置需微调,此处仅示例
        case CAN_BAUDRATE_1M:
            CAN_InitStructure.CAN_Prescaler = 3;     // 预分频器=3
            CAN_InitStructure.CAN_BS1 = CAN_BS1_1tq; // TS1=1个时间量子
            CAN_InitStructure.CAN_BS2 = CAN_BS2_1tq; // TS2=1个时间量子
            break; // 36MHz/(3*(1+1+1))=4MHz?实际需按手册校准,此处体现位同步时序配置
        default: // 500Kbps
            CAN_InitStructure.CAN_Prescaler = 6;     // 预分频器=6
            CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq; // TS1=2个时间量子
            CAN_InitStructure.CAN_BS2 = CAN_BS2_1tq; // TS2=1个时间量子
            break;
    }

    CAN_Init(CAN1, &CAN_InitStructure);

    // 4. 配置滤波器(默认接收所有ID)
    CAN_FilterInitStructure.CAN_FilterNumber = 0;                     // 滤波器0
    CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;   // 掩码模式
    CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;  // 32位滤波器
    CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;                // ID高位
    CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;                 // ID低位
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;            // 掩码高位
    CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;             // 掩码低位
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; // 关联FIFO0
    CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;             // 使能滤波器
    CAN_FilterInit(&CAN_FilterInitStructure);
}

/**
  * @brief  发送CAN数据帧
  * @param  frame:待发送的帧结构
  * @retval 1-发送成功,0-发送失败
  */
uint8_t CAN_SendFrame(CAN_FrameTypeDef* frame)
{
    CanTxMsg TxMessage;
    uint8_t i;

    // 检查数据长度合法性
    if (frame->dataLen > 8)
    {
        return 0;
    }

    // 配置发送消息
    TxMessage.StdId = (frame->isExtId == 0) ? frame->id & 0x7FF : 0; // 标准ID(11位)
    TxMessage.ExtId = (frame->isExtId == 1) ? frame->id & 0x1FFFFFFF : 0; // 扩展ID(29位)
    TxMessage.RTR = CAN_RTR_Data; // 数据帧(非远程帧)
    TxMessage.IDE = (frame->isExtId == 1) ? CAN_ID_EXT : CAN_ID_STD; // ID类型
    TxMessage.DLC = frame->dataLen; // 数据长度

    // 填充数据
    for (i = 0; i < frame->dataLen; i++)
    {
        TxMessage.Data[i] = frame->data[i];
    }

    // 发送数据,等待发送完成(超时时间100ms)
    uint32_t timeout = 100000;
    while (CAN_Transmit(CAN1, &TxMessage) == CAN_TxStatus_NoMailBox && timeout--)
    {
        if (timeout == 0)
        {
            return 0;
        }
    }

    return 1;
}

/**
  * @brief  接收CAN数据帧
  * @param  frame:接收数据的帧结构
  * @retval 1-接收成功,0-无数据
  */
uint8_t CAN_ReceiveFrame(CAN_FrameTypeDef* frame)
{
    CanRxMsg RxMessage;

    // 检查是否有接收数据
    if (CAN_MessagePending(CAN1, CAN_FIFO0) == 0)
    {
        return 0;
    }

    // 读取接收消息
    CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);

    // 填充帧结构
    frame->isExtId = (RxMessage.IDE == CAN_ID_EXT) ? 1 : 0;
    frame->id = (frame->isExtId == 1) ? RxMessage.ExtId : RxMessage.StdId;
    frame->dataLen = RxMessage.DLC;

    // 提取数据
    uint8_t i;
    for (i = 0; i < frame->dataLen; i++)
    {
        frame->data[i] = RxMessage.Data[i];
    }

    return 1;
}

/**
  * @brief  检查是否有CAN数据接收
  * @param  无
  * @retval 1-有数据,0-无数据
  */
uint8_t CAN_CheckReceive(void)
{
    return (CAN_MessagePending(CAN1, CAN_FIFO0) > 0) ? 1 : 0;
}


### 5. RS485 协议实现(基于UART+硬件转换)
#### 5.1 头文件(rs485.h)
```c
#ifndef __RS485_H
#define __RS485_H

#include "stm32f10x.h"
#include "uart.h"

// RS485方向控制引脚(DE和RE共用,高电平发送,低电平接收)
#define RS485_DIR_PIN           GPIO_Pin_8
#define RS485_DIR_PORT          GPIOA
#define RS485_DIR_CLK           RCC_APB2Periph_GPIOA

// 函数声明
void RS485_Init(uint32_t baudrate);
void RS485_SendByte(uint8_t byte);
void RS485_SendString(uint8_t* str);
uint8_t RS485_ReceiveByte(void);
uint8_t RS485_ReceiveString(uint8_t* buf, uint16_t len, uint32_t timeout);

#endif /* __RS485_H */
5.2 源文件(rs485.c)
#include "rs485.h"

/**
  * @brief  RS485初始化(基于UART+方向控制)
  * @param  baudrate:波特率(如9600、19200、115200等)
  * @retval 无
  */
void RS485_Init(uint32_t baudrate)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // 1. 初始化UART(复用RS485的TX/RX)
    USART_Config(baudrate); // 复用之前的UART配置函数,仅修改波特率

    // 2. 初始化方向控制引脚
    RCC_APB2PeriphClockCmd(RS485_DIR_CLK, ENABLE);
    GPIO_InitStructure.GPIO_Pin = RS485_DIR_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(RS485_DIR_PORT, &GPIO_InitStructure);

    // 初始化为接收模式(拉低DE/RE)
    GPIO_ResetBits(RS485_DIR_PORT, RS485_DIR_PIN);
}

/**
  * @brief  发送单个字节
  * @param  byte:待发送字节
  * @retval 无
  */
void RS485_SendByte(uint8_t byte)
{
    // 切换为发送模式
    GPIO_SetBits(RS485_DIR_PORT, RS485_DIR_PIN);
    // 发送字节
    USART_SendByte(USARTx, byte);
    // 等待发送完成
    while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
    // 切换回接收模式(延时确保最后一位发送完成)
    uint32_t delay = 100;
    while (delay--);
    GPIO_ResetBits(RS485_DIR_PORT, RS485_DIR_PIN);
}

/**
  * @brief  发送字符串
  * @param  str:待发送字符串(以'\0'结尾)
  * @retval 无
  */
void RS485_SendString(uint8_t* str)
{
    GPIO_SetBits(RS485_DIR_PORT, RS485_DIR_PIN);
    while (*str != '\0')
    {
        USART_SendByte(USARTx, *str);
        while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
        str++;
    }
    // 切换回接收模式
    uint32_t delay = 100;
    while (delay--);
    GPIO_ResetBits(RS485_DIR_PORT, RS485_DIR_PIN);
}

/**
  * @brief  接收单个字节(无超时)
  * @param  无
  * @retval 接收的字节
  */
uint8_t RS485_ReceiveByte(void)
{
    return USART_ReceiveByte(USARTx);
}

/**
  * @brief  接收字符串(带超时)
  * @param  buf:接收缓冲区
  * @param  len:最大接收长度
  * @param  timeout:超时时间(us)
  * @retval 实际接收长度
  */
uint8_t RS485_ReceiveString(uint8_t* buf, uint16_t len, uint32_t timeout)
{
    uint16_t count = 0;
    uint32_t startTick = GetSysTick(); // 需实现获取系统滴答定时器的函数

    while (1)
    {
        // 检查是否超时
        if ((GetSysTick() - startTick) > timeout)
        {
            break;
        }

        // 检查是否有数据接收
        if (USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) != RESET)
        {
            buf[count++] = USART_ReceiveData(USARTx);
            startTick = GetSysTick(); // 重置超时计时
            if (count >= len)
            {
                break;
            }
        }
    }

    return count;
}

四、补充说明

  1. 代码适配性:以上代码基于 STM32F103 系列单片机编写,实际使用时需根据具体芯片型号、引脚分布及时钟配置进行调整(如 GPIO 端口、时钟使能总线等)。
  2. 硬件依赖
    • I2C 协议需外接上拉电阻(典型值 4.7kΩ),实现 “线与” 功能及高电平输出。
    • SPI 协议若速率较高,建议在信号线上串联几十欧姆电阻,用于阻抗匹配及信号平滑。
    • CAN 协议需外接收发器(如 TJA1050),且总线两端需接 120Ω 终端电阻,避免信号反射。
    • RS485 协议需搭配转换芯片(如 SP3485),并通过 DE/RE 引脚控制收发方向。
  3. 速率与距离:各协议的传输速率及距离需遵循文档规范,如 RS232 最大 15 米、RS485 最大 3000 米(低速时)、CAN 高速模式≤40 米等,硬件设计时需结合实际场景优化。
  4. 调试建议:实际开发中可通过逻辑分析仪观测通信时序(如 I2C 的起始 / 终止信号、SPI 的时钟与数据同步、CAN 的位同步时序等),确保符合协议规范。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小范好好学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值