weiqi7777

cortex-a8裸机系列:第十七章 IIC通信

0
阅读(864) 评论(0)

一、            IIC的总线空闲状态、起始位、结束位

IIC总线上有1个主设备,nn>=1)个从设备。IIC总线上有2种状态;空闲态(所有从设备都未和主设备通信,此时总线空闲)和忙态(其中一个从设备在和主设备通信,此时总线被这一对占用,其他从设备不能工作)

         整个通信分为一个周期一个周期的,两个相邻的通信周期是空闲态。每一个通信周期由一个起始位开始,一个结束位结束,中间是本周期的通信数据。

         下图是起始位和结束位。

clip_image002

         起始位:起始位是一个时间段,在这段时间内总线状态变化情况是:SCL线维持高电平,同时SDA发生一个从高到低的下降沿。

         结束位:结束位也是一个时间段,在这段时间内总线状态变化情况是:SCL维持高电平,同时SDA发生一个从低到高的上升沿。

 

二、            IIC数据传输格式(数据位和ACK

每一个通信周期的发起和结束都是由主设备来做的,从设备只有被动的响应主设备,没法自己自发的去做任何事情。

主设备在每个周期会先发8位的从设备地址(其实8位中只有7位是从设备地址,还有1位表示主设备要写入还是读取)到总线(主设备是以广播的形式发送,只要总线上的所有从设备都能收到这个信息),然后总线上的每个从设备都能收到这个地址,并且收到地址后,和自己的地址比较,看是否相等。如果相等,说明主设备本次通信就是和自己,如果不相等,说明不是和自己通信,忽略后续接收的所有数据。

主设备发送一段数据后,从设备(接收方)需要回应一个ACK。这个响应本身只有1bit位,不能携带有效信息,只能表示要么收到数据,即有效响应;要么表示未收到响应,无效响应。

在某一个通信时刻,主设备和从设备只能有一个在发(占用总线,向总线写),另一个在收(释放总线,从总线读)。如果在某个时间主设备和从设备都试图向总线写那就出大问题了,通信就要出问题。

 

clip_image004

         上图是IIC控制器能产生的四种时序。在真正和外部芯片使用IIC通信时,要将上述的四种时序进行组合实现读写。

         对于1,将要写的地址写入I2CDS寄存器,将I2CSTAT写入0xf0,主发送模式。控制器会自动的产生开始信号,并将数据发送出去,接收响应信号。

         对于2,将要写的地址写入I2CDS寄存器,将I2CSTAT写入0xb0,主接收模式。控制器会自动的产生开始信号,并将数据发送出去,接收响应信号。

         对于3,将要写的数据写入I2CDS寄存器,将I2CCON的第4位中断标志位清零,控制器会自动将数据发送出去,接收响应信号。

         对于4,直接去读I2CCON的第4位中断标志位,为1,说明数据接收完毕,控制器将接收到的数据放入到寄存器I2CDS中,并产生响应信号。

三、            数据在总线上的传输协议

IIC通信时基本的数据单位也是以字节为单位,每次传输的有效数据都是1个字节(8位)。

clip_image006

         起始位后的8clk都是主设备在发送(主设备掌控总线),此时从设备只能读取总线,通过读总线来得知主设备发给从设备的信息;到了第9周期,从设备需要发送ACK给主设备,此时主设备要释放总线,以接收从设备响应。从设备将SDA线拉低,表示响应。如果从设备没有拉低SDA线,发ACK。主设备就应该认为刚刚发送的数据不正确。

四、            IIC的时钟

         IIC的时钟来源于PCLK_PSYS65M),经过了两级分频。在I2CCON寄存器中,第6位,第一级分频,得到中间时钟I2CCLK,然后在根据[3:0]的值,进行第二级分频。得到TX时钟。

clip_image008

五、            S5PV210IIC控制器

通信双方本质上是通过时序在工作,但是时序会比较复杂不利于soc软件完成,于是乎解决方案是soc内部内置了硬件的控制器来产生通信时序。这样编程时,只需要向控制器的寄存器中写入配置值即可。控制器会产生适当的时序在通信线上和对方通信。

IIC控制器有4IIC接口。

结构框图

clip_image010

IIC控制器使用的是PCLK_PSYS时钟。经过一个4-bit的预分频器得到IIC通信的CLK时钟。通信中这个CLK会通过SCL传给从设备。

