一. IIC的基本介绍
1.1IIC的简介
IIC是双线(不算地线)半双工的一种通讯方式(可以双向通讯,但不可以在同一时间双向数据传输).
IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,其时钟信号是由主控器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。
图1 IIC拓扑网络图2 IIC时序图
1.2地址问题
IIC设备地址
协议格式中第一个字节(为slave address)由7位地址和一位R/W读写位组成的,这字节是个器件地址。
常用IIC接口通用器件的器件地址是由种类型号,及寻址码组成的,共7位。
如格式如下:
D7 D6 D5 D4 D3 D2 D1 D0
(1)、器件类型:D7-D4共4位决定的。这是由半导公司生产时就已固定此类型的了,也就是说这4位已是固定的。
(2)、用户自定义地址码:D3-D1共3位。这是由用户自己设置的,通常的作法如EEPROM这些器件是由外部IC的3个引脚所组合电平决定的(用常用的名字如A0,A1,A2)。这也就是寻址码。所以为什么同一IIC总线上同一型号的IC只能最多共挂8片同种类芯片的原因了。
(3)、最低一位就是R/W位,,“0”表示写,“1”表示读(通常读写信号中写上面有一横线,表示低电平)。所以IIC设备通常有两个地址,即读地址和写地址。
IIC 设备的7 位地址是就当前IIC总线而言的,是“相对地址”。不同的IIC总线上的设备可以使用相同的7 位地址,但是它们所在的i2c 总线不同。所以在系统中一个IIC设备的“绝对地址”由二元组(IIC适配器的ID 和设备在该总线上的7 位地址)表示。”,所以这个函数的作用主要是排除同一IIC总线上出现多个地址相同的设备。
1.3 IIC协议
IIC总线在传输数据的过程中一共有三种类型信号,分别为:开始信号、结束信号和应答信号。这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。同时我们还要介绍其空闲状态、数据的有效性、数据传输。
图2 IIC时序图
(1)空闲状态
当IIC总线的数据线SDA和时钟线SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
(2)起始信号与停止信号
起始信号:当时钟线SCL为高期间,数据线SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号;
停止信号:当时钟线SCL为高期间,数据线SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
(3) 应答信号
发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。
应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
对于反馈有效应答位ACK的要求是:接收器在第9个时钟脉冲之前的低电平期间将数据线SDA拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放数据线SDA,以便主控接收器发送一个停止信号P。
(4)数据有效性
IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定;只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。即:数据在时钟线SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。
(5)数据的传达
在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。
二.代码
关于IIC代码部分的内容这里主要讲解HAL块部分的内容。这里HAL初始化的三个部分包括:引脚配置初始化,写数据函数,读数据函数。
在引脚配置的 IICInit 中对IIC的两个 引脚(SCL,SDA)进行引脚的配置,并对IIC相关的库函数进行配置,其中引脚的配置这里不再进行讲解,需要注意的是GPIO的模式要配置成GPIO_MODE_AF_OD 复用开漏输出或者GPIO_Mode_Out_PP数据寄存器输出。
关于IIC的配置,IIC的模式,自身地址,
typedef struct
{
uint32_t I2C_ClockSpeed; /*设置SCL时钟频,此值不低于40000*/
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_ClockSpeed 设置I2C的传输速率,在调用初始化函数时,函数会根据我们输入的数值经过运算后把时钟因子写入到I2C的时钟控制寄存器CCR。而我们写入的这个参数值不得高于400KHz。实际上由于CCR寄存器不能写入小数类型的时钟因子,影响到 SCL的实际频率可能会低于本成员设置的参数值,这时除了通讯稍慢一点以外,不会对I2C的标准通讯造成其它影响。
• I2C_Mode 选择I2C的使用方式,有I2C模式(I2C_Mode_I2C )和SMBus主、从模式(I2C_Mode_SMBusHost、 I2C_Mode_SMBusDevice ) 。 I2C不需要在此处区分主从模式,直接设置I2C_Mode_I2C即可。
• I2C_DutyCycle 设置I 2C的SCL线时钟的占空比。该配置有两个选择,分别为低电平时间比高电平时间为2:1 ( I2C_DutyCycle_2)和16:9 (I2C_DutyCycle_16_9)。其实这两个模式的比例差别并不大,一般要求都不会如此严格,这里随便选就可以了。
• I2C_OwnAddress1 配置STM32的I2C设备自己的地址,每个连接到I2C总线上的设备都要有一个自己的地址,作为主机也不例外。地址可设置为7位或10位(受下面 I2C_AcknowledgeAddress成员决定),只要该地址是I2C总线上唯一的即可。 STM32的I2C外设可同时使用两个地址,即同时对两个地址作出响应,这个结构成员I2C_OwnAddress1配置的是默认的、OAR1寄存器存储的地址,若需要设置第二个地址寄存器OAR2,可使用 I2C_OwnAddress2Config函数来配置,OAR2不支持10位地址。
• I2C_Ack_Enable 配置I 2C应答是否使能,设置为使能则可以发送响应信号。一般配置为允许应答(I2C_Ack_Enable),这是绝大多数遵循I 2C标准的设备的通讯要求,改为禁止应答(I2C_Ack_Disable)往往会导致通讯错误。
• I2C_AcknowledgeAddress 选择I2C的寻址模式是7位还是10位地址。这需要根据实际连接到I2C总线上设备的地址进行选择,这个成员的配置也影响到 I2C_OwnAddress1成员,只有这里设置成10位模式时, I2C_OwnAddress1才支持10位地址。
利用库函数版本编写HAL代码部分如下:
void IICInit(void)
{
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
/* 配置硬件IIC需要的变量 */
I2C_InitTypeDef I2C_InitStructure;
/* 使能与 I2C1 有关的时钟 */
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE );
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
/* PB6-I2C1_SCL、PB7-I2C1_SDA*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*IIC外设初始化*/
/* I2C 配置 */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C ; //配置为普通IIC模式
//I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
//I2C_InitStructure.I2C_OwnAddress1 = SlaveAddress;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //使能自动应答
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 50000; //5K的速度?
/* I2C1 初始化 */
I2C_Init(I2C1, &I2C_InitStructure);
/* 使能 I2C1 */
I2C_Cmd (I2C1,ENABLE);
/*允许应答模式*/
I2C_AcknowledgeConfig(I2C1, ENABLE);
}
void ByteWrite(u8 addr,u8 dataValue)
{
I2C_GenerateSTART(I2C1,ENABLE);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1,HMC_ADDR,I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1,addr);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2C1,dataValue);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C1,ENABLE);
}
u8 ByteRead(u8 addr)
{
u8 dataValue;
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
I2C_GenerateSTART(I2C1,ENABLE);//起始信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1,HMC_ADDR,I2C_Direction_Transmitter);//发送设备地址+写信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//
I2C_Cmd(I2C1,ENABLE);
I2C_SendData(I2C1,addr);//发送存储单元地址,从0开始
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTART(I2C1,ENABLE);//起始信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1,HMC_ADDR,I2C_Direction_Receiver);//发送设备地址+读信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
I2C_AcknowledgeConfig(I2C1,DISABLE);
I2C_GenerateSTOP(I2C1,ENABLE);
while(!(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)));
dataValue=I2C_ReceiveData(I2C1);//读出寄存器数据
return dataValue;
}
审核编辑:汤梓红
-
IIC
+关注
关注
11文章
300浏览量
38314 -
串行总线
+关注
关注
1文章
182浏览量
30616 -
代码
+关注
关注
30文章
4780浏览量
68539
发布评论请先 登录
相关推荐
评论