19:I2C一:程序模拟I2C通信时序

I2C

  • 1、什么是I2C
  • 2、I2C的通信时序
    • 2.1:起始信号
    • 2.2:停止信号
    • 2.3:主机向从机发送一个字节数据
    • 2.4:主机向从机读取一个字节数据
    • 2.5:主机接收应答
    • 2.6:主机发送应答
  • 3、程序模拟I2C的通信时序
    • 3.1:指定地址写一个字节的数据
    • 3.2:指定地址读一个字节的数据

1、什么是I2C

   I2C通信协议和串口通信USART不同,USART是异步,全双工的通信协议。而I2C通信协议是一种同步,半双工,带数据应答,支持总线挂载多设备的通信协议。USART传输数据的时候是先传输数据帧的低位,在传高位。而I2C传输数据的时候是高位先行。

在这里插入图片描述

   由上图所示:有2条总线,数据总线SDA和时钟总线SCL。其中时钟线SCL只能由主机控制,而SDA主机和从机都可以控制。

   当主机向从机发送一个字节数据时:①主机先发送一个起始信号,②然后在发送一个字节的数据(从机地址(7位)+W),然后接收从机的应答信号,③然后在发送一个字节的数据(从机寄存器的地址),然后接收一个应答信号,④然后开始发送需要发送给从机的数据,然后接收应答信号,⑤发送一个停止信号。 如下图所示:
在这里插入图片描述

   当主机读取从机一个字节的数据时:①主机先发送一个起始信号,②然后在发送一个字节的数据(从机地址(7位)+W),然后接收从机的应答信号,③然后在发送一个字节的数据(从机寄存器的地址)④然后主机在发送一个起始信号,⑤然后在发送一个字节的数据(从机地址(7位)+R),然后接收从机的应答信号,⑥然后开始读取SDA上面的数据,读取到一个字节后,给从机发送一个应答信号,如下图所示:
在这里插入图片描述

2、I2C的通信时序

2.1:起始信号

   起始信号和停止信号都是由主机进行发送,主机需要发送起始信号时:SCL高电平期间,SDA从高电平切换到低电平
在这里插入图片描述
使用程序模拟此时序代码如下:

void I2C_W_SCL(uint8_t BitValue)//拉低SCL或者释放SCL(拉高)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);Delay_us(10);
}void I2C_W_SDA(uint8_t BitValue)//给SDA写数据(拉低SDA/释放SDA(拉高))
{GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);Delay_us(10);
}void MyI2C_Start(void)//起始信号
{
//	SCL(1);//释放SCLI2C_W_SDA(1);//释放SDAI2C_W_SCL(1);//释放SCLI2C_W_SDA(0);//拉低SDAI2C_W_SCL(0);//拉低SCL
}

2.2:停止信号

   主机需要发送停止信号时:SCL高电平期间,SDA从低电平切换到高电平
在这里插入图片描述

使用程序模拟此时序代码如下:

void MyI2C_Stop(void)
{I2C_W_SCL(0);I2C_W_SDA(0);I2C_W_SCL(1);I2C_W_SDA(1);
}

