IIC

IIC总线中断发生在:当完成了1字节发送或者接收操作。

最近在看IIC总线,想了解一下其工作的原理是什么,以2440芯片为例,看了一下IIC的例程,基本了解了主机的发送以及接收。

2440作为主机,24C04(EEPROM)作为从机,用的是中断方式发送字节,不是轮询。

以下是我的一些分析:

1.先将主函数贴出如下:

void Main(void)
{
memcpy((unsigned char *)0x0,(unsigned char *)0x30000000,0x1000);//复制到内存

SetSysFclk(FCLK_400M); //设置系统时钟 400M
ChangeClockDivider(2, 1); //设置分频 1:4:8
CalcBusClk(); //计算总线频

Uart_Select(0);
Uart_Init(0, 115200);                                                                    //选择uart0,波特率为115200来打印一些显示的信息。
Test_Iic();
// Test_Iic2();
while(1);
}

主要的是对Test_iiC()函数的分析:

void Test_Iic(void)
{
unsigned int i,j,save_E,save_PE;                                                 //定义4个无符号整形变量
static U8 data[256];                                                                      //定义一个256的数组

Uart_Printf("[ IIC Test(Interrupt) using AT24C04 ]\n");                 //串口打印[ IIC Test(Interrupt) using AT24C04 ]\n

save_E = rGPECON;
save_PE = rGPEUP;                                                                   //配置GPIO的E端口为上拉
rGPEUP |= 0xc000; //Pull-up disable
rGPECON |= 0xa0000000;                                                          //GPE15:IICSDA , GPE14:IICSCL ,配置IIC的引脚

pISR_IIC = (unsigned)IicInt;                                                        //中断函数,清除中断挂起
rINTMSK &= ~(BIT_IIC);                                                             //正常IIC的中断服务程序

                                                                                                    //Enable ACK, Prescaler IICCLK=PCLK/16, Enable interrupt, Transmit clock value Tx clock=IICCLK/16
                                                                                                    // If PCLK 50.7MHz, IICCLK = 3.17MHz, Tx Clock = 0.198MHz
rIICCON = (1<<7) | (0<<6) | (1<<5) | (0xf);

rIICADD = 0x10;                                                                           //2440 slave address = [7:1],这是主机的IIC地址
rIICSTAT = 0x10;                                                                          //IIC bus data output enable(Rx/Tx),设置IIC宗信啊输出数据使能。
rIICLC = (1<<2)|(1);                                                                      // Filter enable, 15 clocks SDA output delay added by junon

Uart_Printf("Write test data into AT24C04\n");                            //在uart0输出Write test data into AT24C04\n

for(i=0;i<256;i++)
Wr24C04(0xa0,(U8)i,i);                                                              //这是具体给24C04写字节的函数,0xa0是从机的地址,(U8)i是往23c04的写数据的地址,i是数据

void Wr24C04(U32 slvAddr,U32 addr,U8 data) //Wr24C04(0xa0,(U8)i,i);       
{
_iicMode = WRDATA;                                                                       //IIC 的工作模式

_iicPt = 0;                                                                                         //对发送的数组个数进行计数
_iicData[0] = (U8)addr;                                                                    //发送的地址
_iicData[1] = data;                                                                           //要发送的数组定义
_iicDataCount = 2;                                                                          //初始化发送的字节数为2

rIICDS = slvAddr;                                                                            //0xa0 ,从机的地址
rIICSTAT = 0xf0;                                                                              //MasTx,Start,开始发送
                                                                                                        //Clearing the pending bit isn't needed because the pending bit has been cleared.
                                                                                                        //当发送完一个字节从这里跳到中断中执行。

void __irq IicInt(void)                                                                         //中断处理函数
{
U32 iicSt,i;

rSRCPND = BIT_IIC;                     //Clear pending bit,源挂起寄存器,清除原挂起寄存器
rINTPND = BIT_IIC;                      //中断挂起寄存器,清除中断挂起寄存器
iicSt = rIICSTAT;                        //IIC总线控制状态寄存器

if(iicSt & 0x8){}                        //When bus arbitration is failed.
if(iicSt & 0x4){}                        //When a slave address is matched with IICADD
if(iicSt & 0x2){}                        //When a slave address is 0000000b
if(iicSt & 0x1){}                       //When ACK isn't received
                             //当出现以上四种情况时,该如何处理。
switch(_iicMode)
{
case POLLACK:
_iicStatus = iicSt;                      //转换到IIC最开始的状态
break;

case RDDATA:                      //读数据
if((_iicDataCount--)==0)
{
_iicData[_iicPt++] = rIICDS;

rIICSTAT = 0x90;                      //Stop MasRx condition
rIICCON = 0xaf;                      //Resumes IIC operation.
Delay(1);                         //Wait until stop condtion is in effect.
                             //Too long time...
                            //The pending bit will not be set after issuing stop condition.
break;
}
_iicData[_iicPt++] = rIICDS;                 //The last data has to be read with no ack.

if((_iicDataCount)==0)
rIICCON = 0x2f;                      //Resumes IIC operation with NOACK.
else
rIICCON = 0xaf;                      //Resumes IIC operation with ACK
break;

case WRDATA:                      //写数据
if((_iicDataCount--)==0)
{
rIICSTAT = 0xd0;                     //Stop MasTx condition
rIICCON = 0xaf;                     //Resumes IIC operation.
Delay(1);                        //Wait until stop condtion is in effect.等到停止条件生效
                           //The pending bit will not be set after issuing stop condition.
break;                         //发出停止条件后,将不会设置挂起位,其实挂起是硬件自动设置的。
}
rIICDS = _iicData[_iicPt++];               //_iicData[0] has dummy.
for(i=0;i<10;i++);                    //for setup time until rising edge of IICSCL,IICSCL的上升沿开始。

rIICCON = 0xaf;                   //resumes IIC operation.恢复IIC操作,接着发送下一字节数据
break;

case SETRDADDR:
                          // Uart_Printf("[ S%d ]",_iicDataCount);
if((_iicDataCount--)==0)
break;                         //IIC operation is stopped because of IICCON[4]
rIICDS = _iicData[_iicPt++];
for(i=0;i<10;i++);                    //For setup time until rising edge of IICSCL
rIICCON = 0xaf;                    //Resumes IIC operation.
break;

default:
break;
}
}


while(_iicDataCount!=-1);                                                               //当没有发送完时,一直在这里等待。

_iicMode = POLLACK;                                                                  //轮询ACk
                                                                                                         //下面这个while(1)其实只能检测发送数据的应答位
while(1)
{
rIICDS = slvAddr;                                                                                //给移位寄存器赋值为从机地址
_iicStatus = 0x100;
rIICSTAT = 0xf0;                        //MasTx,Start
rIICCON = 0xaf;                        //Resumes IIC operation.
                              //从这里跳入中断
while(_iicStatus==0x100);

if(!(_iicStatus&0x1))
break;                          //When ACK is received,当ACk==1,则跳出循环。
}


rIICSTAT = 0xd0;                      //Stop MasTx condition,停止发送。
rIICCON = 0xaf;                      //Resumes IIC operation. 复位IIC的操作
Delay(1);                        //Wait until stop condtion is in effect.
                           //Write is completed.
}


for(i=0;i<256;i++)
data[i] = 0;

Uart_Printf("Read test data from AT24C04\n");

for(i=0;i<256;i++)
Rd24C04(0xa0,(U8)i,&(data[i]));

void Rd24C04(U32 slvAddr,U32 addr,U8 *data)                         //从机地址,地址,数据,Rd24C04(0xa0,(U8)i,&(data[i]));
{
_iicMode = SETRDADDR;                                                        //设置读的地址
_iicPt = 0;
_iicData[0] = (U8)addr;
_iicDataCount = 1;

rIICDS = slvAddr;
rIICSTAT = 0xf0;                                                                      //MasTx,Start
                                                                                               //Clearing the pending bit isn't needed because the pending bit has been cleared.
while(_iicDataCount!=-1);

_iicMode = RDDATA;
_iicPt = 0;
_iicDataCount = 1;

rIICDS = slvAddr;
rIICSTAT = 0xb0;                                           //MasRx,Start
rIICCON = 0xaf;                   //Resumes IIC operation.
while(_iicDataCount!=-1);

*data = _iicData[1];
}

//Line changed 0 ~ f
for(i=0;i<16;i++)
{
for(j=0;j<16;j++)
Uart_Printf("%2x ",data[i*16+j]);
Uart_Printf("\n");
}
rINTMSK |= BIT_IIC;
rGPEUP = save_PE;
rGPECON = save_E;

以上是主机发送和接收的例子,其工作原理其实很简单,如下图所示:

由起始位开始,发送从机的7位地址,第八位是读写方式,之后数据传输方式是数据+ACK位,ACK位是由从机响应的,也就是时钟的第9个周期响应,在第 九个周期内,主机的数据输出位保持为高电平,从机的数据接收位保持为低电平。这个ACK位是由硬件操作的,无需软件控制,每发送一个字节的数据,都会接收到一个响应位。p是停止位。

下图是ACK发生时的电位图:

主机处于发送模式的流程图如下:

当主机处于接收模式时,如下图所示:

黑色显示的是从从机到主机传输,白色显示的是从从主机到从机传输。

其工作流程如下图所示:

 

posted on 2018-10-04 18:48  xiegangqingnian  阅读(429)  评论(0编辑  收藏  举报

导航