1.I2C 协议简介
I2C 通讯协议(Inter - Integrated Circuit) 是由Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC) 间的通讯。在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设;STM32 标准库则是在寄存器与用户代码之间的软件层。对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流。
1.1.I2C的特点
(1) 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个I2C 通讯总线中,可连
接多个I2C 通讯设备,支持多个通讯主机及多个通讯从机。
(2) 一个I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。数
据线即用来表示数据,时钟线用于数据收发同步。
(3) 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访
问。
(4) 总线通过上拉电阻接到电源。当I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都
输出高阻态时,由上拉电阻把总线拉成高电平。
(5) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
(6) 具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达
3.4Mbit/s,但目前大多I2C 设备尚不支持高速模式。
(7) 连接到相同总线的IC 数量受到总线的最大电容400pF 限制。
友善提醒一下:这种协议类的东西,一开始不建议自己写,会使用就好。
2.I2C 初始化结构体详解
跟其它外设一样,STM32 标准库提供了I2C 初始化结构体及初始化函数来配置I2C 外设。初始
化结构体及函数定义在库文件“stm32f10x_i2c.h”及“stm32f10x_i2c.c”中,编程时我们可以结合
这两个文件内的注释使用或参考库帮助文档。了解初始化结构体后我们就能对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;
这些结构体成员说明如下,其中括号内的文字是对应参数在STM32 标准库中定义的宏:
(1) I2C_ClockSpeed
本成员设置的是I2C 的传输速率,在调用初始化函数时,函数会根据我们输入的数值经过运算后
把时钟因子写入到I2C 的时钟控制寄存器CCR。而我们写入的这个参数值不得高于400KHz。实际上由于CCR 寄存器不能写入小数类型的时钟因子,影响到SCL 的实际频率可能会低于本成员
设置的参数值,这时除了通讯稍慢一点以外,不会对I2C 的标准通讯造成其它影响。
(2) I2C_Mode
本成员是选择I2C 的使用方式, 有I2C 模式(I2C_Mode_I2C) 和SMBus 主、从模式(I2C_Mode_SMBusHost、I2C_Mode_SMBusDevice ) 。I2C 不需要在此处区分主从模式,直接设
置I2C_Mode_I2C 即可。
(3) I2C_DutyCycle
本成员设置的是I2C 的SCL 线时钟的占空比。该配置有两个选择,分别为低电平时间比高电平
时间为2:1 ( I2C_DutyCycle_2) 和16:9 (I2C_DutyCycle_16_9)。其实这两个模式的比例差别并不大,一般要求都不会如此严格,这里随便选就可以。
(4) I2C_OwnAddress1
本成员配置的是STM32 的I2C 设备自己的地址,每个连接到I2C 总线上的设备都要有一个自己
的地址,作为主机也不例外。地址可设置为7 位或10 位(受下面I2C_AcknowledgeAddress 成员决
定),只要该地址是I2C 总线上唯一的即可。STM32 的I2C 外设可同时使用两个地址,即同时对两个地址作出响应,这个结构成员I2C_OwnAddress1 配置的是默认的、OAR1 寄存器存储的地址,若需要设置第二个地址寄存器OAR2,可使用I2C_OwnAddress2Config 函数来配置,OAR2 不支持10 位地址,只有7 位。
(5) I2C_Ack_Enable
本成员是关于I2C 应答设置, 设置为使能则可以发送响应信号。本实验配置为允许
应答(I2C_Ack_Enable), 这是绝大多数遵循I2C 标准的设备的通讯要求, 改为禁止应答
(I2C_Ack_Disable) 往往会导致通讯错误。
(6) I2C_AcknowledgeAddress
本成员选择I2C 的寻址模式是7 位还是10 位地址。这需要根据实际连接到I2C 总线上设备的地
址进行选择,这个成员的配置也影响到I2C_OwnAddress1 成员,只有这里设置成10 位模式时,
I2C_OwnAddress1 才支持10 位地址。配置完这些结构体成员值,调用库函数I2C_Init 即可把结构体的配置写入到寄存器中。
3.编程实现
要点:
(1) 配置通讯使用的目标引脚为开漏模式;
(2) 使能I2C 外设的时钟;
(3) 配置I2C 外设的模式、地址、速率等参数并使能I2C 外设;
(4) 编写基本I2C 按字节收发的函数;
(5) 编写读写EEPROM 存储内容的函数;
3.1.初始化I2C 的GPIO
配置GPIO就不用讲解太多了,前面的文章已经讲解很多次了,如果不懂的同学可以看看之前的文章。
static void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能与I2C 有关的时钟*/
EEPROM_I2C_APBxClock_FUN ( EEPROM_I2C_CLK, ENABLE );
EEPROM_I2C_GPIO_APBxClock_FUN ( EEPROM_I2C_GPIO_CLK, ENABLE );
/* I2C_SCL、I2C_SDA*/
GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出
GPIO_Init(EEPROM_I2C_SCL_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出
GPIO_Init(EEPROM_I2C_SDA_PORT, &GPIO_InitStructure);
}
3.2.配置I2C 的模式
static void I2C_Mode_Configu(void)
{
I2C_InitTypeDef I2C_InitStructure;
/* I2C 配置*/
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
/* 高电平数据稳定,低电平数据变化SCL 时钟线的占空比*/
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 =I2Cx_OWN_ADDRESS7;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
/* I2C 的寻址模式*/
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
/* 通信速率*/
I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
/* I2C 初始化*/
I2C_Init(EEPROM_I2Cx, &I2C_InitStructure);
/* 使能I2C */
I2C_Cmd(EEPROM_I2Cx, ENABLE);
}
代码:后期会更新比较好,适合工业级的代码。