嵌入式 > 技术百科 > 详情

I2C的主机从机模拟

发布时间:2024-09-03 发布时间:
|

1、I2C协议


       I2C的协议相信网上已经有很多资料了,这里就不做详细介绍,只做简单说明即可。


       a、I2C协议有两根总线:SDA和SCL。SDA为数据线,而SCL就是主机的时钟线。


       b、I2C是主机控制从机,时钟线只能主机改变。


       c、每个从机都有唯一的地址,主机通过发送从机地址来选择从机。


       d、I2C开始信号:SCL为高电平的时候,SDA由高电平向低电平跳变。


       e、I2C结束信号:SCL为高电平的时候,SDA由低电平向高电平跳变。


       f、主机传输信号的时候,SCL为高电平的时候,传输信号,SCL为低电平的时候改变信号。


       g、主机接收信号的时候,SCL为高电平的时候,接收信号。


2、如果用I/O模拟I2C的时候,一定要记住,是主机控制从机,从机根据主机SCL信号的改变而改变。


3、主机代码:


[html] view plain copy

/****************************************************************************  

  

I2C模拟条件:  

1、HOST先发地址和控制命令给SLAVE;  

2、地址和控制命令占一个字节;  

3、字节格式:  

    7~2             1                     0  

   地址       单/多字节(0/1)       读/写(1/0)  

4、发送多字节时候,第一个字节是地址和控制命令、第二个字节是长度、接下来是数据  

5、发送多字节时候,第一个字节是地址和控制命令、第二个字节是要发送的  

  

******************************************************************************/  

#include "ioCC1110.h"  

#include "hal.h"  

  

#define SCL               P1_2          

#define SDA               P1_3  

#define IN                  0  

#define OUT               1  

BYTE ACK_Flag = 0;  

BYTE I2C_count;//计数器  

BYTE receive_slave[100] = {0x00}; //接收从机的字节  

BYTE send_slave[5] = {0xaa,0x55,0xbb,0x55,0xaa}; //发送字节给从机  

/*初始化I2C*/  

  

void SDA_(BYTE input)  

{  

    if(input == 1)        //SDA输出,p1.3  

        P1DIR |= 0X08;  

    else  

        P1DIR &= 0XF7;    //SDA输入,p1.3  

}  

  

void SCL_(BYTE input)  

{  

    if(input == 1)        //SCL输出,P1.2  

        P1DIR |= 0X04;  

    else                  //SCL输入,P1.2  

        P1DIR &= 0XFB;      

}  

/*启动I2C工作*/  

void START_I2C(void)  

{  

  SDA = 1;           

  SCL = 0;  

//  Delay_us(20);  //这个没有多大影响,可以不要  

  SCL = 1;  

  Delay_us(10);    //最开始50,5us太短了,不能判断,10us可以。  

  SDA = 0;    

  Delay_us(2);   //最开始50,  

  SCL = 0;    

  Delay_us(5);  //最开始50,这个延时和上面的延时可以不要,但是为了SLAVE有足够时间退出中断,就加上  

  

}  

  

/*停止I2C工作*/  

void STOP_I2C(void)  

{  

//   SDA_OUT;   

   SDA = 0;  

   Delay_us(50);;  

   SCL = 1;  

   Delay_us(50);;  

   SDA = 1;  

   Delay_us(50);;  

   SCL = 0;  

   Delay_us(50);;  

}  

  

/*收到从器件的ACK帧,用于写完一个字节后检查*/  

void Receive_SLAVE_ACK(void)  

{         

        SCL = 0;  

//        Delay_us(50);     //这里没有必要  

        SDA = 1;   

        SDA_(IN);  

        SCL = 1;   

        Delay_us(20);      //15us短了,经常出错  

          

        if(1 == SDA)    // 若SDA=1表明非应答,置位非应答标志ACK_Flag  

                ACK_Flag = 1;  

        SDA_(OUT);  

        SCL = 0;  

//        Delay_us(50); //这里也没有必要  

}  

  

  

  

  

/*主器件往从器件里写一个字节*/  

void WriteByte(BYTE writedata)  

{    

//SDA_OUT;   

  SCL = 0;            //SCL为低电平的时候可以改变数据状态  

  for(int i=0;i<8;i++){       

    if(((writedata>>7)&0x01) == 0x01){//先写最高位  

      SDA = 1;  

    }  

    else{  

      SDA = 0;  

    }  

//    Delay_us(10);              //这个可以不要  

    SCL = 1;  

    Delay_us(20);               //这个是等待SLAVE进入中断并接收数据,退出中断,15us不行,要20us  

    writedata = writedata << 1;//写完一位后将低位移到高位      

    SCL = 0;  

//    Delay_us(50);            //这个也可以不要  

  }  

//    Delay_us(50);           //这个其实可以不要  

    SCL = 0;  

}  

  

