STM32 IIC詳解

一、IIC定義

IIC 即Inter-Integrated Circuit(集成電路總線),這種總線類型是由飛利浦半導體公司(後被NXP收購)在八十年代初設計出來的一種簡單、雙向、二線制、同步串行總線,主要是用來鏈接總體電路(ICS) ,IIC是一種多向控制總線,也就是說多個芯片能夠鏈接到同一總線結構下,同時每一個芯片均可以做爲實時數據傳輸的控制源。多主多從的通信協議。git

下文將結合NXP官方的IIC手冊講解IIC協議。下載連接見文末。github

I2C串行總線通常有兩根信號線,一根是雙向的數據線SDA,另外一根是時鐘線SCL。全部接到I2C總線設備上的串行數據SDA都接到總線的SDA上,各設備的時鐘線SCL接到總線的SCL上。速率最高400Kbit/s。app

在1998年的修訂中增長了高速模式,速率高達3.4Mbit/s。(這裏不講,只說快速模式)。函數

 

二、IIC協議規範

2.1 SDA和SCL信號

鏈接到總線的器件輸出級必須是漏極開路或集電極開路才能執行線與的功能,當總線空動畫

閒時這兩條線路都是高電平。SDA 和SCL 都是雙向線路都經過一個電流源或上拉電阻鏈接到正的電源電壓。通常狀況下咱們都採用上拉電阻的方式ui

2.2 數據有效性

在SCL高電平的時候採樣,也就是有效。低電平的時候切換數據spa

2.3 開始和結束信號

起始條件:SCL線是高電平時,SDA線從高電平向低電平切換。設計

中止條件:SCL線是高電平時,SDA線從低電平向高電平切換。code

動畫展現啓動信號blog

代碼實現

void I2C_Start(void)
{
  //IO輸出
  SDA_OUT(); 
  SCL_OUT(); 
  I2C_DELAY();
  //IO置高
  SDA_SET();  
  SCL_SET(); 
  //延時
  I2C_DELAY();  
  //爲低
  SDA_CLR();
  I2C_DELAY();
  I2C_DELAY();
  SCL_CLR();
}

結束信號時相似的方式(不是動圖)

代碼實現

void I2C_Stop(void)
{
  //IO輸出
  SDA_OUT(); 
  SCL_OUT();
  //IO置0
  SDA_CLR();  
  SCL_CLR(); 
  I2C_DELAY();
  SCL_SET();
  //延時
  I2C_DELAY();  
  I2C_DELAY();
  I2C_DELAY();
  //SDA置1
  SDA_SET();
  I2C_DELAY();
  I2C_DELAY();
}

2.4 字節格式

SDA數據線上的每一個字節必須是8位,每次傳輸的字節數量沒有限制。每一個字節後必須跟一個響應位(ACK)。首先傳輸的數據是最高位(MSB),SDA上的數據必須在SCL高電平週期時保持穩定,數據的高低電平翻轉變化發生在SCL低電平時期。

每個字節後面跟着一個ACK,有ACK就能夠繼續寫或讀。NACK,就中止

ACK:主機釋放總線,傳輸完字節最後1位後的SCL的高電處,從機拉低電平。

NACK:主機釋放總線,傳輸完字節最後1位後的SCL的高電處,從機無響應,總線爲高電平。

動畫描述寫入的過程

代碼實現

uint8_t I2C_Send_byte(uint8_t data)
{
  uint8_t k;
  //發送8bit數據
  for(k=0;k<8;k++){
    
    I2C_DELAY();
    if(data&0x80){
      SDA_SET();
    }
    else{
      SDA_CLR();
    }
    data=data<<1;
    I2C_DELAY();
    SCL_SET();
    I2C_DELAY();
    I2C_DELAY();
    SCL_CLR();
  }
  //延時讀取ACK響應
  I2C_DELAY();
  SDA_SET();
  //置爲輸入線
  SDA_IN();
  I2C_DELAY();
  SCL_SET();   
  I2C_DELAY(); //這裏出現了問題,延時變的無限大
  //讀數據
  k=SDA_READ();
  if(k){ NACK響應
    return 0;
  }
  I2C_DELAY();
  SCL_CLR();
  I2C_DELAY();
  SDA_OUT();
  if(k){ NACK響應
    return 0;
  }
  return 1;
}
 
