目录
前言
IIC接口是一种串行数字总线接口,IIC接口只有两根信号线,总线上可以连接多个设备,硬件实现简单,可扩展性强,IIC通信协议可以用普通的GPIO口进行软件模拟,IIC接口主要用于通信速率不强,以及多个器件之间通信的应用场景
1、IIC总线结构
一个器件的IIC接口只有两根数据线,即双向串行数据线SDA和时钟信号SCL,IIC较为常用的器件有OLED显示屏,E2PROM的24C02,MPU6050陀螺仪,所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每一个设备都对应一个唯一的地址。
IIC总线有以下的特点
1、IIC总线只有两根信号线,SDA是双向串行数据线,SCL是时钟信号线,用于数据的收发同步
2、IIC总线可以挂载多个设备,一般有一个主设备、多个从设备,MCU一般作为主设备,外围器件作为从设备,在IIC通信协议中,主动发起通信的就是主设备,被动相应的就是从设备
3、IIC总线上每个器件有7位或10位的地址,主设备发起通信时,会首先发送目标设备地址,只有地址对应的从设备才会做出相应
4、IIC总线的两根信号线有上拉电阻,当IIC器件空闲时,其输出口是高阻态,当所有设备都空闲时,IIC总线是高电平
5、IIC通信有标准模式和快速模式,标准模式传输率为100kbit/s,快速模式传输率为400kbit/s
2、IIC总线通信协议
IIC总线通信总是由主机启动,每个通信过程由起始信号开始,由停止信号结束,一个数据包有八位,每个数据包后有一个应答位和非应答位,例如下面的时序图
起始位:当SCL是高电平时,SDA的下降沿就是起始位,是启动一次IIC通信的起始信号
停止位:当SCL是高电平时,SDA的上升沿就是停止位,是停止一次IIC通信的结束信号
数据位:在SCL的一个时钟周期内传输一个数据位,当SCL为低电平时,发送设备更新SDA的电平,当SCL为高电平时,接收设备读取SDA的电平就是有效的一位数据
数据包:IIC数据通信一个数据包总是8位,也就是1字节的数据
应答信号:在发送完8位数据包时,发送设备在第九个SCL时钟周期采集接收设备的应答信号,若在SCL的第九个周期采集SDA信号为低电平,就是应答ACK,如果采集SDA信号为高电平就是非应答信号NACK
3、IIC的HAL库驱动程序
IIC的HAL驱动程序头文件是stm32f1xx_hal_i2c.h和stm32f1xx_hal_i2c.ex.h
IIC的HAL库驱动程序包含宏定义,结构体定义,宏函数和功能函数,IIC的数据传输方式有阻塞式,中断方式和DMA方式
对IIC接口进行初始化配置的函数是HAL_I2C_Init() 以下为函数原型
HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c); 其中hi2c是IIC接口的对象指针,是I2C_HandleTypeDef的结构体类型指针,在CubeMX自动生成的文件I2C.c中,会为启动的IIC接口定义外设对象变量 例如 I2C_HandleTypeDef hi2c1;
在IIC中的三种发送形式的函数用处差不多,只是接收和发送的形式变了,参数和功能没变,这里我们只介绍几个常用的函数
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
功能:IIC写数据
参数:
*hi2c 设置使用的是那个IIC 例:&hi2c2
DevAddress 写入的地址 设置写入数据的地址 例 0xA0
*pData 需要写入的数据
Size 要发送的字节数
Timeout 最大传输时间,超过传输时间将自动退出传输函数
HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
功能:IIC读一个字节
参数:
*hi2c 设置使用的是那个IIC 例:&hi2c2
DevAddress 写入的地址 设置写入数据的地址 例 0xA0
*pData 需要写入的数据
Size 要发送的字节数
Timeout 最大传输时间,超过传输时间将自动退出传输函数
举例 :发送两个字节
HAL_I2C_Master_Transmit(&hi2c1,0xA1,(uint8_t*)TxData,2,1000) ;;
IIC写数据函数
HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
/* 第1个参数为I2C操作句柄
第2个参数为从机设备地址
第3个参数为从机寄存器地址
第4个参数为从机寄存器地址长度
第5个参数为发送的数据的起始地址
第6个参数为传输数据的大小
第7个参数为操作超时时间 */
功能: IIC写多个数据 该函数适用于IIC外设里面还有子地址寄存器的设备,比方说E2PROM,除了设备地址,每个存储字节都有其对应的地址
参数:
*hi2c: I2C设备号指针,设置使用的是那个IIC 例:&hi2c2
DevAddress: 从设备地址 从设备的IIC地址 例E2PROM的设备地址 0xA0
MemAddress: 从机寄存器地址 ,每写入一个字节数据,地址就会自动+1
MemAddSize: 从机寄存器地址字节长度 8位或16位
写入数据的字节类型 8位还是16位
I2C_MEMADD_SIZE_8BIT
I2C_MEMADD_SIZE_16BIT
在stm32f1xx_hal_i2c.h中有定义
使用HAL_I2C_Mem_Write等于先使用HAL_I2C_Master_Transmit传输第一个寄存器地址,再用HAL_I2C_Master_Transmit传输写入第一个寄存器的数据。可以传输多个数据
void Single_WriteI2C(uint8_t REG_Address,uint8_t REG_data)
{
uint8_t TxData[2] = {REG_Address,REG_data};
while(HAL_I2C_Master_Transmit(&hi2c1,I2C1_WRITE_ADDRESS,(uint8_t*)TxData,2,1000) != HAL_OK)
{
if (HAL_I2C_GetError(&hi2c1) != HAL_I2C_ERROR_AF)
{
Error_Handler();
}
}
}
在传输过程,寄存器地址和源数据地址是会自加的。
至于读函数也是如此,因此用HAL_I2C_Mem_Write和HAL_I2C_Mem_Read,来写读指定设备的指定寄存器数据是十分方便的,让设计过程省了好多步骤。
举例:8位 16位
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);
// 8 位
HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);
HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);
// 16 位
如果只往某个外设中写数据,则用Master_Transmit。 如果是外设里面还有子地址,例如我们的E2PROM,有设备地址,还有每个数据的寄存器存储地址。则用Mem_Write。
Mem_Write是2个地址,Master_Transmit只有从机地址
4、STM32CubeMX配置
点击I2C1 设置为I2C 因为我们的硬件IIC 芯片一般都是主设备,也就是一般情况设置主模式即可
I2C1是参数设置分为两组
、
Master features 主模式特性
I2C Speed Mode: IIC模式设置 快速模式和标准模式。实际上也就是速率的选择。
I2C Clock Speed:I2C传输速率,默认为100KHz,快速模式可以最高400KHZ
Slave features 从模式特性
Clock No Stretch Mode:禁止时钟延长,设置Disabled表示允许时钟延长
IIC时钟拉伸(Clock stretching)
clock stretching通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行.clock stretching是可选的,实际上大多数从设备不包括SCL驱动,所以它们不能stretch时钟.
Primary Address Length selection: 从设备地址长度 设置从设备的地址是7bit还是10bit 大部分为7bit
-Dual Address Acknowledged: 双地址确认
Primary slave address: 从设备初始地址
这里我们保持默认即可
其实在初学者学到这里已经可以简单进行IIC的使用了,在我们配置完成后,常用的外设例如MPU6050和OLED显示屏都有自己的函数库,我们学会寻找和调用这些别人封装好的函数就可以,下面举一个OLED显示屏函数库使用IIC的例子
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
uint8_t *pData;
pData = &Command;
HAL_I2C_Mem_Write(&Hardware_IIC_No,0x78,0x00,I2C_MEMADD_SIZE_8BIT,pData,1,100);
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
uint8_t *pData;
pData = &Data;
HAL_I2C_Mem_Write(&Hardware_IIC_No,0x78,0x40,I2C_MEMADD_SIZE_8BIT,pData,1,100);
}