IIC总线学习笔记

2019-08-22 16:10发布

                                                 通信的桥梁-----IIC总线 人与人之间能进行交流是因为有语言这个工具。电子芯片之间的交流也需要一个工具,IIC总线就是这个工具,通过IIC总线,芯片之间能进行信息交流。但是,交流并不是那么简单的,交流的双方必须有共同语言,就像我们和外国人交流的时候就要用外语,而不能用国语,否则无法交流。芯片通过IIC总线进行交流。

    什么是IIC总线?
    IIC总线由一条SDA数据线和一条SCL时钟线组成,在这两条线上可以挂载多个IIC设备,每个IIC设备都有自己的地址,这些地址由硬件的出厂固定地址位和可编程地址位组成,只有在两条线上传输的值是IIC设备的地址时,这个IIC设备才会作出响应。就像老师上课提问学生一样,只有学生被叫到自己的名字才会站起来回答问题。
    IIC作为电子芯片之间的交流语言,它也有一些语法------IIC通讯协议。
   当SCL时钟线为高电平的时候,如果SDA线由高电平跳到低电平,说明老师准备提问学生了,即IIC主机准备寻找从机。
    这个过程转换为C语言就是void IIC_Start(void){SDA_OUT();     //sda线设置为输出模式IIC_SDA=1;    IIC_SCL=1;delay_us(4);IIC_SDA=0;  // SDA由高跳到低delay_us(4);IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 }      我们把这个过程称为IIC的起始信号。
    注意:上面的delay延时函数不是固定的,要根据硬件设备的固定时序来设置。就像有些学生反应比较快,有些学生反应比较慢,老师不能要求每个学生反应时间都一样。所以主机呼叫从机要根据从机的反应速度来设定等待时间。这个延时十分重要!延时长一点和短一点都无法得到正确的数据。

    做事要有始有终,老师提问完学生当然要请学生坐下啦,IIC用完从机,当然也要把从机释放了。
    这个过程就是主机发出一个终止信号:SCL为高时,SDA由低电平跳到高电平
    void IIC_Stop(void){SDA_OUT();//sda线输出IIC_SCL=0;IIC_SDA=0;//SDA由低跳到高delay_us(4);IIC_SCL=1; IIC_SDA=1;//发送I2C总线结束信号delay_us(4);  }
    终止信号完了之后,IIC处于闲置状态,主机可以呼叫其他设备了,(提问完一个学生,老师又可以去提问另一个学生了!)
                                 
    俗话说:一个巴掌拍不响。老师叫学生起来回答问题,如果学生不乐意当然也就不鸟老师了。当主机呼叫从机时,从机可以应答也可以不应答。即当SCL为高电平的时候,SDA为低电平表明从机应答,否则表明从机不应答。void IIC_Ack(void){IIC_SCL=0;      //首先拉低SCL,防止产生起始或结束信号SDA_OUT();IIC_SDA=0;      //低电平代表应答delay_us(4);IIC_SCL=1;delay_us(4);IIC_SCL=0;}      Ack即从机应答。
    void IIC_NAck(void){IIC_SCL=0;SDA_OUT();IIC_SDA=1;  //高电平代表不应答 delay_us(4);IIC_SCL=1;delay_us(4);IIC_SCL=0;  }
    NAck即是No Ack ,从机不应答。


等待应答也是一个过程:    u8IIC_Wait_Ack(void)    {        u8ucErrTime=0;        SDA_IN();     //SDA设置为输入模式          IIC_SDA=1;        delay_us(4);               IIC_SCL=1;        delay_us(4);             while(READ_SDA)             //SDA为高即未应答        {            ucErrTime++;            if(ucErrTime>250)           //等待一段时间还没应答则发送停止信号            {                IIC_Stop();                return1;            }        }        IIC_SCL=0;//时钟线输出0               return0;                          }



  有了上面的基本信号,主从机可以进行交流了。    voidIIC_Send_Byte(u8 txd)  //发送一个字节的数据                {                              u8t;           SDA_OUT();                 IIC_SCL=0;//拉低时钟线开始数据传输        for(t=0;t<8;t++)        {                                     if(txd&0x80) // 因为先发的是最高位                IIC_SDA=1;      //如果刚刚的位是1则证明SDA为高,所以置1            else                IIC_SDA=0;          //你懂的            txd <<=1;              //为读下一位作准备            delay_us(4);              IIC_SCL=1;          //时钟线高的时候,数据保持稳定,保证正确读取            delay_us(4);             IIC_SCL=0;          //时钟线拉低,允许读下一位            delay_us(4);        }            }                     为什么要先把SCL设置为低电平?其实上面已经有提示,因为在SCL为高电平的时候,SDA的变化代表着起始或者终止信号,所以在SCL为低的时候,才允许SDA变化(这些变化代表数据)。
   
另外因为一个字节(8位)的数据是按照一位一位来传送的,协议规定先传送最高位,所以txd与上0x80,判断最高位什么电平,然后txd自身左移一位,即第6位变第7...以此类推,循环8次,则0~7号位上的数据完成了传送。

        
老师可以提问学生,学生也可以提问老师。同样,主机可以向从机发数据,从机也可以向主机发数据(或主机读取从机数据)u8 IIC_Read_Byte()          //因为接收从最高位开始,所以下面进行左移操作的。       {              u8i,receive=0;              SDA_IN();                //SDA设置为输入              for(i= 0;i < 8;i++ )                            //一个字节需要一位一位地读出来,所以要循环8次              {                     IIC_SCL= 0;                      delay_us(4);                     IIC_SCL= 1;                     receive<<= 1;      //左移一位                     if(READ_SDA)   //当SDA是1的时候读入1否则直接补零                            receive|= 0x01;                       delay_us(1);               }                                                        IIC_Ack();//发送ACK   ,表示读完数据且应答了              returnreceive;       }
    这里的READ_SDA是SDA线的引脚状态号。


以上就是IIC的基本“语法”了。
再来一个实际应用: /************************************************//*                   向从机写数据                    *//************************************************/ //“1”代表主机读取数据,“0”代表主机发送数据 void Single_Write(u8 SlaveAddress,u8 REG_Address,u8REG_Data){       IIC_Start();                                //开始信号       IIC_Send_Byte(SlaveAddress);   //发送设备地址+写信号       IIC_Wait_Ack();                         //等待应答       IIC_Send_Byte(REG_Address);             //发送设备寄存器地址       IIC_Wait_Ack();                         //等待应答       IIC_Send_Byte(REG_Data);            //写数据       IIC_Wait_Ack();                         //等待应答       IIC_Stop();                                       //停止信号       delay_ms(5);} /************************************************//*                   读取从机的数据   *//************************************************/u8 Single_Read(u8 SlaveAddress,u8 REG_Address){       u8 REG_Data;       IIC_Start();                                //开始信号       IIC_Send_Byte(SlaveAddress);   //发送设备地址+写信号       IIC_Wait_Ack();                         //等待应答       IIC_Send_Byte(REG_Address);   //发送设备寄存器地址       IIC_Wait_Ack();                         //等待应答       IIC_Start();                                //再次开始信号       IIC_Send_Byte(SlaveAddress+ 1);    //发送设备地址+读信号       IIC_Wait_Ack();                         //等待应答       REG_Data =IIC_Read_Byte();        //获取数据       IIC_NAck();                                      //不再应答       IIC_Stop();                                       //停止信号       delay_ms(5);               returnREG_Data;                            }



注意:在同一IIC总线上可以挂载的同一IIC设备最大数量是2^(可编程位数),如果可编程位数是3,那么可以挂载这种IIC设备的最大数量就是8个。
小小心得,希望能帮助新人~~~  同时也欢迎大神指点
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
26条回答
zhangdameng
1楼-- · 2019-08-24 18:11
楼主很细心   讲的很详细
gmnail
2楼-- · 2019-08-24 22:32
正点原子 发表于 2016-2-25 22:02
谢谢分享

原子哥,这里读字节中的receive的各个操作有点看不懂,能不能给我讲讲?
谢谢
2557046847
3楼-- · 2019-08-24 23:50
 精彩回答 2  元偷偷看……
学弟007
4楼-- · 2019-08-25 00:46
顶一个
shibusha
5楼-- · 2019-08-25 04:41
挺好的!
贤荟贤惠
6楼-- · 2019-08-25 06:38
有感触啊

一周热门 更多>