I²C(Inter-Integrated Circuit)中文是內部整合電路,屬於串列通訊匯流排。最早是由Philips開發,為了使嵌入式系統、手機等等可以連接低速周邊裝置而發展的,適用於短距離通訊。
I²C只需要兩條線即可完成通訊,分別是SDA與SCL因此非常的節省空間。大多應用於小型元件,最常看到的應該是EEPROM這一類的元件。
那I²C與SPI差別在哪呢?
I²C | SPI | |
---|---|---|
半/全雙工 | 半雙工 | 全雙工 |
傳輸速度 | 較低 | 較高 |
所需腳位 | 2 | 4 or 3 |
通訊 | 允許多個Master | 1 Master n Slave |
資料傳輸方式 | 8bit→8bit | 1 bit逐位交換 |
使用時允許多個Master進行通訊,將各設備SCL與SDA連結即可。每一設備皆由屬於自己的地址,不管是任何設備SDA與SCL都屬於Open Drain 的IO腳,同時並接(Wired-And)在兩條線路上,皆需要加入上拉電阻。當導通時是低電位不導通時為浮接,所以利用電阻將電位拉高。
Wired-And指的是:
→Wired : 直接將設備對應的線接在一起
→And : 也就是大家熟知的And邏輯閘
a | b | output |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
也就是說只有當輸入都為High的時候才會輸出High,任一個為Low即為Low。代表著在匯流排當中只要有一個晶片為Low那就會是Low,而晶片可以輕易地控制Bus輸出Low,但要輸出High實則連其他晶片也必須同時輸出High。
可以看到上圖是一個I²C傳送資料的過程,先說明一下什麼是SDA與SCL:
SDA : 資料線,所以的資料交換都是在SDA上完成
SCL : 時鐘線,由Master輸出用
I²C的傳輸包括了四個部分:最開頭的Start信號,接下來會接上設備的I²C地址與傳送的資料,最後會接上一個Stop信號。
如何開始通訊?可以看到上方一剛開始由SDA與SCL皆處於High的情況,接著SDA向下拉由1變為0,這時SCL一樣保持為High,這個動作就可以當作一個開始信號。
SDA = 1;
Delay();
SCL = 1;
Delay();
SDA = 0;
Delay();
SCL = 0;
Delay();
如何結束通訊? 首先由SDA先降為Low而SCL處在High,接著再將SDA拉回High則代表結束通訊。
SDA = 0;
Delay();
SCL = 1;
Delay();
SDA = 1;
Delay();
在I²C當中交換資料時,是以8bit加上1bit的ACK為一組,每一次交換都是將MSB先傳送。無論是I²C的設備地址還是後續要交換的資料都需要按照這個模式去做傳送。
上方提到每一組資料會在後方接上一個ACK bit,而這個bit會由接收端產生。其中又分為ACK與NACK,ACK為Low而NACK為High。在這過程並非發送端一定時Master,也有可能是由Slave在傳送,而由Master產生ACK或NACK。
當然是不行,可以看看下方這張圖片說明資料有效性。只有在SCL為Low時才可以去做資料的交換,這時候表示SDA處在混亂的階段。相反的當SCL為High時則無法去變更SDA,表示SDA處於穩定狀態。
3. I²C地址說明 :
地址分為7bit與10bit兩種地址,大多設備還是使用7bit作為地址長度。而如何區分究竟是7還是10bit則可以看到下圖,假設是10bit前方5個bit則為11110而後續接上2bit設備地址並且接上r/w(r:1 / w: 0)後等待ACK,接著才會接收剩餘的7bit。
SMBus另外有支援Alert Signal可以在收到Alert時詢問是哪個Slace所發出的,再去對該設備進行後續處理。
2. GPIO腳位 : I2C1的SCL連接為PB6 SDA為PB7
3. 詳細設置
同樣的有分為輪詢與中斷方式
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,uint16_t Size, uint32_t Timeout)
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size,uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size,uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
__weak void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hi2c);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_I2C_MasterTxCpltCallback could be implemented in the user file
*/
}
__weak void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hi2c);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_I2C_MasterRxCpltCallback could be implemented in the user file
*/
}
__weak void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hi2c);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_I2C_SlaveTxCpltCallback could be implemented in the user file
*/
}
__weak void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hi2c);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_I2C_SlaveRxCpltCallback could be implemented in the user file
*/
}