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。(這裏不講,只說快速模式)。函數
鏈接到總線的器件輸出級必須是漏極開路或集電極開路才能執行線與的功能,當總線空動畫
閒時這兩條線路都是高電平。SDA 和SCL 都是雙向線路都經過一個電流源或上拉電阻鏈接到正的電源電壓。通常狀況下咱們都採用上拉電阻的方式ui
在SCL高電平的時候採樣,也就是有效。低電平的時候切換數據spa
起始條件: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(); }
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; }
開始信號—>地址—>讀寫位—>ACK—>數據—>ACK.............—>中止位
這裏只說7位地址,前7位爲地址,最後一位爲讀寫位,1表示讀操做,0表示寫操做
主機發給從機數據,也就是寫,沒有數據轉向時
主機當即讀從機數據,從第一個字節
(Combined)綜合數據格式
通訊頻率由主機掌控,也就是代碼中的延時函數決定的
從上面,咱們得知最高速爲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)
這個地方能看到關於2.4節關於ACK和NACk的說明。
這裏直接給出讀地址和寫地址,也就是最後一位的區別
其實就是按照IIC協議的
讀指定器件的指定寄存器
主機設置完寄存器地址以後,再去讀寫
注意:讀取多個字節,最後一個字節的回包應該是NACK
主機當即從機第一個字節讀取
注意:讀取多個字節,最後一個字節的回包應該是NACK
開源地址:
https://github.com/strongercjd/STM32F207VCT6
點擊查看本文所在的專輯,STM32F207教程
資料下載: