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发生时的电位图:

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

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

黑色显示的是从从机到主机传输,白色显示的是从从主机到从机传输。
其工作流程如下图所示:

原文:https://www.cnblogs.com/zzm1/p/9742821.html