IIC 通信协议详解
IIC(Inter-Integrated Circuit BUS) 集成电路总线。
1,物理层:
设备之间连接方式
IIC的物理特性:
1)IIC通信在控制上是一主多从的模式。
2)IIC由sda/scl两条通信线构成。
scl是串行时钟线,用来控制通讯的时钟频率实现数据收发同步;sda是双向串行数据线,用来控制协议中数据的传输。
3) 总线上拉电阻接到电源。I2C 设备空闲时,输出高阻态,当所有设备都空闲,总线呈现高电平状态。
4)总线上每个设备都有自己的地址,设备的地址可以进行重置。根据地址不同,可以实现选择不同设备进行通信。
6)当多个主机同时使用总线时,利用仲裁方式决定由哪个设备占用。
5)根据IIC传输数据的速度,分为三种传输模式:
标准模式传输速率为 100kbit/s
快速模式为 400kbit/s
高速模式 3.4Mbit/s(但目前大多 I2C 设备尚不支持高速模式)
2,IIC的功能框图解析:
逻辑控制接收dma请求,应答信号以及中断的响应处理。对应相关状态控制寄存器,控制寄存器;
时钟控制寄存器控制scl时钟线的频率;
sda线实现数据 的接收和发送:
接收:数据通过数据移位寄存器转移到数据寄存器,如果接收的数据是地址,需要与设备本身存储的地址进行对比。
发送:数据从数据寄存器转移到数据移位寄存器,发送到sda总线上。
3,协议层(重点):
起始信号:scl高电平时,sda由高电平变低电平产生一个起始信号;
终止信号:scl高电平时,sda由高低平变高电平产生一个终止信号;
数据信号:scl为高电平的时候,sda低电平是数据0,sda为高电平的时候是数据1;
IIC通信的4种模式:
● 从发送器模式
● 从接收器模式
● 主发送器模式
● 主接收器模式
iic四种模式时序图分析(都以七位地址为例)
从发送模式时序图:
主start->主发送写地址->从ack->等待ev1/ev3-1/ev3->从发送数据1->主ack->等待ev3->………->从发送数据n->主nack->等待ev3-2->主stop
主start->主发送读地址->从ack->等待ev1->主发送数据1->从ack->等待ev2->………->主发送数据n->从ack->等待ev2->主stop->等待ev4
主start->等待ev5->主发送写地址->从ack->等待ev6/ev8-1->主发送数据1->从ack->等待ev8->………->主发送数据n->从ack->等待ev8-2->主stop
主start->等待ev5->主发送读地址->从ack->等待ev6->从发送数据1->主ack->等待ev7->…从发送数据n-1->主ack->等待ev7-1->从发送数据n->主nack->等待ev7->主stop
注意:始和终止信号都是由主机发出的,连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号。
4,IIC结构体和常用函数:
typedef struct
{
uint32_t I2C_ClockSpeed; /*!< Specifies the clock frequency.
This parameter must be set to a value lower than 400kHz */
uint16_t I2C_Mode; /*!< Specifies the I2C mode.
This parameter can be a value of @ref I2C_mode */
uint16_t I2C_DutyCycle; /*!< Specifies the I2C fast mode duty cycle.
This parameter can be a value of @ref I2C_duty_cycle_in_fast_mode */
uint16_t I2C_OwnAddress1; /*!< Specifies the first device own address.
This parameter can be a 7-bit or 10-bit address. */
uint16_t I2C_Ack; /*!< Enables or disables the acknowledgement.
This parameter can be a value of @ref I2C_acknowledgement */
uint16_t I2C_AcknowledgedAddress; /*!< Specifies if 7-bit or 10-bit address is acknowledged.
This parameter can be a value of @ref I2C_acknowledged_address */
}I2C_InitTypeDef;
5,IIC常用寄存器
位11:0 CCR[11:0]:快速/标准模式下的时钟控制分频系数(主模式)
该分频系数用于设置主模式下的SCL时钟。
Thigh = CCR ×TPCLK1
Tlow = CCR ×TPCLK1
在I2C快速模式下:
如果DUTY = 0:
Thigh = CCR ×TPCLK1
Tlow = 2 × CCR × TPCLK1
如果DUTY = 1: (速度达到400kHz)
Thigh = 9 × CCR ×TPCLK1
Tlow = 16 × CCR ×TPCLK1
不知道Thigh和Tlow分成这样有什么好处。
位14 DUTY:快速模式时的占空比
0:快速模式下:Tlow/Thigh = 2;
1: 快速模式下:Tlow/Thigh = 16/9(见CCR)。
位1 SMBUS:选择iic工作模式
位10 ACK:应答使能
位15 ADDMODE:寻址模式(从模式)
0:7位从地址(不响应10位地址);
1:10位从地址(不响应7位地址)。
位7:1 ADD[7:1]:接口地址 地址的7~1位。
6,IIC 通信历程(硬件配置的code 软件配置好烦,不想贴)
//硬件实现iic向外设写入一个字节
uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)
{
I2C_GenerateSTART(I2C1, ENABLE);
I2CTimeout = I2CT_FLAG_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
}
I2C_SendData(I2C1, WriteAddr);
I2CTimeout = I2CT_FLAG_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
}
I2C_SendData(I2C1, *pBuffer);
I2CTimeout = I2CT_FLAG_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
I2C_GenerateSTOP(I2C1, ENABLE); //发送停止位
return 1; //返回的结果表示状态
}
//硬件实现iic从外设读出多个字节
uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{
I2CTimeout = I2CT_LONG_TIMEOUT;
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
}
I2C_GenerateSTART(I2C1, ENABLE);
I2CTimeout = I2CT_FLAG_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);//这里有问题
}
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
I2CTimeout = I2CT_FLAG_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);
}
I2C_Cmd(I2C1, ENABLE);
I2C_SendData(I2C1, ReadAddr);
I2CTimeout = I2CT_FLAG_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
}
I2C_GenerateSTART(I2C1, ENABLE);
I2CTimeout = I2CT_FLAG_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
}
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);
I2CTimeout = I2CT_FLAG_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);
}
while(NumByteToRead)
{
if(NumByteToRead == 1)
{
I2C_AcknowledgeConfig(I2C1, DISABLE);//不应答
I2C_GenerateSTOP(I2C1, ENABLE);//停止位
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
{
*pBuffer = I2C_ReceiveData(I2C1);//存放到数组
pBuffer++; //位加
NumByteToRead--; //量减
}
}
I2C_AcknowledgeConfig(I2C1, ENABLE);
return 1;
}