uint8_t I2C_Receive_byte(uint8_t flg)
{
  uint8_t k,data;
  //接收8bit數據
  //置爲輸入線
  
  SDA_IN();
  data=0;
  for(k=0;k<8;k++){
    I2C_DELAY();
    SCL_SET();
    I2C_DELAY();
    //讀數據
    data=data |SDA_READ();
    data=data<<1;
    I2C_DELAY();
    SCL_CLR();
    I2C_DELAY(); 
  }
  data=data>>1; //往回移動1次
  //返回ACK響應
  //置爲輸出線
  SDA_OUT();
  if(flg){
    SDA_SET(); //輸出1-NACK
  }else{
    SDA_CLR();//輸出0-ACK
  }
  I2C_DELAY();
  SCL_SET();
  I2C_DELAY();
  I2C_DELAY();
  SCL_CLR();
  I2C_DELAY();
  SDA_OUT();
  //返回讀取的數據
  return (uint8_t)data;
}

2.5 從機地址和讀寫位

開始信號—>地址—>讀寫位—>ACK—>數據—>ACK.............—>中止位

這裏只說7位地址,前7位爲地址,最後一位爲讀寫位,1表示讀操做,0表示寫操做

主機發給從機數據,也就是寫,沒有數據轉向時

主機當即讀從機數據,從第一個字節

(Combined)綜合數據格式

 

三、計算IIC的頻率

通訊頻率由主機掌控,也就是代碼中的延時函數決定的

從上面,咱們得知最高速爲400Kbit/s。咱們設計300Kbit/s。

速率300Kbit/s,對應週期1/300ms=10/3us≈3us,4分頻就是3/4us。

咱們使用的延時是,1/120MHZ*3*30 =3/4us

也就是頻率是300Kbit/s

和SPI相似,時鐘降低沿時,數據轉換,時鐘上升沿時,採樣數據。也就是時鐘高電平數據有效。

/*120Mhz時鐘時,當ulCount爲1時,函數耗時3個時鐘,延時=3*1/120us=1/40us*/
/*
SystemCoreClock=120000000
us級延時,延時n微秒
SysCtlDelay(n*(SystemCoreClock/3000000));
ms級延時,延時n毫秒
SysCtlDelay(n*(SystemCoreClock/3000));
m級延時,延時n秒
SysCtlDelay(n*(SystemCoreClock/3));
*/
 
#if defined   (__CC_ARM) /*!< ARM Compiler */
__asm void
SysCtlDelay(unsigned long ulCount)
{
    subs    r0, #1;
    bne     SysCtlDelay;
    bx      lr;
}
#elif defined ( __ICCARM__ ) /*!< IAR Compiler */
void
SysCtlDelay(unsigned long ulCount)
{
    __asm("    subs    r0, #1\n"
       "    bne.n   SysCtlDelay\n"
       "    bx      lr");
}
 
#elif defined (__GNUC__) /*!< GNU Compiler */
void __attribute__((naked))
SysCtlDelay(unsigned long ulCount)
{
    __asm("    subs    r0, #1\n"
       "    bne     SysCtlDelay\n"
       "    bx      lr");
}
 
#elif defined  (__TASKING__) /*!< TASKING Compiler */                           
/**/
#endif /* __CC_ARM */
 
 
/*
 * @brief  SysCtlDelay
 * @param  ulCount 延時值,必須大於0
 * @retval None
 */
void SysCtlDelay_IIC(unsigned long ulCount)
{
    SysCtlDelay(ulCount);
}
 
 
/定義時鐘頻率,300KHz
#define I2C_DELAY()  SysCtlDelay_IIC(30)

四、PCF8536

4.1 Acknowledge

這個地方能看到關於2.4節關於ACK和NACk的說明

4.2 Addressing

這裏直接給出讀地址和寫地址,也就是最後一位的區別

4.3 讀寫時序

其實就是按照IIC協議的

讀指定器件的指定寄存器

主機設置完寄存器地址以後,再去讀寫

注意:讀取多個字節,最後一個字節的回包應該是NACK

主機當即從機第一個字節讀取

注意:讀取多個字節,最後一個字節的回包應該是NACK

 

開源地址:

https://github.com/strongercjd/STM32F207VCT6

 

點擊查看本文所在的專輯,STM32F207教程

 

資料下載:

https://pan.baidu.com/s/1lRMxv2EnHRUtugIZloMrxg  提取碼:npz0

相關文章
相關標籤/搜索