协议上的主要信号
IIC硬件上只有两根线:时钟线SCL与数据线SDA。协议上上有以下几个信号:start,stop,ack,nack。
-
start:由主机发出,当SCL位于高电平时,SDA发生一个下降沿信号(电平由高到低)
-
stop:由主机发出,当SCL处于高电平时,SDA发生一个上升沿信号(电平由低到高)
-
ack:由数据接收方发出。当主机发送完1 Byte数据后,释放SDA线,在第九个时钟时,从机将SDA拉低;或者当主机接收完1 Byte数据后,仍然想继续接收数据,则在第九个时钟时,将SDA拉低。
-
nack:由主机发出,当主机接收完1Byte数据后,若不想继续接收数据,则在第九个时钟时,主机将SDA拉高
发送与接收数据流程
- 因为IIC频率支持100—400Khz,即SCL周期为2.5—10us,所以SCL一个周期中高电平时间约为1.25—5us。
- IIC传输数据的过程如下:
- 发送一个start开始信号
- 发送1Byte数据,高7位为设备地址,最后一位是读写位,此时是0(表示写,因为下一步要写入寄存器地址)
- 等待从机发出ack应答信号
- 发送一个start开始信号
- 发送寄存器地址(寄存器地址可能是8位、10位或16位,这里以8位举例)
- 等待从机发出ack应答信号
- 当主机是写入1Byte数据操作时
(1). 发送1Byte数据
(2). 等待从机发出ack应答信号
(3). 发送stop信号- 当主机是读取1Byte数据操作时
(1). 发送一个start开始信号
(2). 发送1Byte数据,高7位为设备地址,最后一位是读写位,此时是1(表示读,因为下一步要读取设备寄存器的值)
(3). 等待从机发出ack应答信号
(4). 接收1Byte数据(寄存器的数值)
(5). 发送nack信号
(6). 发送stop信号
- 数据传输时,在SCL处于高电平时,SDA电平不能变化;只有SCL处于低电平时,SDA电平才可以变化。
附带模拟IIC代码(默认寄存器地址为8位,默认一次只传输1Byte数据)
#ifndef __ANALOG_IIC_H_
#define __ANALOG_IIC_H_
#define IIC_ACK_ERR (1)
#define IIC_ACK_OK (0)
#define IIC_WRITE (0)
#define IIC_READ (1)
#define IIC_TRAN_ERR (2)
#define IIC_TRAN_OK (3)
/*IIC结构体*/
typedef struct
{
unsigned char Drive_Address;
unsigned char Reg_Address;
unsigned char Byte;
unsigned char Dir;
void (*delay)(void);
void (*SCL_Out)(void);
void (*SCL_In)(void);
void (*SCL_High)(void);
void (*SCL_Low)(void);
void (*SDA_Out)(void);
void (*SDA_In)(void);
void (*SDA_High)(void);
void (*SDA_Low)(void);
unsigned char (*SDA_State)(void);
}IIC_t;
/*IIC传输一个字节*/
unsigned char IIC_Tranfer_Byte(IIC_t *iic,unsigned char dir);
#endif
#include "Analog_iic.h"
/*IIC初始化,按实际开发环境自行编写, 即映射IIC_t结构体中的操作函数*/
/*IIC开始信号*/
void IIC_Start(IIC_t *iic)
{
iic->SCL_Out();
iic->SDA_Out();
iic->SCL_Low();
iic->SDA_High();
iic->delay();
/*在SCL高电平时,拉低SDA*/
iic->SCL_High();
iic->delay();
iic->SDA_Low();
iic->delay();
iic->SCL_Low();
iic->delay();
iic->delay();
}
/*IIC停止信号*/
void IIC_Stop(IIC_t *iic)
{
iic->SCL_Out();
iic->SDA_Out();
iic->SCL_Low();
iic->SDA_Low();
iic->delay();
/*在SCL高电平时,拉高SDA*/
iic->SCL_High();
iic->delay();
iic->SDA_High();
iic->delay();
iic->SCL_Low();
iic->delay();
iic->delay();
}
/*IIC发送Nack信号*/
void IIC_Send_Nack(IIC_t *iic)
{
/*释放SDA*/
iic->SCL_Out();
iic->SDA_Out();
iic->SCL_Low();
iic->delay();
/*拉高SDA并保持*/
iic->SDA_High();
iic->SCL_High();
iic->delay();
iic->delay();
iic->SCL_Low();
iic->delay();
iic->delay();
}
/*IIC等待Ack信号*/
unsigned char IIC_Wait_Ack(IIC_t *iic)
{
unsigned short time = 0;
/*释放SDA*/
iic->SCL_Out();
iic->SDA_Out();
iic->SCL_Low();
iic->SDA_High();
iic->SDA_In();
iic->delay();
/*等待从机拉低SDA*/
iic->SCL_High();
iic->delay();
while(iic->SDA_State())
{
time ++;
if(time >= 0xffff)
{
IIC_Stop(iic);
return IIC_ACK_ERR;
}
}
iic->delay();
iic->SCL_Low();
iic->delay();
iic->delay();
return IIC_ACK_OK;
}
/*发送一个字节*/
void IIC_Send_Byte(IIC_t *iic,unsigned char Byte)
{
unsigned char i;
iic->SCL_Out();
iic->SDA_Out();
iic->SCL_Low();
iic->SDA_Low();
iic->delay();
for(i = 0;i < 8;i ++)
{
/*一位一位发送Byte*/
if( Byte & (0x80) )
iic->SDA_High();
else
iic->SDA_Low();
/*送出SCL时钟*/
iic->SCL_High();
iic->delay();
iic->delay();
iic->SCL_Low();
iic->delay();
iic->delay();
Byte <<= 1;
}
}
/*接收一个字节*/
unsigned char IIC_Recive_Byte(IIC_t *iic)
{
unsigned char byte = 0;
unsigned char i;
iic->SCL_Out();
iic->SDA_In();
iic->SCL_Low();
iic->delay();
for(i = 0;i < 8;i ++)
{
iic->SCL_High();
iic->delay();
byte <<= 1;
if(iic->SDA_State())
byte |= 0x01;
else
byte &= (0xFE);
iic->delay();
iic->SCL_Low();
iic->delay();
iic->delay();
}
return byte;
}
/*IIC传输一个字节*/
unsigned char IIC_Tranfer_Byte(IIC_t *iic,unsigned char dir)
{
unsigned char _Drive_Addre = 0;
iic->Dir = dir;
/*发送开始信号*/
IIC_Start(iic);
/*发送器件地址--写*/
_Drive_Addre = ( (iic->Drive_Address << 1) | (0x00) );
IIC_Send_Byte(iic,_Drive_Addre);
/*等待ACK信号*/
if(IIC_Wait_Ack(iic))
{
return IIC_ACK_ERR;
}
/*重新开始*/
IIC_Start(iic);
/*发送寄存器地址*/
IIC_Send_Byte(iic,iic->Reg_Address);
/*等待ACK信号*/
if(IIC_Wait_Ack(iic))
{
return IIC_ACK_ERR;
}
/*器件地址*/
_Drive_Addre = ( (iic->Drive_Address << 1) | (iic->Dir) );
/*如果是读,则需要再次写入器件地址*/
if(iic->Dir == IIC_READ)
{
/*发送开始信号*/
IIC_Start(iic);
/*发送器件地址*/
IIC_Send_Byte(iic,_Drive_Addre);
/*等待ACK信号*/
if(IIC_Wait_Ack(iic))
{
return IIC_ACK_ERR;
}
iic->Byte = IIC_Recive_Byte(iic);
IIC_Send_Nack(iic); //发送Nack
}
/*如果是写,则直接写入数据*/
else if(iic->Dir == IIC_WRITE)
{
/*写入数据*/
IIC_Send_Byte(iic,iic->Byte);
/*等待ACK信号*/
if(IIC_Wait_Ack(iic))
{
return IIC_ACK_ERR;
}
}
/*停止信号*/
IIC_Stop(iic);
return IIC_TRAN_OK;
}