STM32學習筆記:IIC通信協議詳解(附帶軟件模擬源碼)


什么是IIC(I2C)?

  IIC 即Inter-Integrated Circuit(集成電路總線),這種總線類型是由飛利浦半導體公司設計出來的一種簡單、雙向二線制同步串行總線。它是一種多向控制總線,也就是說多個芯片可以連接到同一總線結構下,同時每個芯片都可以作為實時數據傳輸的控制源。這種方式簡化了信號傳輸總線接口。

  那么也就是說,只要收發雙方同時接入SDA(雙向數據線)SCL(同步時鍾線)便可以進行通信。

  I2C總線的工作速度分為 3 種模式(實際上,IIC的通信速率由SCL決定):

    S(標准模式),測量與控制場合;

    F(快速模式),速率為 400kb/s;(默認)

    Hs(高速模式),速率為 3.4Mb/s。

 

IIC接線框圖

  一般情況下,SCL與SDA默認由上拉電阻拉高。這也是為了方便通信協議。

  多機連接時,為了區分不同的從機,我們會使用自定義的地址碼進行區分。

   

IIC的通信狀態

  IIC的通信要注意以下6個知識點:

    1.空閑狀態

    2.開始信號

    3.停止信號

    4.應答信號

    5.數據的有效性

    6.數據傳輸

 

空閑狀態:

  在IIC中規定,當SDA、SCL同時為高電平時,視為空閑狀態。

  注意,這個規定是通信設備通信前的判斷條件。

 

開始信號 & 停止信號:

  在IIC中規定,當SCL為高電平,且SDA從高到低的跳變時,視為數據開始傳輸;

  在IIC中規定,當SCL為高電平,且SDA從低到高的跳變時,視為數據停止傳輸;

  

 

數據有效性 & 數據傳送 & 應答信號(ACK)

  數據有效性:

    在傳輸數據時,應保證數據在SCL的上升沿到來之前准備好,並在下降沿到來之前必須穩定

    (由於在電路中,電平的跳變往往伴隨着毛刺。)

  數據傳送:

    在I2C總線上傳送的每一位數據都有一個時鍾脈沖相對應(或同步控制),即在SCL串行時鍾的配合下,在SDA上逐位地串行傳送每一位數據。

    在一般情況下,傳輸數據時,從數據的最高有效位開始發送

  應答信號:

    在IIC中規定,發送方每發送1個字節(8位)后需要接收接收方發送的應答信號

    ACK為0時,視為有效應答;ACK為1時,視為無效響應

    總結:誰發了數據,誰就要接收一個應答信號

 STM32 模擬IIC完整代碼

/*
    https://blog.csdn.net/return_oops/article/details/80965437
*/
//使用IIC1 掛載M24C02,OLED,LM75AD,HT1382    PB6,PB7
 
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
 
//IO操作函數     
#define IIC_SCL    PBout(6) //SCL
#define IIC_SDA    PBout(7) //SDA     
#define READ_SDA   PBin(7)  //輸入SDA 
 
//IIC所有操作函數
void IIC_Init(void);                //初始化IIC的IO口                 
void IIC_Start(void);                //發送IIC開始信號
void IIC_Stop(void);                  //發送IIC停止信號
void IIC_Send_Byte(u8 txd);            //IIC發送一個字節
u8 IIC_Read_Byte(unsigned char ack);//IIC讀取一個字節
u8 IIC_Wait_Ack(void);                 //IIC等待ACK信號
void IIC_Ack(void);                    //IIC發送ACK信號
void IIC_NAck(void);                //IIC不發送ACK信號
 
void I2C_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr);
uint16_t I2C_ReadByte(uint16_t addr,uint8_t device_addr,uint8_t ByteNumToRead);//寄存器地址,器件地址,要讀的字節數          


