51单片机I2C通信(AT24C02)

目录

目录

前言

一、I2C协议的相关介绍

二、I2C的逻辑时序

三、I2C读写AT24C02

四、代码

总结


前言


       

        本篇介绍的是以51单片机开发板上的AT24C02存储芯片为例的I2C通信,文中大量篇幅会倾向于介绍I2C的通信协议,逻辑时序和开发方法。

     先来简短地介绍下存储器,存储器主要分为两类:易失性存储器(RAM)和非易失性存储器(ROM),也就是存储器中的数据掉电丢失和掉电不丢失的区别。RAM的读写速度要远大于ROM,容量较小且成本昂贵,典型的如单片机的内存,手机内存电脑内存等。ROM是只读存储器,但也可以进行写操作,是因为早期的ROM是不可以重复写入数据的,所以关于它的命名就是个历史遗留问题,不用太过于执着,就好比火车还叫火车,即使它早就是不烧煤了。AT24C02是一种EEPROM(Electrically Erasable Programmable Read - Only Memory)即电控可擦除可编程只读存储器,容量大小为2k bit,类似的还有如W25Qxx系列,这些存储芯片多数都是用I2C通信的方式读写的。

一、I2C协议的相关介绍


        I2C(Inter-Integrated Circuit)其实是IIC BUS的简称,即内部集成电路总线,是PHILIPS(飞利浦)公司发明的一种通信协议。
       

 硬件线路:

         I2C的通信只需要两根传输线,结构上很简单。
        ①SCL(serial clock):时钟线,是I2C主设备向从设备提供时钟信号的通道。
        ②SDA(serial data): 数据线,通过SDA线传输数据。
        I2C的传输线外接了上拉电阻,当总线处于空闲状态时,SCL和SDA默认都为高点平,IIC传递信息的方式以拉低电平为主要手段,为什么要这样设计呢?我是这样想的,因为实现从高电平到低电平的跳变很容易,只需要接地就可以了,外面接了上拉电阻,默认状态一直是高电平,所以一接地就会产生下降沿,设备通过检测这些下降沿实现相互通信。这样设计方便控制,相反则需要设备向外输出电流,这样会增加芯片的负担。
        通信特征:
        ①串行:I2C属于穿行通信,所有数据以bit为单位在SDA上串行传输。
        ②同步:I2C拥有时钟线SCL,通信双方工作在同一个时钟下,所以为同步通信。
        ③半双工:由于只有一根数据线,不能同时进行数据发送和接收,故I2C为半双工。
        ④低速率:I2C一般都用于同一平台的两个IC之间的通信,而且传输的数据量较小,所以本身的传输速率相比于其他协议很低(一般也就几百KHz)。
        ⑤非差分:因为I2C通信速率不高,且双方距离很近,所以使用电平信号通信。
        ⑥一主多从:此外I2C总线上还可以挂载多设备。
I2C的优缺点都很明显,优点是硬件资源利用率高,传输稳定,设计精巧,广泛易用,缺点就是传输速率较低。
        主要用途:
        SOC和周边外设之间的通信(典型的如EEPROM、电容触摸IC),AT24C02等存储设备,MPU6050等器件。

二、I2C的逻辑时序


        51单片机并没有集成I2C的硬件外设,所以要想实现I2C通信,必须要用软件来模拟I2C的时序,它的几个个基本的时序单元是真样的:

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

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

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

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

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

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

     

         发送一帧数据:第一个字节为地址,意为向某某地址发送某某数据

        接收一帧数据:第一个字节为地址,意为从某某地址接收到某某数据

         这就是一帧完整数据了,以起始信号S为帧头,终止信号P为帧尾,其间包括地址字节,数据字节,发送应答和接收应答。   

三、I2C读写AT24C02


        AT24C02存储器读写非常简单,并不需要操纵寄存器,它的内部有0-255个地址位,都可以存储8位数据,8*256=2048bit,刚好是 2K bit,也就是AT24C02的容量大小。可以用I2C的通信层函数对其某地址位上进行读操作和写操作。

四、代码


        I2C通信层代码:

