标准AVR单片机模拟I2C总线的主机程序
扫描二维码
随时随地手机看文章
.H文件预处理
typedef unsignedcharINT8U;//0~255
typedef signedcharINT8S;//-128~127
typedef unsigned int INT16U;//0~65535
typedef signed int INT16S;//-32768~32767
typedef unsigned long INT32U;//0~0xFFFFFFFF
typedef signed long INT32S;//0x8000 0000~7FFFFFFF
typedef float FP32;//Single precision floating point
typedef double FP64; //Double precision floating point
#defineXTAL 1//晶振频率,单位MHz
#definem_delayus(x)__delay_cycles((unsigned long)(x*XTAL))
#definem_delayms(x)__delay_cycles((unsigned long)(x*XTAL*1000UL))
#definem_delays(x)__delay_cycles((unsigned long)(x*XTAL*1000000UL))
#defineBIT0 0x01
#defineBIT1 0x02
#defineBIT2 0x04
#defineBIT3 0x08
#defineBIT4 0x10
#defineBIT5 0x20
#defineBIT6 0x40
#defineBIT7 0x80
//假设SCL是端口PD6,SDA是端口PD7,WP是端口PD5
#definem_EnE2pWrite PORTD&=~BIT5 //允许EEPROM读写
#definem_DiE2pWrite PORTD|=BIT5 //EEPROM只读
#definem_I2CWritePort DDRD|=BIT7//I2C写时,SDA端口方向为输出
#definem_I2CReadPort DDRD&=~BIT7//I2C读时,SDA端口方向为输入
#definem_SetSCLPORTD|=BIT6 //PD6=1,SCL置位
#definem_ClrSCLPORTD&=~BIT6 //PD6=0,SCL清0
#definem_SetSDA PORTD|=BIT7 //PD7=1,SDA置位
#definem_ClrSDA PORTD&=~BIT7//PD7=0,SDA清0
#definem_SDAIn (PIND&BIT7) //SDA读入
#definem_I2C_Delaym_delayus(5)//I2C延时,不同器件可能不同
#definem_I2C_StopDelay m_delayms(10)//在下一次产生Start之前,总线空闲时间
void I2C_Start();//产生I2C总线的起始状态
void I2C_Write(INT8U dat);//向I2C总线写1个字节的数据
INT8U I2C_Read();//从从机读取1个字节的数据
INT8U I2C_GetAck();//读取从机应答位
void I2C_PutAck(INT8U ack);//主机产生应答位或非应答位
void I2C_Stop();//产生I2C总线的停止状态
INT8U I2C_Puts(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size);//I2C总线综合发送函数,向从机发送多个字节的数据
INT8U I2C_Gets(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size);//I2C总线综合接收函数,从从机接收多个字节的数据
INT8U I2C_DigitalPot(INT8U SlaveAddr,INT8U dat);//I2C总线数字电位器发送程序
.C文件
void I2C_Start()
{
m_I2CWritePort;//端口方向为输出
m_SetSDA;
m_I2C_Delay;
m_SetSCL;
m_I2C_Delay;
m_ClrSDA;
m_I2C_Delay;
m_ClrSCL;
m_I2C_Delay;
}
void I2C_Write(INT8Udat)
{
m_I2CWritePort;//端口方向为输出
for(INT8U t=0;t<8;t++)
{
if ((dat&0x80)!=0)
{
m_SetSDA; //SDA=1
}
else
{
m_ClrSDA; //SDA=0
}
m_I2C_Delay;
dat<<=1;
m_SetSCL; //SCK=1
m_I2C_Delay;
m_ClrSCL; //SCK=0
m_I2C_Delay;
}
}
INT8UI2C_Read()
{
INT8U dat;
m_I2CReadPort;//端口方向为输入
for(INT8U t=0;t<8;t++)
{
m_SetSCL;//SCK=1
dat<<=1;
if(m_SDAIn)
{
dat|=0x01;
}
m_ClrSCL;//SCK=0
m_I2C_Delay;
}
return dat;
}
INT8UI2C_GetAck()
{
INT8Uack=0;
m_I2CReadPort;//端口方向为输入
//总线准备,接受应答
m_SetSDA;
m_I2C_Delay;
m_SetSCL;
m_I2C_Delay;
if(m_SDAIn!=0)
ack =1;
m_ClrSCL;
m_I2C_Delay;
return ack;
}
/******************************************************************************
函数:I2C_PutAck()
功能:主机产生应答位或非应答位
参数:
ack=0:主机产生应答位;ack=1:主机产生非应答位
说明:
主机在接收完每一个字节的数据后,都应当产生应答位
主机在接收完最后一个字节的数据后,应当产生非应答位
******************************************************************************/
void I2C_PutAck(INT8Uack)
{
m_I2CWritePort;//端口方向为输出
if(ack==0)
{
m_ClrSDA;
}
else
{
m_SetSDA;
}
m_I2C_Delay;
m_SetSCL;
m_I2C_Delay;
m_ClrSCL;
m_I2C_Delay;
m_SetSDA;
}
/******************************************************************************
函数:I2C_Stop()
功能:产生I2C总线的停止状态
说明:
SCL处于高电平期间,当SDA出现上升沿时停止I2C总线
不论SDA和SCL处于什么电平状态,本函数总能正确产生停止状态
本函数执行后,I2C总线处于空闲状态
******************************************************************************/
void I2C_Stop()
{
m_I2CWritePort;//端口方向为输出
m_ClrSDA;
m_I2C_Delay;
m_SetSCL;
m_I2C_Delay;
m_SetSDA;
m_I2C_StopDelay;
}
/******************************************************************************
函数:I2C_Puts()
功能:I2C总线综合发送函数,向从机发送多个字节的数据
参数:
SlaveAddr:从机地址(7位纯地址,不含读写位,0xxx xxxx)
SubAddr:从机的子地址
SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址
*dat:要发送的数据
Size:数据的字节数
返回:
0:发送成功
1:在发送过程中出现异常
说明:
本函数能够很好地适应所有常见的I2C器件,不论其是否有子地址
当从机没有子地址时,参数SubAddr任意,而SubMod应当为0
******************************************************************************/
INT8UI2C_Puts(INT8USlaveAddr,INT16USubAddr,INT8USubMod,INT8U*dat,INT16USize)
{
m_EnE2pWrite; //允许EEPROM读写
INT8Ua[3];
if (Size==0) return1;//检查长度,在接收过程中出现异常
a[0]=(SlaveAddr<<1);//准备从机地址
if (SubMod>2) SubMod = 2;//检查子地址模式
//确定子地址
switch (SubMod)
{
case 0:
break;
case 1:
a[1]=(INT8U)(SubAddr);
break;
case 2:
a[1]=(INT8U)(SubAddr >> 8);
a[2]=(INT8U)(SubAddr);
break;
default:
break;
}
//主机发送从机地址(a[0]),接着发送子地址(如果有子地址的话)(a[1],a[2])
I2C_Start();
for (INT8Ut=0;t<=SubMod;t++)
{
I2C_Write(a[t]);
if (I2C_GetAck())
{
I2C_Stop();
m_DiE2pWrite;//EEPROM只读
return 1;
}
}
//主机发送数据
while(Size--)
{
I2C_Write(*dat++);
if (I2C_GetAck())
{
I2C_Stop();
m_DiE2pWrite;//EEPROM只读
return 1;
}
}
//发送完毕,停止I2C总线,并返回结果
I2C_Stop();
m_DiE2pWrite;//EEPROM只读
return 0;//发送成功
}
/******************************************************************************
函数:I2C_Gets()
功能:I2C总线综合接收函数,从从机接收多个字节的数据
参数:
SlaveAddr:从机地址(7位纯地址,不含读写位,0xxx xxxx)
SubAddr:从机的子地址
SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址
*dat:保存接收到的数据
Size:数据的字节数
返回:
0:接收成功
1:在接收过程中出现异常
说明:
本函数能够很好地适应所有常见的I2C器件,不论其是否有子地址
当从机没有子地址时,参数SubAddr任意,而SubMod应当为0
******************************************************************************/
INT8UI2C_Gets(INT8USlaveAddr,INT16USubAddr,INT8USubMod,INT8U*dat,INT16USize)
{
m_EnE2pWrite; //允许EEPROM读写
INT8Ua[3];
if(Size=