/*主器件从从器件里面读取一个字节*/  

BYTE ReadByte(void)  

{  

  BYTE TempData = 0;  

  SCL = 0;  

  for(int i=0;i<8;i++){  

    SDA = 1;  

    SDA_(IN);  

//    Delay_us(10);           //这个没有什么影响  

    SCL = 1;  

    Delay_us(150);           //这个时间不能太短,不然的话就会读错1位  

    TempData <<= 1;  

    if(1 == SDA)  

        TempData |= 0x01;  

    else  

        TempData |= 0x00;    

    SCL = 0;  

  }  

  SCL = 0;  

  SDA_(OUT);  

//  Delay_us(50);  

  return (TempData);  

}  

  

void I2C_Bytes_Test(void)  

{  

  

/***********************写单字节正常*****************************/  

#if 0   

    START_I2C();  

    WriteByte(0xa4);//写单字节的命令  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

        return;   

    }  

    WriteByte(0xaa);  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

      return;  

    }  

    STOP_I2C();  

#endif  

/***********************************************************/  

 /*****************************写多字节********************/  

#if 0   

    START_I2C();  

    WriteByte(0xa6);//写字多节的命令  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

        return;   

    }  

    WriteByte(0x05);//写多字节长度  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

        return;   

    }  

    for(int i=0;i<5;i++){   //开始写多字节  

       WriteByte(send_slave[i]);  

       Receive_SLAVE_ACK();  

       if(ACK_Flag == 1){  

          return;   

      }  

    }  

    STOP_I2C();  

#endif      

/**********************************************************/  

/*****************I2C读正常**************************/  

  

    START_I2C();  

    WriteByte(0xa5);  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

       

      return;  

    }  

    WriteByte(0x03);            //读的长度  

    Receive_SLAVE_ACK();  

    if(ACK_Flag == 1){  

      return;  

    }  

    for(int i=0;i<3;i++){  

        receive_slave[i] = ReadByte();  

        Receive_SLAVE_ACK();  

        if(ACK_Flag == 1)  

          return;  

//        UART1_Send_BYTE(receive_slave[i]);        

    }  

    STOP_I2C();  

    LED0 = 0;  

    UART1_Send_String(receive_slave,3);  

  

/**********************************************************/  

}  



4、从机是在中断里面接收的,每次SCL上升沿的时候进入中断。代码:


[html] view plain copy

#define START_STATE 0           //开始  

#define CONTROL_STATE 1        //控制命令  

#define ACK_STATE 2           //ACK应答  

#define NOACK_STATE 3        //非ACK应答  

#define WRITE_STATE 4       //写从机  

#define READ_STATE 5        //读从机  

#define STOP_STATE 6        //停止  

  

BYTE STATE = 0;  

BYTE address = 0;          //接收到的从机地址  

BYTE count = 0;            //接收到一位计数,产生一个字节的计数  

BYTE receive_BYTE;         //从机接收主机单个字节  

BYTE receive_buf[20] = {0x00};         //从机接收主机数据的buffer  

BYTE write_buf[20] = {0x47,0x55,0x11};//从机发送数据给主机的buffer  

BYTE receive_len = 0;     //从接接收主机多字节时的长度  

BYTE send_len = 0;        //从机要发送给主机字节的长度  

BYTE write_end = 0;       //主机是否对从机写完  

BYTE read_end = 0;        //主机是否接收完从机发送的数据  

BYTE length_ACK = 0;      //从机是否接收到了要发送数据给主机的长度  

BYTE read_num = 0;  

BYTE Temp = 0;      

  

  

void PORT1_InterruptInit(void)  

{  

  EA = 1;  

  P1DIR &= 0XFB;//P1.2输入,scl  

  IEN2 |= 0X10;//P1中断使能,scl  

  P1IEN |= 0x04;//P1.2中断使能,scl  

  PICTL &= ~0X02; //P1上升沿中断,0:rising edge  

  P1IFG &= ~0x04;  

}  

  

#pragma vector = P1INT_VECTOR  

 __interrupt void P1_ISR(void)  