void IIC_Init(void)
{                         
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOB, ENABLE );    
       
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
 
    IIC_SCL=1;
    IIC_SDA=1;
 
}
//產生IIC起始信號
void IIC_Start(void)
{
    SDA_OUT();     //sda線輸出
    IIC_SDA=1;            
    IIC_SCL=1;
    delay_us(4);
     IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
    delay_us(4);
    IIC_SCL=0;//鉗住I2C總線,准備發送或接收數據 
}      
//產生IIC停止信號
void IIC_Stop(void)
{
    SDA_OUT();//sda線輸出
    IIC_SCL=0;
    IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
     delay_us(4);
    IIC_SCL=1; 
    IIC_SDA=1;//發送I2C總線結束信號
    delay_us(4);                                   
}
//等待應答信號到來
//返回值:1,接收應答失敗
//        0,接收應答成功
u8 IIC_Wait_Ack(void)
{
    u8 ucErrTime=0;
    SDA_IN();      //SDA設置為輸入  
    IIC_SDA=1;delay_us(1);       
    IIC_SCL=1;delay_us(1);     
    while(READ_SDA)
    {
        ucErrTime++;
        if(ucErrTime>250)
        {
            IIC_Stop();
            return 1;
        }
    }
    IIC_SCL=0;//時鍾輸出0        
    return 0;  
} 
//產生ACK應答
void IIC_Ack(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=0;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
}
//不產生ACK應答            
void IIC_NAck(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=1;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
}                                          
//IIC發送一個字節
//返回從機有無應答
//1,有應答
//0,無應答              
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
        SDA_OUT();         
    IIC_SCL=0;//拉低時鍾開始數據傳輸
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1;       
        delay_us(2);   //對TEA5767這三個延時都是必須的
        IIC_SCL=1;
        delay_us(2); 
        IIC_SCL=0;    
        delay_us(2);
    }     
}         
//讀1個字節,ack=1時,發送ACK,ack=0,發送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
    unsigned char i,receive=0;
    SDA_IN();//SDA設置為輸入
  for(i=0;i<8;i++ )
    {
    IIC_SCL=0; 
    delay_us(2);
        IIC_SCL=1;
    receive<<=1;
    if(READ_SDA)receive++;   
        delay_us(1); 
  }                     
    if (!ack)
            IIC_NAck();//發送nACK
    else
            IIC_Ack(); //發送ACK   
    return receive;
}
 
void I2C_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr)
{
    IIC_Start();  
    
    if(device_addr==0xA0) //eeprom地址大於1字節
        IIC_Send_Byte(0xA0 + ((addr/256)<<1));//發送高地址
    else
        IIC_Send_Byte(device_addr);        //發器件地址
    IIC_Wait_Ack(); 
    IIC_Send_Byte(addr&0xFF);   //發送低地址
    IIC_Wait_Ack(); 
    IIC_Send_Byte(data);     //發送字節                               
        IIC_Wait_Ack();                     
        IIC_Stop();//產生一個停止條件 
    if(device_addr==0xA0) //
        delay_ms(10);
    else
        delay_us(2);
}
 
uint16_t I2C_ReadByte(uint16_t addr,uint8_t device_addr,uint8_t ByteNumToRead)  //讀寄存器或讀數據
{    
        uint16_t data;
        IIC_Start();  
        if(device_addr==0xA0)
            IIC_Send_Byte(0xA0 + ((addr/256)<<1));
        else
            IIC_Send_Byte(device_addr);    
        IIC_Wait_Ack();
        IIC_Send_Byte(addr&0xFF);   //發送低地址
        IIC_Wait_Ack(); 
 
        IIC_Start();      
        IIC_Send_Byte(device_addr+1);        //發器件地址
        IIC_Wait_Ack();
        if(ByteNumToRead == 1)//LM75溫度數據為11bit
        {
            data=IIC_Read_Byte(0);
        }
        else
            {
                data=IIC_Read_Byte(1);
                data=(data<<8)+IIC_Read_Byte(0);
            }
        IIC_Stop();//產生一個停止條件        
        return data;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM