linux之I2C裸机驱动解析(转)

时间:2021-02-18 12:59:33

1      硬件特性

1.1 概述

I2C总线是由Philips公司开发的两线式串行总线,这两根线为时钟线(SCL)和双向数据线(SDA)。由于I2C总线仅需要两根线,因此在电路板上占用的空间更少,带来的问题是带宽较窄。I2C在标准模式下传输速率最高100Kb/s,在快速模式下最高可达400kb/s。属于半双工。

在嵌入式系统中,I2C应用非常广泛,大多数微控制器中集成了I2C总线,一般用于和RTC,EEPROM,智能电池电路,传感器,LCD以及其他类似设备之间的通信。

1.2 I2C总线传输时序

linux之I2C裸机驱动解析(转)

1.3 I2C总线的信号状态

1、  空闲状态:SDA和SCL都是高电平;

2、  开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传输数据;

3、  结束条件(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传输数据;

4、  数据有效:在SCL的高电平期间,SDA保持稳定,数据有效。SDA的改变只能发生在SCL的低电平期间;

5、  ACK信号:数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。

1.4 从设备地址

linux之I2C裸机驱动解析(转)

I2C总线从设备使用7位地址,最后一个为读写控制位。下图是eeprom的原理图,我们可以计算出它的地址为0x50。

linux之I2C裸机驱动解析(转)

1.5 I2C读写方式

多字节写的时序

linux之I2C裸机驱动解析(转)

多字节读的时序

linux之I2C裸机驱动解析(转)

具体可参考datasheet

附:ok6410裸机I2C代码。

 #define INTPND (*(volatile unsigned long*)0x4a000010)
#define SRCPND (*(volatile unsigned long*)0x4a000000)
#define INTMSK (*(volatile unsigned long*)0x4a000008)
#define GPECON (*(volatile unsigned long*)0x56000040)
#define GPEUP (*(volatile unsigned long*)0x56000048) #define IICCON (*(volatile unsigned char*)0x54000000)
#define IICSTAT (*(volatile unsigned char*)0x54000004)
#define IICDS (*(volatile unsigned char*)0x5400000C) #define SLAVE_WRITE_ADD 0xa0 /* 写入数据时;方向位(第0位)为0 */
#define SLAVE_READ_ADD 0xa1 /* 读取数据时;方向位(第0位)为1 */ void delay(int i)
{
int j = ;
while (i--)
{
for (j=;j<;j++)
{
;
}
}
} void i2c_init()
{
//1.a 初始化中断
INTPND |= (<<);
SRCPND |= (<<);
INTMSK &= ~(<<); IICCON |= (<<); //1.b 设置scl时钟
IICCON &= ~(<<);
IICCON &= ~(0xf<<);
IICCON |= (0x5<<); //2. 设置IICSTAT
IICCON |= (<<); //3.设置引脚功能
GPECON |= (0x2<<)|(0x2<<);
GPEUP |= (0x3<<); //4.允许产生ACK
IICCON |= (<<);
} void write_byte(unsigned char xchar, unsigned char daddr)
{
/* 写入数据时,每发送一个数据收到一个ACK就产生一次中断
* 写入下次发送的数据之后要清除中断 */ //1. 设置处理器为主设备+发送模式
IICSTAT |= (<<); //2. 将从设备的地址写入到IICDS寄存器
IICDS = SLAVE_WRITE_ADD; //清除中断
IICCON &= ~(<<); //3. 写入0xF0写入IICSTAT M/T Start
IICSTAT = 0xF0; //4. 等待ACK的产生
while ((IICCON & (<<)) == )
delay(); //5.1写入字节的地址到IICDS寄存器
IICDS = daddr; //5.2清除中断
IICCON &= ~(<<); //5.3等待ACK的产生
while ((IICCON & (<<)) == )
delay(); //6. 将要传输的字节数据写入IICDS寄存器
IICDS = xchar; //7. 清除中断
IICCON &= ~(<<); //8. 等待ACk的产生
while ((IICCON & (<<)) == )
delay(); //9. 写入0xD0到IICSTAT
IICSTAT = 0xD0; //10. 清除中断
IICCON &= ~(<<); delay();
} void read_data(unsigned char *buf, unsigned char daddr, int length) /* 结合eeprom手册 */
{
/* 每接收一个数据产生一个中断 */ int j =;
unsigned char unusedata; //1. 设置处理器为主设备+发送模式
IICSTAT |= (<<); //2. 将从设备的地址写入到IICDS寄存器
IICDS = SLAVE_WRITE_ADD; //清除中断
IICCON &= ~(<<); //3. 写入0xF0写入IICSTAT M/T-Start
IICSTAT = 0xF0; //4. 等待ACK的产生
while ((IICCON & (<<)) == )
delay(); //5.1写入eeprom内部地址
IICDS = daddr; //5.2清除中断
IICCON &= ~(<<); //5.3等待ACK的产生
while ((IICCON & (<<)) == )
delay(); /**************eeprom代码**************/
/**************************************/
/***************i2c代码****************/ //设置为主设备接收模式
IICSTAT &= ~(<<);
IICSTAT |= (<<); //2.写入从设备地址到IICDS /* 从设备地址成功发送之后产生中断,故要清除中断 */
IICDS = SLAVE_READ_ADD;
//清除中断
IICCON &= ~(<<); //3.写入0xB0到IICSTAT开始接收,每接收道一个数据就产生一个中断
IICSTAT = 0xb0; //等待中断
while ((IICCON & (<<)) == )
delay(); #if 0
/***写入设备内部地址***/
IICDS = daddr;
IICCON &= ~( << );
while((IICCON & ( << )) == )
{
delay();
}
#endif //***丢掉收到的第1个字节 第一个数据无效 丢弃!
unusedata = IICDS;
IICCON &= ~(<<);
while ((IICCON & (<<)) == )
delay(); for(j=;j<length;j++)
{
if(j == (length - ))
{
IICCON &= ~(<<);
} //5.1 从IICDS里取出数据
buf[j]=IICDS; //5.2 清除中断
IICCON &= ~(<<); //4.等待中断
while ((IICCON & (<<)) == )
delay();
} //写入0x90到IICSTAT
IICSTAT = 0x90; // 清除中断
IICCON &= ~(<<);
} void i2c_test()
{
int i=;
unsigned char sbuf[]={};
unsigned char dbuf[]={}; i2c_init(); for(i=;i<;i++)
{
sbuf[i] = i+;
dbuf[i] = ;
} printf("dbuf befor I2C read:\r\n");
for(i =; i<;i++)
{
if(i%==)
printf("\r\n"); /* */ printf("%d\t",dbuf[i]); /*t-空格 */
} for(i=;i<;i++)
write_byte(sbuf[i],i); printf("i2c reading, plese wait!\n\r"); read_data(dbuf,,); printf("dbuf after I2C read:\r\n"); for(i =; i<;i++)
{
if(i%==)
printf("\r\n"); printf("%d\t",dbuf[i]);
}
}