关键字: linux i2c,IO模拟i2c, u-boot i2c, uboot i2c, 通用i2c
1. 这几天高lkt芯片,北京凌安的,你懂的,然后需要i2c, 而且是在u-boot下面的,
很多u-boot已经自动i2c的驱动了,但是不排除有部分不带的,例如rk的。
我之前在3288, 3399上就发现rk偷懒没做i2c uboot下的接口,也有可能是我的版本太久,
但不管怎样,做一个通用的i2c io模拟驱动多好??
其实我已经做了,而且这个驱动是经过实践的,非常好用,稳定的,而且移植性很强。既可以用在u-boot,也可以稍加修改,用在内核甚至应用层程序中,
源码里面都有注释。
现在把他记下来。
2. 废话少说,直接贴代码,通用的,自己注意改一下io定义。
#include <common.h>
#include <asm/gpio.h>
#include <i2c.h>
#include "lkt4106.h"
#include "des.h"
#define lkt_4106_slave_addr 0x50
#define PDU_MAX_LEN 256
//#define Lin_Dbg////调试用的打印
#ifdef Lin_Dbg
? ? ? ? #define PDBG(fmt, args...)? printf(fmt, ## args)
? ? ? ? #else
? ? ? ? #define PDBG(fmt, args...) /* empty debug slot */
#endif
void hex_str_to_uchar(unsigned char *dst, char *src, int len);
unsigned short get_crc16(unsigned char *pucFrame, unsigned short usLen);
#define I2C_SCL_PIN_NUM 102
#define I2C_SDA_PIN_NUM 103
#define I2C7_SDA_H? do{ gpio_direction_output(I2C_SDA_PIN_NUM, 1);gpio_set_value(I2C_SDA_PIN_NUM, 1);} while(0);//gpio2 b0, sda
#define I2C7_SDA_L? do{ gpio_direction_output(I2C_SDA_PIN_NUM, 0);gpio_set_value(I2C_SDA_PIN_NUM, 0);} while(0);//gpio2 b0, sda
#define I2C7_SCL_H? do{ gpio_direction_output(I2C_SCL_PIN_NUM, 1);gpio_set_value(I2C_SCL_PIN_NUM, 1);} while(0);//gpio2 A7, scl
#define I2C7_SCL_L? do{ gpio_direction_output(I2C_SCL_PIN_NUM, 0);gpio_set_value(I2C_SCL_PIN_NUM, 0);} while(0);//gpio2 A7, scl
int I2C7_IO_read_sda()
{
static int ret = -1;
gpio_direction_input(I2C_SDA_PIN_NUM);
udelay(1);
ret = gpio_get_value(I2C_SDA_PIN_NUM);
return ret;
}
/**
* I2C_start - 启动I2C传输
* @void: none
*/
void I2C_start(void)
{
I2C7_SDA_H; //发送起始条件的数据信号
udelay(1);
I2C7_SCL_H; //发送起始条件的时钟信号
udelay(4);//起始条件建立时间大于4.7us,不能少于
I2C7_SDA_L; //发送起始信号,起始条件锁定时间大于4us,这里也必须大于4us
? ? udelay(5);
I2C7_SCL_L; //钳住I2C总线,准备发送或接收
? ? udelay(2);
}
/**
* I2C_stop - 停止I2C传输
* @void: none
*/
void I2C_stop(void) //释放I2C总线
{
I2C7_SDA_L;//发送停止条件的数据信号
udelay(2);
I2C7_SCL_H;
udelay(4);//起始条件建立时间大于4us
I2C7_SDA_H;//发送I2C总线停止信号
udelay(5);//停止条件锁定时间大于4us
}
/**
* I2C_stop - 停止I2C传输
* @ackBit: 主机对从机发送的应答信号,在数据显示,1 bit,应答为0,非应答为1
* 例如发送I2C_send_ack(0)表示不应答
*/
void I2C_send_ack(int ackBit)
{
//发送的应答或非应答信号
if(ackBit){
I2C7_SDA_H;
}else{
I2C7_SDA_L;
}
udelay(2);
I2C7_SCL_H;//置时钟线为高使应答位有效
udelay(4);//时钟高周期大于4us,不同于器件发送到主机的应答信号
I2C7_SCL_L;
udelay(2);
}
/**
* I2C_write_one_byte - 向I2C发送一个字节的数据
* @ucData: 无符号8位,要发送的数据
*
* return: bAck,成功写入返回1,否则,失败返回0
*/
int I2C_write_one_byte(unsigned char ucData)
{
static int bACK = 0;
unsigned char i;
i=8;
while(i--){//8 位没发送完继续发送
if((ucData & 0x80)==0x80){
I2C7_SDA_H;//I2C MSB高位先发
}else{
I2C7_SDA_L;
}
udelay(2);
I2C7_SCL_H;//置时钟线为高通知被控器开始接收数据位
udelay(2);
I2C7_SCL_L;
ucData=ucData<<1;
}
udelay(1);
I2C7_SDA_H; //8位数据发送完,释放I2C总线,准备接收应答位
udelay(2);
I2C7_SCL_H;//开始接收应答信号
udelay(4);
if(1 == I2C7_IO_read_sda()){//应答只需普通最小数据锁存的延时时间
bACK=0;//高电平说明无应答
}else{
bACK=1;//低电平有应答
}
I2C7_SCL_L;//发送结束钳住总线准备下一步发送或接收数据
gpio_direction_output(I2C_SDA_PIN_NUM, 1);
udelay(2);
return(bACK);//正确应答返回1
}
/**
* I2C_read_one_byte - 向I2C读取一个字节的数据
* @void: none
*
* return: 返回一个读到的数据,1个字节
*/
unsigned char I2C_read_one_byte(void)
{
static unsigned char i=0,byteData=0;
gpio_direction_input(I2C_SDA_PIN_NUM);//置数据线为输入方式
i=8;
while(i--){
udelay(1);
I2C7_SCL_L; //置钟线为零准备接收数据
udelay(2);//时钟低周期大于4.7us
I2C7_SCL_H;//置时钟线为高使数据线上数据有效
udelay(1);
byteData=byteData<<1;
if(1==I2C7_IO_read_sda()) {++byteData;}
udelay(1);
}
I2C7_SCL_L;//8 位接收完置时钟线和数据线为低准备发送应答或非应答信号
udelay(1);
return(byteData);
}
/**
* I2C_read_str - 从I2C设备读入一串数据
* @ucSla: slave,从器件地址
* @ucAddress: 器件里面,要读的寄存器地址
* @ucBuf: 要读入的buf,读到数据存在这里
* @ucCount: 计划读入的字节数
*
* return: 读入数成功返回读到字节数,否则返回0
*/
unsigned int I2C_read_str(unsigned char ucSla,unsigned char ucAddress,unsigned char *ucBuf,unsigned int ucCount)
{
int i=0;
// I2C_start();
///////////////这段代码用在i2c eeprom上面//////////////////////
// if(1 != I2C_write_one_byte(ucSla)){//write one byte里包含应答
// I2C_stop();
// return 0;//选从器件的地址
// }
// printf("I2C_read_str, sla addr: %02x. \n", ucSla);
// if(1 != I2C_write_one_byte(ucAddress)){//选第一个寄存器地址
// I2C_stop();
// return 0;
// }
// printf("I2C_read_str, sla ucAddress: %02x. \n", ucAddress);
///////////////这段代码用在i2c eeprom上面//////////////////////
I2C_start();
//printf("I2C_read_str, i2c start.\n");
if(1 != I2C_write_one_byte(ucSla+1)){//发送读器件命令,ucSla+1表示要读的器件是ucSla
I2C_stop();
return 0;
}
//printf("I2C_read_str, I2C_write_one_byte:%02x.\n", ucSla+1);
i=ucCount;
while(i--){//执行ucount次,最后还要执行一次递减,例如,i=3,那么i=2,1,0, 跳出循环等于-1;
*ucBuf=I2C_read_one_byte();//读从器件寄存器
if(i)
I2C_send_ack(0);//未接收完所有字节,发送应答信号, 低电平应答,i2c飞利浦很低调
ucBuf++;
}
I2C_send_ack(1);//接收完所有字节,发送非应答信号, 1表示不应答
I2C_stop();
//printf("read str, finished, uCount:%d, i:%d. \n", ucCount, i);
return ucCount;
}
/**
* I2C_write_str - 向I2C设备写入一串数据
* @ucSla: slave,从器件地址
* @ucAddress: 器件里面,要读的寄存器地址
* @ucData: 要写入的数据数组
* @ucNo: 期望写入的个数
*
* return: 正确返回写入字节数,否则返回0
*/
unsigned int I2C_write_str(unsigned char ucSla,unsigned char ucAddress,unsigned char *ucData,unsigned int ucNo)
{
int i;
I2C_start();
if(1 != I2C_write_one_byte(ucSla)){
I2C_stop();//写入失败,直接发停止命令
return 0;//往I2C写命令
}
///////////////这段代码用在i2c eeprom上面//////////////////////
// if(1 != I2C_write_one_byte(ucAddress)){//
// I2C_stop();
// return 0;//写寄存器地址
// }
/////////////////这个加密芯片,不能用//////////////////////////
i=ucNo;
while(i--){
if(1 != I2C_write_one_byte(*ucData)){
I2C_stop();
return 0;//写数据
}
ucData++;
}
I2C_stop();//最后停止,返回1表示成功写入数组
//printf("read str, finished, uCount:%d, i:%d. \n", ucNo, i);
return ucNo;
}
?3. 使用方法:
#define lkt_4106_slave_addr 0x50
unsigned char test_cmd[] = {0x00, 0x05, 0x00, 0x84, 0x00, 0x00, 0x08};
int ret;
ret = I2C_write_str(lkt_4106_slave_addr, 0, test_cmd, sizeof(test_cmd));
if(!ret) {
printf("lkt41062 test cmd error %d.\n", ret);
} else {
printf("lkt41062 test cmd success.\n");
}