news 2026/4/20 17:14:37

【I2C协议】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【I2C协议】

规格和从机地址传输

1、I2C的信号

2、数据的有效性(I2C是电平触发采样)

SDA的可变化时机数据线(SDA)上的数据只能在时钟线(SCL)为低电平时进行改变。

SDA的稳定性SCL为高电平期间,SDA必须保持稳定。

在SCL为高电平时,SDA上的数据被视为有效,接收方会在这个时刻对SDA的状态进行采样。

3、数据的传输单元(单位)

I²C总线以字节(8位)为单位传输数据,每次传输一个字节。

4、数据的传输顺序(MSBor LSB)

在I²C数据传输中,数据的传输顺序是MSB(Most Significant Bit,最高有效位)先传输。

5、数据传输方向(读or写)

主机是要给从机发送数据,还是要读取从机的数据。

从机地址传输

7 位从机地址(最常用)

这是 I2C 总线的标准地址格式,支持最多 128 个从机地址(27=128),但其中部分地址被预留为特殊用途(如广播地址),实际可用地址约 112 个。

  • 地址字节结构:1 个字节共 8 位,高 7 位为真正的从机地址最低位(第 0 位)为读写控制位(R/W)
    • 当最低位为0时:表示主设备接下来要向从机写入数据(写操作)
    • 当最低位为1时:表示主设备接下来要从从机读取数据(读操作)

10 位从机地址(扩展格式)

当系统需要挂载超过 128 个从设备时,会采用 10 位从机地址格式,支持最多 1024 个独立地址

  • 地址字节结构:10 位地址需要分两个字节发送
    • 第 1 个字节(地址字节 1):高 5 位固定为11110(10 位地址的标志位),接下来 2 位为 10 位地址的高 2 位,最低位为读写控制位(R/W)
    • 第 2 个字节(地址字节 2):完整的 8 位为 10 位地址的低 8 位
  • 选通过程:主设备发送完两个地址字节后,只有 10 位地址完全匹配的从设备才会应答,后续通信流程与 7 位地址一致。

数据读取

特性未移位从机地址移位从机地址
位数7 位(0x00~0x7F)8 位(0x00~0xFE)
定义芯片手册标注的原始地址未移位地址左移 1 位(×2)
用途配置硬件寄存器、代码中表示从机身份实际通信中发送的 8 位地址字节(含读写位)
编程使用直接写入硬件寄存器(库函数自动处理)仅用于底层通信函数的手动实现

有寄存器区分的通信流程:

写操作

起始信号 → 从机地址(写) → ACK → 寄存器地址 → ACK → 数据 → ACK → 停止信号

读操作

起始信号 → 从机地址(写) → ACK → 寄存器地址 → ACK → 重新起始 → 从机地址(读) → ACK → 数据 → NACK/ACK → 停止信号

无寄存器区分的通信流程:

写操作:一般不会使用这种情况,无内存地址无意义。

起始信号 → 从机地址(写) → ACK → 数据 → ACK → 停止信号

读操作

起始信号 → 从机地址(读) → ACK → 数据 → NACK/ACK → 停止信号

关键差异:有寄存器设备必须先发送寄存器地址告知从机操作位置;无寄存器设备直接进行数据交互,无中间寻址环节。

软件I2C(IO模拟I2C)

函数封装