IIC总线控制逻辑(前台代表是I2CCONI2CSTAT两个寄存器),主要负责产生I2C通信时序,实际编程中要发起起始位、停止位、接收ACK等都是通过这两个寄存器实现。

移位寄存器(shift registe,将寄存器中的字节数据,一位一位的发送出去),实现将数据按位从SDA发送出去。

地址寄存器+比较器,如果IIC控制器作为从设备的时候,需要一个地址来实现与主设备的通信。210可以通过这个地址寄存器来设置地址。

一、    I2CCON寄存器

clip_image011

clip_image013

clip_image015

第7位:acknowledge generation, IIC控制器产生响应信号。当在发送的时候,IIC控制器不会控制SDA产生响应。,在读取的时候,主设备如果要继续读取数据,应该在读完一个数据后,发送ACK响应,如果不准备读取数据了,应该发送NACK响应,以提示从设备准备接收响应信号。所以在读操作最后一个数据之前,要将这一位置0

第六位: 设置IIC时钟的第一级分频

第五位: 设置IIC中断使能/禁止功能,推荐将该位置1,即使不使用I2C中断功能。因为为0I2CCON[4]会工作不正确。

第四位: IIC中断挂起标志位,为1表示IIC中断产生,此时SCL为低电平,IIC通信停止。写入0,清除中断标志位,重启IIC通信。

第三到0位: 设置预分频值。

 

Note中提到:

中断产生,Interrupt pending flag1

l  1个字节传输或者接收完成,并且响应也处理完成,会产生中断

l  或者是IIC当做从设备,接收到地址和自己的地址匹配时,也会置1

l  总线仲裁错误。

二、    IICSTAT寄存器

clip_image016

clip_image018

7、第6位:模式选择。选择当前IIC通信时以什么模式。有四种模式。

                   从接收模式,从发送模式,主发送模式,主接收模式

5位:忙信号状态。读的话,0忙,1不忙。

                   写的话,写0表示发停止位、写1表示发开始位,然后再I2CDS中的数据会自动的传输出去。

4位:数据输出使能、禁止。IIC通信的时候要使能,不传输的时候可以禁止。

3位:仲裁状态标志。 0总线仲裁成功,1总线仲裁不成功。

2位:作为从设备,判断外部发的地址是否和自己的地址匹配,1表示匹配。

1位:接收的地址为0判断,为1,表示接收的地址是0.

0位:上一次接收数据状态,其实就是判断接收到的响应是什么。为0,表示接收到响应,为1表示没有接收到响应。

三、    I2CADDA

设置I2C控制器作为从设备时的地址

clip_image020

 

I2CDS I2C上传输的数据,需要向外发送数据时,将数据写入到该寄存器中。当从外读取数据时,读取该寄存器。

clip_image022

六、            210gsensor传感器

九鼎开发板上的gsensor的原理图。使用IIC进行通信。 加速度传感器。

clip_image024

         前面的芯片是一个电源芯片,通过PWMTOUT3来控制电源输出是否时能。通过控制PWMTOUT3,就可以实现gsensor芯片的上电和断电,从而节省功耗。

         gsensorSDASCL使用的是s5pv210I2C端口0

         编程时,要在gsensor_init函数中要去初始化相应的GPIO,要把相应的GPIO设置为正确的模式和输入输出值。

 

         一般传感器的接口有两种:模拟接口和数字接口。模拟接口是用接口电平变化来作为输出(如模拟接口的压力传感器,在压力不同时输出电平在0~3.3V范围内变化,每一个电压对应一个压力),soc需要用AD接口来对接这种传感器对它输出的数据进行AD转换,准换得到数字电压值,再用数字电压值去校准得到压力值;数字接口是后来发展出来的,数据接口的sensor是在模拟接口的sensor基础上,内部集成了AD,直接(通过一定的总线接口协议,一般是IIC)输出数字值的的参数,外部soc直接通过总线接口,读取传感器输出的参数即可(如gsensor,电容触摸屏IC)。

 

         该传感器的地址。数据手册KXTE9-2050 Specifications Rev 3的第8页,有说明该传感器的IIC地址是0001111

clip_image026

         IIC通信格式

clip_image028

