1、概述
I2C总线是PHLIPS公司推出的一种串行总线,是具备多主机系统所需的包括总线裁决和高低速器件同步功能的高性能串行总线。此通信方式为半双工。 I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。 I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。
2、IIC总线寻址方式
主机在发送起始信号后必须发送一个字节的数据,该数据的高7位为从机地址,最低位表示后续字节的传送方向,‘0’表示主机发送数据,‘1’表示主机接收数据;总线上所有的从机接收到该字节数据后都将这7位地址与自己的地址进行比较,如果相同,则认为自己被主机寻址,然后再根据第8位将自己定位发送器或接收器。
3、起始信号
当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。 起始信号由主机发起,当主机发起起始信号后,总线就处于占领状态;当主机发出停止信号时,总线恢复空闲状态。
以下为起始信号的代码段:
void IIC_Start( void)
{
IIC_SCL_H(); //拉高SCL
BSP_IIC_Delay( );
IIC_SDA_H( ); //SDA拉高
BSP_IIC_Delay( );
BSP_IIC_Delay( );
IIC_SDA_L( ); //SDA拉低
BSP_IIC_Delay( );
IIC_SCL_L( ); //SCL复位
}
4、停止信号
当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。 以下为停止信号的代码段:
void IIC_Stop(void)
{
IIC_SCL_H( );
BSP_IIC_Delay( );
IIC_SDA_L( );
BSP_IIC_Delay( );
BSP_IIC_Delay( );
IIC_SDA_H();
BSP_IIC_Delay( );
IIC_SCL_L( ); //SCL复位
}
5、有效数据
当主机发起起始信号后,需要向从机进行寻址,发送相应的从机地址给从机进行响应。 SCL为时钟线,SDA为数据线。IIC总线进行数据传送时,SCL信号为高电平时,SDA电平必须保持稳定,即此时作为接收器接收数据。当SCL为低电平时,SDA的高低电平状态才允许变化,此时即为发送器发送数据。 SCL作为连接在收发双方上时钟线就解决了不同步的问题。
IIC总线通信时每个字节为8位长度,数据传送时,先传送最高位,后传送低位,当8位发送完后,从机将SDA拉低,表示应答。如还是高电平,表示非应答,即可能没有接收到。所以发送一个完整的字节,SCL会产生9个时钟信号。 下面为发送0xA0的时序图:
以下为发送字节的代码段:
uint8_t IIC_Send( uint8_t uSendByte )
{
uint8_t uIndex = 0;
uint8_t uAskState = BSP_OK;
uint16_t count = 0;
for( uIndex = 0; uIndex < 8; uIndex ++ )
{
if( uSendByte & 0x80 )
{
IIC_SDA_H();
}
else
{
IIC_SDA_L();
}
BSP_IIC_Delay( ); //延时
IIC_SCL_H( ); // SCL高电平 SDA要求稳定 接收器接收数据 先将SDA的电平拉高,再拉高SCL的
BSP_IIC_Delay( ); //延时
BSP_IIC_Delay( ); //延时
IIC_SCL_L( ); // SCL低电平 SDA允许变化 发送器发送数据
uSendByte <<= 1;
}
IIC_SDA_H( ); //拉高SDA和SCL 释放总线 等待从机将SDA拉低进行应答
IIC_SCL_H( );
BSP_IIC_Delay( );
if( 0 == IIC_SDA_READ( ) )
{
uAskState = BSP_OK;
}
else
{
uAskState = BSP_ERR;
}
BSP_IIC_Delay( );
IIC_SCL_L();
BSP_IIC_Delay( );
return uAskState;
}
6、应答信号和非应答信号
当主机作为接收器接收从机返回的数据时,接收完一个字节主机需要发起一个应答信号告诉从机,该字节接收完成。当主机接收完所有的数据后需要发起一个非应答信号给从机,方便从机准备接收停止信号,停止此次的通信。 以下为应答信号和非应答信号的时序图,SCL信号一致,都是维持在高电平,应答信号SDA为低电平,非应答信号SDA为高电平。 以下为相关的代码段
E_Bool IIC_AskOrNo( uint8_t ask_or_no )
{
/* 每发送完一个数据字节后,发送器释放SDA,接收器拉高SDA(接收时BMS为接收器),
在SCL处于高电平期间保持稳定的高电平接收器产生一个有效的否应答信号 */
//应答和非应答的时候SCL时序一样,只是SDA维持的电平不一样,低电平是应答信号,高电平是非应答信号
IIC_SCL_L( );
BSP_IIC_Delay( );
//if( ask_or_no) //应答
( ask_or_no==1) ?(IIC_SDA_L( )):(IIC_SDA_H( ));
BSP_IIC_Delay();
IIC_SCL_H( );
BSP_IIC_Delay( );
IIC_SCL_L();
BSP_IIC_Delay();
return True;
}
7、接收数据
当主机向从机发送读地址后,从机开始向主机回复相关数据 。相关代码段如下:
uint8_t IIC_Recv_Byte( )
{
/* 接收数据,高位在前 */
uint16_t count = 0;
uint8_t uBitIndex = 0;
uint8_t uReceiveByte = 0;
IIC_SDA_H( );
IIC_SCL_L();
BSP_IIC_Delay();
for( uBitIndex = 0; uBitIndex < 8; uBitIndex ++)
{
uReceiveByte <<= 1;
IIC_SCL_H();
/*count = 0; 等待SCL电平变化 可以屏蔽
do{
__nop();
if( count ++ > WAIT_CLK_TIMES )
{
IIC_Stop( uIIC_ID );
break;
}
}while( 0 == IIC_SCL_READ( uIIC_ID ) );*/
BSP_IIC_Delay();
if( IIC_SDA_READ() )
{
uReceiveByte |= 0X01;
}
BSP_IIC_Delay( );
IIC_SCL_L();
BSP_IIC_Delay();
}
return uReceiveByte;
}
8、IIC通信流程
8.1读数据
1、主机发送一个起始信号 2、主机发送从机写地址 3、主机发送要写寄存器的地址 4、主机发送从机读地址 5、主机开始收取数据,每收取完一个字节,主机发送一个应答信号给从机。当全部的字节都收取完了以后,主机发送一个非应答信号给从机。 6、主机发送一个停止信号给从机 相关代码段如下: 从机写地址为:0xA0 读地址为:0xA1
uint8_t ReadReg( uint8_t uRegAdr, uint8_t uRegNum, uint8_t *pData )
{
uint8_t uIndex = 0;
uint8_t uState = BSP_ERR;
E_Bool eAsk = False;
uState = IIC_Start();
if( BSP_OK==uState )
{
uState = IIC_Send( 0xA0 );
if(BSP_OK==uState)
{
///写寄存器地址
uState = IIC_Send( uRegAdr );
if(BSP_OK==uState)
{
uState = IIC_Send( 0xA1 );
if(BSP_OK==uState)
{
for( uIndex = 0 ; uIndex < uRegNum; uIndex ++ )
{
pData[uIndex] = Recv_Byte(); //读寄存器数据
if(uIndex < (uRegNum - 1) )
{
eAsk =IIC_AskOrNo(1);
if(False==eAsk)
{
uState = BSP_ERR;
break;
}
}
}
if(uIndex==uRegNum)
{
IIC_AskOrNo(0); //收完最后一个字节发送一个非应答位
}
}
}
}
}
IIC_Stop();
return uState;
}
8.2写数据
写数据相对于读数据来说较为简单。前三个步骤和读数据一样。 1、主机发送一个起始信号 2、主机发送从机写地址 3、主机发送要写寄存器的地址 4、主机发送需要写的数据,发送完毕后主机发送一个停止信号给从机。 相关代码段如下:
uint8_t WriteReg( uint8_t uRegAdr, uint8_t uRegNum, uint8_t* pData )
{
uint8_t uIndex = 0;
uint8_t uState = BSP_ERR;
uState=IIC_Start();
if(BSP_OK==uState)
{
uState = IIC_Send( 0xA0 );
if(BSP_OK==uState)
{
uState = IIC_Send( uRegAdr );
if(BSP_OK==uState)
{
for(uIndex=0; uIndex<uRegNum; uIndex++)
{
uState = IIC_Send( pData[uIndex] );
if(uState != BSP_OK)
{
break;
}
}
}
}
}
IIC_Stop();
return uState;
}
|