{  

/*        if(P1IFG>0)         //按键中断  

        {  

          P1IFG = 0;  

          LED1 = ~LED1;  

        }  

        P1IF = 0;          //清中断标志  

 */  

   if(P1IFG>0)  

   {   

     P1IFG = 0;  

     switch(STATE){  

        case START_STATE:  

              SDA_(IN);  

              if(SDA){  

                  while(SDA);                

                  while(SCL);  

                  STATE = CONTROL_STATE;   

                    

              }  

              break;  

        case CONTROL_STATE:  

              address <<= 1;  

              if(1 == SDA)  

                  address |= 0x01;  

              else  

                  address |= 0x00;                   

              count++;  

              if(8 == count){  

                  count = 0;    

                    

                  if((address & 0xfc) == 0xa4){  

                      STATE = ACK_STATE;                                              

                  }  

                  else  

                      STATE = NOACK_STATE;                                    

              }  

              break;  

        case ACK_STATE:  

                

              SDA_(OUT);//SDA设置为输出  

              SDA = 0;  

              if((write_end == 1) || (read_end == 1) ){  //已经读完或者写完  

                  STATE = STOP_STATE;  

                  write_end = 0;  

                  read_end = 0;  

              }  

              else{  

                  if((address & 0x01) == 0x00)       //主机写SLAVE  

                      STATE = WRITE_STATE;  

                  else{  

                      STATE = READ_STATE;  

//                      UART1_Send_BYTE(STATE);  

                  }  

              }  

              break;  

        case NOACK_STATE:  

              SDA_(OUT);  

              SDA = 1;  

              address = 0;  

                  STATE = START_STATE;  

              break;  

        case WRITE_STATE:                      //主机写从机  

              SDA_(IN);                        //这里将SDA置为输入,是因为发送ACK时候置为输出了  

              if((address & 0x02) == 0x00){    //写单字节  

                  receive_BYTE <<= 1;  

                  if(1 == SDA)  

                      receive_BYTE |= 0X01;  

                  else  

                      receive_BYTE |= 0X00;  

                    

                  count++;  

                  if(8 == count){  

                      STATE = ACK_STATE;  

//                      UART1_Send_BYTE(receive_BYTE);  

                      count = 0;  

                      write_end = 1;  

                  }               

              }  

              else{  

                  receive_buf[receive_len] <<= 1;  

                  if(1 == SDA)  

                      receive_buf[receive_len] |= 0x01;  

                  else  

                      receive_buf[receive_len] |= 0x00;  

                  count++;  

                  if(8 == count){                     //接收到了8个字节  

                      count = 0;  

                      receive_len++;  

                      UART1_Send_BYTE(receive_buf[receive_len-1]);  

                      if(receive_len >= (receive_buf[0]+1)){  //这里+1是因为要先写长度  

                          write_end = 1;  

                          receive_len = 0;  

                      }      

                      STATE = ACK_STATE;  

                  }  

              }  

              break;   

        case READ_STATE:                            //主机读从机  

              if(!length_ACK){                      //主机发送过从机长度  

                    

                  SDA_(IN);                            

                  send_len <<= 1;  

                  if(1 == SDA)  

                      send_len |= 0x01;  

                  else  

                      send_len |= 0x00;  

                    

                  count++;  

                  if(8 == count){  

                      length_ACK = 1;           //主机发送长度给从机        

                      LED0 = 0;  

//                      UART1_Send_BYTE(send_len);  

                      count = 0;  

                      STATE = ACK_STATE;  

                  }  

              }  

              else{  

                  SDA_(OUT);  

                  Temp = write_buf[read_num];  

                  Temp <<= count;  

                  UART1_Send_BYTE(Temp);  

                  if((Temp & 0x80) == 0x80)  

                      SDA = 1;  

                  else  

                      SDA = 0;  

                  count++;  

                  if(count == 8){    //移了7位,正好读一个字节  

                      count = 0;  

                      read_num++;  

                      if(read_num >= send_len){//读完了所有数据  

                          read_num = 0;  

                          read_end = 1;  

                          length_ACK = 0;    //将接收长度置0  

                      }      

                      STATE = ACK_STATE;  

                  }     

              }  

              break;  

        case STOP_STATE:  

              SDA_(IN);  

              while(!SDA);                

              address = 0;  

//              receive_BYTE = 0;  

              receive_len = 0;  

              send_len = 0;  

              LED1 = ~LED1;  

              STATE = START_STATE;  

              break;  

        default:          

              break;  

     }  

   }  

   P1IF = 0;  

 }  


 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
Intel Skylake新架构的秘密:逆超线程.单核猛增