.H文件预处理
typedef unsigned char INT8U; //0~255
typedef signed char INT8S; //-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
#define XTAL 1 //晶振频率,单位MHz
#define m_delayus(x) __delay_cycles((unsigned long)(x*XTAL))
#define m_delayms(x) __delay_cycles((unsigned long)(x*XTAL*1000UL))
#define m_delays(x) __delay_cycles((unsigned long)(x*XTAL*1000000UL))
#define BIT0 0x01
#define BIT1 0x02
#define BIT2 0x04
#define BIT3 0x08
#define BIT4 0x10
#define BIT5 0x20
#define BIT6 0x40
#define BIT7 0x80
//假设SCL是端口PD6,SDA是端口PD7,WP是端口PD5
#define m_EnE2pWrite PORTD&=~BIT5 //允许EEPROM读写
#define m_DiE2pWrite PORTD|=BIT5 //EEPROM只读
#define m_I2CWritePort DDRD|=BIT7 //I2C写时,SDA端口方向为输出
#define m_I2CReadPort DDRD&=~BIT7 //I2C读时,SDA端口方向为输入
#define m_SetSCL PORTD|=BIT6 //PD6=1,SCL置位
#define m_ClrSCL PORTD&=~BIT6 //PD6=0,SCL清0
#define m_SetSDA PORTD|=BIT7 //PD7=1,SDA置位
#define m_ClrSDA PORTD&=~BIT7 //PD7=0,SDA清0
#define m_SDAIn (PIND&BIT7) //SDA读入
#define m_I2C_Delay m_delayus(5) //I2C延时,不同器件可能不同
#define m_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(INT8U dat)
{
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;
}
}
INT8U I2C_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;
}
INT8U I2C_GetAck()
{
INT8U ack=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(INT8U ack)
{
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
******************************************************************************/
INT8U I2C_Puts(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size)
{
m_EnE2pWrite; //允许EEPROM读写
INT8U a[3];
if (Size==0) return 1; //检查长度,在接收过程中出现异常
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 (INT8U t=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
******************************************************************************/
INT8U I2C_Gets(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size)
{
m_EnE2pWrite; //允许EEPROM读写
INT8U a[3];
if(Size==0) return 1;//检查长度,在接收过程中出现异常
a[0]=(SlaveAddr<<1);//准备从机地址
if(SubMod>2) SubMod=2;//检查子地址模式
//如果是有子地址的从机,则主机要先发送从机地址和子地址
if(SubMod!=0)
{
//确定子地址
if(SubMod==1)
{
a[1]=(INT8U)(SubAddr);
}
else
{
a[1]=(INT8U)(SubAddr>>8);
a[2]=(INT8U)(SubAddr);
}
//发送从机地址写,接着发送子地址
I2C_Start();
for(INT8U t=0;t<=SubMod;t++)
{
I2C_Write(a[t]);
if(I2C_GetAck())
{
I2C_Stop();
m_DiE2pWrite; //EEPROM只读
return 1;
}
}
}
//这里的I2C_Start()对于有子地址的从机是重复起始状态
//对于无子地址的从机则是正常的起始状态
I2C_Start();
//发送从机地址读
I2C_Write(a[0]+1);
if(I2C_GetAck())
{
I2C_Stop();
m_DiE2pWrite; //EEPROM只读
return 1;
}
//主机接收数据
while(1)
{
*dat++ = I2C_Read();
if(--Size==0)
{
I2C_PutAck(1);
break;
}
I2C_PutAck(0);
}
//接收完毕,停止I2C总线,并返回结果
I2C_Stop();
m_DiE2pWrite; //EEPROM只读
return 0;
}
/******************************************************************************
函数:I2C_DigitalPot()
功能:I2C总线数字电位器发送程序
参数:
SlaveAddr:从机地址(7位纯地址,不含读写位,0xxx xxxx)
dat:要发送的数据
返回:
0:发送成功
1:在发送过程中出现异常
说明:
本函数能够很好地适应常见的数字电位器,如AD5245,AD5241
******************************************************************************/
INT8U I2C_DigitalPot(INT8U SlaveAddr,INT8U dat)
{
I2C_Start();
//主机发送从机地址
I2C_Write(SlaveAddr<<1);
if(I2C_GetAck())
{
I2C_Stop();
return 1;
}
//主机发送控制字
I2C_Write(0x00);
if(I2C_GetAck())
{
I2C_Stop();
return 1;
}
//主机发送数据
I2C_Write(dat);
if(I2C_GetAck())
{
I2C_Stop();
return 1;
}
//接收完毕,停止I2C总线,并返回结果
I2C_Stop();
return 0;
}
使用范例:
1. 写EEPROM,从机地址1010 000?,子地址0x00(单字节),发送数据a (2字节)
I2C_Puts (0x50,0, 1, &a, 2);
2. 读EEPROM,从机地址1010 000?,子地址0x00(单字节),接受数据b (2字节)
I2C_Gets (0x50,0, 1, &b, 2);
3. 写数字电位器,地址0101 100?,写入数据0xFF
I2C_DigitalPot ( 0x2C, 0xFF);
I2C读写EEPROM流程图
上一篇:IAR库文件生成及使用方法
下一篇:6. avr定时器/计数器1 --TC1 --输入捕捉模式 (捕获外部事件模式)
推荐阅读最新更新时间:2024-03-16 15:47