介绍
I²C(Inter-Integrated Circuit)中文是内部整合电路,属于串列通讯汇流排。最早是由Philips开发,为了使嵌入式系统、手机等等可以连接低速周边装置而发展的,适用于短距离通讯。
I²C只需要两条线即可完成通讯,分别是SDA与SCL因此非常的节省空间。大多应用于小型元件,最常看到的应该是EEPROM这一类的元件。
那I²C与SPI差别在哪呢?
I²C汇流排架构
使用时允许多个Master进行通讯,将各设备SCL与SDA连结即可。每一设备皆由属于自己的地址,不管是任何设备SDA与SCL都属于Open Drain 的IO脚,同时并接(Wired-And)在两条线路上,皆需要加入上拉电阻。当导通时是低电位不导通时为浮接,所以利用电阻将电位拉高。
Wired-And指的是:
→Wired : 直接将设备对应的线接在一起
→And : 也就是大家熟知的And逻辑闸
也就是说只有当输入都为High的时候才会输出High,任一个为Low即为Low。代表着在汇流排当中只要有一个晶片为Low那就会是Low,而晶片可以轻易地控制Bus输出Low,但要输出High实则连其他晶片也必须同时输出High。
I²C Protocol
可以看到上图是一个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与NACK说明
上方提到每一组资料会在后方接上一个ACK bit,而这个bit会由接收端产生。其中又分为ACK与NACK,ACK为Low而NACK为High。在这过程并非发送端一定时Master,也有可能是由Slave在传送,而由Master产生ACK或NACK。
那是不是任何时候都能去透过SDA做资料交换呢?
当然是不行,可以看看下方这张图片说明资料有效性。只有在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。
.IOC档设定
左侧Connectivity选单当中可以选择I2C1-I2C3这边以I2C示範。点选后可以看到上方可以设置I2C的模式选择I2C开启。SMBus-Alert-ModeSMBus-two-wire-interfaceSMBus另外有支援Alert Signal可以在收到Alert时询问是哪个Slace所发出的,再去对该设备进行后续处理。
2. GPIO脚位 : I2C1的SCL连接为PB6 SDA为PB7
3. 详细设置
函数说明
同样的有分为轮询与中断方式
Master
传送轮询方式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);
Slave
传送轮询方式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);
中断回调函式-Master
传送中断回调函式__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 */}
中断回调函式-Slave
传送中断回调函式__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 */}