嵌入式常用总线协议记录(I2C,SPI, Modbus 485, CAN,2024年最新含小米、腾讯、阿里

59 阅读9分钟
delay\_us(10);
scl_high;
delay\_us(10);
scl_low;

}


I2C 发送数据:



void IIC_Send_Byte(u8 txd) {
u8 t;
// MYSDA_OUT; MYIIC_CLK_LOW; //拉低时钟开始数据传输 ,SCL为低,SDA变高或者变低(数据位),SCL变高,SCL变低,期间SDA为1既1,为0既0 for(t=0;t<8;t++) //一个字节8位,一位一位发送 {
MYIIC_CLK_LOW; if((txd&0x80)>>7) //从最高位开始发送,如果是1,发送高电平 MYIIC_DATA_HIGH; else MYIIC_DATA_LOW; txd<<=1; //SDA处理完毕,此时可以将SCL拉高接受数据,拉高以后延时拉低 delay_us(10); // MYIIC_CLK_HIGH; delay_us(10); MYIIC_CLK_LOW; delay_us(5); } }


I2C数据读取 :



//读1个字节,ack=1时,发送ACK,ack=0,发送nACK u8 IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; // MYSDA_IN;//SDA设置为输入 for(i=0;i<8;i++ ) { MYIIC_CLK_LOW; //SCL为由低变高,在SCL高的时候去读 SDA的数据 delay_us(10); MYIIC_CLK_HIGH; receive<<=1; //第一次这里还是0,第二次开始每次接收的数据做移动一位,从高位开始接收 if(MYIIC_DATA_STATE)receive++; //如果数据为1,++以后就是1,数据为0,不执行就是0; delay_us(10); } if (!ack) IIC_NAck();//发送nACK else IIC_Ack(); //发送ACK return receive; }


I2C等待 ACK:



u8 MYIIC_Wait_Ack(void) { u8 ucErrTime=0; delay_us(5); MYIIC_DATA_HIGH;delay_us(5); //MCU DATA 置高,外面高就是高,外面低就是低 MYIIC_CLK_HIGH; delay_us(5); //CLK 高电平期间数据有效 while(MYIIC_DATA_STATE) //低电平为有应答,高电平无应答 { ucErrTime++; if(ucErrTime>250) { MYIIC_Stop(); return 1; }
} delay_us(10); MYIIC_CLK_LOW; return 0; }


半双工!!!


## 二、SPI


### SPI 基本介绍


SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步通信总线,常用于短距离通讯,主要应用于 EEPROM、FLASH、实时时钟、AD 转换器、还有数字信号处理器和数字信号解码器之间。


SPI的4根线:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/d4c1efc22bd446019b40fd862b76117e~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=lhwPaJTdDiGE3uDpipXI%2B%2B6lUwE%3D)


* SDO/MOSI – 主设备数据输出,从设备数据输入;
* SDI/MISO – 主设备数据输入,从设备数据输出;
* SCLK – 时钟信号,由主设备产生;
* CS/SS – 从设备使能信号,由主设备控制。当有多个从设备的时候,因为每个从设备上都有一个片选引脚接入到主设备机中,当我们的主设备和某个从设备通信时将需要将从设备对应的片选引脚电平拉低或者是拉高(一般来说是低电平选中)。


SPI 以主从方式工作,通常有一个主设备和一个或多个从设备。通信由主设备发起,主设备通过 CS 选择要通信的从设备,然后通过 SCLK 给从设备提供时钟信号,数据通过 MOSI 输出给从设备,同时通过 MISO 接收从设备发送的数据。


主设备能够控制时钟,因为SPI通信并不像UART或者IIC通信那样有专门的通信周期,起始信号,结束信号;所以SPI协议能够通过控制时钟信号线,当没有数据交流的时候我们的时钟线要么是保持高电平要么是保持低电平。


### SPI 的通讯模式


SPI 的 有四种模式:


CPOL=0,表示当SCLK=0时处于空闲态,空闲低电平,所以有效状态就是SCLK处于高电平时  
 CPOL=1,表示当SCLK=1时处于空闲态,空闲高电平,所以有效状态就是SCLK处于低电平时  
 CPHA=0,表示数据采样是在第1个边沿  
 CPHA=1,表示数据采样是在第2个边沿


所以最终有 4 种配置可选 :




| CPOL | CPHA |
| --- | --- |
| 1 | 1 |
| 0 | 0 |
| 1 | 0 |
| 0 | 1 |


当 CPHA= 1 的时候:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/46ae142751984e819a86a750115f71fa~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=6%2BvJeA1AS%2BxrccnF5%2FL6dfBoSbU%3D)


当 CPHA= 0 的时候:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6754f837ace34cc5b5752d0e24336810~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=RlZHuJ96xBI5pjiP0jW4%2BqnGesU%3D)


不同的从设备可能在出厂时由工厂配置为某种通信模式,无法改变,所以我们得根据从设备的通讯模式来设置主设备的模式。


比如我们常用的 SPI Flash W25QXX 系列,可以在文档资料中看到其支持的工作模式:


![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/7be55bf8ca55403488748208c9bb2f08~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=JkyBG7G1K2nKDOrq9hYkoycRAaw%3D)


### SPI速度


已经有器件可达 50Mbps,一般情况下,SPI模块的最大时钟频率为系统时钟频率的1/2。


全双工!!!


## 三、MODBUS RTU 485


### 3.1 RS485


RS-485则是通信线路,只是一种硬件接口。


基本概念:


[RS485百度百科](https://gitee.com/vip204888)


RS485具有A、B两根线,并且通过A、B两根线之间的压差来定义1和0。A和B的压差在+(2-6)V内为高电平,A和B的压差在-(2-6)V内为低电平。由此就定义了RS485的电气接口和电平。


虽然RS485通讯链 用一对双绞线将 各个接口的 “A”“ B” 端连接起来,就可以进行通信了,但是为了保证整个系统的稳定性,还是建议将各个设备共 负极(地),来确保网络的稳定性。


半双工!!!


### 3.2 Modbus 协议


基本概念:


[Modbus通讯协议百度百科](https://gitee.com/vip204888)


在进行多机通信的时候,Modbus协议规定每个控制器必须要知道它们的设备地址,识别按照地址发送过来的数据,决定是否要产生动作,产生何种动作,如果要回应,控制器将生成的反馈信息用Modbus协议发出。


Modbus有两种通信传输方式,一种是ASCII模式,一种是RTU模式。


* RTU方式:也叫十六进制 例如:发送0x03:0000 0011
* ASC方式:0x03 (发送0 :0x30:0011 0000 )( 发送3:0x33:0011 0011)


ASCII 的通信效率低,方便调试,实际应用比较少;工业上都采用RTU方式,效率高。


工业应用上一般 Modbus 485 波特率为9600 或者 4800,因为速度越快传输距离越短,速度太慢也影响效率


### 3.3 Modbus 和 RS485 关系和区别


RS485只是一种硬件接口,他只是把来自单片机UART的信号,翻转电平进行传输,并驱动线缆。  
 所以RS485其实只是一种硬件接口驱动芯片。  
 Modbus是一种软件协议,规定了一种语言。  
 Modbus可以跑在RS485上,也能跑在RS232上,也能跑在POWERBUS。  
 Modbus可以支持多种电气接口,如RS-232、RS-485等,还可以在各种介质上传送,如双绞线、光纤、无线等。


同理。  
 RS485上可以用Modbus,也能用其他自拟协议。协议只是软件。


Modbus只是通信协议的一种,就像汉语和英语一样,就是一种交流的语言,一种机器之间交流的语言。那么在交流之前肯定要有沟通的桥梁吧,那就是传输媒介485或232或其他电气规则,同一种协议可以用不同的传输媒介方式如485或232但是同一传输线路上不能同时存在两种协议.


### 3.4 Modbus-RTU 协议格式说明


![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/d72ed74f5242493689228411c2cf1b85~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=eUD%2FaXKNXMwoOD0VN38cB%2BTOPxY%3D)


#### 3.4.1 地址码


通讯地址: 0~247


对于一主多从的通讯架构中,主机为0。从设备地址为 1~247。


主机发送地址码 0 ,一般称为广播数据,从机无需响应。


#### 3.4.2 功能码


ModBus通讯规约可定义的功能码为1到127。


比如: 03H 为 读寄存器功能码; 16H 为 写寄存器功能码。


![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/b1408258ed9d4160ab5fdbd36f51a65c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=dTcgzVkNwTeXZYut%2B49KUF%2BXLc8%3D)  
 从设备的回应数据包格式:


* 回应数据包和主机查询的数据包格式包是一致的
* 正常回应时,功能码与主机发的功能码一致(1~127)
* 异常的回应,功能码要在收到的功能码基础上加上128 例如:发 0x03 收:0x03 +128


#### 3.4.3 数据区


常见的功能码的数据吗格式一般在Modbus通用协议中已经做了规范。


比如: 对于03功能码,后面跟的数据包括 2个字节表示寄存器地址,2个字节表示想要读取的寄存器个数  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/7a19a64da3804807ba088db28da4789f~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=z7hAv5GuM7BWXbmnXIvYMfCsntQ%3D)  
 返回的3功能码,后接1个字节的返回字节个数(该个数应为上述读取寄存器个数的两倍,因为一个寄存器对应两个字节,比如上图中返回的数据应该是 01 03 20 xxxxxxx)


再看个例子:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6907362085574000885eddbdd81cbc63~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=dtmy60PbCqYPoQSJySLK8SE67v0%3D)


