--- 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²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(); I²C on STM32F429-Discovery ===================================== 實驗一: touch panel ................... 觀察及使用stm32f429i_discovery_ioe.c,並量測對應的訊號 - `Data sheet `_ demo code: [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) 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.bmp start bit -> send address and transmitter bit -> ack -> senddata(register address 0x00) .. image:: /embedded/scope_6.bmp start bit -> send address and receiver bit -> ack -> data send by slave .. image:: /embedded/scope_8.bmp stop bit -> start bit -> send address and transmitter bit -> ack -> senddata(register address 0x01) .. image:: /embedded/scope_9.bmp start bit -> send address and receiver bit -> ack -> data send by slave 實驗二: 兩塊(以上)板子的數據傳輸 ................... 一塊板子當作Master,其他的作為Slave,實驗I2C的資料傳輸 (待補) (NVIC & I2C interrupt) Reference ===================================== - `reference_manual `_ - `wikipedia `_ - `http://www.eng.auburn.edu/~nelson/courses/elec5260_6260/STM32F4%20I2C.pdf <_http://www.eng.auburn.edu/~nelson/courses/elec5260_6260/STM32F4%20I2C.pdf>`_