IIC协议详解

IIC协议详解软件模拟 IIC 程序代码详解概述 通过 stm32 模拟 IIC 协议读取传感器 86BSD 压力传感器的压力值和温度值数据 利用通信波形来深入理解 IIC 协议 MCU STM32F103 从设备地址 0x28 利用逻辑分析仪进行波形分析 IIC 协议简介 I2C 总线是由 Philips 公司开发的一种简单 双向二线制同步串行总线 它只需要两根线即可在连接于总线上的器件之间传送信息 IIC 接口介绍 SDA 串行数据线 总线空闲时为高电平 SCL 串行时钟线 总线空闲时为高电平 注意 SDA SC

软件模拟IIC程序代码详解



概述:
  通过stm32模拟IIC协议读取传感器86BSD压力传感器的压力值和温度值数据,利用通信波形来深入理解IIC协议。
  MCU-STM32F103,从设备地址0x28,利用逻辑分析仪进行波形分析。




1.IIC协议简介

I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。

1.1 IIC接口介绍

1.2 IIC总线特征

2. IIC总线协议详解

IIC总线协议的构成
  IIC总线协议构成主要构成如下图所示:
  在这里插入图片描述
  用逻辑分析仪捕捉的传输2Byte数据的IIC协议如下图所示:
在这里插入图片描述下面依次分解。








2.1 起始条件

总线上必须以一个起始条件作为数据传输的起点,当SCL为高电平时,SDA由高电平变为低电平时为起始条件,如下图所示。
在这里插入图片描述
说明:起始信号和停止信号总是由主设备发出的。




2.2 从设备地址+R/W位

之前介绍过每个连接到IIC总线上的器件都有唯一的地址,主设备要想与从设备通信就必须先校验从设备地址,7bit从设备地址+1bit读/写位共同构成了1Byte的数据,这1Byte数据在起始条件之后,并在SCL的8个周期内将其放置在了SDA数据线上,如下图所示。
在这里插入图片描述
  ★★★注意:8bit地址数据的D0位位读/写位,D0为1时为主设备向从设备读模式,D0为0时为主设备向从设备写模式,上图中可以看出从设备地址为0x51(0101_0001),实际上该传感器设备的出厂地址为0x28(0010_1000),由此看出D7-D1为设备的出厂地址D0为1,所以此时的模式是向传感器取数据,同时观察逻辑分析仪第三通道同样识别为Read模式。




2.3 ACK应答

2.4 数据传输

2.5 停止条件

总线上必须以一个停止条件作为数据传输的终点,当SCL为高电平时,SDA由低电平变为高电平时为停止条件,如下图所示。
在这里插入图片描述
说明:起始信号和停止信号总是由主设备发出的。




3.利用软件模拟IIC协议读取传感器内部的数据

3.1 起始条件

代码:

void I2C_START(void) { 
     /* 当SCL高电平时,SDA出现一个下降沿表示I2C总线启动信号 */ I2C_SDA_H; I2C_SCL_H; i2c_Delay(); I2C_SDA_L;//SDA先产生下降沿 i2c_Delay(); I2C_SCL_L; i2c_Delay(); } 

3.2 停止条件

void I2C_STOP(void) { 
     /* 当SCL高电平时,SDA出现一个上升沿表示I2C总线停止信号 */ I2C_SDA_L; I2C_SCL_H; i2c_Delay(); I2C_SDA_H;//SDA先产生上升沿 } 

3.3 主设备等待来自从设备的应答

//返回值:1:接收应答失败,0:接收应答成功 uint8_t I2C_Wait_Ack(void) { 
     uint8_t re; I2C_SDA_H; /* CPU释放SDA总线 */ i2c_Delay(); I2C_SCL_H; /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */ i2c_Delay(); if (I2C_SDA_STATE !=0) /* CPU读取SDA口线状态 */ { 
     re = 1; } else { 
     re = 0;//从设备主动将SDA拉低,则接收到应答 } I2C_SCL_L; i2c_Delay(); return re; } 

3.4 主设备应答

代码:

void I2C_ACK(void) { 
     I2C_SDA_L; /* CPU驱动SDA = 0 */ i2c_Delay(); I2C_SCL_H; /* CPU产生1个时钟 */ i2c_Delay(); I2C_SCL_L; i2c_Delay(); I2C_SDA_H; /* CPU释放SDA总线 */ } 

3.5 主设备非应答

代码:

void I2C_NACK(void) { 
     I2C_SDA_H; i2c_Delay(); I2C_SCL_H; i2c_Delay(); I2C_SCL_L; i2c_Delay(); } 

3.6 时钟信号的产生

通过以上程序中的

i2c_Delay(); 

延时的使用产生了周期性的SCL时钟。

static void i2c_Delay(void) { 
     uint8_t i; //循环次数为10时,SCL频率 = 240KHz  for (i = 0; i < 10; i++); } 

3.7 发送一个字节

void I2C_SendByte(uint8_t _ucByte) { 
     uint8_t i; /* 先发送字节的高位bit7 */ for (i = 0; i < 8; i++) { 
     if (_ucByte & 0x80) { 
     I2C_SDA_H; } else { 
     I2C_SDA_L; } i2c_Delay(); I2C_SCL_H; i2c_Delay(); I2C_SCL_L; if (i == 7) { 
     I2C_SDA_H; // 释放总线 } _ucByte <<= 1; /* 左移一个bit */ i2c_Delay(); } } 

3.8 接收一个字节并作出应答

uint8_t I2C_ReceiveByte(u8 ack) { 
     uint8_t i; uint8_t value; /* 读到第1个bit为数据的bit7 */ value = 0; for (i = 0; i < 8; i++) { 
     value <<= 1; I2C_SCL_H; i2c_Delay(); if (I2C_SDA_STATE !=0) { 
     value++; } I2C_SCL_L; i2c_Delay(); } if(ack==0) I2C_NACK(); else I2C_ACK(); return value; } 

4. IIC实践

————–手动分割————————-以下为主设备对从设备一次数据的获取流程————-这才是重点↓———————-

数据提取
模式1:

测试传感器是否应答,地址信息D0为1(主设备向从设备读数据)。
在这里插入图片描述
主机发送从设备地址后等待应答:




void _86BSD_ReadData(unsigned char*Read) { 
     unsigned char i; I2C_START(); I2C_SendByte(0x51);//从机的地址 //0101 0001 I2C_Wait_Ack();//等待从机响应 I2C_STOP(); } 

模式2: 主设备向从设备读取两个字节的数据。
在这里插入图片描述

void _86BSD_ReadData(unsigned char*Read) { 
     unsigned char i; I2C_START(); I2C_SendByte(0x51);//从机的地址 //0101 0001 I2C_Wait_Ack(); //等待从机响应 /*气压*/ *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(0);//0 主机非应答 I2C_STOP(); } 

模式3: 主设备向从设备读取三个字节的数据。
在这里插入图片描述

I2C_START(); I2C_SendByte(0x51);//从机的地址 //0101 0001 I2C_Wait_Ack();//等待从机应答 /*气压*/ *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(0);//0 主机非应答 I2C_STOP(); 

逻辑分析仪捕捉波形:在这里插入图片描述
观察波形,从机收到地址信息后产生应答ACK,随之返回数据0x1E,主机收到数据后产生应答ACK,从机又返回数据0x1C,主机收到数据后产生应答ACK,从机又返回数据0x64,主机收到数据后不再需要从机的数据所以产生非应答NACK。

模式4: 主设备向从设备读取四个字节的数据。
在这里插入图片描述

void _86BSD_ReadData(unsigned char*Read) { 
     unsigned char i; I2C_START(); I2C_SendByte(0x51);//从机的地址 //0101 0001 I2C_Wait_Ack(); /*气压*/ *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; /*温度*/ *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(0);//0 主机非应答 I2C_STOP(); } 

★★★如有错误欢迎指导。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/212877.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午7:03
下一篇 2026年3月18日 下午7:03


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号