#### 3.4.4 CRC校验


CRC 校验:CRC - 16 ,低位在前,高位在后。这个CRC校验代码网上也很多,下面是我自己上传的一个资源:


[CRC校验代码](https://gitee.com/vip204888)


#### 3.4.5 寄存器地址与PLC组态地址


![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/377d00ca2a3a4e629ad6e07cf5d0e4b3~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=ZjPq3c1qgTbqACWZFAuV%2FF%2F2m1E%3D)


如图所示,对于从机而言,有寄存器地址 和 PLC组态地址的分别:


* 寄存器PLC地址指存放于控制器中的地址,这些控制器可以是PLC,也可以使触摸屏,或是文本显示器。PLC地址一般采用10进制描述,共有5位,其中第一位代码寄存器类型。
* 寄存器modbus协议地址指通信时使用的寄存器地址,在实际编程中,由于寄存器PLC地址前缀的区分作用,所以只需说明后4位数,而且需转换为4位十六进制地址。  
 例如PLC地址40001对应寻址地址0x0000,40002对应寻址地址0x0001,寄存器寻址地址一般使用16进制描述。再如,PLC寄存器地址40003对应协议地址0002,PLC寄存器地址30003对应协议地址0002。在实际编程中,由于前缀的区分作用,所以只需说明后4位数,而且需转换为4位十六进制地址。
* 支持 Modbus 协议的设备或软件,使用时用户直接设置或看到的应当是 Modbus 数据地址。Modbus 地址所访问的数据,是通过各种 “功能”读写而来。 功能码是 Modbus 地址的底层。 如果 Modbus 通 信的一方提供的所谓 Modbus 协议只有功能码,则需要注意了解此 功能号与 Modbus 地址间的对应关系。


## 四、CAN


![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/3601c1c58ac54458aa0512f93dd31422~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=RTr18CBnr0h2TUmN8UcIu3gplso%3D)
![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/4eac15693aaa4a5e998998f23374197e~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771259376&x-signature=KWe72vGpAhRvpCYT0eoOt5KiOro%3D)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://gitee.com/vip204888)**