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

news2024/11/25 6:56:29

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);//释放SCL
	I2C_W_SDA(1);//释放SDA
	I2C_W_SCL(1);//释放SCL
	I2C_W_SDA(0);//拉低SDA
	I2C_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。因为BitAction
	I2C_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。因为BitAction
		I2C_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);//释放SDA
	I2C_W_SCL(1);//释放SCL
	I2C_W_SDA(0);//拉低SDA
	I2C_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。因为BitAction
		I2C_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.coloradmin.cn/o/2128643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

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

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

“爱满中华”与“民生之语”——全国人民的幸福之音!

近年来,随着科技的不断进步,数字化手段在各个领域的应用越来越广泛。在此背景下,我国宣传部推出了“爱满中华”全民自助补贴APP,旨在通过数字化手段,更有效地推行全民扶贫补助政策,而“爱满中华”自助补贴平台和“民生之语”利民通讯软件也正式被我国中信办称为国家网络未来工程…

组合总和IV(力扣---动态规划)

文章目录 1.题目描述2.解题思路3.代码实现 1.题目描述 题目描述见&#xff1a;组合总和IV 2.解题思路 视频参考&#xff1a; 组合总和IV 以上述的示例1为例&#xff1a; dp[4]的含义是什么&#xff1f;和为4的组合有多少种 一般动态规划是可以由前面的dp[3]推导而来的 dp[3…

使用Lua碰到的问题、踩坑记录

文章目录 1. 获得整除结果2. 在数组末尾添加元素的两种写法3. 接收可变参数 ...4. 复杂表结构的定义5. 一行代码, 同时定义多个变量6. 数组与集合的遍历方式7. 函数声明和调用的位置关系 1. 获得整除结果 用 “/” 来做除法, 得到的结果中总会带有小数. 可以使用 math.modf() …

windows下使用 vscode 远程X11服务GUI显示的三种方法

总结三种方法&#xff1a; 前言 ssh连接要使用-XY。 -X 表示ForwardX11&#xff0c;-Y 是ForwardX11Trusted&#xff0c;就是允许了X11转发&#xff0c;可以实现图形显示&#xff0c;虽然很多工具会默认使用这些参数&#xff0c;但是一般手动加也不会报错&#xff0c;所以就…

命令行中的引号

程序&#xff0c;或者说如果main函数中的参数带引号&#xff0c;那么带引号的部分会被当作一个单独的参数&#xff0c;并传递给main函数。并且解析后&#xff0c;引号会被去掉&#xff0c;并分别传递给argv中的各个参数。 如图所示&#xff0c;第3个参数中的引号被丢弃。

引领智能家居新风尚,WTN6040F门铃解决方案——让家的呼唤更动听

在追求高效与便捷的智能家居时代&#xff0c;每一个细节都承载着我们对美好生活的向往。WTN6040F&#xff0c;作为一款专为现代家庭设计的低成本、高性能门铃解决方案&#xff0c;正以其独特的魅力&#xff0c;悄然改变着我们的居家生活体验。 芯片功能特点&#xff1a; 1.2.4…

ubuntu使用wireshark抓取数据

工具 aircrack-ng工具&#xff1b;wireshark工具 sudo apt-get install aircrack-ng2 sudo add-apt-repository ppa:wireshark-dev/stable sudo apt update sudo apt install -y wireshark使用 airmon-ng 执行ifconfig查看网卡 设置网卡为监听模式&#xff1a;sudo airmo…

UNION嵌套STRUCT的两种类型

1. STRUCT里面的总长度大于UNION中的最大长度 在UNION类型中&#xff0c;嵌套如STRUCT类型&#xff0c;其中STRUCT的类型还比UNION类型中最大的类型的长度还长的时候&#xff0c;会如何处理呢&#xff0c;看下面示例 程序源码 #include "stdafx.h"typedef unsigned…

研究生考试报名上传手持身份证照片,如何拍清晰并且过审

研究生考试是中国高等教育中的一项重要考试&#xff0c;在每年9月下旬开始。在报名过程中&#xff0c;上传手持身份证照片是一个关键步骤&#xff0c;它直接关系到报名是否能够顺利通过审核。本文将为你提供4个实用的技巧&#xff0c;帮助你拍摄出既清晰又符合要求的手持身份证…