clip_image029

         S 开始起

         Sr 重新发起开始为

         P 停止位

         SAD + W:  地址加写操作

         SAD + R 地址加读操作

         ACK 从设备给主设备的响应

         NACK 主设备给从设备的响应。

         DATA 数据

 

通信速率,IIC最高400hz

clip_image031

 

七、            IIC总线的通信流程

1.    210的主发送流程图

作为主设备发送的流程图。

clip_image033

在操作之前,会有一定的要求。

clip_image034

 

2.    210的主接收流程图

作为主设备接收的流程图。

clip_image036

 

 

3.    IIC读写一个字节实例代码

1)     写一个字节

对于写,过程是

 起始位  设备地址(写) 设备响应  数据地址  数据地址响应  数据 数据响应(从机发) 停止位

 

网上的代码就是

clip_image038

 

2)     读一个字节

过程

起始位 设备地址(写) 设备响应 数据地址 数据地址响应  重起始位  设备地址(读) 设备地址响应  数据  数据响应(主机发)

网上的代码如下:

clip_image040

 

 

自己写的代码,加入了超时判断,通过函数的返回值,可以知道是成功还是哪一个超时.

写一个字节

int  gsensor_write_byte(uint8_t add, uint8_t data)

{

    int timeout =TIMEOUT_VALUE;

    volatile int i;

    int res=0;

    //发设备地址

    I2CDS = SLAVE_ADDRESS_W;

    I2CSTAT = 0xf0;

#ifdef DETECT_TIMEOUT

    while( (I2CCON&0x10) == 0){ //发设备地址响应超时判断,超时返回1

       if(timeout-- == 0)

       {     

           res = 1;

           return res;

       }         

       delay_us(10);

    }

#else

    while( (I2CCON&0x10) == 0);

#endif

    if(I2CSTAT&0x01)    //如果设备地址(写)无响应,置为标志

    {

       res |=  (1<<7 | 1<<3);

    }

//  printf("dev(write) I2CSTAT: 0x%x\r\n",I2CSTAT);

    //发数据地址

    I2CDS = add;

    I2CCON &= ~(1<<4);  //清除中断,重新发起写操作,写地址

#ifdef DETECT_TIMEOUT

    timeout =TIMEOUT_VALUE;

    while( (I2CCON&0x10) == 0){ //发数据地址响应超时判断,超时返回2

       if(timeout-- == 0)

       {

           res = 2;

           return res;

       }

       delay_us(10);

    }

#else

    while( (I2CCON&0x10) == 0);

#endif

    if(I2CSTAT&0x01 )  //如果数据地址无响应,置为标志

    {

       res |=  (1<<7 | 1<< 5);

    }

//  printf("add I2CSTAT: 0x%x\r\n",I2CSTAT);

    //发送数据

    I2CDS = data;

    I2CCON &= ~(1<<4);  //清除中断,重新发起写操作,写数据

#ifdef DETECT_TIMEOUT

    timeout =TIMEOUT_VALUE;

    while( (I2CCON&0x10) == 0){ //发数据响应超时判断,超时返回3

       if(timeout-- == 0)

       {

           res = 3;

           return res;

       }

       delay_us(10);

    }

#else

    while( (I2CCON&0x10) == 0);

#endif

    if(I2CSTAT&0x01 )  //如果写数据无响应,置为标志

    {

       res |=  (1<<7 | 1<< 6);

    }

//  printf("data(write) I2CSTAT: 0x%x\r\n",I2CSTAT);

    //发停止位

    I2CSTAT = 0xd0;  

#ifdef DETECT_TIMEOUT

    timeout = TIMEOUT_VALUE;

    while( (I2CSTAT&0x20) == 0){ //判断IIC BUS不忙超时判断,超时返回4

       if(timeout-- == 0)

       {

           res = 4;

           return res;

       }

       delay_us(10);

    }

#else

    while( (I2CSTAT&0x20) == 0);

#endif

    I2CCON = IIC_CON_VALUE;  //重新设置I2CCON,为下一次做准备

    for(i=0; i<50;i++);     //延时,以保证两次IIC操作间隔不太快

    return res;

}

 

使用逻辑分析仪抓的波形。

clip_image041

 

对于读一个字节

int gsensor_read_byte(uint8_t add, uint8_t *data)