2.3:主机向从机发送一个字节数据

   SCL低电平期间,主机将数据位依次给SDA写入数据(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节,从机读取到一个字节,会有一个应答信号。

在这里插入图片描述
使用程序模拟此时序代码如下:

/*发送数据前,主机会发送一个起始信号,由上面的程序可知:起始信号发送完后SCL和SDA都被拉低。所以函数里面不用在拉低,直接向SDA写入数据即可。
*/
void MyI2C_SendByte(uint8_t Byte)//主机向从机发送一个字节,高位先行
{I2C_W_SDA(Byte & 0x80);//给SDA写入数据,只要Byte不是0,那么写入的就是1。因为BitActionI2C_W_SCL(1);//释放SCL,从机读取数据I2C_W_SCL(0);//拉低SCL,主机准备给SDA写入字节的次高位数据I2C_W_SDA(Byte & 0x40);//给SDA写入数据,I2C_W_SCL(1);//释放SCL,从机读取数据I2C_W_SCL(0);//拉低SCL,主机准备给SDA写入字节的次次高位数据...
}

代码优化如下:

/*发送数据前,主机会发送一个起始信号,由上面的程序可知:起始信号发送完后SCL和SDA都被拉低。所以函数里面不用在拉低,直接向SDA写入数据即可。
*/
void MyI2C_SendByte(uint8_t Byte)//主机向从机发送一个字节,高位先行
{for(uint8_t i = 0;i<8;i++){I2C_W_SDA(Byte & (0x80 >> i));//给SDA写入数据,只要Byte不是0,那么写入的就是1。因为BitActionI2C_W_SCL(1);//释放SCL,从机读取数据I2C_W_SCL(0);//拉低SCL,主机准备给SDA写入字节的次高位数据}
}

2.4:主机向从机读取一个字节数据

   接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA,即SDA拉高

在这里插入图片描述
使用程序模拟此时序代码如下:

uint8_t I2C_R_SDA(void)//主机读SDA数据,即判断引脚的电平
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);Delay_us(10);return BitValue;
}/*主机接收数据之前,主机会给从机发送一个字节,由上面的代码得发送完数据后此时SCL为低电平。之后释放SDA总线让从机拥有SDA总线的控制权。
*/
uint8_t MyI2C_ReceiveByte(void)
{uint8_t Byte = 0x00;//接收数据的变量I2C_W_SDA(1);//主机释放SDA,让控制权给从机,而SCL也是低电平,释放的一瞬间,从机就给SDA写入了数据I2C_W_SCL(1);if(I2C_R_SDA() == 1)//读取第一位数据{Byte |= 0x80;}I2C_W_SCL(0);//主机拉低SCL,让从机给SDA写入数据	I2C_W_SCL(1);//主机拉高SCL,准备开始读取SDA上面的数据if(I2C_R_SDA() == 1){Byte |= 0x40;}I2C_W_SCL(0);//主机拉低SCL,让从机给SDA写入数据	...return Byte;
}

代码优化如下:

uint8_t MyI2C_ReceiveByte(void)
{uint8_t Byte = 0x00;//接收数据的变量I2C_W_SDA(1);//主机释放SDA,让控制权给从机for(uint8_t i =0; i<8;i++){I2C_W_SCL(1);//主机拉高SCL,准备开始读取SDA上面的数据if(I2C_R_SDA() == 1){Byte |= (0x80 >> i);}	I2C_W_SCL(0);//主机拉低SCL,准备让从机给SDA写入数据	}return Byte;
}

2.5:主机接收应答

   接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0(SDA被从机拉低)表示应答,数据1(SDA没有被从机拉低)表示非应答(主机在接收之前,需要释放SDA,即SDA拉高

在这里插入图片描述

使用程序模拟此时序代码如下:

uint8_t I2C_R_SDA(void)//主机读SDA数据,即判断引脚的电平
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);Delay_us(10);return BitValue;
}uint8_t MyI2C_ReceiveACK(void)//主机接收应答
{
/*主机发送完一个字节后,SCL为低电平,等待从机给SDA上面写入数据,如果拉低则代表接收成功
*/uint8_t ACKBit;I2C_W_SDA(1);//主机释放SDA,让控制权给从机I2C_W_SCL(1);//主机释放SCL,准备开始读取SDA上面的数据ACKBit = I2C_R_SDA();I2C_W_SCL(0);//主机拉低SCL,进入下一个时序单元,主机准备给SDA写入字节的数据return ACKBit;
}

2.6:主机发送应答

   主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答。

void MyI2C_SendACK(uint8_t ACKBit)//主机发送应答
{
/*从机发送完一个字节后,SCL为低电平,等待主机给SDA上面写数据,如果拉低代表接收成功。
*/I2C_W_SDA(ACKBit);//主机给SDA写入应答信号,0为应答,1为非应答I2C_W_SCL(1);//主机拉高SCL,让从机读取应答信号I2C_W_SCL(0);//主机拉低SCL,准备写入数据
}

3、程序模拟I2C的通信时序

3.1:指定地址写一个字节的数据

在这里插入图片描述
用程序模拟I2C时序实现如上图的波形:
①I2C.c文件使用程序模拟I2C时序程序如下:

#include "stm32f10x.h"                 
#include "Delay.h"                 //#define SCL(x)          GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)(x))
//#define SDA(x)          GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)(x))void I2C_W_SCL(uint8_t BitValue)//拉低SCL/释放SCL(拉高)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);Delay_us(10);
}void I2C_W_SDA(uint8_t BitValue)//给SDA写数据(拉低SDA/释放SDA(拉高))
{GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);Delay_us(10);
}uint8_t I2C_R_SDA(void)//主机读SDA数据,即判断引脚的电平
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);Delay_us(10);return BitValue;
}void MyI2C_Init(void)
{//1. 使能挂载在APB2总线上面的片上外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//2. 对GPIO_PA10/PA11进行配置GPIO_InitTypeDef GPIOInitStruct;GPIOInitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_OD;//输出开漏模式,0才有驱动能力,开漏模式没有输出低电平时,也可以作为输入GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz;//最大输出速度GPIO_Init(GPIOB,&GPIOInitStruct);GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);//先给输出高电平,释放总线//PB10连接SCL。PB11连接SDA
}void MyI2C_Start(void)//起始信号
{I2C_W_SDA(1);//释放SDAI2C_W_SCL(1);//释放SCLI2C_W_SDA(0);//拉低SDAI2C_W_SCL(0);//拉低SDA
}void MyI2C_Stop(void)
{I2C_W_SCL(0);I2C_W_SDA(0);I2C_W_SCL(1);I2C_W_SDA(1);
}void MyI2C_SendByte(uint8_t Byte)//主机向从机发送一个字节,高位先行
{for(uint8_t i = 0;i<8;i++){I2C_W_SDA(Byte & (0x80 >> i));//给SDA写入数据,只要Byte不是0,那么写入的就是1。因为BitActionI2C_W_SCL(1);//释放SCL,从机读取数据I2C_W_SCL(0);//拉低SCL,主机准备给SDA写入字节的次高位数据}
}uint8_t MyI2C_ReceiveByte(void)
{uint8_t Byte = 0x00;//接收数据的变量I2C_W_SDA(1);//主机释放SDA,让控制权给从机for(uint8_t i =0; i<8;i++){I2C_W_SCL(1);//主机释放SCL,准备开始读取SDA上面的数据if(I2C_R_SDA() == 1){Byte |= (0x80 >> i);}	I2C_W_SCL(0);//主机拉低SCL,让从机给SDA写入数据	}return Byte;
}void MyI2C_SendACK(uint8_t ACKBit)//主机发送应答
{
/*从机发送完一个字节后,SCL为低电平,等待主机给SDA上面写数据,如果拉低代表接收成功。
*/I2C_W_SDA(ACKBit);I2C_W_SCL(1);I2C_W_SCL(0);
}uint8_t MyI2C_ReceiveACK(void)//主机接收应答
{
/*主机发送完一个字节后,SCL为低电平,等待从机给SDA上面写入数据,如果拉低则代表接收成功
*/uint8_t ACKBit;I2C_W_SDA(1);//主机释放SDA,让控制权给从机I2C_W_SCL(1);//主机释放SCL,准备开始读取SDA上面的数据ACKBit = I2C_R_SDA();I2C_W_SCL(0);//主机拉低SCL,进入下一个时序单元,主机准备给SDA写入字节的数据return ACKBit;
}

②主函数文件模拟波形的程序如下:

#include "stm32f10x.h"   
#include "MyI2C.h"
int main(void)
{MyI2C_Init();MyI2C_Start();//发送一个起始信号MyI2C_SendByte(0xD0);//发送一个字节的数据(从机地址7位+W),0表示写,1表示读MyI2C_ReceiveACK();//接收从机应答信号MyI2C_SendByte(0x19);//发送一个字节的数据(从机中的寄存器地址)MyI2C_ReceiveACK();//接收从机应答信号MyI2C_SendByte(0xAA);//发送一个字节的数据(需要发送给从机的数据)MyI2C_ReceiveACK();//接收从机应答信号MyI2C_Stop();//发送一个停止信号
}

3.2:指定地址读一个字节的数据

在这里插入图片描述

#include "stm32f10x.h"   
#include "MyI2C.h"
int main(void)
{uint8_t Data;MyI2C_Init();MyI2C_Start();//发送一个起始信号MyI2C_SendByte(0xD0);//发送一个字节的数据(从机地址7位+W),0表示写,1表示读MyI2C_ReceiveACK();//接收从机应答信号MyI2C_SendByte(0x19);//发送一个字节的数据(从机中的寄存器地址)MyI2C_ReceiveACK();//接收从机应答信号MyI2C_Start();//发送一个起始信号MyI2C_SendByte(0xD1);//发送一个字节的数据(从机地址7位+R),0表示写,1表示读MyI2C_ReceiveACK();//接收从机应答信号Data = MyI2C_ReceiveByte();//主机接收一个字节的数据MyI2C_SendACK(1);//主机发送非应答信号MyI2C_Stop();//发送一个停止信号
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/422104.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NX1872三维电气布线

电气部件审核与定义

Windows系统介绍

文章目录 1、Windows启动过程1.1 启动过程BIOS1.2 启动过程MBR1.3 启动过程 GPT1.4 启动过程BootMgr1.5 启动过程Winload.exe1.6 启动过程1.7 explorer.exe 2、Windows重要进程及组件2.1 注册表Services注册表服务管理器Services.mscsc.exe计划任务taskschd.msc计划任务schtask…

区块链学习笔记1--比特币

区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。 从狭义上来说&#xff1a;区块链是一种按照时间顺序将数据区块以顺序相连的方式组合成的一种链式数据结构&#xff0c;并以密码学的方式保证的不可篡改和不可伪造的分布式账本。 意思就是…

第67期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

组播 2024 9 11

PIM&#xff08;Protocol Independent Multicast&#xff09;是一种常用的组播路由协议&#xff0c;其独立于底层的单播路由协议&#xff0c;能够在多种网络环境中有效地实现多播路由功能。PIM主要有两种模式&#xff1a;PIM Sparse Mode (PIM-SM) 和 PIM Dense Mode (PIM-DM)&…

DDoS对策是什么?详细解说DDoS攻击难以防御的理由和对策方法

攻击规模逐年增加的DDoS攻击。据相关调查介绍&#xff0c;2023年最大的攻击甚至达到了700Gbps。 为了抑制DDoS攻击的危害&#xff0c;采取适当的对策是很重要的。 特别是在网站显示花费时间或频繁出现504错误的情况下&#xff0c;可能已经受到了DDoS攻击&#xff0c;需要尽早采…

Spring面试

一、对Spring的理解 &#xff08;一&#xff09;Spring的发展史 &#xff08;二&#xff09;Spring的体系结构 &#xff08;三&#xff09;Spring相关组件 1.Spring和SpringMVC的关系 2.Spring和SpringBoot的关系 3.Spring和SpringCloud的关系 4.Spring和SpringSecurity的…

C语言基础——⑩③数据结构——②栈和队列

一、栈(Stack) 1、基本概念 栈是一种逻辑结构&#xff0c;是特殊的线性表。特殊在&#xff1a; 只能在固定的一端操作 只要满足上述条件&#xff0c;那么这种特殊的线性表就会呈现一种“后进先出”的逻辑&#xff0c;这种逻辑就被称为栈。栈 在生活中到处可见&#xff0c;比…

为什么企业需要数据目录?

想象一下&#xff0c;如果在没有目录系统的庞大图书馆里寻找一本特定的书&#xff0c;你可能会耗费无数个小时搜索&#xff0c;但最终却一无所获。 同理&#xff0c;企业的数据如果没有一个组织良好、易于搜索的系统&#xff0c;也无法充分发挥其潜力。企业数据目录能够简化这一…

Kafka 基础与架构理解

目录 前言 Kafka 基础概念 消息队列简介&#xff1a;Kafka 与传统消息队列&#xff08;如 RabbitMQ、ActiveMQ&#xff09;的对比 Kafka 的组件 Kafka 的工作原理&#xff1a;消息的生产、分发、消费流程 Kafka 系统架构 Kafka 的分布式架构设计 Leader-Follower 机制与…

进击J6:ResNeXt-50实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、实验目的&#xff1a; 阅读ResNeXt论文&#xff0c;了解作者的构建思路对比之前介绍的ResNet50V2、DenseNet算法使用ResNeXt-50算法完成猴痘病识别 二、实…

jmeter之仅一次控制器

仅一次控制器作用&#xff1a; 不管线程组设置多少次循环&#xff0c;它下面的组件都只会执行一次 Tips&#xff1a;很多情况下需要登录才能访问其他接口&#xff0c;比如&#xff1a;商品列表、添加商品到购物车、购物车列表等&#xff0c;在多场景下&#xff0c;登录只需要…

【EJB】会话Bean(Session Bean)

单例会话****bean在每个应用程序中实例化一次&#xff0c;并存在于应用程序的生命周期中。单例会话bean是为单个企业bean实例在客户端之间共享和并发访问的环境而设计的。 单例会话Bean提供了与无状态会话Bean相似的功能&#xff0c;但与它们不同&#xff0c;因为每个应用程序…

【CanMV K230 AI视觉】人脸关键部位

【CanMV K230 AI视觉】人脸关键部位 人脸关键部位 &#xff08;动态测试效果可以去下面网站自己看。&#xff09; B站视频链接&#xff1a;已做成合集 抖音链接&#xff1a;已做成合集 人脸关键部位 人脸关键部位检测&#xff0c;主要检测脸部轮廓、眉毛、眼睛、鼻子和嘴巴&a…

【Kubernetes】K8s 的鉴权管理(二):基于属性 / 节点 / Webhook 的访问控制

K8s 的鉴权管理&#xff08;二&#xff09;&#xff1a;基于属性 / 节点 / Webhook 的访问控制 1.基于属性的访问控制&#xff08;ABAC 鉴权&#xff09;2.基于节点的访问控制&#xff08;node 鉴权&#xff09;2.1 读取操作2.2 写入操作 3.基于 Webhook 的访问控制3.1 基于 We…

什么是 Grafana?

什么是 Grafana&#xff1f; Grafana 是一个功能强大的开源平台&#xff0c;用于创建、查看、查询和分析来自多个来源的数据。通过可视化仪表盘&#xff08;Dashboard&#xff09;&#xff0c;它能够帮助用户监控实时数据、生成历史报告&#xff0c;甚至进行预测分析。Grafana…

深入解读Docker核心原理:Cgroups资源限制机制详解

在容器化技术中&#xff0c;除了资源的隔离&#xff0c;如何有效地控制和分配系统资源同样至关重要。Cgroups&#xff08;Control Groups&#xff09; 是Linux内核提供的一个强大机制&#xff0c;允许限制、监控和隔离进程组的系统资源使用情况。Cgroups是Docker实现容器资源限…

ffmpeg实现视频的合成与分割

视频合成与分割程序使用 作者开发了一款软件&#xff0c;可以实现对视频的合成和分割&#xff0c;界面如下&#xff1a; 播放时&#xff0c;可以选择多个视频源&#xff1b;在选中“保存视频”情况下&#xff0c;会将多个视频源合成一个视频。如果只取一个视频源中一段视频…

初识爬虫1

学习路线&#xff1a;爬虫基础知识-requests模块-数据提取-selenium-反爬与反反爬-MongoDB数据库-scrapy-appium。 对应视频链接(百度网盘)&#xff1a;正在整理中 爬虫基础知识&#xff1a; 1.爬虫的概念 总结&#xff1a;模拟浏览器&#xff0c;发送请求&#xff0c;获取…

Minimax-秋招正式批-面经(SQL相关)

1. 谈谈对聚簇索引的理解 聚簇索引 InnoDB通过主键聚集数据&#xff0c;如果没有定义主键&#xff0c;InnoDB会选择非空的唯一索引代替。如果没有这样的索引&#xff0c;InnoDB会隐式定义一个主键来作为聚簇索引聚簇索引就是按照每张表的主键构造一颗B树&#xff0c;同时叶子…