LinuxI2C驱动--从两个访问eeprom的例子开始

本小节介绍两个在linux应用层访问eeprom的方法,并给出示例代码方便大家理解。第一个方法是通过sysfs文件系统对eeprom进行访问,第二个方法是通过eeprom的设备文件进行访问。这两个方法分别对应了i2c设备驱动的两个不同的实现,在后面的小结会详细的分析。


我们嵌入式系统中的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个寻址单位。


1. 通过sysfs文件系统访问I2C设备

eeprom的设备驱动在/sys/bus/i2c/devices/0-0050/目录下把eeprom设备映射为一个二进制节点,文件名为eeprom。对这个eeprom文件的读写就是对eeprom进行读写。

我们可以先用cat命令来看下eeprom的内容。

[root@FORLINX210]# cat eeprom                                                                      
�����������X�����������������������������������������������
  • 1
  • 2
  • 1
  • 2

发现里面都是乱码,然后用echo命令把字符串“test”输入给eeprom文件,然后再cat出来。

[root@FORLINX210]# echo "test" > eeprom  
[root@FORLINX210]# cat eeprom                                                 
test                                                                            
�����������X�����������������������������������������������
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

就会发现字符串test已经存在eeprom里面了,我们知道sysfs文件系统断电后就没了,也无法对数据进行保存,为了验证确实把“test”字符串存储在了eeprom,可以把系统断电重启,然后cat eeprom,会发现test还是存在的,证明确实对eeprom进行了写入操作。