#include "delay.h" //IO操作函数 #define CT_IIC_SCL_1 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_15, GPIO_PIN_SET) //SCL #define CT_IIC_SCL_0 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_15, GPIO_PIN_RESET) //SCL #define CT_IIC_SDA_1 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET) //SCL #define CT_IIC_SDA_0 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET) //SCL #define CT_READ_SDA HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_15) //输入SDA //控制I2C速度的延时 void CT_Delay(void) { delay_us(2); } //电容触摸芯片IIC接口初始化 void CT_IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOG_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /* SCL */ GPIO_InitStruct.Pin = GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; /* 开漏输出 */ GPIO_InitStruct.Pull = GPIO_PULLUP; /* 内部上拉 */ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOG, GPIO_PIN_15, GPIO_PIN_SET); /* 拉高SCL */ /* SDA */ GPIO_InitStruct.Pin = GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; /* 开漏输出 */ GPIO_InitStruct.Pull = GPIO_PULLUP; /* 内部上拉 */ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); /* 拉高SDA */ } void CT_SDA_IN(void) { GPIO_InitTypeDef GPIO_InitStruct; //PB15设置为输入模式 GPIO_InitStruct.Pin = GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP;; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } void CT_SDA_OUT(void) { GPIO_InitTypeDef GPIO_InitStruct; //PB15设置为开漏输出模式 GPIO_InitStruct.Pin = GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } //产生IIC起始信号 void CT_IIC_Start(void) { CT_SDA_OUT(); //sda线输出 CT_IIC_SDA_1; CT_IIC_SCL_1; delay_us(5); CT_IIC_SDA_0;//START:when CLK is high,DATA change form high to low delay_us(5); CT_IIC_SCL_0;//钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号 void CT_IIC_Stop(void) { CT_SDA_OUT();//sda线输出 CT_IIC_SCL_1; CT_IIC_SDA_0;//STOP:when CLK is high DATA change form low to high delay_us(5); CT_IIC_SDA_1;//发送I2C总线结束信号 } //等待应答信号到来 //返回值:1,接收应答失败 // 0,接收应答成功 uint8_t CT_IIC_Wait_Ack(void) { uint8_t ucErrTime=0; CT_SDA_IN(); //SDA设置为输入 CT_IIC_SDA_1; delay_us(2); CT_IIC_SCL_1; delay_us(2); //CT_Delay(); while(CT_READ_SDA) { ucErrTime++; if(ucErrTime>250) { CT_IIC_Stop(); return 1; } CT_Delay(); } CT_IIC_SCL_0;//时钟输出0 return 0; } //产生ACK应答 void CT_IIC_Ack(void) { CT_IIC_SCL_0; CT_SDA_OUT(); CT_Delay(); CT_IIC_SDA_0; CT_Delay();CT_Delay(); CT_IIC_SCL_1; CT_Delay();CT_Delay(); CT_IIC_SCL_0; } //不产生ACK应答 void CT_IIC_NAck(void) { CT_IIC_SCL_0; CT_SDA_OUT(); CT_Delay(); CT_IIC_SDA_1; CT_Delay(); CT_IIC_SCL_1; CT_Delay(); CT_IIC_SCL_0; } //IIC发送一个字节 //返回从机有无应答 //1,有应答 //0,无应答 void CT_IIC_Send_Byte(uint8_t txd) { uint8_t t; CT_SDA_OUT(); CT_IIC_SCL_0;//拉低时钟开始数据传输 CT_Delay(); for(t=0;t<8;t++) { if(((txd&0x80)>>7)==1)CT_IIC_SDA_1; else if(((txd&0x80)>>7)==0)CT_IIC_SDA_0; txd<<=1; delay_us(5); CT_IIC_SCL_1; delay_us(5); CT_IIC_SCL_0; delay_us(5); } } //读1个字节,ack=1时,发送ACK,ack=0,发送nACK uint8_t CT_IIC_Read_Byte(unsigned char ack) { uint8_t i,receive=0; CT_SDA_IN();//SDA设置为输入 //delay_us(30); for(i=0;i<8;i++ ) { CT_IIC_SCL_0; delay_us(4); CT_IIC_SCL_1; receive<<=1; if(CT_READ_SDA)receive++; delay_us(4); } if (!ack)CT_IIC_NAck();//发送nACK else CT_IIC_Ack(); //发送ACK return receive; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:21:36

新能源汽车高压能量管理策略:根据整车能量需求,通过划分整车高压能量分配优先级,对整车能量进行分级管理

新能源汽车高压能量管理策略&#xff1a;根据整车能量需求&#xff0c;通过划分整车高压能量分配优先级&#xff0c;对整车能量进行分级管理&#xff0c;通过给出高压件工作使能命令及可用功率或者扭矩限值&#xff0c;让车辆在不同工况下实现高压能量流的合理分配。 6页。踩下…

作者头像 李华
网站建设 2026/4/18 5:13:10

与1500+企业共创,打造更懂游戏的Data+AI平台

12 月 17 日至 19 日&#xff0c;2025 中国游戏产业年会在徐汇举行。这场中国游戏行业规格最高、影响最广的年度盛会&#xff0c;汇聚了诸多来自政府、学界、行业的权威人士&#xff0c;从游戏的文化价值、科技价值、社会价值等议题出发&#xff0c;共同探讨游戏的未来发展方向…

作者头像 李华
网站建设 2026/4/18 1:57:35

新能源汽车动力经济性能EDQ目标分解SSTS(共100多行内容概述)

新能源汽车动力经济性能EDQ目标分解SSTS&#xff0c;100多行踩下电门时扭矩分配的逻辑有多烧脑&#xff1f;混动系统工程师老张盯着屏幕上的EDQ分解模型&#xff0c;随手点开了项目组的SSTS算法核心代码。这个控制策略直接关系到车辆加速性能和能耗表现的平衡点&#xff0c;就像…

作者头像 李华
网站建设 2026/4/18 2:08:04

Langchain-Chatchat构建知识图谱辅助问答系统

Langchain-Chatchat构建知识图谱辅助问答系统 在企业数字化转型的浪潮中&#xff0c;一个看似简单却长期困扰组织效率的问题正日益凸显&#xff1a;员工每天要花多少时间&#xff0c;在散落于几十个文件夹、上百份PDF和无数邮件中的制度文档里“大海捞针”&#xff1f;新员工入…

作者头像 李华
网站建设 2026/4/20 10:26:04

Java毕设选题推荐:基于springboot的足球训练营系统的设计与实现设计与实现学员全周期管理(报名、档案、健康数据)、智能课程排期与预约【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华