I2c时序 与AT24C02之间的通信实验

                             I2c - AT24C02实验

1.1 I2C总线介绍

I2C总线(Inter IC BUS)是由Philips公司开发的一种通用数据总线 两根通信线:SCL(Serial Clock)、SDA(Serial Data) 同步、半双工,带数据应答 通用的I2C总线,可以使各种设备的通信标准统一,对于厂家来说,使用成熟的方案可以缩短芯片设计周期、提高稳定性,对于应用者来说,使用通用的通信协议可以避免学习各种各样的自定义协议,降低了学习和应用的难度.

1.2 引脚及电路

1.3 I2C电路规范

所有的I2C设备的SCL连在一起,SDA连在一起 设备的SCL和SDA均要配置成开漏输出模式 SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右 开漏输出和上拉电阻的共同作用实现了“线与”的功能,此设计主要是为了解决多机通信互相干扰的问题。

2.1 I2C时序结构

起始条件:SCL高电平期间,SDA从高电平切换到低电平。

终止条件:SCL高电平期间,SDA从低电平切换到高电平。

2.2 发送一个字节

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

2.3 接收一个字节

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

2.4 应答信号

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

2.5 I2C数据帧

S:起始位 标志。  SLAVE ADDRESS+W:地址写(高四位固定值1010),如果是写则是0xA0 。  SLAVE ADDRESS+W:读则是0xA1.                                                                                                    RA:接收应答,0表示应答,1表示非应答 。    SA:发送应答                                                        S:BYTE:发送的字节 。     R:BYTE:读字节                                                                                 P:停止标志。   

解读:主机发送一条数据--->从机收到(RA:0)--->主机写一个字节(S:BYTE 1)--->从机收到(RA:0)--->.....

主机读取一条数据--->从机收到(此时控制权交给从机)--->从机接收一个字节--->主机未应答-->结束

3.1 代码实现

分为三个模块:I2C.C,AT24CO2.C,min.c三个文件。

void I2C_Start(void)
{
	I2C_SDA=1;//先给数据(SDA)拉高,因为它的状态不稳定
	I2C_SCL=1;//从机读取SDA
	I2C_SDA=0;
	I2C_SCL=0;
}
/**
  * @brief  停止位
  * @param  无
  * @retval 无
  */
void I2C_Stop(void)
{
	I2C_SDA=0;
	I2C_SCL=1;
	I2C_SDA=1;
}
/**
  * @brief  发送一个字节 I2C_SendByte
  * @param  Byte 发送的字节
  * @retval 无
  */
void I2C_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		//发送的数据就是传进来的数据
		I2C_SDA=Byte&(0x80>>i);//先取出最高位,因为高位在前
		I2C_SCL=1;//上升沿(sda不可变化),读取数据
		I2C_SCL=0;//下降沿,SDA开始变化右移
	}
}
/**
  * @brief  接收一个字节
  * @param  无
  * @retval Byte
  */
unsigned char I2C_ReceiveByte(void)
{
	unsigned char i,Byte=0x00;
	//这里置1,主机放掉控制权,交给从机
	I2C_SDA=1;//主机在接收之前要释放SDA
	for(i=0;i<8;i++)
	{
		I2C_SCL=1;//主机读取
		if(I2C_SDA){Byte|=(0x80>>i);}
		I2C_SCL=0;
	}
	return Byte;
}
/**
  * @brief  发送应答
  * @param  AckBit应答位 0表示应答,1表示非应答
  * @retval 无
  */
void I2C_SendAck(unsigned char AckBit)
{
	I2C_SDA=AckBit;
	I2C_SCL=1;
	I2C_SCL=0;
}
/**
  * @brief  接收应答
  * @param  无
  * @retval AckBit
  */
unsigned char I2C_ReceiveAck(void)
{
	unsigned char AckBit;
	I2C_SDA=1;//释放应答,控制权交给从机
	I2C_SCL=1;//主机读取数据
	AckBit=I2C_SDA;//收到0表示应答,1表示非应答
	I2C_SCL=0;
	return AckBit;
}	

根据上面的AT24C02数据帧来写AT24CO2.C文件

#define AT24C02_ADDRESS    0xA0
/**
  * @brief  AT24C02_WriteByte 写一个字节
  * @param  WordAddress 要写入字节的地址
            Data 要写入字节数据
  * @retval 
  */
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
	
	I2C_Start();//起始位
	I2C_SendByte(AT24C02_ADDRESS);//主机地址写
	I2C_ReceiveAck();//从机接收应答
	I2C_SendByte(WordAddress);//字地址
	I2C_ReceiveAck();//从机接收应答
	I2C_SendByte(Data);//主机发送数据
	I2C_ReceiveAck();
	I2C_Stop();//结束位

	
}	
/**
  * @brief  AT24C02_ReadByte 读取一个字节
  * @param  WordAddress 读取字节的地址
  * @retval Data来接收读出的数据
  */
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
	unsigned char Data;
	I2C_Start();//开始位
	I2C_SendByte(AT24C02_ADDRESS);
	I2C_ReceiveAck();
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS|0x01);//主机地址读
	I2C_ReceiveAck();//在此接收完,从机将获取总线控制权
	Data=I2C_ReceiveByte();//从机接收数据
	I2C_SendAck(1);//主机发送应答,为了不让主机应答所以置1
	I2C_Stop();//结束位
	return Data;
}	

最后在主函数里测试

unsigned char KeyNum;
unsigned int Num;
void main()
{
	LCD_Init();
	LCD_ShowNum(1,1,Num,5);
    while(1)
    {
       KeyNum=Key();
	   if(KeyNum==1)
	   {
		   Num++;
		   LCD_ShowNum(1,1,Num,5);
	   } 
       if(KeyNum==2)
       {
		   Num--;
		   LCD_ShowNum(1,1,Num,5);
	   }
       if(KeyNum==3)//写数据
       {
		   AT24C02_WriteByte(0,Num%256);//写入低八位
           Delay(5);
           AT24C02_WriteByte(1,Num/256);//写入高八位	
           Delay(5);	
           LCD_ShowString(2,1,"Write Ok");	
           Delay(1000);	 
           LCD_ShowString(2,1,"        ");//清空		   
	   }	
       if(KeyNum==4)//读数据
       {
		   /*
		     Num=AT24C02_ReadByte(0);
	         Num|=AT24C02_ReadByte(1)<<8;
		    1.先读取0地址的数据存到Num中
		    2.再读取1地址的数据然后左移8位(后面补零),即得到16位的二进制数
            3.再将1地址的数据按位或0地址的数据
            4.此时高八位是1的地址,低八位是0的地址,即得到一个完整的16位数		   
		   */
		   Num=AT24C02_ReadByte(0);//读取低八位
		   Num|=AT24C02_ReadByte(1)<<8; // << 优先级高于 |
		   LCD_ShowNum(1,1,Num,5);
           LCD_ShowString(2,1,"Read Ok");	
		   Delay(1000);	 
           LCD_ShowString(2,1,"       ");
		   
       }		   	   
    }
}

新手入行C51,如有错误请指正!

  • 11
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值