I2C(通信协议)

简介

I2C有两根双向信号线。一根是数据线SDA,一根是时钟线SCL。两根线都必须通过一个上拉电阻接到电源。

信号

 

开始

void start()
{
	SDA=1;
	SCL=1;	
	delay();  //5us
	SDA=0; 
	delay();  //5us
	SCL=0;
}

发送字节

void send_data(unsigned char byte)
{
	unsigned char i;
	for(i=0;i<8;i++) //字节拆分按位传递
	{	//SCL为高电平 读取SDA稳定数据 所以SDA变化在前
		SDA=byte&(0x80>>i); //从最高位依次传递给SDA 
		delay();
		SCL=1;
		delay();
		SCL=0;
	}
}

接收字节 

unsigned char read()
{
	unsigned char i,byte=0x00;
	SDA=1; 
	for(i=0;i<8;i++) //字节按位接收
	{	
		SCL=1;
		delay();
		if(SDA){byte|=(0x80>>i);}
		delay();
		SCL=0;
	}
	return byte;
}

接收应答

bit read_sck()
{
	bit ACK;
	SDA=1;
	delay();    //5us
	SCL=1;
	delay();    //5us
	ACK=SDA;
	delay();    //5us
	SCL=0;
	return ACK;
}

发送应答

void send_sck(bit ACK) { 
    // 根据ACK的值设置数据线SDA  
    SDA = ACK; // 注意:通常ACK是低电平,NACK是高电平  
    delay();   // 保持SDA状态,等待从机读取 
    SCL = 1;   // 将时钟线SCL拉高 
    delay();   // 等待时钟线稳定 
    SCL = 0;   // 将时钟线SCL拉低,结束应答  
    delay();   // 等待时钟线稳定  
	SDA = 1;   //释放数据线 线权交给从机
}

结束

void end()
{
	SDA=0;	
	SCL=1;
	delay();  //5us
	SDA=1;
	delay_ms(10); //10ms 
}

注意:I2C协议规定了数据如何发送与接收,但是时序会根据芯片而有所不同

24C08存储芯片为例

写操作

主机发送开始---器件地址()---接收应答---发送写地址---接收应答---发送数据---接收应答---结束

页写操作

主机发送开始---器件地址()---接收应答---发送写地址---接收应答---发送数据---接收应答---发送数据---接收应答(此时写入的数据地址会自增1,到达页边界会回到页首,覆盖原数据)---结束

当前地址读

主机发送开始---器件地址()---接收应答---接收数据(此时读取的地址是上次操作后的数据地址自增1,到达页边界会回到页首)---发送非应答--结束

随机读

主机发送开始---器件地址()---接收应答---读取的数据地址---接收应答---开始---器件地址()---接收应答---接收数据---发送非应答--结束

顺序读

主机发送开始---器件地址()---接收应答---读取的数据地址---接收应答---开始---器件地址()---接收应答---接收数据---发送应答---接收数据---(此时按顺序读,到达页边界会回到页首)---发送非应答--结束

代码

#include <reg52.h>
#include "Delay.H"
#include <intrins.H>

#define delay(){_nop_();_nop_();_nop_();_nop_();_nop_();} //五个机器周期 5微妙 一周期多长时间与晶振有关

sbit SDA =P2^0;	//数据
sbit SCL =P2^1; //时钟
sbit button =P1^7; //按钮


/**
  * @brief  开始信号
  * @param  
  * @param 
  * @param 
  * @retval 
  */
void start()
{
	SDA=1;
	SCL=1;	
	delay();
	SDA=0; 
	delay();
	SCL=0;
}
/**
  * @brief  结束信号
  * @param  
  * @param 
  * @param 
  * @retval 
  */
void end()
{
	SDA=0;	
	SCL=1;
	delay();
	SDA=1;
	delay_ms(10);
}

void send_data(unsigned char dat)
{
	unsigned int i=0;
	for(i;i<8;i++)
	{
		SDA=dat&(0x80>>i);
		delay();
		SCL=1;
		delay();
		SCL=0;
	}
}
unsigned char read()
{
	unsigned char i=0,byte=0x00;
	for(i;i<8;i++) //字节拆分按位接收
	{	
		SCL=1;
		delay();
		if(SDA){byte|=(0x80>>i);} //高位在前低位在后   
		delay();
		SCL=0;
	}
	return byte;
}

// 假设SCL和SDA是控制I2C时钟线和数据线的宏或变量  
// delay函数用于提供必要的延迟,确保时序正确  
  
void send_sck(bit ACK) { 
    // 根据ACK的值设置数据线SDA  
    SDA = ACK; // 注意:通常ACK是低电平,NACK是高电平
    delay();   // 保持SDA状态,等待从机读取 
    SCL = 1;   // 将时钟线SCL拉高 
    delay();   // 等待时钟线稳定 
    SCL = 0;   // 将时钟线SCL拉低,结束应答  
    delay();   // 等待时钟线稳定  
	  SDA = 1;   //释放数据线 线权交给从机
}
//接收应答
bit read_sck()
{
	bit ACK;
	SDA=1;
	delay();    //5us
	SCL=1;
	delay();    //5us
	ACK=SDA;
	delay();    //5us
	SCL=0;
	return ACK;
}

main(void) 
{ 
	if(!button)
	{
		unsigned int j=0;
		while(!button);
		//写数据
			start();
			send_data(0xa0); //器件
			read_sck();
			send_data(0x00); //地址
			read_sck();
			send_data(0xaa); //数据
			read_sck();
			end();
			start();
			send_data(0xa0); //器件
			read_sck();
			send_data(0x01); //地址
			read_sck();
			send_data(0x22); //数据
			read_sck();
			end();
			start();
			send_data(0xa0); //器件
			read_sck();
			send_data(0x02); //地址
			read_sck();
			send_data(0xdd); //数据
			read_sck();
			end();
		
//		//页写
//			start();
//			send_data(0xa0); //器件
//			read_sck();
//			send_data(0x05); //地址
//			read_sck();
//			send_data(0xa1); //数据
//			read_sck();
//			send_data(0xa2); //数据
//			read_sck();
//			send_data(0xa3); //数据
//			read_sck();
//			send_data(0xa4); //数据
//			read_sck();
//			end();
			
			
//		//当前地址读 需要与写或上一次读配合使用  因为读的数据地址指针需要有操作记录 
//		start();
//		send_data(0xa1); //器件
//		read_sck();
//		P3=read();
//		end();
//		//随机读
//		start();
//		send_data(0xa0); //器件
//		read_sck();
//		send_data(0x03); //地址
//		read_sck();
//		start();
//		send_data(0xa1); //器件
//		read_sck();
//		P3=read();
//		send_sck(1);   //非应答停止接收
//		end();
//			//顺序读
//			start();
//			send_data(0xa0); //器件
//			read_sck();
//			send_data(0x00); //地址
//			read_sck();
//			start();
//			send_data(0xa1); //器件
//			read_sck();
//			read();
//			send_sck(0);   //应答继续接收
//			read();
//			send_sck(0);   //应答继续接收
//			P3=read();
//			send_sck(1);   //非应答停止接收
//			end();
	}

} 
 
 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欧的曼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值