版本 a5af2736c4d7c011375cb154b03067a8ff635c16
Changes from a5af2736c4d7c011375cb154b03067a8ff635c16 to dc35ae6f35ed122e88dff4e4123df3b0570ea92b
---
title: I2C
categories: I2C, Peripherals, STM32F4, STM32F429
...
I²C 簡介
=====================================
I²C(Inter-Integrated Circuit)是內部整合電路的稱呼,是一種串列通訊匯流排,由Philips公司在1980年代為了讓主機板、手機及嵌入式系統用以連接低速週邊裝置而發展,主要應用在board-to-board,它的設計並不能應用到長距離裝置的通訊,由於協定中從樸裝置位址只有7個位元,因此一組I²C的最多裝置位址定義至127個。
I²C只使用兩條雙向開放汲極(Open Drain)(master and slave只能把電位拉到LOW或是讓他OPEN)(串列資料(SDA)及串列時脈(SCL))並利用電阻將電位上拉。I²C允許相當大的工作電壓範圍,但典型的電壓準位為+3.3V或+5v。
I²C bus
...................
.. image:: /embedded/i2c_bus.png
藉由Master發送訊號來控制與Slave之間的通訊
I²C與I²S的差異
...................
I²C(Inter-Integrated Circuit ) 與 I²S當初同樣是為進行音頻資料的串列通訊協議,但就功能面的支援還是有差異, I²C提供多個主控端串接多個從屬裝置其同步與資料透過雙線式雙向同步傳遞,其傳輸速率分為有100khz,400khz and 3.5mhz 由於這些時脈都是處理較普遍的裝置, , I²S提供三線式主控端單向多從屬裝置, 其傳輸速率分為有?與周2.5MHz.
I²C(Inter-Integrated Circuit ) 與 I²S當初同樣是為進行音頻資料的串列通訊協議,但就功能面的支援還是有差異, I²C提供多個主控端串接多個從屬裝置其同步與資料透過雙線式雙向同步傳遞,其傳輸速率分為有100khz,400khz and 3.5mhz 由於這些時脈都是處理較普遍的裝置,I²S提供三線式主控端單向多從屬裝置, 其傳輸速率分為有?與周2.5MHz.
- I²S (inter-integrated sound) : STM32F429 Discovery 提供兩組 I²S介面(由內部的多工器切換 SPI2 及 SPI3這兩組) ,[1] pp.37 ; 所以基本上一個時脈以400ns計算, 其傳輸頻率為2.5MHz,將足夠提供24bits之48khz取樣的雙聲道音源, 主要是支援MPGE2,4等; 而44.1kKHz 取樣頻率則是支援MP3格式 :
24bits x 48000Hz x 2channel = 2304000bits/Sec = 2.304Mbps
- STM32F429 Discovery I2S and SPI 的腳位定義 (FPQ 144)
pin 96 , I2S2_MCK | pin 74 , I2S2_CK/SPI2_SC | pin 76 , I2S2_SD/ SPI2_MOS
- 傳輸匯流排I²C 為 SCK (Continuous serial clock) , SD 兩條, 如果是採用Board to Cable 建議加上一條GND 。
- 在I²S匯流排通道上只有一個傳輸控制器與一個主控端(Master)角色的監控裝置該,該匯流排共有三條傳輸線分別為,同步時脈 SCK (Continuous serial clock) , 字串選擇的WS (Select dates) 以及進行資料傳輸的 SD(serial data ) 三條傳輸線, 並含GND共地線。
- 傳輸匯流排I²C 為 SCK (Continuous serial clock)與 SD 兩條, 如果是採用Board to Cable 建議加上一條GND 。
- 在I²S匯流排通道上只有一個傳輸控制器與一個主控端(Master)角色的監控裝置,該匯流排共有三條傳輸線分別為,同步時脈 SCK (Continuous serial clock) , 字串選擇的WS (Select dates) 以及進行資料傳輸的 SD(serial data ) 三條傳輸線, 並含GND共地線。
- I²S的角色可以是一個主控端(Master)監控裝置,也可以同時是一個傳輸控制器(Tx), 接收器(Rx) 或是兩個從端角色的裝置。
- I²S 介面傳輸資料具有兩個左右音訊通道的資料, 它靠 WS = 0 設定為零時; 定義為 通道 1 (左聲道資料讀取) ; WS = 1 ; 通道 2 (右聲道資料讀取)。
I²C 特性
...................
- I²C 串列傳輸包括四個部分:起始信號、設備位址發送、數據傳送和停止信號。
- 只能Master<->Slave,無法Slave<->Slave,每個slave都要有一個特定且唯一的位址。
- START condition: SCL=High 且 SDA為負緣
- STOP condition: SCL=High 且 SDA=為正緣
.. image:: /embedded/i2c_condition.png
- 由Master發送起始信號來開起通訊,所有的slave device接收到起始信號後會進入接收數據模式。接著Master需要發送通訊目標設備的address及R/W資訊。
- STM32F429提供兩種 address mode - 7bit and 10bit
- 7bit mode: 發送 7bit 的address (MSB) 及一位元的 R(1)/W(0)後,該 address 的slave端會發送一個bit的 Ack(acknowledge) bit,ACK=0表設定成功(Slave把SDA拉到LOW),開始數據傳送。
.. image:: /embedded/i2c_com.png
(7bit mode)
- 10bit mode: 總共用兩個byte來傳送 address資訊。第一個byte的前五個位元需為 "11110" 來表示要使用 10-bit addressing。
.. image:: /embedded/i2c_10bit.gif
(10bit mode)
- 資料傳輸
- 在 SCL 為 HIGH 時,SDA 必須是 stable 的。
- SDA 只能在 SCL 為 LOW 時改變。
- 一次傳一個 byte,每個byte都跟著一個 ACK bit ,在 ACK bit 時 Master 會釋放bus,此時 Slave 端需把 SDA 拉 LOW,否則 Master 需要發出 STOP訊號 或是重新發送 START 訊號。
- 在每個 byte 之間 Slave 端可以把 SCL 拉 LOW 來強制讓傳輸暫停。
I²C interrupt and Event
......................................
(內容待補)
.. image:: /embedded/i2c_interrupt.png
- status register
.. image:: /embedded/i2c_status_register(1).png
.. image:: /embedded/i2c_status_register(2).png
.. code-block:: c
/**
* @brief SR1 register flags
*/
#define I2C_FLAG_SMBALERT ((uint32_t)0x10008000)
#define I2C_FLAG_TIMEOUT ((uint32_t)0x10004000)
#define I2C_FLAG_PECERR ((uint32_t)0x10001000)
#define I2C_FLAG_OVR ((uint32_t)0x10000800)
#define I2C_FLAG_AF ((uint32_t)0x10000400)
#define I2C_FLAG_ARLO ((uint32_t)0x10000200)
#define I2C_FLAG_BERR ((uint32_t)0x10000100)
#define I2C_FLAG_TXE ((uint32_t)0x10000080)
#define I2C_FLAG_RXNE ((uint32_t)0x10000040)
#define I2C_FLAG_STOPF ((uint32_t)0x10000010)
#define I2C_FLAG_ADD10 ((uint32_t)0x10000008)
#define I2C_FLAG_BTF ((uint32_t)0x10000004)
#define I2C_FLAG_ADDR ((uint32_t)0x10000002)
#define I2C_FLAG_SB ((uint32_t)0x10000001)
/**
* @brief SR2 register flags
*/
#define I2C_FLAG_DUALF ((uint32_t)0x00800000)
#define I2C_FLAG_SMBHOST ((uint32_t)0x00400000)
#define I2C_FLAG_SMBDEFAULT ((uint32_t)0x00200000)
#define I2C_FLAG_GENCALL ((uint32_t)0x00100000)
#define I2C_FLAG_TRA ((uint32_t)0x00040000)
#define I2C_FLAG_BUSY ((uint32_t)0x00020000)
#define I2C_FLAG_MSL ((uint32_t)0x00010000)
(暫存器可參考 http://www.eng.auburn.edu/~nelson/courses/elec5260_6260/STM32F4%20I2C.pdf)
- I2C bus “events” from flags
I2C Master Events
.. code-block:: c
/* --EV5 */
#define I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */
/* --EV6 */
#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */
#define I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ((uint32_t)0x00030002) /* BUSY, MSL and ADDR flags */
/* --EV9 */
#define I2C_EVENT_MASTER_MODE_ADDRESS10 ((uint32_t)0x00030008) /* BUSY, MSL and ADD10 flags */
/* Master RECEIVER mode -----------------------------*/
/* --EV7 */
#define I2C_EVENT_MASTER_BYTE_RECEIVED ((uint32_t)0x00030040) /* BUSY, MSL and RXNE flags */
/* Master TRANSMITTER mode --------------------------*/
/* --EV8 */
#define I2C_EVENT_MASTER_BYTE_TRANSMITTING ((uint32_t)0x00070080) /* TRA, BUSY, MSL, TXE flags */
/* --EV8_2 */
#define I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */
I2C Slave Events
.. code-block:: c
/* --EV1 */
#define I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED ((uint32_t)0x00020002) /* BUSY and ADDR flags */
#define I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED ((uint32_t)0x00060082) /* TRA, BUSY, TXE and ADDR flags */
/* Slave RECEIVER mode --------------------------*/
/* --EV2 */
#define I2C_EVENT_SLAVE_BYTE_RECEIVED ((uint32_t)0x00020040) /* BUSY and RXNE flags */
/* --EV4 */
#define I2C_EVENT_SLAVE_STOP_DETECTED ((uint32_t)0x00000010) /* STOPF flag */
/* Slave TRANSMITTER mode -----------------------*/
/* --EV3 */
#define I2C_EVENT_SLAVE_BYTE_TRANSMITTED ((uint32_t)0x00060084) /* TRA, BUSY, TXE and BTF flags */
#define I2C_EVENT_SLAVE_BYTE_TRANSMITTING ((uint32_t)0x00060080) /* TRA, BUSY and TXE flags */
/* --EV3_2 */
#define I2C_EVENT_SLAVE_ACK_FAILURE ((uint32_t)0x00000400) /* AF flag */
four I²C modes
...................
(內容待補) (要怎麼把圖縮小= =?)
以下都以7 bit adress mode 作範例
Slave transmitter
------------------------------------
.. image:: /embedded/i2c_slave_transmitter.png
Slave receiver
------------------------------------
.. image:: /embedded/i2c_slave_receiver.png
Master transmitter
------------------------------------
.. image:: /embedded/i2c_master_transmitter.png
WriteOneByte
- 產生 start bit: I2C_GenerateSTART()
- 等待EV5: while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
- 傳送7bit address與傳輸模式: I2C_Send7bitAddress(i2cx, i2c_ADDR, I2C_Direction_Transmitter);
- 等待EV6: while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
- 傳送一個byte: I2C_SendData(I2Cx);
- 確認BYTE傳送完成: while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTER) );
- 產生 stop bit: I2C_GenerateSTOP();
Master receiver
------------------------------------
.. image:: /embedded/i2c_master_receiver.png
ReadOneByte
- 產生 start bit: I2C_GenerateSTART()
- 等待EV5: while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
- 傳送7bit address與傳輸模式: I2C_Send7bitAddress(i2cx, i2c_ADDR, I2C_Direction_Receiver);
- 等待EV6: while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
- 確認BYTE接收完成: while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
- 讀取暫存器: I2C_ReceiveData(I2Cx);
- 產生 stop bit: I2C_GenerateSTOP();
flow chart(two boards)
......................................
master transmitter/slave receiver
.. image:: /embedded/flow_chart_master_t_slave_r.jpg
I²C on STM32F429-Discovery
=====================================
實驗一: touch panel
...................
觀察及使用觸控螢幕,並量測對應的i2c訊號
- `Data sheet <http://www.st.com/web/en/resource/technical/document/datasheet/CD00186725.pdf>`_
demo code:
.. code-block:: c
git clone https://github.com/colin8930/stm32_touchpanel_i2c.git
cd stm32_touchpanel_i2c
make
make flash
[stm32f429i_discovery_ioe.h]
.. code-block:: c
#define IOE_I2C I2C3
#define IOE_I2C_CLK RCC_APB1Periph_I2C3
#define IOE_I2C_SCL_PIN GPIO_Pin_8
#define IOE_I2C_SCL_GPIO_PORT GPIOA
#define IOE_I2C_SCL_GPIO_CLK RCC_AHB1Periph_GPIOA
#define IOE_I2C_SCL_SOURCE GPIO_PinSource8
#define IOE_I2C_SCL_AF GPIO_AF_I2C3
#define IOE_I2C_SDA_PIN GPIO_Pin_9
#define IOE_I2C_SDA_GPIO_PORT GPIOC
#define IOE_I2C_SDA_GPIO_CLK RCC_AHB1Periph_GPIOC
#define IOE_I2C_SDA_SOURCE GPIO_PinSource9
#define IOE_I2C_SDA_AF GPIO_AF_I2C3
#define IOE_I2C_DR ((uint32_t)0x40005C10)
stm32f429i_discovery_ioe.c:
GPIO 設定
.. code-block:: c
GPIO_InitTypeDef GPIO_InitStructure;
/* 設定RCC clock*/
/* Enable IOE_I2C and IOE_I2C_GPIO_PORT & Alternate Function clocks */
RCC_APB1PeriphClockCmd(IOE_I2C_CLK, ENABLE);
RCC_AHB1PeriphClockCmd(IOE_I2C_SCL_GPIO_CLK | IOE_I2C_SDA_GPIO_CLK |
IOE_IT_GPIO_CLK, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
/* Reset IOE_I2C IP */
RCC_APB1PeriphResetCmd(IOE_I2C_CLK, ENABLE);
/* Release reset signal of IOE_I2C IP */
RCC_APB1PeriphResetCmd(IOE_I2C_CLK, DISABLE);
/*設定PA8、PC9 的Alternate function設為 GPIO_AF_I2C3*/
/* Connect PXx to I2C_SCL*/
GPIO_PinAFConfig(IOE_I2C_SCL_GPIO_PORT, IOE_I2C_SCL_SOURCE, IOE_I2C_SCL_AF);
/* Connect PXx to I2C_SDA*/
GPIO_PinAFConfig(IOE_I2C_SDA_GPIO_PORT, IOE_I2C_SDA_SOURCE, IOE_I2C_SDA_AF);
/* 設定 SCL、SDA pin*/
/* IOE_I2C SCL and SDA pins configuration */
GPIO_InitStructure.GPIO_Pin = IOE_I2C_SCL_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(IOE_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = IOE_I2C_SDA_PIN;
GPIO_Init(IOE_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
I2C參數設定
.. code-block:: c
I2C_InitTypeDef I2C_InitStructure;
/* If the I2C peripheral is already enabled, don't reconfigure it */
if ((IOE_I2C->CR1 & I2C_CR1_PE) == 0)
{
/* 設定 I2C參數*/
/* IOE_I2C configuration */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED;
/* Initialize the I2C peripheral */
I2C_Init(IOE_I2C, &I2C_InitStructure);
/* Enable the I2C peripheral */
I2C_Cmd(IOE_I2C, ENABLE);
}
touch panel demo code 一開始會先檢查 device ID及通訊是否正確
.. code-block:: c
uint16_t IOE_ReadID(void)
{
uint16_t tmp = 0;
/* Read device ID */
tmp = I2C_ReadDeviceRegister(0);
tmp = (uint32_t)(tmp << 8);
tmp |= (uint32_t)I2C_ReadDeviceRegister(1);
/* Return the ID */
return (uint16_t)tmp;
}
.. code-block:: c
uint8_t I2C_ReadDeviceRegister(uint8_t RegisterAddr)
{
uint8_t tmp = 0;
/* Enable the I2C peripheral */
I2C_GenerateSTART(IOE_I2C, ENABLE);
/* Test on EV5 and clear it */
IOE_TimeOut = TIMEOUT_MAX;
while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_SB));
/* Disable Acknowledgement */
I2C_AcknowledgeConfig(IOE_I2C, DISABLE);
/* Transmit the slave address and enable writing operation */
I2C_Send7bitAddress(IOE_I2C, IOE_ADDR, I2C_Direction_Transmitter);
/* Test on EV6 and clear it */
IOE_TimeOut = TIMEOUT_MAX;
while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_ADDR));
/* Read status register 2 to clear ADDR flag */
IOE_I2C->SR2;
/* Test on EV8 and clear it */
IOE_TimeOut = TIMEOUT_MAX;
while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_TXE));
/* Transmit the first address for r/w operations */
I2C_SendData(IOE_I2C, RegisterAddr);
/* Test on EV8 and clear it */
IOE_TimeOut = TIMEOUT_MAX;
while ((!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_TXE)) || (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_BTF)));
/* Regenerate a start condition */
I2C_GenerateSTART(IOE_I2C, ENABLE);
/* Test on EV5 and clear it */
IOE_TimeOut = TIMEOUT_MAX;
while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_SB));
/* Transmit the slave address and enable writing operation */
I2C_Send7bitAddress(IOE_I2C, IOE_ADDR, I2C_Direction_Receiver);
/* Test on EV6 and clear it */
IOE_TimeOut = TIMEOUT_MAX;
while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_ADDR));
/* Read status register 2 to clear ADDR flag */
IOE_I2C->SR2;
/* Test on EV7 and clear it */
IOE_TimeOut = TIMEOUT_MAX;
while (!I2C_GetFlagStatus(IOE_I2C, I2C_FLAG_RXNE));
/* End the configuration sequence */
I2C_GenerateSTOP(IOE_I2C, ENABLE);
/* Load the register value */
tmp = I2C_ReceiveData(IOE_I2C);
/* Enable Acknowledgement */
I2C_AcknowledgeConfig(IOE_I2C, ENABLE);
/* Return the read value */
return tmp;
}
(待補)
SCL: PA8
SDA: PC9
slave address: 0x82 (0x01000010)
.. image:: /embedded/i2c_picture.jpg
.. image:: /embedded/scope_5.jpg
start bit -> send address and transmitter bit -> ack -> senddata(register address 0x00)
.. image:: /embedded/scope_6.jpg
start bit -> send address and receiver bit -> ack -> data send by slave
.. image:: /embedded/scope_8.jpg
stop bit -> start bit -> send address and transmitter bit -> ack -> senddata(register address 0x01)
.. image:: /embedded/scope_9.jpg
start bit -> send address and receiver bit -> ack -> data send by slave
.. image:: /embedded/scope_12.jpg
改變I2C CLOCK SPEED -> 400kHz
.. image:: /embedded/scope_14.jpg
要注意探棒衰減...
實驗二: 兩塊(以上)板子的數據傳輸
...................
一塊板子當作Master,其他的作為Slave,實驗I2C的資料傳輸
(待補)
(NVIC & I2C interrupt)
Reference
=====================================
- `reference_manual <http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf>`_
- `wikipedia <http://zh.wikipedia.org/wiki/I%C2%B2C>`_
- `http://www.eng.auburn.edu/~nelson/courses/elec5260_6260/STM32F4%20I2C.pdf <_http://www.eng.auburn.edu/~nelson/courses/elec5260_6260/STM32F4%20I2C.pdf>`_