当然,因为eeprom已经映射为一个文件了,我们还可以通过文件I/O写应用程序对其进行简单的访问测试。比如以下程序对特定地址(0x40)写入特定数据(Hi,this is an eepromtest!),然后再把写入的数据在此地址上读出来。

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>int main(void){int fd, size, len, i;char buf[50]= {0};char *bufw="Hi,this is an eepromtest!";//要写入的数据len=strlen(bufw);//数据长度fd= open("/sys/bus/i2c/devices/0-0050/eeprom",O_RDWR);//打开文件if(fd< 0){printf("####i2c test device open failed####/n");return(-1);}//写操作lseek(fd,0x40,SEEK_SET); //定位地址,地址是0x40if((size=write(fd,bufw, len))<0)//写入数据{printf("write error\n");return 1;}printf("writeok\n");//读操作lseek(fd,0x40, SEEK_SET);//准备读,首先定位地址,因为前面写入的时候更新了当前文件偏移量,所以这边需要重新定位到0x40.if((size=read(fd,buf,len))<0)//读数据{printf("readerror\n");return 1;}printf("readok\n");for(i=0; i< len; i++)printf("buff[%d]=%x\n",i, buf[i]);//打印数据close(fd);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

2. 通过devfs访问I2C设备

linux的i2c驱动会针对每个i2c适配器在/dev/目录下生成一个主设备号为89的设备文件,简单的来说,对于本例的eeprom驱动,/dev/i2c/0就是它的设备文件,因此接下来的eeprom的访问就变为了对此设备文件的访问。

我们需要用到两个结构体i2c_msg和i2c_rdwr_ioctl_data。

struct i2c_msg { //i2c消息结构体,每个i2c消息对应一个结构体__u16 addr; /* 从设备地址,此处就是eeprom地址,即0x50 */__u16 flags;    /* 一些标志,比如i2c读等*/__u16 len;      /* i2c消息的长度 */__u8 *buf;      /* 指向i2c消息中的数据 */};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
 struct i2c_rdwr_ioctl_data {struct i2c_msg __user *msgs;    /* 指向一个i2c消息 */__u32 nmsgs;            /* i2c消息的数量 */};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

对一个eeprom上的特定地址(0x10)写入特定数据(0x58)并在从此地址读出写入数据的示例程序如下所示。

#include <stdio.h>
#include <linux/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>int main()
{int fd,ret;struct i2c_rdwr_ioctl_data e2prom_data;fd=open("/dev/i2c/0",O_RDWR);//打开eeprom设备文件结点if(fd<0){perror("open error");}e2prom_data.nmsgs=2; e2prom_data.msgs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));//分配空间if(!e2prom_data.msgs){perror("malloc error");exit(1);}ioctl(fd,I2C_TIMEOUT,1);/*超时时间*/ioctl(fd,I2C_RETRIES,2);/*重复次数*//*写eeprom*/e2prom_data.nmsgs=1;//由前面eeprom读写分析可知,写eeprom需要一条消息(e2prom_data.msgs[0]).len=2; //此消息的长度为2个字节,第一个字节是要写入数据的地址,第二个字节是要写入的数据(e2prom_data.msgs[0]).addr=0x50;//e2prom 设备地址(e2prom_data.msgs[0]).flags=0; //写(e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);(e2prom_data.msgs[0]).buf[0]=0x10;// e2prom 写入目标的地址(e2prom_data.msgs[0]).buf[1]=0x58;//写入的数据ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);//通过ioctl进行实际写入操作,后面会详细分析if(ret<0){perror("ioctl error1");}sleep(1);/*读eeprom*/e2prom_data.nmsgs=2;//读eeprom需要两条消息(e2prom_data.msgs[0]).len=1; //第一条消息实际是写eeprom,需要告诉eeprom需要读数据的地址,因此长度为1个字节(e2prom_data.msgs[0]).addr=0x50; // e2prom 设备地址(e2prom_data.msgs[0]).flags=0;//先是写(e2prom_data.msgs[0]).buf[0]=0x10;//e2prom上需要读的数据的地址(e2prom_data.msgs[1]).len=1;//第二条消息才是读eeprom,(e2prom_data.msgs[1]).addr=0x50;// e2prom 设备地址 (e2prom_data.msgs[1]).flags=I2C_M_RD;//然后是读(e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的地址。(e2prom_data.msgs[1]).buf[0]=0;//初始化读缓冲,读到的数据放到此缓冲区ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);//通过ioctl进行实际的读操作if(ret<0){perror("ioctl error2");}printf("buff[0]=%x\n",(e2prom_data.msgs[1]).buf[0]);/***打印读出的值,没错的话,就应该是前面写的0x58了***/close(fd);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

3. 总结

本小节介绍了两种在linux应用层访问eeprom的方法,并且给出了示例程序,通过sysfs文件系统访问eeprom操作简单,无需了解eeprom的硬件特性以及访问时序,而通过devfs访问eeprom的方法则需要了解eeprom的读写时序。

后面分析后会发现,第一种通过sysfs文件系统的二进制结点访问eeprom的方法是由eeprom的设备驱动实现的,是一种专有的方法;而第二种通过devfs访问eeprom的方法是linux i2c提供的一种通用的方法,访问设备的能力有限。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cookie详解整理

1.Cookie的诞生 由于HTTP协议是无状态的&#xff0c;而服务器端的业务必须是要有状态的。Cookie诞生的最初目的是为了存储web中的状态信息&#xff0c;以方便服务器端使用。比如判断用户是否是第一次访问网站。目前最新的规范是RFC 6265&#xff0c;它是一个由浏览器服务器共同…

骁龙820和KryoCPU:异构计算与定制计算的作用 【转】

本文转载自&#xff1a;https://www.douban.com/group/topic/89037625/ Qualcomm骁龙820处理器专为提供创新用户体验的顶级移动终端而设计。为实现消费者所期望的创新&#xff0c;移动处理器必须满足日益增长的计算需求且降低功耗&#xff0c;同时还要拥有比以往更低的温度&…

LNMP

准备工作 建立一个软件包目录存放 mkdir -p /usr/local/src/ 清理已经安装包 rpm -e httpd rpm -e mysql rpm -e php yum -y remove httpd yum -y remove mysql yum -y remove php #搜索apache包 rpm -qa http* #强制卸载apache包 rpm -e --nodeps 查询出来的文件名 #检查是否卸…

算法复习——计算几何基础(zoj1081)

题目&#xff1a; Statement of the Problem Several drawing applications allow us to draw polygons and almost all of them allow us to fill them with some color. The task of filling a polygon reduces to knowing which points are inside it, so programmers have …

亚马逊Rekognition发布针对人脸检测、分析和识别功能的多项更新

今天亚马逊Rekognition针对人脸检测、分析和识别功能推出了一系列更新。这些更新将为用户带来多项能力的改今&#xff0c;包括从图像中检测出更多人脸、执行更高精度的人脸匹配以及获得图像中的人脸得到更准确的年龄、性别和情感属性。Amazon Rekognition的客户可以从今天开始使…

(转)CentOS分区操作详解

CentOS分区操作详解 原文&#xff1a;http://blog.csdn.net/yonggeit/article/details/77924393 磁盘分区 分区格式的两种选择&#xff1a;MBR和GPT 分区命令&#xff1a; parted的操作都是实时生效的&#xff0c;小心使用&#xff0c;主要是用于大于2T硬盘&#xff0c;支持MBR…

linux驱动中地址空间转换

在linux kernel 中&#xff0c;物理地址是不能直接使用的&#xff0c;必须通过转换才可以。转换分为两种&#xff0c; 静态和动态。 静态就是下面那种&#xff0c;不过&#xff0c;静态的地址转换&#xff0c;还需要在kernel 初始化的时候作映射。 动态映射是使用 ioremap 函…

getClass()和.class的区别

getClass()和.class的区别 在学习反射时想到了这个问题&#xff0c;.getClass()和.class有没有什么区别&#xff1f; 当然&#xff0c;最明显的区别就是.getClass()是一个对象实例的方法&#xff0c;只有对象实例才有这个方法&#xff0c;具体的类是没有的。类的Class类实例是通…

华为敏捷 DevOps 实践:产品经理如何开好敏捷回顾会议

开篇小故事&#xff1a;前几年&#xff0c;一本叫《沉思录》的书在国内突然曝光度很多&#xff0c;因为前某国家领导人“摆案头&#xff0c;读百遍”。《沉思录》是古罗马皇帝马可奥勒写给自己的书&#xff0c;内容大部分是在鞍马劳顿中写的。其实有一句“我们所听到的不过只是…

斐波那契数列的鬼畜的性质

斐波那契数列的鬼畜的性质 斐波那契数列定理1 \(gcd(f[i],f[i1])1\) 利用辗转相减法 证明&#xff1a;\(gcd(f[i],f[i1])\)\(gcd(f[i1]-f[i],f[i])\)\(gcd(f[i-1],f[i])\)\(....\)\(gcd(f[1],f[2])1\) 斐波那契数列定理2 \(f[mn]f[m-1]f[n]f[m]f[n1]\) 证明&#xff1a;\(f[mn]…

xml与java对象转换 -- XStreamAlias

XStreamAlias使用 一、 特点: 简化的API; 无映射文件; 高性能,低内存占用; 整洁的XML; 不需要修改对象;支持内部私有字段,不需要setter/getter方法 提供序列化接口; 自定义转换类型策略; XStream的优点很多&#xff0c;但是也有一些小bug&#xff0c;比如在定义别名中的下划线…

windows下安装zabbix_agent

Server端在linux系统上&#xff0c;server端版本为2.2.6&#xff0c;是以前就装好的已经跑了很久的稳定版。目前的需求是要将新业务的服务器添加到该监控队列。而这些服务器是windows系统。 第一次下载了最新版的zabbix_agent for windows。按照正常程序安装完成后&#xff0c;…

JS和Jquery获取和修改label的值

获取值&#xff1a; label标签在JS和Jquery中使用不能像其他标签一样用value获取它的值&#xff1a; var labeldocument.getElementById("id");var valuelabel.value;var value$("#id").val();可以这样&#xff1a;JS: var labeldocument.getElementById(&…

Linux内核访问外设I/O--动态映射(ioremap)和静态映射(map_desc)

本篇文章主要介绍了"Linux内核访问外设I/O--动态映射(ioremap)和静态映射(map_desc)"&#xff0c;主要涉及到Linux内核访问外设I/O--动态映射(ioremap)和静态映射(map_desc)方面的内容&#xff0c;对于Linux内核访问外设I/O--动态映射(ioremap)和静态映射(map_desc)感…

点击显示隐藏盒子函数

示例&#xff1a;&#xff08;手机导航栏&#xff09; <header> <div class"logo"></div> <p class"text">微蜂传媒</p> <div class"nav_btn" οnclick"showHide(.dropdown_menu)"></div> …

MyBatis 缓存机制

Mybatis 有两级缓存&#xff1a; 一级缓存&#xff1a; 也称为本地缓存&#xff0c;SqlSession级别的缓存。一级缓存是一直开启的&#xff1b; 与数据库同一次会话期间查询到的数据会放在本地缓存中&#xff0c;以后如果需要获取相同的数据&#xff0c;直接从缓存中拿&#xff…

Android虚拟化引擎VirtualApp探究

2019独角兽企业重金招聘Python工程师标准>>> 介绍 首先需要说明的是&#xff0c;VirtualApp并不是前些阵子滴滴开源的插件化框架VirtualApk。 VirtualApp是一个更加黑科技的东西&#xff0c;他可以创建一个虚拟空间&#xff0c;你可以在虚拟空间内任意的安装、启动和…

揭开全景相机的创业真相

&#xff08;Bubl全景相机&#xff09; 国外一开源&#xff0c;国内就自主。这在VR&#xff08;虚拟现实&#xff09;领域体现的淋漓尽致——Google的Cardborad一开源&#xff0c;国内就有数百家厂商蜂拥做了各种插手机的VR盒子。到了全景相机&#xff0c;这一幕似乎又开始重演…

一个厉害的网站

2019独角兽企业重金招聘Python工程师标准>>> dromara 发现一个网站&#xff0c;发现上面的开源项目真的都非常厉害诶。 转载于:https://my.oschina.net/miaojiangmin/blog/2934221