I2C简介
I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
I2C物理层的特点
1.它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。
2.一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) , 一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数 据收发同步。
3.每个连接到总线的设备都有一个独立的地址,主机可以利用这个 地址进行不同设备之间的访问。
4.总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当 所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
5.多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由 哪个设备占用总线。 具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高 速模式。
I2C的协议层
I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、
仲裁、时钟同步和地址广播等环节。
I2C基本读写过程
主机写数据到从机:
I2C基本读写过程
通讯复合格式:
通讯的起始和停止信号
• 当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况 表示通讯的起始。
• 当 SCL 是高电平时 SDA
线由低电平向高电平切换,表示通讯的 停止。 • 起始和停止信号一般由主机产生。
数据有效性
I2C使用SDA信号线来传输数据,使用SCL信号线进行数据同步。
SDA数据线在SCL的每个时钟周期传输一位数据。
• SCL为高电平的时候SDA表示的数据有效,即此时的SDA为高电平时 表示数据“1”,为低电平时表示数据“0”。 •
当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平 切换,为下一次表示数据做好准备。
地址及数据方向
• I2C总线上的每个设备都有自己的独立地址,主机发起通讯时,通过 SDA信号线发送设备地址(SLAVE_ADDRESS)来查找从机。设备地址可以是7位或10位。 •
紧跟设备地址的一个数据位R/W用来表示数据传输方向,数据方向位为 “1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。
响应
I2C的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种信号。
传输时主机产生时钟,在第9个时钟时,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。
STM32的I2C特性及架构
软件模拟协议:使用CPU直接控制通讯引脚的电平,产生出符合通讯协议标准的逻辑。
硬件实现协议:由STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。
1.通讯引脚
STM32芯片有多个I2C外设,它们的I2C通讯信号引出到不同的GPIO引脚上,使用时必须配置到这些指定的引脚.
2.时钟控制逻辑
SCL线的时钟信号,由I2C接口根据时钟控制寄存器(CCR)控制, 控制的参数主要为时钟频率。 •
可选择I2C通讯的“标准/快速”模式,这两个模式分别I2C对应 100/400Kbit/s的通讯速率。 •
在快速模式下可选择SCL时钟的占空比,可选Tlow/Thigh=2或 Tlow/Thigh=16/9模式。 •
CCR寄存器中12位的配置因子CCR,它与I2C外设的输入时钟源共同 作用,产生SCL时钟。STM32的I2C外设输入时钟源为PCLK1。
3.数据控制逻辑
I2C的SDA信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器(DR)、地址寄存器(OAR)、PEC寄存器以及SDA数据线。
• 当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过SDA信号线发送出去; •
当从外部接收数据的时候,数据移位寄存器把SDA信号线采样到的数据一位一位地存储到“数据寄存器”中。
STM32的I2C通讯过程
使用I2C外设通讯时,在通讯的不同阶段它会对“状态寄存器(SR1及SR2)”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。
1.主发送器
在发送了地址和清除了ADDR位后, 主设备通过内部移位寄存器将字节从DR寄存器发送到SDA线上。
主设备等待,直到TxE被清除,(见图245的EV8)。
当收到应答脉冲时:
● TxE位被硬件置位,如果设置了INEVFEN和ITBUFEN位,则产生一个中断。
如果TxE被置位并且在上一次数据发送结束之前没有写新的数据字节到DR寄存器,则BTF被硬件置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器
将清除BTF位。关闭通信在DR寄存器中写入最后一个字节后,通过设置STOP位产生一个停止条件(见图245的EV8_2),然后I2C接口将自动回到从模式(M/S位清除)。
2.主接收器
在发送地址和清除ADDR之后,I2C接口进入主接收器模式。在此模式下,I2C接口从SDA线接收数据字节,并通过内部移位寄存器送至DR寄存器。在每个字节后,I2C接口依次执行以下操作:
● 如果ACK位被置位,发出一个应答脉冲。
● 硬件设置RxNE=1,如果设置了INEVFEN和ITBUFEN位,则会产生一个中断(见图246的EV7)。
如果RxNE位被置位,并且在接收新数据结束前,DR寄存器中的数据没有被读走,硬件将设置BTF=1,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再读出I2C_DR寄存器将清除BTF位。
关闭通信主设备在从从设备接收到最后一个字节后发送一个NACK。接收到NACK后,从设备释放对SCL和SDA线的控制;主设备就可以发送一个停止/重起始条件。
● 为了在收到最后一个字节后产生一个NACK脉冲,在读倒数第二个数据字节之后(在倒数第二个RxNE事件之后)必须清除ACK位。
● 为了产生一个停止/重起始条件,软件必须在读倒数第二个数据字节之后(在倒数第二个RxNE事件之后)设置STOP/START位。
● 只接收一个字节时,刚好在EV6之后(EV6_1时,清除ADDR之后)要关闭应答和停止条件的产生位。
在产生了停止条件后,I2C接口自动回到从模式(M/SL位被清除)
3.从发送器
在接收到地址和清除ADDR位后,从发送器将字节从DR寄存器经由内部移位寄存器发送到SDA线上。
从设备保持SCL为低电平,直到ADDR位被清除并且待发送数据已写入DR寄存器。(见下图中的EV1和EV3)。
当收到应答脉冲时:
● TxE位被硬件置位,如果设置了ITEVFEN和ITBUFEN位,则产生一个中断。
如果TxE位被置位,但在下一个数据发送结束之前没有新数据写入到I2C_DR寄存器,则BTF位被置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器
将清除BTF位。
4.从接收器
在接收到地址并清除ADDR后,从接收器将通过内部移位寄存器从SDA线接收到的字节存进DR
寄存器。I2C接口在接收到每个字节后都执行下列操作:
● 如果设置了ACK位,则产生一个应答脉冲
● 硬件设置RxNE=1。如果设置了ITEVFEN和ITBUFEN位,则产生一个中断。
如果RxNE被置位,并且在接收新的数据结束之前DR寄存器未被读出,BTF位被置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器将清除BTF位。
关闭从通信
在传输完最后一个数据字节后,主设备产生一个停止条件, I2
C接口检测到这一条件时:
● 设置STOPF=1,如果设置了ITEVFEN位,则产生一个中断。
硬件设计
24C02 的 SCL 和 SDA 分别连在 STM32F1 的 PB6 和 PB7 上的
24C02 (EEPROM)芯片
本实验板中的 EEPROM 芯片 (型号:AT24C02) 的 SCL 及 SDA 引脚连接到了 STM32 对应的 I2C引脚中,结合上拉电阻,构成了 I2C 通讯总线,它们通过 I2C 总线交互。EEPROM 芯片的设备地址一共有 7 位,其中高 4 位固定为:1010 b,低 3 位则由 A0/A1/A2 信号线的电平决定,见图EEPROM 设备地址 ,图中的 R/W 是读写方向位,与地址无关。
按照我们此处的连接,A0/A1/A2 均为 0,所以 EEPROM 的 7 位设备地址是:101 0000b,即 0x50。
由于 I2C 通讯时常常是地址跟读写方向连在一起构成一个 8 位数,且当 R/W 位为 0 时,表示写方向,所以加上 7 位地址,其值为“0xA0”,常称该值为 I2C 设备的“写地址”;当 R/W 位为 1时,表示读方向,加上 7 位地址,其值为“0xA0”,常称该值为“读地址”。
EEPROM 芯片中还有一个 WP 引脚,具有写保护功能,当该引脚电平为高时,禁止写入数据,当引脚为低电平时,可写入数据,我们直接接地,不使用写保护功能。
编程要点
(1) 配置通讯使用的目标引脚为开漏模式;
(2) 使能 I2C 外设的时钟;
(3) 配置 I2C 外设的模式、地址、速率等参数并使能 I2C 外设;
(4) 编写基本 I2C 按字节收发的函数;
(5) 编写读写 EEPROM 存储内容的函数;
(6) 编写测试程序,对读写数据进行校验。
代码详解
串口初始化
//部分代码使用宏定义,具体请看.h文件
void GPIOx_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_X,ENABLE); //开时钟
GPIO_InitTypeDef GPIO_InitStuct;
GPIO_InitStuct.GPIO_Mode = GPIO_Mode_X; //开漏输出
GPIO_InitStuct.GPIO_Pin = GPIO_Pin_SCL|GPIO_Pin_SDA;
GPIO_InitStuct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_X,&GPIO_InitStuct);
}
I2C初始化
//部分代码使用宏定义,具体请看.h文件
void Hardware_Config(void)
{
GPIOx_Config();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); //开IIC的时钟
I2C_DeInit(EEPROM_I2C_X); //将外设 I2Cx 寄存器重设为缺省值 即初始化
I2C_InitTypeDef I2C_InitStuct;
I2C_InitStuct.I2C_Mode = I2C_Mode_I2C; //设置 I2C 为 I2C 模式
I2C_InitStuct.I2C_DutyCycle = I2C_DutyCycle_2; //I2C 快速模式 Tlow / Thigh = 2
I2C_InitStuct.I2C_OwnAddress1 = I2Cx_OWN_ADDRESS7; //该参数用来设置第一个设备自身地址,它可以是一个 7 位地址或者一个 10 位地址。
I2C_InitStuct.I2C_Ack =I2C_Ack_Enable; //使能应答(ACK)
I2C_InitStuct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位应答地址
I2C_InitStuct.I2C_ClockSpeed = I2C_Speed; //该参数用来设置时钟频率,这个值不能高于 400KHz。
I2C_Init(EEPROM_I2C_X,