周末放松,在家整理工作资料. 看到一段2000年前后编写,并且现在偶尔使用的代码,拿出来给大家分享. 这是一个软件模拟I2C总线代码,用于通过GPIO口驱动类似于AT24C02一类的器件。
该代码使用标准C语言编写,已经在Atmel,Renases,NEC,TI,TOSHIBA,ST,Microchip等多家公司的不同型号8位,16位,以及32位的MCU,DSP,Cortext-M3/M4上使用。
该代码驱动AT24C02,BQ32000,PCF8563等I2C总线的器件。
这是我参加工作后写的第一段设备驱动代码,后来换了多次工作,不同的平台,不同的产品,不同的应用,一直使用没有作过太多的修改。
=======================================================================================================
//以下是I2C.H内容
//根据不同的芯片平台,实现vWatchDog函数
extern void vWatchDog(void);
//I2C_SCK的GPIO定义
#define I2CSCLH P1.6=1
#define I2CSCLL P1.6=0
//I2C_SDA的GPIO定义
#define I2CSDA P1.7
#define I2CSDAH P1.7=1
#define I2CSDAL P1.7=0
//转换I2C_SDA的GPIO方向
#define I2CSDAOUT PM1.7=0
#define I2CSDAIN PM1.7=1
extern void vI2CInit(void);
extern void vI2CDelay(void);
extern void vI2CSendReStart(void);
extern void vI2CSendStart(void);
extern void vI2CSendStop(void);
extern void vI2CRdWtACK(void);
extern unsigned char ucI2CWaitACK(void);
extern void vI2CSendByte(unsigned char);
extern unsigned char ucI2CRecvByte(void);
========================================================================================================
//以下是I2C.C内容
//初始化I2C总线
void vI2CInit(void)
{
//这里进行GPIO口的设置,并初始化I2C总线
I2CSCLH;
I2CSDAH;
I2CSDAOUT;
return ;
}
//软件延时,这个延时函数并不精确,如果需要精确延时可以使用MCU的定时器来实现
//
void vI2CDelay(void)
{
register unsigned char ucI;
ucI=20;
while(ucI){
vWatchDog(); //清除看门狗,防止看门狗溢出导致MCU复位
--ucI;
}
return ;
}
//重新发送start信号
void vI2CSendReStart(void)
{
I2CSDAOUT;
I2CSDAH;
vI2CDelay();
I2CSCLH;
vI2CDelay();
vI2CDelay();
I2CSDAL;
return ;
}
//发送start信号
void vI2CSendStart(void)
{
I2CSDAOUT;
I2CSCLH;
vI2CDelay();
I2CSDAL;
return ;
}
//发送stop信号
void vI2CSendStop(void)
{
register unsigned int uI;
I2CSDAOUT;
I2CSDAL;
I2CSCLH;
vI2CDelay();
I2CSDAH;
vI2CDelay();
vI2CDelay();
uI=1000;
while(uI){
vWatchDog(); //清除看门狗,防止看门狗溢出导致MCU复位
--uI;
}
I2CSCLH;
I2CSDAH;
return ;
}
//发送ACK信号
void vI2CRdWtACK(void)
{
I2CSDAOUT;
vI2CDelay();
I2CSDAL;
I2CSCLH;
vI2CDelay();
vI2CDelay();
I2CSCLL;
vI2CDelay();
vI2CDelay();
return ;
}
//等待ACK信号
unsigned char ucI2CWaitACK(void)
{
unsigned char ucI2CNoAck=0;
I2CSCLL;
vI2CDelay();
I2CSDAH;
I2CSDAIN;
vI2CDelay();
vI2CDelay();
I2CSCLH;
vI2CDelay();
vI2CDelay();
if(I2CSDA){
ucI2CNoAck=1; //没有等到ACK
}
I2CSCLL;
vI2CDelay();
I2CSDAL;
vI2CDelay();
vI2CDelay();
return ucI2CNoAck; //返回ACK状态
}
//发送一个字节数据到I2C总线
void vI2CSendByte(unsigned char ucI2CTmp)
{
register unsigned char ucI;
I2CSDAOUT;
vI2CDelay();
for(ucI=0;ucI<8;ucI++){
I2CSCLL;
vI2CDelay();
if(ucI2CTmp&0x80){
I2CSDAH;
} else {
I2CSDAL;
}
ucI2CTmp<<=1;
vI2CDelay();
I2CSCLH;
vI2CDelay();
}
return ;
}
//从I2C总线读取一个字节的数据
unsigned char ucI2CRecvByte(void)
{
register unsigned char ucI,ucI2CTmp;
I2CSCLL;
I2CSDAH;
I2CSDAIN;
vI2CDelay();
vI2CDelay();
for(ucI=0;ucI<8;ucI++){
I2CSCLH;
vI2CDelay();
ucI2CTmp<<=1;
if(I2CSDA){
ucI2CTmp|=0x01;
} else {
ucI2CTmp&=0xfe;
}
I2CSCLL;
vI2CDelay();
vI2CDelay();
}
return ucI2CTmp;
}
|