I2C,SPI,DAC,看门狗,MODBUS-TCP
I2C介绍
1.什么是I2C总线协议?
I2C(Inter-Integrated Circuit),集成电路总线,是一种广泛用于嵌入式系统的同步、串行、半双工通信协议,用于在同一块电路板上的集成电路之间进行通信。
I2C总线:是一种物理通信架构,用于连接多个设备,实现设备之间的数据传输。
I2C协议:是一套规则,定义了IIC总线上数据通信的过程,包括如何启动通信、传输数据、应答信号、结束通信等。
2.I2C总线的特点
1.双线通信
SCL:负责同步数据; SDA:负责数据传输;
2.同步通信
数据传输由时钟信号线控制,确保同步。
3.串行数据传输
每次只传输一位数据,通过时间序列完成完整的数据包传输。
4.半双工传输
在同一时间内,通信双方只能有一个方向的数据传输,即发送方和接收方需要轮流接收或发送数据,不能同时进行。
5.主从架构
主设备(Master):控制通信过程,生成时钟信号
从设备(Slave):被动响应主设备的命令
6.支持多主多从
同一总线上可以连接多个主设备和从设备,但需要进行冲突检测和仲裁
7.从机地址唯一性
每个从设备都有一个唯一的7位或10位从机地址,主设备通过从机地址指定要通信的从设备
8.速度灵活
标准模式(Standard Mode):100KHz
快速模式Fast Mode):400KHz
高速模式(High-Speed Mode):3.4 MHz
超高速模式(Ultra-Fast Mode):5 MHz
3.I2C协议
1.I2C信号
I2C总线为开漏输出结构,外部需要接上拉电阻维持高电平,I2C总线空闲表现为时钟线和数据线均保持高电平持续一段时间。
2、数据的有效性(I2C是电平触发采样)
SDA的可变化时机数据线(SDA)上的数据只能在时钟线(SCL)为低电平时进行改变。
SDA的稳定性SCL为高电平期间,SDA必须保持稳定。在SCL为高电平时,SDA上的数据被视为有效,接收方会在这个时刻对SDA的状态进行采样。
3、数据的传输单元(单位)I²C总线以字节(8位)为单位传输数据,每次传输一个字节。
4、数据的传输顺序(MSBor LSB)在I²C数据传输中,数据的传输顺序是MSB,先传输。
5、数据传输方向(读or写)主机是要给从机发送数据,还是要读取从机的数据。
SPI介绍
SPI总线是一种用于在微控制器和外部设备之间进行串行通信的同步通信协议。它常用于连接微控制器与外部设备,如传感器,存储器,外部ADC(模数转换器)等。
1.SPI通信引脚
SPI总线通常由4根线组成,包括:SCK,MOSI,MISO,NSS/CS.
SCK:由主设备(通常是微控制器)提供时钟信号,用于同步数据的传输。
MOSI:主设备将数据发送到从设备的线路。
MISO:从设备将数据发送到主设备的线路。
CS:用于选择特定的从设备进行通信。当片选线被拉低时,表示从设备被选中,可以进行通信;当片选线被拉高时,表示通信结束或者没有被从设备选中。
数据传输原理
通信始终由主器件发起。当主器件通过MOSI引脚向从设备发起数据时,从器件同时通过MISO引脚做出响应。这是一个数据输出和数据输入都由同一时钟进行同步的全双工通信过程。
2.SPI的传输方式
SPI总线有三种传输方式:全双工,半双工以及单工传输
全双工传输
半双工传输:在同一个时刻,只能为一个传输方向。在SPI的半双工通信模式下,SCK用于时钟输出,MOSI信号线(主模式下)或MISO信号线(从模式下)用于数据传输。
SPI为主模式时
SPI为从模式时
单工传输:在同一时刻,只有一个传输方向,只能发送或者只能接收。
只发送模式其实就是全双工模式:在发送引脚(主模式下的MOSI或从模式下的MISO)上发送数据,接收引脚(主模式下的MISO或从模式下的MOSI)可用作通用IO引脚,在这种情况下,应用程序只需要忽略接收缓冲区中的数据即可。
只接收模式下:如果是主模式,片选拉低,且使能了SPI时,通信会立即开始,只有当SPI控制器被关闭禁止且当前接收结束时才停止;如果是从模式,只有片选信号被拉低并且一直有来自主器件的SCK输入,SPI就会继续接收数据。
3.SPI通信协议
SPI通信是一种同步通信,意味着数据的传输需要在主设备和从设备之间保持同步。根据时钟信号,设备得以在正确的时机发送和接收数据,从而确保数据传输的有效性以及数据传输的速度。时钟信号由主设备产生,通过SCK引脚输出,主从设备的通信便根据SCK上的时钟信号进行数据收发。
SPI控制器挂载在APB2时钟总线上,使用的是APB2时钟总线提供的时钟,SPI控制器自身还有一个时钟分频器,用于将APB2总线上的时钟,进行一定系数的分频后,在进行输出。时钟的分频系数由SPLCR1寄存器中的BR位控制。
从器件选择(NSS)引脚管理
片选引脚有两种方式:软件管理片选引脚和硬件管理片选引脚
软件管理片选引脚:既不使用SPI控制器自身的NSS引脚,使用普通的IO引脚控制片选。
硬件管理片选引脚:使用SPI控制器自身的NSS引脚,进行设备的片选。NSS有两种模式:片选引脚拉至低电平是使能运行,片选引脚拉高时是取消片选。
数据帧格式
数据帧长度:每个数据帧的长度均为8位或者16位
数据帧方式格式:进行数据的发送时,可以从最高有效位开始发送(MSB),也可以从最低有效位开始发送(LSB)。
时钟极性和时钟相位
时钟极性(CPOL):为0时,SCK时钟线空闲状态下时低电平,为1时,是高电平。
时钟相位(CPHA):为1时,采样数据的第二个边沿。为0时,采样数据的第一个边沿。
相互结合有4种可能的时序关系
DAC控制器
数字-模拟转换器是一种将数字信号(通常是二进制数)转换为模拟信号(连续变化的电压或电流信号)的电子设备。其核心作用是将离散的数字信号转换成可直接驱动物理世界的连续信号,通常用于音频,视频,传感器控制,测量等领域。
1.DAC的种类
R-2R梯形DAC:最常见的DAC之一,基于电阻的网络
加权电阻DAC:每个输入位权重不同
电流型DAC:通过控制电流源输出模拟信号
Sigma-Delta DAC:基于过采样和噪声整形的DAC
2.DAC的性能参数
分辨率:转换精度(通常用位数表示,如8位,16位,24位)
采样率:每秒钟处理的样本数(通常以赫兹为单位)
信噪比(SNR):输出信号与噪声的比例
总谐波失真(THD):描述了在输出的模拟信号中,除去理想信号的基频(原始信号)之外,其他频率成分(即谐波)的相对强度。
线性度:输入到输出信号之间的关系是否是线性的
3.DAC的使用原理
DAC模块是12位电压输出数模转换器。DAC可以按8位或12位模式进行配置,并且可与
DMA控制器配合使用。在12位模式下,数据可以采用左对齐或右对齐。DAC有两个输出
通道,每个通道各有一个转换器。在DAC双通道模式下,每个通道可以单独进行转换:当
两个通道组合在一起同步执行更新操作时,也可以同时进行转换。可通过一个输入参考电压
引脚VREF+(与ADC共享)来提高分辨率。
4.DAC 主要特性
两个DAC 转换器:各对应一个输出通道
12位模式下数据采用左对齐或右对齐
同步更新功能
生成噪声波
生成三角波
使用原理:参考电压,对齐保持寄存器,触发模式,生成波,输出缓冲器
对齐保持寄存器
对于DAC通道X,必须将想要转换的数据输入到下面三个寄存器中:
8位右对齐,12位左对齐,12位右对齐。
触发模式
自动触发:对齐保持寄存器中写入数据后立即转换到DORX寄存器中。
触发模式:软触发,定时器触发,外部引脚触发。
生成波
DHRx中的数值不仅可以直接搬运到DORx寄存器输出,还可以叠加生成波来实现叠加信号输出。分为三角波和噪声波。
独立看门狗
看门狗介绍
五、MODBUS-TCP
基础知识
TCP/IP协议基础
- TCP(传输控制协议):面向连接的、可靠的传输层协议
- IP(网际协议):负责将数据从源主机传输到目标主机的网络层协议
- 端口:TCP/IP协议中标识应用程序的逻辑地址,Modbus TCP/IP默认使用端口502
- 套接字:IP地址和端口号的组合,用于唯一标识网络中的应用程序
字节基础与数据表示
- 位:计算机中最小的信息单位,取值为0或1
- 字节:8位组成一个字节,范围0-255(0x00-0xFF)
- 字:16位组成一个字,范围0-65535(0x0000-0xFFFF)
高低字节与字节序
在Modbus协议中,数据通常以16位字为单位传输:
- 高字节:16位数据中的前8位(高位)
- 低字节:16位数据中的后8位(低位)
例如:对于数值0x1234
- 高字节是
0x12 - 低字节是
0x34
字节序指的是多字节数据在内存中的存储顺序:
- 大端序:高字节在前,低字节在后(Modbus协议使用)
- 小端序:低字节在前,高字节在后(大多数x86架构CPU使用)
寄存器类型与功能
Modbus协议定义了四种不同类型的寄存器,每种寄存器都有特定的用途和地址范围。可以简单理解为四种不同的"数据存储区域",用于不同类型的信息交换。
寄存器类型总览
以下是四种寄存器类型的对比:
| 寄存器类型 | 标识前缀 | 读写属性 | 数据类型 | 地址范围(十进制) | 实际应用场景 |
|---|---|---|---|---|---|
| 线圈寄存器 | 0x | 可读可写 | 位(布尔值) | 00001-09999 | 控制开关设备(如继电器、阀门) |
| 离散输入寄存器 | 1x | 只读 | 位(布尔值) | 10001-19999 | 读取传感器状态(如限位开关、报警信号) |
| 输入寄存器 | 3x | 只读 | 16位整数 | 30001-39999 | 采集模拟量数据(如温度、压力) |
| 保持寄存器 | 4x | 可读可写 | 16位整数 | 40001-49999 | 存储配置参数(如设定值、运行模式) |
注:表中的"标识前缀"是行业内常用的简写方式,用于快速区分寄存器类型。例如"0x"代表线圈寄存器,"1x"代表离散输入寄存器,以此类推。
线圈寄存器
- 功能:相当于设备的"开关",用于控制各种开关设备。
- 特点:可读可写,每个线圈只存储一个位(0或1)。
- 地址范围:00001-09999(对应偏移量0-9998)。
实际例子:
- 地址00001:控制加热器的开关(0=关闭, 1=开启)
- 地址00002:控制风扇的启停(0=停止, 1=运行)
常用操作:使用功能码05(写单个线圈)或15(写多个线圈)来控制设备。
离散输入寄存器
- 功能:相当于设备的"状态指示灯",用于监控各种传感器的状态。
- 特点:只读,每个寄存器只存储一个位(0或1)。
- 地址范围:10001-19999(对应偏移量0-9998)。
实际例子:
- 地址10001:温度传感器的报警状态(0=正常, 1=超温)
- 地址10002:门禁开关的状态(0=关闭, 1=打开)
常用操作:使用功能码02(读离散输入)来获取传感器状态。
输入寄存器
- 功能:相当于设备的"传感器数据采集器",用于存储从传感器读取的模拟量数据。
- 特点:只读,每个寄存器存储一个16位整数(范围0-65535)。
- 地址范围:30001-39999(对应偏移量0-9998)。
实际例子:
- 地址30001:当前温度值(例如读取到250,表示25.0℃)
- 地址30002:湿度值(例如读取到605,表示60.5%RH)
常用操作:使用功能码04(读输入寄存器)来采集传感器数据。
保持寄存器
- 功能:相当于设备的"配置参数存储器",用于存储和修改设备的设置参数。
- 特点:可读可写,每个寄存器存储一个16位整数(范围0-65535)。
- 地址范围:40001-49999(对应偏移量0-9998)。
实际例子:
- 地址40001:温度设定点(例如设置为300,表示30.0℃)
- 地址40002:系统运行模式(0=手动模式, 1=自动模式)
常用操作:使用功能码03(读保持寄存器)或06(写单个保持寄存器)来读写参数。
通信帧结构
Modbus TCP/IP帧格式
Modbus TCP/IP通信帧基于TCP协议,由以下部分组成:
| 字段 | 长度(字节) | 描述 |
|---|---|---|
| 事务处理标识符 | 2 | 唯一标识每个Modbus请求/响应事务 |
| 协议标识符 | 2 | 0x0000表示Modbus协议 |
| 长度字段 | 2 | 后续字节的数量(单位标识符+功能码+数据域) |
| 单位标识符 | 1 | 用于标识在一个TCP连接上的不同Modbus从站,相当于RTU的地址域 |
| 功能码域 | 1 | 指定要执行的操作,如03(读保持寄存器)、06(写单个寄存器) |
| 数据域 | 可变 | 包含请求或响应的数据,长度取决于功能码和具体操作 |
常用功能码详解
读操作功能码
功能码01:读线圈寄存器
- 功能:读取线圈寄存器的当前值
请求帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 起始地址高字节 | 起始地址低字节 | 数量高字节 | 数量低字节 |
|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 6 | 1字节 | 0x01 | 1字节 | 1字节 | 1字节 | 1字节 |
响应帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 字节数 | 数据1 | 数据2 | … | 数据n |
|---|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 3+n | 1字节 | 0x01 | 1字节 | 1字节 | 1字节 | … | 1字节 |
示例:读取从站地址1的线圈寄存器(起始地址0x0000,读取2个)
- 请求帧:
00 01 00 00 00 06 01 01 00 00 00 02 - 响应帧(假设值为0x01, 0x00):
00 01 00 00 00 04 01 01 01 01
功能码02:读离散输入寄存器
- 功能:读取离散输入寄存器的当前值
请求帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 起始地址高字节 | 起始地址低字节 | 数量高字节 | 数量低字节 |
|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 6 | 1字节 | 0x02 | 1字节 | 1字节 | 1字节 | 1字节 |
响应帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 字节数 | 数据1 | 数据2 | … | 数据n |
|---|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 3+n | 1字节 | 0x02 | 1字节 | 1字节 | 1字节 | … | 1字节 |
示例:读取从站地址1的离散输入寄存器(起始地址0x0000,读取2个)
- 请求帧:
00 01 00 00 00 06 01 02 00 00 00 02 - 响应帧(假设值为0x01, 0x00):
00 01 00 00 00 04 01 02 01 01
功能码03:读保持寄存器
- 功能:读取保持寄存器的当前值
请求帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 起始地址高字节 | 起始地址低字节 | 数量高字节 | 数量低字节 |
|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 6 | 1字节 | 0x03 | 1字节 | 1字节 | 1字节 | 1字节 |
响应帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 字节数 | 数据1高字节 | 数据1低字节 | … | 数据n高字节 | 数据n低字节 |
|---|---|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 3+2n | 1字节 | 0x03 | 1字节 | 1字节 | 1字节 | … | 1字节 | 1字节 |
示例:读取从站地址1的保持寄存器(起始地址0x0000,读取1个)
- 请求帧:
00 01 00 00 00 06 01 03 00 00 00 01 - 响应帧(假设寄存器值为0x1234):
00 01 00 00 00 05 01 03 02 12 34
功能码04:读输入寄存器
- 功能:读取输入寄存器的当前值
请求帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 起始地址高字节 | 起始地址低字节 | 数量高字节 | 数量低字节 |
|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 6 | 1字节 | 0x04 | 1字节 | 1字节 | 1字节 | 1字节 |
响应帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 字节数 | 数据1高字节 | 数据1低字节 | … | 数据n高字节 | 数据n低字节 |
|---|---|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 3+2n | 1字节 | 0x04 | 1字节 | 1字节 | 1字节 | … | 1字节 | 1字节 |
示例:读取从站地址1的输入寄存器(起始地址0x0000,读取1个)
- 请求帧:
00 01 00 00 00 06 01 04 00 00 00 01 - 响应帧(假设寄存器值为0x2500):
00 01 00 00 00 05 01 04 02 25 00
写操作功能码
功能码05:写单个线圈
- 功能:设置单个线圈寄存器的值(0或1)
请求帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 线圈地址高字节 | 线圈地址低字节 | 值高字节 | 值低字节 |
|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 6 | 1字节 | 0x05 | 1字节 | 1字节 | 0x00 | 0x00/0xFF |
注:值为0x0000表示关闭线圈,0xFF00表示打开线圈
响应帧格式:与请求帧相同
示例:设置从站地址1的线圈寄存器(地址0x0000)为1
- 请求帧:
00 01 00 00 00 06 01 05 00 00 FF 00 - 响应帧:
00 01 00 00 00 06 01 05 00 00 FF 00
功能码06:写单个保持寄存器
- 功能:设置单个保持寄存器的值
请求帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 寄存器地址高字节 | 寄存器地址低字节 | 数据高字节 | 数据低字节 |
|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 6 | 1字节 | 0x06 | 1字节 | 1字节 | 1字节 | 1字节 |
响应帧格式:与请求帧相同
示例:设置从站地址1的保持寄存器(地址0x0000)值为0x1234
- 请求帧:
00 01 00 00 00 06 01 06 00 00 12 34 - 响应帧:
00 01 00 00 00 06 01 06 00 00 12 34
功能码0F:写多个线圈
- 功能:设置多个线圈寄存器的值
请求帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 起始地址高字节 | 起始地址低字节 | 数量高字节 | 数量低字节 | 字节数 | 数据1 | 数据2 | … | 数据n |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 7+n | 1字节 | 0x0F | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | … | 1字节 |
响应帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 起始地址高字节 | 起始地址低字节 | 数量高字节 | 数量低字节 |
|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 6 | 1字节 | 0x0F | 1字节 | 1字节 | 1字节 | 1字节 |
示例:设置从站地址1的线圈寄存器(起始地址0x0000,设置4个)值为0x05(二进制0101)
- 请求帧:
00 01 00 00 00 08 01 0F 00 00 00 04 01 05 - 响应帧:
00 01 00 00 00 06 01 0F 00 00 00 04
功能码10(0x10):写多个保持寄存器
- 功能:设置多个保持寄存器的值
请求帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 起始地址高字节 | 起始地址低字节 | 数量高字节 | 数量低字节 | 字节数 | 数据1高字节 | 数据1低字节 | … | 数据n高字节 | 数据n低字节 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 7+2n | 1字节 | 0x10 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | … | 1字节 | 1字节 |
响应帧格式:
| 事务处理ID | 协议ID | 长度 | 单位ID | 功能码 | 起始地址高字节 | 起始地址低字节 | 数量高字节 | 数量低字节 |
|---|---|---|---|---|---|---|---|---|
| 2字节 | 0x0000 | 6 | 1字节 | 0x10 | 1字节 | 1字节 | 1字节 | 1字节 |
示例:设置从站地址1的保持寄存器(起始地址0x0000,设置2个)值为0x1234和0x5678
- 请求帧:
00 01 00 00 00 0B 01 10 00 00 00 02 04 12 34 56 78 - 响应帧:
00 01 00 00 00 06 01 10 00 00 00 02
错误响应处理
常见的错误响应包括:
- 功能码不支持:从站返回功能码+0x80
- 地址无效:返回错误码0x02
- 数据无效:返回错误码0x03
- 连接超时:主站未在规定时间内建立TCP连接
- 响应超时:主站未在规定时间内收到响应