{

    int timeout =TIMEOUT_VALUE;

    int res=0;

    volatile int i;

    //发送设备地址(写)

    I2CDS = SLAVE_ADDRESS_W;

    I2CSTAT = 0xf0;

#ifdef DETECT_TIMEOUT

    while( (I2CCON&0x10) == 0){ //发设备地址(写)响应超时判断,超时返回1

       if(timeout-- == 0)

       {

           res = 1;

           return res;;

       }

       delay_us(10);

    }

#else

    while( (I2CCON&0x10) == 0);

#endif

    if(I2CSTAT&0x01)    //如果设备地址(写)无响应,置为标志

    {

       res |=  (1<<7 | 1<<3);

    }

//  printf("dev(write) I2CSTAT: 0x%x\r\n",I2CSTAT);

 

    //发送读数据地址

    timeout =TIMEOUT_VALUE;

    I2CDS= add;

    I2CCON &= ~(1<<4);

#ifdef DETECT_TIMEOUT   

    while( (I2CCON&0x10) == 0){ //发读数据地址响应超时判断,超时返回2

       if(timeout-- == 0)

       {

           res = 2;

           return res;;

       }

       delay_us(10);

    }

#else

    while( (I2CCON&0x10) == 0);

#endif

    if(I2CSTAT&0x01 )  //如果数据地址无响应,置为标志

    {

       res |=  (1<<7 | 1<< 5);

    }

//  printf("add I2CSTAT: 0x%x\r\n",I2CSTAT);

    //重新产生开始信号,并重新发送设备地址(读)

    timeout =TIMEOUT_VALUE;

    I2CDS = SLAVE_ADDRESS_R;

    I2CSTAT = 0xb0;

    I2CCON &= ~(1<<4);

#ifdef DETECT_TIMEOUT

    while( (I2CCON&0x10) == 0){ //发设备地址(读)响应超时判断,超时返回3

       if(timeout-- == 0)

       {

           res = 3;

           return res;;

       }

       delay_us(10);

    }

#else

    while( (I2CCON&0x10) == 0);

#endif

    if(I2CSTAT&0x01)    //如果设备地址(读)无响应,置为标志

    {

       res |=  (1<<7 | 1<<4);

    }

//  printf("dev(read) I2CSTAT: 0x%x\r\n",I2CSTAT);

    //读取数据,读数据时不产生ACK信号

    timeout =TIMEOUT_VALUE;

    I2CCON &= ~(1<<7 | 1<<4); //清中断标志位,关闭ACK应答

#ifdef DETECT_TIMEOUT

    while( (I2CCON&0x10) == 0){ //读数据响应超时判断,超时返回4

       if(timeout-- == 0)

       {

           res = 4;

           return res;;

       }

       delay_us(10);

    }

#else

    while( (I2CCON&0x10) == 0);

#endif

    if( !(I2CSTAT&0x01) )  //如果读数据有响应,置为标志

    {

       res |=  (1<<7 | 1<< 6);

    }

//  printf("data(read) I2CSTAT: 0x%x\r\n",I2CSTAT);

 

    *data = I2CDS;

    //发停止位

    I2CSTAT = 0x90;

    timeout = TIMEOUT_VALUE;

#ifdef DETECT_TIMEOUT

    while( (I2CSTAT&0x20) == 0){ //判断IIC BUS不忙超时判断,超时返回5

       if(timeout-- == 0)

       {

           res = 5;

           return res;;

       }

       delay_us(10);

    }

#else

    while( (I2CSTAT&0x20) == 0);

#endif

    I2CCON = IIC_CON_VALUE;   //重新设置I2CCON,为下一次做准备

    for(i=0; i<50;i++);     //延时,以保证两次IIC操作间隔不太快

    return res;

}

 

使用逻辑分析仪抓的波形

clip_image043

 

 

八、            调试过程中,出现的问题:

         IIC调试中,出现以下情况,SCL出现8个周期后就一直为高电平了。要等自己设置的超时时间后,才回归正常。

clip_image045

 

    通过代码测试,发现将IIC的管脚的上拉使能,可以有效减小上面出现的情况。不过在最开始IIC数据传输的时候,也是有可能发生的。但是时间越久,发生的几率就越低了。

clip_image046

         隔一段时间,就发现很正常了。

clip_image048

 

         对于IIC器件,不能一直读取,否则会出现响应一直是NANK

clip_image050