Linux 下使用IIC总线 读写 EEPROM by 韩大卫 @吉林师范大学

handawei@jusontech.com

转载请务必表明出处

******************* **********************************************

2012.7.16

1,本文给出了linux 下使用IIC总线读写EEPROM 的实现程序。

2, 本文给出了在编程中遇到的几种非常隐蔽的错误的解决方法。

3,本文的读写程序非常通用:

i2c -d /dev/i2c-1 -s 0x51 0x05 18 -----Write 18 to the register: 0x05 of the i2c-slave address: 0x51

i2c -d /dev/i2c-10 0x57 0x05 ------Read the register: 0x05 of the i2c-slave address: 0x57

i2c 0x40 0x0f ----- 在默认路径下读 i2c 从设备地址为0x40的 0x0f的地址(或寄存器地址)

我们嵌入式系统中的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,RET

RY)这样的程序不能得到通用,唯一的办法是在中使用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 RETRY3

static 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;

int

i2c_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 1

data->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;

}

int

i2c_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;

}

int

i2c_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;

}

void

i2c_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

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define I2C_DEFAULT_TIMEOUT1

#define I2C_DEFAULT_RETRY3

typedef 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);

int

i2c_read_data(u16 addr, u8 offset, u8 *val);

int

i2c_write_data(u16 addr, u8 offset, u8 val);

int

i2c_open(unsigned char* dev, unsigned int timeout, unsigned int retry);

#endif

************* ************************************************

by 韩大卫 @吉林师范大学

iic获取salve设备地址_Linux下使用IIC总线读写EEPROM(读写i2c从设备通用程序)相关推荐

  1. iic获取salve设备地址_I2C从设备地址(Slave Address)的设置与获得

    易获得的资料不在这里列出. 这里首先要明确一个设备是即可以做为主设备(master)也可以作为从设备(slave)的.而像微处理器这种设备可以同时作为主设备和从设备,只要使用两对I2C接口就行了.以下 ...

  2. linux下载python的地址_Linux下Python获取IP地址的代码

    <lnmp一键安装包>中需要获取ip地址,有2种情况:如果服务器只有私网地址没有公网地址,这个时候获取的IP(即私网地址)不能用来判断服务器的位置,于是取其网关地址用来判断服务器在国内还是 ...

  3. arm shell 获取本地键盘输入值_linux下获取按键响应事件【转】

    1.问题 通过一个死循环将读取键盘对应的设备文件将触发键盘事件在屏幕上打印出来,按esc退出程序 代码是在unbuntu10.04编译执行通过的 2.input_event描述 在Linux内核中,i ...

  4. linux ntp时钟服务器地址_Linux 下同步时间,另附 NTP 服务器地址

    Linux 下 NTP 同步:ntpdate –u [IP地址或域名] Linux 下获取当前时间:date Windows 大家应该都知道...吗? 国家授时中心 NTP 服务器 NTSC NTP ...

  5. popen 如何获取指令执行情况_Linux下使用popen()执行shell命令

    简单说一下popen()函数 函数定义 #include FILE* popen(const char *command , const char *type );int pclose(FILE *s ...

  6. Linux下编程获取本地IP地址的常见方法

    代码编译运行平台:Linux 64bits+g++(-m64),-m64表示生成64bits的程序. 在进行Linux网络编程时,经常用到本机IP地址.本文罗列一下常见方法,以备不时之需. 获取本机I ...

  7. ARP原理概述——基于WinPcap发送ARP请求数据包获取远程MAC地址

    ARP原理概述--基于WinPcap发送ARP请求数据包获取远程MAC地址 ARP协议 ARP概述 ARP工作原理 ARP数据包格式 编写程序发送ARP请求获取本机和远程IP的MAC 注意: ARP协 ...

  8. android otg主从切换,一种自动切换OTG设备主从模式下的Vbus电压的方法与电路与流程...

    本发明涉及otg设备的检测领域,更具体地说,涉及一种自动切换otg设备主从模式下的vbus电压的方法与电路. 背景技术: 现有技术中,具备otg功能的设备通过检测usb_id引脚的状态来判断工作为ho ...

  9. IIC软件模拟-读写EEPROM

    这里写目录标题 1.IIC简介 2. I2C 基本读写过程 2.1.主机写数据到从机 2.2.主机由从机中读数据 2.3.读和写数据 2.4.地址及数据方向 2.5.响应信号 3.软件模拟I2C 4. ...

最新文章

  1. 跨平台PHP调试器设计及使用方法——探索和设计
  2. 数据结构Java版之红黑树(八)
  3. Ext2.0框架的Grid使用介绍(转)
  4. 说说设计模式~组合模式(Composite)
  5. Xcode模拟器相关操作
  6. 工作中发现的相对布局中的一个小技巧
  7. Windows App开发之集合控件与数据绑定
  8. spring boot: Bean的初始化和销毁 (一般注入说明(三) AnnotationConfigApplicationContext容器 JSR250注解)...
  9. 运行差分灰狼时出现 关于“索引超出数组元素的数目(0)和矩阵维度问题以及图例的问题”的解决办法
  10. centos7安装MySql(yum方式)
  11. bzoj 4260: Codechef REBXOR(01字典树)
  12. asp.net 独立缓存服务器的研究
  13. python invalid character_python提示invalid character in identifier
  14. Linux中的用户切换:su和su - 的区别
  15. DB破解(暗黑破坏神辅助)使用方法
  16. VMware虚拟机Mac OS X 扩展内存方法
  17. 龙世界java下载_龙世界3-龙王之剑
  18. Web前端-HTML
  19. 变量、表达式与顺序语句
  20. 数字化改革“1612”详解

热门文章

  1. 第五人格服务器正在维护中怎么办,第五人格新联动刚来就出问题,紧急停服维护,这得补偿多少?...
  2. 牛客 小米校招 小明的字符串 循环队列
  3. 五分钟you-get入门
  4. 用功譬若掘井,与其多绝掘数井,而皆不及泉,何若老守一井,力求泉而用之不竭乎?
  5. 如何快速深度写论文?
  6. S-Trees UVA - 712(建树即可)
  7. 10分钟上手pythonpandas_10分钟pandas教程
  8. java 手电筒_Android实现简单手电筒功能
  9. 计算机视觉大型攻略 —— 特征与匹配(3)特征描述符
  10. 【好记性不如烂笔头】快速排序(三)非递归实现随机快排