【三刷C语言】各种注意事项

这里是阿川的博客&#xff0c;祝您变得更强 ✨ 个人主页&#xff1a;在线OJ的阿川 &#x1f496;文章专栏&#xff1a;C语言入门到进阶 &#x1f30f;代码仓库&#xff1a; 写在开头 现在您看到的是我的结论或想法&#xff0c;但在这背后凝结了大量的思考、经验和讨论 目录 1.…

Linux中yum命令

1.Linux常见软件安装方式 a.yum/apt b.rpm安装包安装 c.源码安装 2.yum常用指令 在root权限下可以安装、卸载程序 安装 yum install [package] 卸载 yum remove [package] 还可以使用yum list列出yum源中所有可安装程序 yum list

【Flink Flick CDC】学习笔记

文章目录 FlinkFlink CDC关于转换算子的解释(Transformation)Flink CDC 与 Debezium 有何关系Savepoint 和 CheckpointingSavepoint 和 Checkpointing 的区别 ![请添加图片描述](https://i-blog.csdnimg.cn/direct/8834c721df794978bde12e4e764ed946.png) Flink Flink是一个无…

windows服务管理插件 nssm

NSSM是一个windows下服务管理插件&#xff0c;可以填加、删除、启动、停止服务 1.下载 官网&#xff1a;http://nssm.cc 下载页面&#xff1a;http://nssm.cc/download 直接下载&#xff1a;http://nssm.cc/release/nssm-2.24.zip 2.食用 以填加php8.2为例 2.1.将nssm.ex…

HarmonyOS开发实战( Beta5.0)自定义装饰器实践规范

介绍 本示例介绍通过自定义装饰器在自定义组件中自动添加inspector (布局回调)方法并进行调用。 效果图预览 不涉及 使用说明 在自定义组件上添加自定义装饰器CallbackObserver&#xff0c;并根据参数设置对应的方法名和需要绑定的组件的ID。编译工程&#xff0c;可以根据…

[240912] X-CMD 发布 v0.4.10:新增 spf 模块 | 新增 gh model 功能 | advise、brew 等模块修复

目录 X-CMD 发布 v0.4.10&#x1f4c3;Changelog✨ gh✨ spf - 终端文件管理器&#x1f4a1; advise&#x1f4e6; brew - MacOs 系统的包管理工具&#x1f4c2; df &#x1f4bf;&#x1f4dd; TLDR✅ 升级指南 X-CMD 发布 v0.4.10 &#x1f4c3;Changelog ✨ gh 新增了 gh …

软考 -- 软件设计师 -- 二轮复习(3) -- 数据结构(持续更新)

软考 – 软件设计师 – 二轮复习(3) – 数据结构(持续更新) 文章目录 软考 -- 软件设计师 -- 二轮复习(3) -- 数据结构(持续更新)前言一、时间、空间复杂度二、递归式时间复杂度三、线性表四、栈五、栈和队列六、串七、朴素模式匹配八、KMP模式匹配九、数组十、矩阵十一、树、二…

思路好!硕士生一作发TOP期刊!!!

2024年8月26日&#xff0c;福建理工大学建筑与城乡规划学院2023级城乡规划学硕士研究生岳谞攀以第一作者在国际知名期刊《Sustainable Cities and Society》在线发表题为“Constructing an urban heat network to mitigate the urban heat island effect from a connectivity p…

[网络]TCP/IP协议 之 TCP协议的核心机制(2)

文章目录 TCP核心机制1. 确认应答2. 超时重传3. 连接管理三次握手四次挥手 4. 滑动窗口5. 流量控制6. 拥塞控制7. 延时应答8. 捎带应答9. 粘包问题10. 异常情况 TCP核心机制 1. 确认应答 (上篇) 2. 超时重传 (上篇) 3. 连接管理 建立连接的流程: 三次握手 断开连接的流程…

大模型书籍丨国内顶尖院校出品,非常火爆的LLM大模型入门中文书来了

最近有一本人工智能入门的书比较火&#xff0c;这本书集合了最新的产品、技术&#xff0c;并通过顶尖院校的教授书写而成。我今天阅读了第一章&#xff0c;感觉浅显易懂&#xff0c;顺便把笔记也做出来了&#xff0c;供大家参考。 大语言模型入门 第一部分 背景与基础知识 第…