Linux下使用I2C总线读写 EEPROM(读写i2c从设备通用程序)

来源:互联网 发布:迷你图书管理系统java 编辑:程序博客网 时间:2024/05/17 01:02
1,本文给出了linux 下使用IIC总线读写i2c从设备的实现程序。2,  本文给出了在编程中遇到的几种非常隐蔽的错误的解决方法。3,本文的读写程序非常通用:i2c -d /dev/i2c-1 -s 0x51 0x05 18    -----Write 18 to the register: 0x05 of the i2c-slave address: 0x51i2c -d /dev/i2c-10 0x57 0x05           ------Read the register: 0x05 of the i2c-slave address: 0x57i2c 0x40 0x0f  ----- 在默认路径下读 i2c 从设备地址为0x40的 0x0f的地址(或寄存器地址)4,本程序以EEPROM为例,最后出了如何读写i2c下的从设备的通用程序,而不是针对于EEPROM这一种芯片。另篇文章《linux下使用i2c总线与E2PROM通信》中有针对EEPROM的一些操作。但原理是与本文相同的。 我们嵌入式系统中的E2PROM 是 24C02.先简单了解一下这款芯片:AT24C02的存储容量为2Kb,内容分成32页,每页8B,共256B,操作时有两种寻址方式:芯片寻址和片内子地址寻址。   (1)芯片寻址:AT24C02的芯片地址为1010,其地址控制字格式为 1010A2A1A0R/W。其中A2,A1,A0可编程地址选择位。A2,A1,A0引脚接高、低电平后得到确定的三位编码,与1010形成7位编码, 即为该器件的地址码。R/W为芯片读写控制位,该位为0,表示芯片进行写操作。   (2)片内子地址寻址:芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。我们采用的是第2种 寻址方式。另外,有一个问题需要了解一下,就是EEPROM 与flash , 什么时候使用EEPROM,什么时候用FLASH合适。******************** From Www.baidu.com : Flash存储器又称闪存,它结合了ROM和RAM的长处,不仅具备电子可擦除可编程(EEPROM)的性能,还可以快速读取数据(NVRAM的优势),使数据不会因为断电而丢失。U盘和MP3里用的就是这种存储器。用作存储Bootloader以及操作系统或者程序代码,或者直接当硬盘使用(U盘)。 一, EEPROM以单字节读写,FLASH部分芯片只能以块方式擦除(整片擦除),部分芯片可以单字节写入(编程),一般需要采用块写入方式; 二,FLASH比EEPROM读写速度更快,可靠性更高。三,价格方面比较,FLASH比EEPROM贵。 So,我们的版卡参数信息,等一些固定的,小量的,不需要经常修改资料信息放在EEPROM中。而flash作为存储程序的存储器,存放操作系统代码等需要快速读写的,经常访问的数据。************** ********************************************************** *******************************************************先介绍下遇到的一些问题:问题一: Bad address在使用ioctl 在用户层将包装好的data 发送给内核,但是运行结果显示: error = Bad address我原来以为是不是给我访问地址不对, 可是地址是正确的。后来看到了报错的位置:在内核代码 driver/i2c/i2c-dev.c ,函数i2cdev_ioctl_rdrw()中if (copy_from_user(&rdwr_arg,(struct i2c_rdwr_ioctl_data __user *)arg, sizeof(rdwr_arg)))  return -EFAULT;这条语句返回了错误提示bad address 。经过查资料,出错的非常原因隐蔽又非常简单:  copy_to_user的定义是:      copy_to_user ( void  __user  *  to ,   const   void   *  from ,   unsigned   long  n );      可是这个unsigned long 在32bit的处理器上是等于 unsigned int 的,都是4个字节,32bit。所以我最初在eeprom_io.h中有这样的定义:typedef unsigned long u32;在eeprom_io.c中:ioctl(fd, I2C_RDWR, (u32)iocs);我们的版卡的处理器是64bit mips系列处理器,unsigned long 就是8个字节,64bit。交叉编译器不会报错的。但运行后就会由于字节的问题一直有 Bad address的错误反馈。这误导我单纯了认为是地址不对。后来我将typedef unsigned long u32 改为 typedef unsigned long u64;ioctl(fd, I2C_RDWR, (u64)iocs) 。才将这个问题解决。************** ***************************************************************问题二,程序执行一次write 后再执行write的时候出现:Input/output error同样的,执行一次write 后再执行 read 也有这样的错误。我将addr改为   0x90,0x10  。运行结果都是报错: Input output error找到报错的位置:是在  XXX_i2c_xfer () 中下的一的执行函数: octeon_i2c_simple_write()    cmd = __raw_readq(i2c->twsi_base + SW_TWSI);        printk(KERN_DEBUG "%s:after readq cmd = %llx\n",__func__,cmd);        if ((cmd & SW_TWSI_R) == 0) {                 if (octeon_i2c_lost_arb(cmd))                        goto retry;                ret = -EIO;              这个EIO 在用户层上 printf("%s:error = %s\n",__FUNCTION__,strerror(errno));会显示input/output error 观察dmesg:[ 2656.285759] octeon_i2c_xfer: num = 1[ 2656.285769] octeon_i2c_xfer:msgs[0].addr = 57, msgs[0].flags = 0, msgs[0].len = 2[ 2656.285780] octeon_i2c_xfer:msgs[0].buf[0] = 0[ 2656.285789] octeon_i2c_simple_write:[ 2656.285797] octeon_i2c_simple_write:cmd = 8090570000000000[ 2656.285808] octeon_i2c_simple_write:msgs[0].buf[1] = 10,cmd = 8090570000000010[ 2656.285820] octeon_i2c_simple_write:msgs[0].buf[0] = 0,cmd = 8090570000000010[ 2656.285948] octeon_i2c_simple_write:after readq cmd = 090570000000020[ 2656.285955] 我将正确执行的程序dmesg 打印出来作为对比:[ 4312.259857] octeon_i2c_xfer: num = 1[ 4312.259866] octeon_i2c_xfer:msgs[0].addr = 51, msgs[0].flags = 0, msgs[0].len = 2[ 4312.259878] octeon_i2c_xfer:msgs[0].buf[0] = 2[ 4312.259887] octeon_i2c_simple_write:[ 4312.259895] octeon_i2c_simple_write:cmd = 8090510000000000[ 4312.259906] octeon_i2c_simple_write:msgs[0].buf[1] = 30,cmd = 8090510000000030[ 4312.259918] octeon_i2c_simple_write:msgs[0].buf[0] = 02,cmd = 8090510000000230[ 4312.260227] octeon_i2c_simple_write:after readq cmd = 01905100ffffffff第一次write执行成功,这说明代码没有问题,那么第二次执行失败,应该是有别的原因,上网查了一下24C02 的资料,原来是这样:“数据写完之后,给一个停止信号后一定要延时10MS,24C02需要这么久载入数据”这是24C02的电气特性。在write函数使一次用usleep(10000) 。补充一下:我们的CPU 为6内核500M主频,计算处理能力极强。如果在用户层写一般的延时程序根本不起作用,一般使用2个for,一个for执行10000次,在PC上就有会明显的延时。 但是在我们的嵌入式系统中,使用8个for语句,每个for执行10000次,丝毫没有影响,起不到一丝的延时作用。另外,单纯的使用for作用延时,会将CPU处于满负荷阻塞状态,影响其他功能,所以,建议大家使用usleep()函数,usleep(10000), 正好是10Ms,这样最佳的使用了CPU时间。********* ******************************************************************* ************************************************************问题三,关于 fd = i_open("/dev/i2c-2",TIMEOUT,RETRY);这个语句本身没有问题,在我的系统上也测试通过了,但是其他系统有使用出错的情况,我观察他的dmesg:[ 4515.609931] octeon_i2c_xfer: num = 1[ 4515.609941] octeon_i2c_xfer:msgs[0].addr = 57, msgs[0].flags = 0, msgs[0].len = 2[ 4515.609952] octeon_i2c_xfer:msgs[0].buf[0] = 2[ 4515.609961] octeon_i2c_simple_write:[ 4515.609969] octeon_i2c_simple_write:cmd = 8090570000000000[ 4515.609980] octeon_i2c_simple_write:msgs[0].buf[1] = 01,cmd = 8090570000000001[ 4515.609992] octeon_i2c_simple_write:msgs[0].buf[0] = 02,cmd = 8090570000000201[ 4515.610117] octeon_i2c_simple_write:after readq cmd = 0090570000000020我执行了一次正确的write,  打印出结果作为对比:[ 4312.259857] octeon_i2c_xfer: num = 1[ 4312.259866] octeon_i2c_xfer:msgs[0].addr = 51, msgs[0].flags = 0, msgs[0].len = 2[ 4312.259878] octeon_i2c_xfer:msgs[0].buf[0] = 2[ 4312.259887] octeon_i2c_simple_write:[ 4312.259895] octeon_i2c_simple_write:cmd = 8090510000000000[ 4312.259906] octeon_i2c_simple_write:msgs[0].buf[1] = 30,cmd = 8090510000000030[ 4312.259918] octeon_i2c_simple_write:msgs[0].buf[0] = 02,cmd = 8090510000000230[ 4312.260227] octeon_i2c_simple_write:after readq cmd = 01905100ffffffff后来经过研究,是访问的i2c bus 出了问题。原因如下:在我的版卡的datasheet 上:   24C02  是挂在I2C0  上的,/dev/i2c-0, /dev/i2c2--/dev/i2c-9  都是属于I2c0的,这样访问这些bus都可以。但是 /dev/i2c-1,/devi2c-10---/dev/i2c-33 这些的都是属于I2C1的  ,所以单纯的 fd = i_open("/dev/i2c-2",TIMEOUT,RETRY)这样的程序不能得到通用,唯一的办法是在中使用argv ,将路径作为参数传进来, 进而能进入正确的/dev/i2c-* 号bus。 同时为了做到简化,将/dev/i2c-0作为默认路径。 使用时候可以 i2c -d /dev/i2c-1 0x57 0x10                  使用 /dev/i2c-1 作为路径i2c 0x57 0x10                                         使用默认路径/dev/i2c-0.**************************************** *****************************************************下面是 实现代码 . Main.c :***********************************************#include "i2c.h"#define TIMEOUT3#define RETRY3static int fd;static u16 addr;static u8 offset;int i_open(unsigned char* dev, unsigned int timeout, unsigned int retry){        return i2c_open(dev,timeout,retry); }int read_data(u16 addr, u8 offset, u8 *val){int ret;ret = i2c_read_data(addr,offset,val);if(ret < 0){printf("%s error!\n",__FUNCTION__);exit(-1);}printf("read success, val  = %02x\n",*val);return 0;}int write_data(u16 addr, u8 offset, char* argv){int ret;u8 val = (unsigned char)strtoul(argv,0,16);ret = i2c_write_data(addr,offset,val);if(ret < 0){printf("%s error!\n",__FUNCTION__);exit(-1);}printf("write success , val  = %02x\n",val);usleep(10000);    // 延时程序return 0;}int help_info(void){printf("\nUsage: i2c  [-d PATH] ADDR OFFSET\n");printf("\nOr:    i2c  [-d PATH] -s ADDR OFFSET DATA \n");        printf("\nRead or Write  the register of i2c slave\n");printf("\nFor example\n");printf("\ti2c 0x51 0x05\t\t\t\t\t\tRead the register: 0x05 of the address: 0x51\n");printf("\ti2c -d /dev/i2c-10 0x51 0x05\t\t\t\tRead the register: 0x05 of the address: 0x51\n");printf("\ti2c -d /dev/i2c-1 -s 0x51 0x05 18\t\t\tWrite 18 to the register: 0x05 of the address: 0x51\n\n");return 0;}void i2c_path(char* argv){fd = i_open(argv,TIMEOUT,RETRY);if( fd < 0 ){printf("i2c_open error!\n");exit(-1);}}void i2c_addr(char * argv){char *s = argv;addr= bin2bcd(atoi(s+2));}void i2c_offs(char *argv){char *s = argv;offset= bin2bcd(atoi(s+2));}int main(int argc,char* argv[]){u8 val;switch(argc){case 2 :{if(!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")){help_info();}else{printf("cmd error!\n");exit(-1);}}break;case 3 :{i2c_path("/dev/i2c-1");i2c_addr(argv[1]);i2c_offs(argv[2]);read_data(addr,offset,&val);}break;case 5 :{if(!strcmp(argv[1],"-d")){i2c_path(argv[2]);i2c_addr(argv[3]);i2c_offs(argv[4]);read_data(addr,offset,&val);}else if(!strcmp(argv[1],"-s")){i2c_path("/dev/i2c-1");i2c_addr(argv[2]);i2c_offs(argv[3]);write_data(addr,offset,argv[4]);}else {printf("cmd error!\n");exit(-1);}}break;case 7 :{if(!strcmp(argv[1],"-d")){i2c_path(argv[2]);if(!strcmp(argv[3],"-s")){i2c_addr(argv[4]);i2c_offs(argv[5]);write_data(addr,offset,argv[6]);}}else {printf("cmd error!\n");exit(-1);}}break;default:printf("Please input --help or -h  for help information\n");}close(fd);return 0;}******************************* **************************************i2c.c :************** ************************************#include "i2c.h"static int fd;inti2c_read_data(u16 addr, u8 offset, u8 *val){int i,ret = 0;struct i2c_rdwr_ioctl_data *data;if ((data = (struct i2c_rdwr_ioctl_data *)malloc(sizeof(struct i2c_rdwr_ioctl_data))) == NULL)return -1;data->nmsgs = 2;if ((data->msgs = (struct i2c_msg *)malloc(data->nmsgs * sizeof(struct i2c_msg))) == NULL) {ret = -1;goto errexit3;}if ((data->msgs[0].buf = (unsigned char *)malloc(sizeof(unsigned char))) == NULL) {ret = -1;goto errexit2;}if ((data->msgs[1].buf = (unsigned char *)malloc(sizeof(unsigned char))) == NULL) {ret = -1;goto errexit1;}data->msgs[0].addr = addr;data->msgs[0].flags = 0;data->msgs[0].len = 1;data->msgs[0].buf[0] = offset;data->msgs[1].addr = addr;data->msgs[1].flags = I2C_M_RD;data->msgs[1].len = 13;//original data is 1data->msgs[1].buf[0] = 0;if ((ret = __i2c_send(fd, data)) < 0)goto errexit0;for(i = 0 ;i < data->msgs[1].len; i++)val[i] = data->msgs[1].buf[i];errexit0:free(data->msgs[1].buf);errexit1:free(data->msgs[0].buf);errexit2:free(data->msgs);errexit3:free(data);return ret;}inti2c_write_data(u16 addr, u8 offset, u8 val){int ret = 0;struct i2c_rdwr_ioctl_data *data;if ((data = (struct i2c_rdwr_ioctl_data *)malloc(sizeof(struct i2c_rdwr_ioctl_data))) == NULL)return -1;data->nmsgs = 1;if ((data->msgs = (struct i2c_msg *)malloc(data->nmsgs * sizeof(struct i2c_msg))) == NULL) {ret = -1;goto errexit2;}if ((data->msgs[0].buf = (unsigned char *)malloc(2 * sizeof(unsigned char))) == NULL) {ret = -1;goto errexit1;}data->msgs[0].addr = addr;data->msgs[0].flags = 0;data->msgs[0].len = 2;data->msgs[0].buf[0] = offset;data->msgs[0].buf[1] = val;if ((ret = __i2c_send(fd, data)) < 0)goto errexit0;errexit0:free(data->msgs[0].buf);errexit1:free(data->msgs);errexit2:free(data);return ret;}inti2c_open(unsigned char* dev, unsigned int timeout, unsigned int retry){if ((fd = open(dev, O_RDWR)) < 0)return fd;__i2c_set(fd, timeout, retry);return fd;}static int__i2c_send(int fd, struct i2c_rdwr_ioctl_data *data){if (fd < 0)return -1;if (data == NULL)return -1;if (data->msgs == NULL || data->nmsgs == 0)return -1;return ioctl(fd, I2C_RDWR, (unsigned long)data) ;}static int__i2c_set(int fd, unsigned int timeout, unsigned int retry){if (fd == 0 )return -1;ioctl(fd, I2C_TIMEOUT, timeout ? timeout : I2C_DEFAULT_TIMEOUT);ioctl(fd, I2C_RETRIES, retry ? retry : I2C_DEFAULT_RETRY);return 0;}voidi2c_close(int fd){if (fd < 0)return;close(fd);}unsigned bcd2bin(unsigned char val){               return (val & 0x0f) + (val >> 4) * 10;  }unsigned char bin2bcd(unsigned val){               return ((val / 10) << 4) + val % 10;}    i2c.h :************* ********************************************#ifndef I2C_H#define I2C_H#include <linux/types.h>#include <linux/i2c.h>#include <linux/i2c-dev.h>#include <linux/rtc.h>#include <stdio.h>#include <time.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <sys/types.h>#include <sys/ioctl.h>#include <errno.h>#include <string.h>#define I2C_DEFAULT_TIMEOUT1#define I2C_DEFAULT_RETRY3typedef unsigned char           u8;typedef unsigned short          u16;typedef unsigned int            u32;typedef unsigned long long      u64;typedef signed char             s8;typedef short                   s16;            typedef int                     s32;typedef long long               s64;unsigned bcd2bin(unsigned char val);unsigned bcd2bin(unsigned char val);static int__i2c_send(int fd, struct i2c_rdwr_ioctl_data *data);static int__i2c_set(int fd, unsigned int timeout, unsigned int retry);inti2c_read_data(u16 addr, u8 offset, u8 *val);inti2c_write_data(u16 addr, u8 offset, u8 val);inti2c_open(unsigned char* dev, unsigned int timeout, unsigned int retry);#endif************* ************************************************ 

0 0