#include <REGX52.H>
sbit I2C_SCL=P2^1;        //AT24C02的I2C引脚定义
sbit I2C_SDA=P2^0;

void I2C_Start(void)      //起始信号
{
	I2C_SDA=1;            //起始信号必须先拉高SCL和SDA,以确保后面的时序正确
	I2C_SCL=1;                
	I2C_SDA=0;
	I2C_SCL=0;
}

void I2C_Stop(void)       //终止信号
{
	I2C_SDA=0;
	I2C_SCL=1;
	I2C_SDA=1;
}

void I2C_SendByte(unsigned char Byte)    //发送一个字节
{
	unsigned char i;
	for(i=0;i<8;i++)                     //循环8次,采样每一位的数据
	{
		I2C_SDA=Byte&(0x80>>i);
		I2C_SCL=1;
		I2C_SCL=0;
	}
}

unsigned char I2C_ReceiveByte(void)       //接收一个字节
{
	unsigned char i,Byte=0x00;
	I2C_SDA=1;
	for(i=0;i<8;i++)
	{
		I2C_SCL=1;
		if(I2C_SDA){Byte|=(0x80>>i);}
		I2C_SCL=0;
	}
	return Byte;
}

void I2C_SendAck(unsigned char AckBit)     //发送应答
{
	I2C_SDA=AckBit;
	I2C_SCL=1;
	I2C_SCL=0;
}

unsigned char I2C_ReceiveAck(void)         //接收应答
{
	unsigned char AckBit;
	I2C_SDA=1;
	I2C_SCL=1;
	AckBit=I2C_SDA;
	I2C_SCL=0;
	return AckBit;
}

       I2C读写 AT24C02代码:

#include <REGX52.H>
#include "I2C.h"
#define AT24C02_ADDRESS		0xA0     //AT24C02作为从机的地址,AT24C02的固定地址为1010,
//可配置地址本开发板上为000所以SLAVE ADDRESS+W为0XA0,SLAVE ADDRESS+R为OXA1
void AT24C02_WriteByte(unsigned char WordAddress,Data)       //向AT24C02中某地址写数据
{
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS);   //发送I2C要通信的从机地址,即AT24C02挂载在总线上的地址
	I2C_ReceiveAck();                
	I2C_SendByte(WordAddress);       //AT24C02中的地址位,与上面的地址概念不同,不要混淆,
	I2C_ReceiveAck();                //可以理解为扇区,可选的地址值为0~255
	I2C_SendByte(Data);              //写入该地址位的数据
	I2C_ReceiveAck();
	I2C_Stop();
}

unsigned char AT24C02_ReadByte(unsigned char WordAddress)   //读出AT24C02中某地址的数据
{
	unsigned char Data;
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS);
	I2C_ReceiveAck();
	I2C_SendByte(WordAddress);            
	I2C_ReceiveAck();
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS|0x01);  //读操作时从机地址最低位置1
	I2C_ReceiveAck();
	Data=I2C_ReceiveByte();              //读某扇区的数据存到Data缓存中
	I2C_SendAck(1);
	I2C_Stop();
	return Data;
}

        示例程序:

#include <REGX52.H>
#include "AT24C02.h"
#include "Delay.h"
unsigned char Data1,Data2,Data3;
void main()
{
    //分别向0、1、2地址位中写入0xAA、0xBB、0xCC
    AT24C02_WriteByte(0,0xAA); Delay(5);
    AT24C02_WriteByte(1,0xBB); Delay(5);
    AT24C02_WriteByte(2,0xCC); Delay(5);
    //分别读出0、1、2地址中的数据并存到Data1,Data2,Data3变量中
    Data1=AT24C02_ReadByte(0); Delay(5);
    Data2=AT24C02_ReadByte(1); Delay(5);
    Data3=AT24C02_ReadByte(2); Delay(5);
    while(1){;}
}

总结


        对照时序图来理解I2C的代码,每一条语句都是和时序中的每一步一一对应。

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

且看林地几华里

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

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

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

打赏作者

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

抵扣说明:

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

余额充值