分类: 嵌入式
2012-01-05 14:57:40
/**
* 模拟I2C总线内核驱动程序
* 运行平台:CPU->9G45(ARM9) 系统:Linux
* Lzy 2012-12-25
*/
#include
#include
#include
#include
#include "IIC.h"
/******************************9G45管脚控制部分****************************************/
/**
* 功能:改变SDA引脚输入输出的状态
* 输入:state-> 0:输出 1:输入
*/
static void setsda_dir(int state)
{
if (state)
at91_set_gpio_input(I2C_SDA, 0);
else
at91_set_gpio_output(I2C_SDA, 0);
}
/**
* 功能:SDA引脚输出高低电平
* 输入:state-> 1:高电平 0:低电平
*/
static void setsda_val(int state)
{
at91_set_gpio_value(I2C_SDA, state);
}
/**
* 功能:获取sda引脚电平
*/
static int getsda(void)
{
return at91_get_gpio_value(I2C_SDA);
}
/**
* 功能:改变SCL引脚输入输出的状态
* 输入:state-> 0:输出 1:输入
*/
void setscl_dir(int state)
{
if (state)
at91_set_gpio_input(I2C_SCL, 0);
else
at91_set_gpio_output(I2C_SCL, 0);
}
/**
* 功能:SCL引脚输出高低电平
* 输入:state-> 1:高电平 0:低电平
*/
static void setscl_val(int state)
{
at91_set_gpio_value(I2C_SCL, state);
}
/**
* 功能:获取scl引脚电平
*/
static int getscl(void *data)
{
return at91_get_gpio_value(I2C_SCL);
}
/******************************IIC驱动程序部分**************************************/
/*
*函数:I2C_Init()
*功能:I2C 总线初始化,使总线处于空闲状态
*说明:在 开始处,通常应当要执行一次本函数
*/
void I2C_Init(void)
{
setscl_val(H); /* 输出为高电平 */
I2C_Delay();
setsda_dir(OUT);
setsda_val(H);
I2C_Delay();
}
/**
* 函数:I2C_Start()
* 功能:产生I2C 总线的起始状态
* 说明:
* SCL处于高电平期间,当SDA 出现下降沿时启动I2C 总线
* 不论 SDA 和SCL 处于什么电平状态,本函数总能正确产生起始状态
* 本函数也可以用来产生重复起始状态
* 本函数执行后,I2C 总线处于忙状态
*/
void I2C_Start(void)
{
I2C_Init(); /* 初始化I2C引脚 */
setsda_val(L); /* 产生开始信号 */
I2C_Delay();
setscl_val(L);
I2C_Delay();
}
/**
* 函数:I2C_Write()
* 功能:向I2C 总线写1 个字节的数据
* 参数:
* dat:要写到总线上的数据
*/
void I2C_Write(char dat)
{
unsigned char t = 8;
setsda_dir(OUT); /* SDA设置为输出模式 */
do
{
if(dat & 0x80)
setsda_val(H);
else
setsda_val(L);
dat <<= 1;
udelay(1);
setscl_val(H);
I2C_Delay();
setscl_val(L);
I2C_Delay();
} while ( --t != 0 );
}
/**
* 函数:I2C_Read()
* 功能:从从机读取1 个字节的数据
* 返回:读取的一个字节数据
*/
char I2C_Read(void)
{
char dat;
unsigned char t = 8;
setsda_dir(IN); /* SDA设置为输入模式 */
udelay(1);
do
{
setscl_val(H);
I2C_Delay();
setscl_val(L);
I2C_Delay();
dat <<= 1;
if ( getsda() )
dat |= 0x01;
udelay(1);
} while ( --t != 0 );
return dat;
}
/**
* 函数:I2C_GetAck()
* 功能:读取从机应答位
* 返回:
* 0:从机应答
* 1:从机非应答
* 说明:
* 从机在收到每个字节的数据后,要产生应答位
* 从机在收到最后 1 个字节的数据后,一般要产生非应答位
*/
char I2C_GetAck(void)
{
char ack = 1;
I2C_Init();
setsda_dir(IN); /* SDA设置为输入模式 */
udelay(2);
ack = getsda(); /* 获得应答信号 */
setscl_val(L);
I2C_Delay();
return ack;
}
/**
* 函数:I2C_PutAck()
* 功能:主机产生应答位或非应答位
* 参数:
* ack=0:主机产生应答位
* ack=1:主机产生非应答位
* 说明:
* 主机在接收完每一个字节的数据后,都应当产生应答位
* 主机在接收完最后一个字节的数据后,应当产生非应答位
*/
void I2C_PutAck(char ack)
{
setsda_dir(OUT); /* SDA设置为输出模式 */
udelay(1);
if(ack)
setsda_val(H);
else
setsda_val(L);
I2C_Delay();
setscl_val(H);
I2C_Delay();
setscl_val(L);
I2C_Delay();
}
/**
* 函数:I2C_Stop()
* 功能:产生I2C 总线的停止状态
* 说明:
* SCL处于高电平期间,当SDA 出现上升沿时停止I2C 总线
* 不论 SDA 和SCL 处于什么电平状态,本函数总能正确产生停止状态
* 本函数执行后,I2C 总线处于空闲状态
*/
void I2C_Stop(void)
{
setsda_dir(OUT); /* SDA设置为输出模式 */
setsda_val(L);
I2C_Delay();
setscl_val(H);
I2C_Delay();
setsda_val(H);
I2C_Delay();
udelay(I2C_STOP_WAIT_VALUE); //在下一次产生Start 之前,要加一定的延时
}
/**
* 函数:I2C_Puts()
* 功能:I2C 总线综合发送函数,向从机发送多个字节的数据
* 参数:
* SlaveAddr:从机地址(7 位纯地址,不含读写位)
* SubAddr:从机的子地址
* SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址
* *dat:要发送的数据
* Size:数据的字节数
* 返回:
* 0:发送成功
* 1:在发送过程中出现异常
* 说明:
* 本函数能够很好地适应所有常见的 I2C 器件,不论其是否有子地址
* 当从机没有子地址时,参数 SubAddr 任意,而SubMod 应当为0
*/
int I2C_Puts(unsigned char SlaveAddr, unsigned int SubAddr,
unsigned char SubMod, char *dat, unsigned int Size)
{
//定义临时变量
unsigned char i;
char a[3];
//检查长度
if ( Size == 0 ) return 0;
//准备从机地址
a[0] = (SlaveAddr << 1);
//检查子地址模式
if ( SubMod > 2 ) SubMod = 2;
//确定子地址
switch ( SubMod )
{
case 0:
break;
case 1:
a[1] = (char)(SubAddr);
break;
case 2:
a[1] = (char)(SubAddr >> 8);
a[2] = (char)(SubAddr);
break;
default:
break;
}
//发送从机地址,接着发送子地址(如果有子地址的话)
SubMod++;
I2C_Start();
for ( i=0; i<SubMod; i++ )
{
I2C_Write(a[i]);
if ( I2C_GetAck() )
{
I2C_Stop();
return 1;
}
}
//发送数据
do
{
I2C_Write(*dat++);
if ( I2C_GetAck() )
break;
} while ( --Size != 0 );
//发送完毕,停止I2C 总线,并返回结果
I2C_Stop();
if ( Size == 0 )
{
return 0;
}
else
{
return 1;
}
}
/**
* 函数:I2C_Gets()
* 功能:I2C 总线综合接收函数,从从机接收多个字节的数据
* 参数:
* SlaveAddr:从机地址(7 位纯地址,不含读写位)
* SubAddr:从机的子地址
* SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址
* *dat:保存接收到的数据
* Size:数据的字节数
* 返回:
* 0:接收成功
* 1:在接收过程中出现异常
* 说明:
* 本函数能够很好地适应所有常见的 I2C 器件,不论其是否有子地址
* 当从机没有子地址时,参数 SubAddr 任意,而SubMod 应当为0
*/
int I2C_Gets(unsigned char SlaveAddr, unsigned int SubAddr,
unsigned char SubMod, char *dat, unsigned int Size)
{
//定义临时变量
unsigned char i;
char a[3];
//检查长度
if ( Size == 0 ) return 0;
//准备从机地址
a[0] = (SlaveAddr << 1);
//检查子地址模式
if ( SubMod > 2 ) SubMod = 2;
//如果是有子地址的从机,则要先发送从机地址和子地址
if ( SubMod != 0 )
{
//确定子地址
if ( SubMod == 1 )
{
a[1] = (char)(SubAddr);
}
else
{
a[1] = (char)(SubAddr >> 8);
a[2] = (char)(SubAddr);
}
//发送从机地址,接着发送子地址
SubMod++;
I2C_Start();
for ( i=0; i<SubMod; i++ )
{
I2C_Write(a[i]);
if ( I2C_GetAck() )
{
I2C_Stop();
return 1;
}
}
}
//这里的I2C_Start()对于有子地址的从机是重复起始状态
//对于无子地址的从机则是正常的起始状态
I2C_Start();
I2C_Write(a[0]+1); //发送从机地址 读
if ( I2C_GetAck() )
{
I2C_Stop();
return 1;
}
//接收数据
for (;;)
{
*dat++ = I2C_Read();
if ( --Size == 0 )
{
I2C_PutAck(1);
break;
}
I2C_PutAck(0);
}
//接收完毕,停止I2C 总线,并返回结果
I2C_Stop();
return 0;
}
源码: IIC.rar