IIC通信基础知识点

目录

 一、常见的串行通信协议

二、IIC通信工作原理

三、相关规定

四、应答位

 五、数据帧格式

六、具体组合形式

 七、总线寻址(上述所说的七位地址)

八、软件模拟IIC通信时序


 一、常见的串行通信协议

UART 异步通信(一条数据线输入,一条数据线输出)

IIC  同步串行方式通信(一条时钟线SCL,一条数据线SDA)

SPI 同步串行方式通信(一条时钟线,一条数据输入线,一条数据输出线)

二、IIC通信工作原理

IIC总线上可以挂多个器件,每个器件有唯一的地址,从而标识通信目标(从机通过对比自己的地址与主机发送的是否相符,以确定自己是否为发射的对象)

数据通信方式 :主从方式 主机主动取得联系从机,从机被动回应数据

三、相关规定

  • 数据位的有效性规定 :SCL高电平时,数据线上数据必须保持稳定,只有SCL为低电平,SDA状态才能发生变化(数据在时钟线SCL的上升沿到来之前就需要准备好,并在下降沿到来之前必须保持稳定
  • IIC的起始与终止信号:SCL为高电平时,SDA由高->低   为起始信号 (总线被占用)       SDA由低->高   为终止信号  (总线空闲)
  • IIC字节的传送与应答:每个字节8位数 数据传送先传送最高位  每个字节后必须跟随一位应答位(即8+1 9位)                                                                                                                       
  • 应答位为0 成功接收数据   应答位为1 表示来不及接收数据                                                                                                                                         注意:数据有效性与IIC的起始终止信号不冲突,正是由于SCL为高时SDA电平发生变化,才会被视作为起始与终止信号而不是有效的数据。即为了数据有效,SDA不能发生变化   (即起始与终止信号不受数据有效性约束)                                                                                     

 注意要点:SDA使用完后要将其拉高释放,否则低电平时总线被占用,不可继续使用,无论是否设置为高电平都会被拉低成低电平。(SDA与SCL两条线均为线与的关系【即这两条线上诸多器件中任一器件拉低电平,该条线将会保持低电平】) 

四、应答位

1、从机发送应答位给主机,来不及接收主机数据时发出非应答,主机则终止数据传输;而从机空闲时,从机发出应答(数据第九位为0),即主机根据从机发出的应答位判断从机是否成功接收数据(主机发送数据时)(检测应答)

2、主机收到最后一个数据后,向从机发送结束传送的信号(对从机发送非应答,过程中会将SDA拉低),然后从机释放SDA线(防止SDA拉低以后主机无法将其拉高),从而允许主机产生终止信号 (主线接收数据时(发送应答)

其次,主机在发送应答后,也要释放SDA线,防止将其拉低后影响从机后续的数据传送

 五、数据帧格式

IIC总线上传送的数据是广义的,即包括地址信号,又包括真正的数据信号。

规定:在起始信号后必须传送一个从机的地址,从机地址占7位,第八位是数据的传送方向位(0表示主机发送数据,1为主机接收数据)

六、具体组合形式

  1、主机发送数据(且整个过程都只发送数据)

  2、主机接收数据(且整个过程中都只接收数据)见下图

  

3、主机先发送数据再接收数据 见下图

 注意看清主体是从机还是主机

 整体思路

 注意:主机发送数据时,如果是正常发送数据,无需等待从机非应答信号,直接产生终止信号即可。

 七、总线寻址(上述所说的七位地址)

IIC总线协议规定7位寻址

1、寻址字节的位定义:D7~D1为组成从机的地址 D0为数据传送方向位(“0"为主机向从机写数据 ”1“为主机向从机读数据)

2、从机地址由固定部分可编程部分组成,可编程部分决定了系统中能够接入的最大从机个数 (例如3位可编程位,表示最多8个从机接入)

3、主机发送地址时,总线上每个从机都将其与自己的地址相比较,以确定自己是否为发送器或接收器

八、软件模拟IIC通信时序

 

 器件地址 A0 (由于固定位1010 且E0 E1 E2均接地为0 —-> 1010000)  [从机地址]

串行EEPROM典型产品:AT24C系列器件                AT24C02(256字节)             读写图示意如下

九、代码实现

#include <reg52.h>
#include <intrins.h>

#define AT24c02ADDR 0xA0
#define I2cRead 1
#define I2cWrite 0
#define uchar unsigned char

sbit SCL = P2^1;
sbit SDA = P2^0;  
sbit LA=P2^2;
sbit LB=P2^3;
sbit LC=P2^4;  //数码管段选

unsigned char smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
					0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

bit ACKFlag;  //由于只有0和1两种状态,设为bite型

void delay(unsigned int z)    
{
	unsigned int x,y;
	for(x=z;x>0;x--)
	   for(y=120;y>0;y--);
}


//延时5us
void delay5us()
{
    _nop_();
}


void timefrist()
{
	EA = 1;          //打开总开关
	ET0 = 1;         //中断函数0的开关
	TR0 = 1;         //打开定时器0开关
	TMOD = 0x01;      //模式一,定时模式
	TH0 = 0xED;       //这里改值了,到上限就5ms(改初值)
	TL0 = 0xFF;
//开关、模式、赋初值
}
unsigned char miao=0;
void DigDisplay(unsigned char h)
{
	unsigned char a=h%10;                //个位
	unsigned char b=h/10;               //十位
	static unsigned char wei=0;         //静态变量,就是函数执行完也不会抹去这个变量的值,在次使用函数值可以用(只有初值时才会初始的,后来不改变)
	switch(wei)                        //这里重新布局,我们这里不断交换显示,用一次函数就显示一个,当快速多显示就动态显示	
	{
		case 0: LA=1;LB=1;LC=1;P0 = smgduan[b];break;      //wei值0与1不断交换,就动态显示
		case 1:	LA=0;LB=1;LC=1;P0 = smgduan[a];break;  
	}
	wei++;                                            //用过之后++
	if(wei==2)                                 //我们只要1与0不断交换,为2时就重新回0
	{
		wei = 0;
	}
}

//起始信号
void I2cStart()
{
    SCL=1;
    SDA=1;
    delay5us();
    SDA=0;
    delay5us();
}

//终止信号
void I2cStop()
{
    SCL=0;
    SDA=0;
    SCL=1;
    delay5us();
    SDA=1;
    delay5us();

}

//主机检测应答函数
bit ReadACK()
{
    SCL=0;
    SCL=1;
    delay5us();
    if(SDA)  //非应答
    {
         SCL=0;  //拉低时钟线,后续数据才能允许变化
         return (1);

    } else {  //应答
         SCL=0;
         return (0);
    }
}

//主机发送应答函数
void SendACK(bit i)
{
    SCL = 0;
    if(i)
    {
        SDA=1;  //非应答
    }  else {
        SDA=0;  //应答
    }
    SCL=1;  //时钟总线为低时应答无效,所以拉高时钟线
    delay5us();
    SCL=0;
    SDA=1;   //释放总线

}

//发送字节函数
void I2cSendByte(uchar DAT)
{
    uchar i;
    for(i=0;i<8;i++)
    {
        SCL=0;  //时钟线为0时才允许数据发生变化
        if(DAT & 0x80)  //通过与运算可以将最高位取出来赋值给SDA
            SDA=1;
        else
            SDA=0;
        SCL=1;    //拉高以后ATC02开始从SDA读数据
        DAT<<=1;  //DAT不断左移可以将数字逐渐移到最高位取出
    }
    SCL=0;   
    SDA=1;  //释放数据总线
}

//接收字节函数
uchar I2cReadByte()
{
    uchar i,DAT=0x00;  //从SDA中读出数据并将其存入DAT中
    for(i=0;i<8;i++)
    {
        DAT<<=1;  //由于我们最开始得到的是最右边一位的数,但是我们先得到为最高位,所以不断左移到最高位
        SCL = 0;  //SCL拉低才允许SDA变化(从机将数据置于SDA线)
        SCL = 1;  //SCL置高以后从机已经将数据置于SDA线上
        if(SDA)
            DAT |= 0x01;  
    }
    return(DAT);  
}

   

//写数据函数(主机发送数据)
void AT24C02Write(uchar ADDR,DAT)
{
    I2cStart();
    I2cSendByte(0xA0 + 0);  //I2C发送一个字节
    if(ReadACK()) 
        ACKFlag=1;  //非应答
    else 
        ACKFlag=0;  //应答
    I2cSendByte(ADDR);  //I2C发送写入的地址
    if(ReadACK()) 
        ACKFlag=1;  //非应答
    else 
        ACKFlag=0;  //应答
    I2cSendByte(DAT);   //I2C发送发送数据
    if(ReadACK()) 
        ACKFlag=1;  //非应答
    else 
        ACKFlag=0;  //应答
    I2cStop();  //终止信号
}

//读数据函数(主机接收数据)
uchar At24c02Read(uchar ADDR)
{
    uchar DAT;
    I2cStart();
    I2cSendByte(0xA0 + 0);  //发送从机地址 (发送) 
    if(ReadACK())  //主机检测应答(从机发送应答)
        ACKFlag=1;  //非应答
    else 
        ACKFlag=0;  //应答
    I2cSendByte(ADDR);
    ReadACK();  //无论应答还是非应答 下面均会主机发起始信号(无所谓应答或非应答)
    I2cStart();
    I2cSendByte(0xA0 + 1);  //主机发送从机地址  (接收)
    if(ReadACK())  //主机检测应答(从机发送应答)
        ACKFlag=1;  //非应答
    else 
        ACKFlag=0;  //应答
    DAT = I2cReadByte();  //DAT用于存放从从机读到的数据
    SendACK(1);    //主机发送非应答
    I2cStop();
    return(DAT); 
}

void main()
{
    timefrist();  //定时器0初始化
    EA=0;  //屏蔽中断(关总开关)
    AT24C02Write(2,9); //主机写数据至第二个单元
    delay(1);  //由于主机正处理上述事件,会产生非应答,所以让其延时一下
    miao = At24c02Read(2);  //主机从第二个单元读数据
    EA=1;  //打开中断开关
    while(1);
}

//定时器中断函数
void timer0() interrupt 1
{
	 TH0 = 0xED;         //重新定义5ms初始值
	 TL0 = 0xFF;
	 DigDisplay(miao);   //显示,不断5ms显示就是动态显示
}

  • 11
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值