1、IIC的速度:标准模式100Kbit/s、快速模式下400Kbit/s、高速模式下3.4Mbit/s
2、理论上IIC地址是8位,其中1位广播地址,7位地址,2^7=128,理论上IIC可以挂载128个器件。但IIC总线上可挂接的设备数量受总线的最大电容400pF限制,(管脚都是有输入电容、PCB也会有寄生电容)所有在实际设计中不超过8个器件。
1、 IIC的概念
IIC协议是一种用于电子设备之间进行通信和数据交互的串行通信协议。
IIC协议采用双线结构传输数据,一根数据线SDA,一根时钟线SCL。其中SDA线用于双向数据传输,SCL线则用于同步数据传输的时钟信号。通信始终由主设备控制,从设备被动接收和回应。
2、 IIC详解
-
主机有权发起和结束一次通信,从机只能被动呼叫
-
当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能来防止错误产生
-
每个连接到IIC总线上的器件都有一个唯一的地址(7bit),且每个器件都可以作为主机也可以作为从机(但同一时刻只能一个主机),总线上的器件增加和删除不影响其它器件正常工作
-
IIC总线在通信时总线上发出数据的器件为发送器,接收数据的器件为接收器。
IIC总线可以通过外部连线进行检测,便于系统故障诊断和调试,故障可以立即被寻址,软件也有利于标准化和模块化,缩短开发时间。
理论上IIC地址是8位,其中1位广播地址,7位地址,2^7=128,理论上IIC可以挂载128个器件。但IIC总线上可挂接的设备数量受总线的最大电容400pF限制,(管脚都是有输入电容、PCB也会有寄生电容)所有在实际设计中不超过8个器件。
串行的8位双向数据传输速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3Mbit/s
3、 IIC协议
IIC时序包括起始信号、停止信号、应答信号和数据传输。
3.1 信号的时序
3.1.1 起始信号
起始信号就是在SCL时钟信号为高电平时,SDA数据信号出现下降沿就表示为起始信号。
3.1.2 停止信号
停止信号是SCL为高电平时,SDA出现上升沿表示停止信号
3.1.3 数据传输
SDA上数据的变化,只能在SCL为低电平的时候,SCL为高电平时候,SDA上的数据不变。
3.1.4 应答信号
应答信号是主机发送完8位数据后,等待从机应答,也就是等待从机告诉主机它接收到了数据,应答信号由从机发送,主机提供应答信号所需的时钟,主机只需要让SCL为高电平,检测SDA是高电平或是低电平以及持续时间,即刻知道应答信号是否有效。
3.2 IIC设备地址格式
3.2.1 写数据操作
-
主机发送起始信号
-
发送器件地址+读操作(0):8位
-
从机发送应答信号
-
主机发送写入寄存器地址
-
从机发送应答
-
主机发送写入寄存器数据
-
从机发送应答
-
主机发送停止
3.2.2 读数据操作
IIC读时许操作要比写时许操作要多一点步骤,读时许分为四个步骤,第一步发送设备地址,第二步发送要读取的寄存器地址,第三步从新发送设备地址,最后一步从器件输出要读取的寄存器值。
- 主机发送起始信号
- 主机发送器件地址+写操作(0):要先告诉设备我是要操作这个寄存器,要先写入寄存器地址
- 从机发送应答信号
- 主机从新发送起始信号
- 主机发送要读取的寄存器地址+读操作(1):已经告诉过设备我需要操作的寄存器,这里就可以来读取寄存器中数据
- 从机发送应答信号
- 从IIC器件里面读取到数据
- 主机发出NACK信号,表示读取完毕,不需要从机再发送ACK信号
- 主机发送停止信号
3.2.3 单个和多个写/读数据
写入单个字节:
开始信号+设备地址(7bit)写(1bit)+等待从机应答+寄存器地址(8bit)+等待从机应答+写入的数据(8bit)+等待从机应答+停止位
写入多个字节:
开始信号+设备地址(7bit)写(1bit)+等待从机应答+寄存器地址(8bit)+等待从机应答+写入数据1(8bit)+等待从机应答+写入数据2(8bit)+等待从机应答+……+停止位
读取一个字节:
开始信号+设备地址(7bit)写(1bit)+等待从机应答+寄存器地址(8bit)+等待从机应答开始信号+设备地址(7bit)读(1bit)+等待从机应答+读取寄存器数据(8bit)+主机应答+停止位
读取多个字节:
开始信号+设备地址(7bit)写(1bit)+等待从机应答+寄存器地址(8bit)+等待从机应答开始信号+设备地址(7bit)读(1bit)+等待从机应答+读取寄存器数据1(8bit)+主机应答+读取寄存器数据2(8bit)+主机应答+……+停止位
4、 STM32的软件IIC
根据IIC时许可知,IIC时许的模拟需要起始信号、停止信号、应答和非应答信号,以及读写字节操作。
4.1 起始信号
根据IIC时许可知,IIC时许的模拟需要起始信号、停止信号、应答和非应答信号,以及读写字节操作。
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
SDA配置为输出模式,SDA为高电平,SCL(推挽输出)为高电平,一个延时然后让SDA产生下降沿,表示起始信号。这里把SCL时钟线拉低的原因是SCL为低电平时,SDA上的数据变化才能被接收到,SCL为高电平,是不允许SDA上数据发生变化的,为后续的发送或接收数据做准备。
4.2 停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//发送I2C总线结束信号
delay_us(4);
}
SCL从为高电平,SDA产生一个上升沿表示结束信号。代码中SCL、SDA均为低电平,短暂延时后拉高SCL,再拉高SDA让SDA再SCL在高电平时候产生一个上升沿信号表示结束。
IIC信号在数据传输过程中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
SCL=1时 数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号。
4.3 应答、非应答信号及等待应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
应答信号,SCL从低电平变化为高电平时,持续4us时间SDA为低电平,然后SCL从高电平变化为低电平。
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
非应答信号,SCL从低电平变为高电平时,持续4us时间SDA为高电平,然后SCL从高电平变化为低电平。
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
等待从机给出应答,在主机向从机读取数据时,读取成功需要从机返回一个标志,这个标志就是需要取读取SDA数据线,在SCL=1时,SDA是否由设备端拉低为0。
4.4 发送一个字节
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
SCL为低电平的时候,将要发送的数据给到SDA数据线上(这个时候可以给),SCL为高电平时候,就会产生一个脉冲信号将SDA上面的数据发送给从机设备。SCL为高电平时,SDA上面的数据不能发生变化(即使变化也是无效的,会记录SDA产生上升沿前的数据)。
IIC发送数据是从最高位开始,然后一步一步的将8bit数据发送到SDA上。
4.5 读取一个字节
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
读取数据也是在SCL从低电平变化为高电平后,获取SDA上面发送的数据。获取完8bit数据后,主机发送应答信号给从机。
4.6 模拟IIC获取AT24C02
4.6.1 读取数据
发送起始信号,发送设备地址+写命令,等待从机应答,发送设备寄存器地址,等待应答,从新发送起始信号,发送设备寄存器地址,等待应答,读数据,发送停止信号。
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
u8 temp=0;
IIC_Start();
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr>>8);//发送高地址
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//产生一个停止条件
return temp;
}//在AT24CXX里面的指定地址开始读出长度为Len的数据
//该函数用于读出16bit或者32bit的数据.
//ReadAddr :开始读出的地址
//返回值 :数据
//Len :要读出数据的长度2,4
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
{
u8 t;
u32 temp=0;
for(t=0;t<Len;t++)
{
temp<<=8;
temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);
}
return temp;
}
指定开始地址,读多少个数据
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
while(NumToRead)
{
*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
4.6.2 写数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
IIC_Start();
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr>>8);//发送高地址
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
delay_ms(10);
}
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
{
u8 t;
for(t=0;t<Len;t++)
{
AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
}
}
指定地址写入多少个数据
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
while(NumToWrite--)
{
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
}
}
4.6.3 检测AT24C02是否正常
//检查AT24CXX是否正常
//这里用了24XX的最后一个地址(255)来存储标志字.
//如果用其他24C系列,这个地址要修改
//返回1:检测失败
//返回0:检测成功
u8 AT24CXX_Check(void)
{
u8 temp;
temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX
if(temp==0X55)return 0;
else//排除第一次初始化的情况
{
AT24CXX_WriteOneByte(255,0X55);
temp=AT24CXX_ReadOneByte(255);
if(temp==0X55)return 0;
}
return 1;
}
5、 STM32的硬件IIC
STM32控制器内置硬件IIC模块,支持配置IIC主机和从机模式。
硬件IIC发送数据,不会被中断所打断,因为硬件IIC发送数据是由硬件完成的,只受外部晶振时钟影响。
主模式时, I2C接口启动数据传输并产生时钟信号。串行数据传输总是以起始条件开始并以停止
条件结束。起始条件和停止条件都是在主模式下由软件控制产生。
从模式时, I2C接口能识别它自己的地址(7位或10位)和广播呼叫地址。软件能够控制开启或禁止广播呼叫地址的识别。
数据和地址按8位/字节进行传输,高位在前。跟在起始条件后的1或2个字节是地址(7位模式为1
个字节, 10位模式为2个字节)。地址只在主模式发送。
在一个字节传输的8个时钟后的第9个时钟期间,接收器必须回送一个应答位(ACK)给发送器。
硬件IIC:利用STM32芯片中的硬件IIC外设,和USART串口外设类似,只需要配置好对应的寄存器,外设就会产生标准串口协议的时许。
5.1 硬件IIC
5.1.1 时钟控制
时钟控制寄存器(I2C_CCR)用来控制在主模式下,输出时钟频率(标准模式、快速模式)。
Thigh:高电平的时间
Tlow: 低电平的时间
Tpclk1:1/36MHz
时钟周期SCL:高电平时间+低电平时间
5.1.2 数据控制
数据控制寄存器(I2C_DR)
-
当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过SDA信号线发送出去。(从数据寄存器中发)
-
当从外部接收数据的时候,数据移位寄存器把SDA信号线采样到的数据一位一位地存储到“数据寄存器”中。(从数据寄存器中拿)
CPU或DMA从DATA REGISTER(数据寄存器)中获取数据。这个里面可以是外设返回的数据,也可以是CPU向外设发送的数据。
如果IIC配置在从机模式,就需要设置自身的地址,在寄存器I2C_OAR1和I2C_OAR2中
5.1.3 逻辑控制
前面看了IIC时钟信号的配置、数据的配置,逻辑控制就是描述:IIC起始信号、停止信号、应答信号等配置。
控制寄存器(I2C_CR1)
在I2C_SR1寄存器中,只要看bit10、bit7、bit6、bit3、bit2、bit1这几个位的信息。
5.2 硬件IIC软件配置
5.2.1 IIC初始化
I2C_InitStuctrue.I2C_Ack=I2C_Ack_Enable; // 设置ACK应答
I2C_InitStuctrue.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;// 设置设备地址位数
I2C_InitStuctrue.I2C_ClockSpeed=400000; // 设置IIC通信时钟速度
I2C_InitStuctrue.I2C_DutyCycle=I2C_DutyCycle_2; // 设置时钟信号占空比时间
I2C_InitStuctrue.I2C_Mode=I2C_Mode_I2C; // 设置为IIC通信模式
I2C_InitStuctrue.I2C_OwnAddress1=STM32_I2C_OWN_ADDR; // 设置IIC控制器地址
I2C_Init(EEPROM_I2C,&I2C_InitStuctrue);
I2C_Cmd(EEPROM_I2C,ENABLE);
5.2.2 写入一个字节
// 发送起始信号
I2C_GenerateSTART(I2C1, ENABLE);
// 检测EVS事件 模式选择
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
// 发送设备写地址
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
// 检测EV事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
// 发送要操作设备内部的地址
I2C_SendData(EEPROM_I2C,addr);
// 检测字节发送
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR);
I2C_SendData(EEPROM_I2C,data);
// 检测EV事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED )==ERROR);
// 发送停止信号
I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
5.1.3 读取一个字节
// 判断IIC总线是否忙碌
while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY)) {;}
// 发送起始信号
I2C_GenerateSTART(I2C1, ENABLE);
// 等待事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
// 发送设备地址写数据
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
// 等待事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED )==ERROR);
// 发送要操作设备内部寄存器地址
I2C_SendData(EEPROM_I2C,addr);
// 检测事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR);
// 发送起始信号
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
// 检测事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT )==ERROR);
// 发送设备读地址操作
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Receiver);
// 检测
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);
// 读取数据
*data=I2C_ReceiveData(EEPROM_I2C);
// 发送非应答信号
I2C_AcknowledgeConfig(EEPROM_I2C,DISABLE);
// 发送停止信号
I2C_GenerateSTOP(EEPROM_I2C,ENABLE);