提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
一、I2C协议简介
I2C通讯协议(Inter - Integrated Circuit) 是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单, 可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路 (IC) 间的通讯。I2C跟USART一样,可以通过分层进行讲解
1.1、I2C物理层
- 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连 接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。
- 一个 I2C 总线只使用两条总线线路,一条双向串行数据线 (SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
- 每个连接到总线的设备都有一个独立的地址,主机可以利用地址进行不同设备之间的访 问。
- 总线通过上拉电阻(一般为4.7K)接到电源。当 I2C 设备空闲时会输出高阻态,而当所有设备都空闲都输出高阻态时,由上拉电阻把总线拉成高电平(SCL和SDA都是高电平)。
- 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
- 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I 2C 设备尚不支持高速模式。
- 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制。
1.2、协议层
I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
- 主机的I2C接口产生传输起始信号S,此时连接到I2C总线上的所有从机都会接收到该信号;
- 所有从机开始等待主机接下来广播的从机地址信号SLAVE_ADDRESS(7或10位);
- 传输方向的选择R/W,该位为 0 时,表示主机向从机写数据;为 1 时,主机由从机读数据;
- 从机接收到匹配的地址后,主机或从机会返回一个应答ACK或非应答NACK信号,接收到ACK后,主机才能继续发送或接收数据。
1.2.1、 通讯的起始和停止信号
1.2.2、数据有效性
- SCL为高电平时SDA表示的数据有效。
1.2.3、 响应
二、I2C特性及架构
直接控制 GPIO 引脚电平产生通讯时序时,需要由 CPU 控制每个时刻的引脚状态,所以称 之为“软件模拟协议”方式。
“硬件协议”,STM32 的 I2C 片上外设专门负责实现 I2C 通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU 只要检测该 外设的状态和访问数据寄存器,就能完成数据收发。
2.1、架构剖析
2.2、I2C初始化结构体
typedef struct {
uint32_t I2C_ClockSpeed; /* 设置 SCL 时钟频率,此值要低于400000 */
uint16_t I2C_Mode; /* 指定工作模式,可选 I2C 模式及 SMBUS 模式 */
uint16_t I2C_DutyCycle; /* 指定时钟占空比,可选low/high = 2:1及16:9模式*/
uint16_t I2C_OwnAddress1; /* 指定自身的 I2C 设备地址 */
uint16_t I2C_Ack; /* 使能或关闭响应 (一般都要使能) */
uint16_t I2C_AcknowledgedAddress; /* 指定地址的长度,可为 7 位及 10 位 */
} I2C_InitTypeDef;
三、I2C——读写EEPROM实验
EEPROM 是一种掉电后数据不丢失的存储器,常用来存储一些配置信息,以便系统重新上电的 时候加载之。EEPOM 芯片最常用的通讯方式就是 I2C 协议。EEPROM 可以单个字节擦写。
I2C流程:
- 配置通讯使用的目标引脚为开漏模式;
- 使能 I2C 外设的时钟;
- 配置 I2C 外设的模式、地址、速率等参数并使能 I2C 外设;
- 编写基本 I2C 按字节收发的函数;
- 编写读写 EEPROM 存储内容的函数;
3.1、EEPROM芯片:
按照EEPROM原理图中连接,A0/A1/A2均为0,所以EEPROM的七位地址是:1010000b,即0x50。由于 I2C 通讯时常常是地址跟读写方向连在一起构成一个 8 位数,且当 R/W 位为 0 时,表示写 方向,所以加上 7 位地址,其值为“0xA0”,常称该值为 I2C 设备的“写地址”;当 R/W 位为 1 时,表示读方向,加上 7 位地址,其值为“0xA1”,常称该值为“读地址”。
EEPROM 芯片中还有一个 WP 引脚,具有写保护功能,当该引脚电平为高时,禁止写入数据,当 引脚为低电平时,可写入数据,我们直接接地,不使用写保护功能。
3.2、EEPROM读写时序
通过I2C接口读取设备X的寄存器Y的值的过程:
- 主设备发送起始信号(S);
- 发送从设备X地址+读写位0;
- 主设备读取 从设备X 应答信号ACK;
- 主设备 发送寄存器Y 地址;
- 主设备读取 从设备 应答信号ACK;
- 主设备重复起始信号(S);
- 发送从设备地址+读写位1;
- 主设备读取应答信号ACK;
- 读取数据;
- 从设备发送NACK;
- 发送停止信号;
3.3、读写EEPROM:
/********************************************************************
@function:实现多字节的ATC02的写操作
@param:
hi2c:i2c操作句柄;
DevAddress:从机设备地址;
MemAddress:从机寄存器地址;
MemAddSize:从机寄存器地址长度;
pData:发送数据的起始地址;
Size:传输数据大小;
Timeout:操作超时时间;
@retval:
********************************************************************/
HAL_StatusTypeDef I2C_Write_Buffer(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint16_t count=0;
for(count=0; count<Size; count++)
{
if(HAL_I2C_Mem_Write(hi2c, DevAddress, MemAddress, MemAddSize, pData, 1, Timeout)==HAL_OK)
{
MemAddress ++;
pData ++;
HAL_Delay(5);
}
else
{
printf("i2c write is error \t\n");
return HAL_ERROR;
}
}
return HAL_OK;
}
/*这里自己实现多字节的ATC02的读操作*/
HAL_StatusTypeDef I2C_Read_Buffer(I2C_HandleTypeDef *hi2c,\
uint16_t DevAddress, uint16_t MemAddress,\
uint16_t MemAddSize, uint8_t *pData, uint16_t Size,\
uint32_t Timeout)
{
uint16_t count=0;
for(count=0;count<Size;count ++)
{
if(HAL_I2C_Mem_Read(hi2c,DevAddress,MemAddress, MemAddSize,pData,1,Timeout)==HAL_OK)
{
MemAddress ++;
pData ++;
//HAL_Delay(5);
}
else
{
printf("i2c read is error \n");
return HAL_ERROR;
}
}
return HAL_OK;
}
总结
I2C读写EEPROM操作,主要通过HAL_I2C_Mem_Write函数按字节写入。