用户空间使用i2c_dev
============================================
作者:yuanlulu
http://blog.csdn.NET/yuanlulu
版权没有,但是转载请保留此段声明
============================================
第1章 用户空间使用i2c_dev
对于注册的i2c适配器,用户空间也可以使用它们。在Linux内核代码文件/include/linux/i2c-dev.c中针对每个适配器生成一个主设备号为89的设备节点,实现了文件操作接口,用户空间可以通过i2c设备节点访问i2c适配器。适配器的编号从0开始,和适配器的设备节点的次设备号相同。
i2c适配器的设备节点是/dev/i2c-x,其中x是数字,代表适配器的编号。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。
1.1 前期准备
为了在用户空间的程序当中操作i2c适配器,必须在程序中包含以下两句:
#include<linux/i2c-dev.h>
#include<linux/i2c.h>
这两个头文件中定义了之后需要用到的结构体和宏。
然后就可以打开设备节点了。但是打开哪一个呢?因为适配器的编号并不固定。为此我们在中端中运行以下命令:
[root@zlg /]# cat /sys/class/i2c-dev/i2c-0/name
PNX4008-I2C0
[root@zlg /]# cat /sys/class/i2c-dev/i2c-1/name
PNX4008-I2C1
[root@zlg /]# cat /sys/class/i2c-dev/i2c-2/name
USB-I2C
如果我们想打开第二个适配器,刚好它的编号是1,对应的设备节点是/dev/i2c-1。那么可以用下面的方法打开它:
int fd;
if ((fd = open("/dev/i2c-1",O_RDWR))< 0) {
/* 错误处理 */
exit(1);
}
打开适配器对应的设备节点,i2c-dev为打开的线程建立一个i2c_client,但是这个i2c_client并不加到i2c_adapter的client链表当中。当用户关闭设备节点时,它自动被释放。
1.2 IOCTL控制
查看include/linux/i2c-dev.h文件,可以看到i2c-dev支持的IOCTL命令。如<!--[if supportFields]> REF _Ref283302932 /h <![endif]-->程序清单 3.1<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。
程序清单<!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1 <![endif]-->1<!--[if supportFields]><![endif]--> i2c-dev IOCTL命令
#define I2C_RETRIES 0x0701 /*设置收不到ACK时的重试次数 */
#define I2C_TIMEOUT 0x0702 /* 设置超时时限的jiffies */
#define I2C_SLAVE 0x0703 /*设置从机地址 */
#define I2C_SLAVE_FORCE 0x0706 /* 强制设置从机地址 */
#define I2C_TENBIT 0x0704 /*选择地址位长:=0 for 7bit , != 0 for 10 bit */
#define I2C_FUNCS 0x0705 /*获取适配器支持的功能 */
#define I2C_RDWR 0x0707 /*Combined R/W transfer (one STOP only) */
#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */
#define I2C_SMBUS 0x0720 /*SMBus transfer */
下面进行一一解释。
1. 设置重试次数
ioctl(fd, I2C_RETRIES,m);
这句话设置适配器收不到ACK时重试的次数为m。默认的重试次数为1。
2. 设置超时
ioctl(fd, I2C_TIMEOUT,m);
设置SMBus的超时时间为m,单位为jiffies。
3. 设置从机地址
ioctl(fd, I2C_SLAVE,addr);
ioctl(fd, #defineI2C_SLAVE_FORCE, addr);
在调用read()和write()函数之前必须设置从机地址。这两行都可以设置从机的地址,区别是第二行无论内核中是否已有驱动在使用这个地址都会成功,第一行则只在该地址空闲的情况下成功。由于i2c-dev创建的i2c_client不加入i2c_adapter的client列表,所以不能防止其它线程使用同一地址,也不能防止驱动模块占用同一地址。
4. 设置地址模式
ioctl(file,I2C_TENBIT,select)
如果select不等于0选择10比特地址模式,如果等于0选择7比特模式,默认7比特。只有适配器支持I2C_FUNC_10BIT_ADDR,这个请求才是有效的。
5. 获取适配器功能
ioctl(file,I2C_FUNCS,(unsignedlong *)funcs)
获取的适配器功能保存在funcs中。各比特的含义如<!--[if supportFields]>REF _Ref283305554 /h <![endif]-->程序清单 3.2<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。具体的含义可以参考<!--[if supportFields]>REF _Ref283456550 /r /h <![endif]-->第4章<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->。
程序清单<!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1 <![endif]-->2<!--[if supportFields]><![endif]--> I2C FUNCTIONALILTY
/* include/linux/i2c.h */
#define I2C_FUNC_I2C 0x00000001
#define I2C_FUNC_10BIT_ADDR 0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004/*I2C_M_{REV_DIR_ADDR,NOSTART,..}*/
#define I2C_FUNC_SMBUS_PEC 0x00000008
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK 0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000/* I2C-like block xfer */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 0x10000000 /* I2C-like block xfer */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */
6. I2C层通信
ioctl(file,I2C_RDWR,(structi2c_rdwr_ioctl_data *)msgset);
这一行代码可以使用I2C协议和设备进行通信。它进行连续的读写,中间没有间歇。只有当适配器支持I2C_FUNC_I2C此命令才有效。参数是一个指针,指向一个结构体,它的定义如<!--[if supportFields]>REF _Ref283305956 /h <![endif]-->程序清单 3.3<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。其中i2c_msg的定义参考<!--[if supportFields]> REF _Ref283227534 /h <![endif]-->程序清单 1.7<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->。
程序清单<!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1 <![endif]-->3<!--[if supportFields]><![endif]--> i2c_rdwr_ioctl_data
struct i2c_rdwr_ioctl_data {
structi2c_msg __user *msgs; /* 指向i2c_msgs数组 */
__u32nmsgs; /* 消息的个数 */
};
msgs[] 数组成员包含了指向各自缓冲区的指针。这个函数会根据是否在消息中的flags置位I2C_M_RD来对缓冲区进行读写。从机的地址以及是否使用10比特地址模式记录在每个消息中,忽略之前ioctl设置的结果。
7. 设置SMBus PEC
ioctl(file,I2C_PEC,(long )select);
如果select不等于0选择SMBus PEC (packet error checking),等于零则关闭这个功能,默认是关闭的。
这个命令只对SMBus传输有效。这个请求只在适配器支持I2C_FUNC_SMBUS_PEC时有效;如果不支持这个命令也是安全的,它不做任何工作。
8. SMBus通信
ioctl(file, I2C_SMBUS, (i2c_smbus_ioctl_data*)msgset);
这个函数和I2C_RDWR类似,参数的指针指向i2c_smbus_ioctl_data类型的变量,它的定义如<!--[if supportFields]> REF _Ref283308161 /h <![endif]-->程序清单 3.4<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。如何填写i2c_smbus_ioctl_data的各个成员,参考<!--[if supportFields]> REF _Ref283455290 /r/h <![endif]-->4.3<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->节。
程序清单<!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1 <![endif]-->4<!--[if supportFields]><![endif]--> i2c_smbus_ioctl_data
struct i2c_smbus_ioctl_data {
__u8read_write;
__u8command;
__u32size;
unioni2c_smbus_data __user *data;
};
1.3 i2c_dev使用例程
要想在用户空间使用i2c适配器,首先要如3.1<!--[if gte mso 9]><![endif]-->节所示,选择某个适配器的设备节点打开,然后才能进行通信。
1.3.1 read()/write()
通信的方式有两种,一种是使用操作普通文件的接口read()和write()。这两个函数间接调用了i2c_master_recv和i2c_master_send。但是在使用之前需要使用I2C_SLAVE设置从机地址,设置可能失败,需要检查返回值。这种通信过程进行I2C层的通信,一次只能进行一个方向的传输。
下面的程序是ARM与E2PROM芯片通信的例子,如<!--[if supportFields]> REF _Ref283651035 /h <![endif]-->程序清单 3.5<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。
程序清单<!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1 <![endif]-->5<!--[if supportFields]><![endif]--> 使用read()/write()与i2c设备通信
#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#define CHIP "/dev/i2c-0"
#define CHIP_ADDR 0x50
int main()
{
printf("hello,this is i2c tester/n");
int fd =open(CHIP, O_RDWR);
if (fd< 0) {
printf("open"CHIP"failed/n");
gotoexit;
}
if (ioctl(fd,I2C_SLAVE_FORCE, CHIP_ADDR) < 0) { /*设置芯片地址 */
printf("oictl:setslave address failed/n");
gotoclose;
}
struct i2c_msg msg;
unsignedchar rddata;
unsignedchar rdaddr[2] = {0, 0}; /* 将要读取的数据在芯片中的偏移量 */
unsignedchar wrbuf[3] = {0, 0, 0x3c}; /* 要写的数据,头两字节为偏移量 */
printf("inputa char you want to write to E2PROM/n");
wrbuf[2]= getchar();
printf("writereturn:%d, write data:%x/n", write(fd, wrbuf, 3), wrbuf[2]);
sleep(1);
printf("writeaddress return: %d/n",write(fd, rdaddr, 2)); /* 读取之前首先设置读取的偏移量 */
printf("readdata return:%d/n", read(fd, &rddata, 1));
printf("rddata:%c/n", rddata);
close:
close(fd);
exit:
return0;
}
1.3.2 I2C_RDWR
还可以使用I2C_RDWR实现同样的功能,如<!--[if supportFields]> REF _Ref283651333 /h <![endif]-->程序清单 3.6<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。此时ioctl返回的值为执行成功的消息数。
程序清单<!--[if supportFields]> STYLEREF 1 /s <![endif]-->3<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1 <![endif]-->6<!--[if supportFields]><![endif]--> 使用I2C_RDWR与I2C设备通信
#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#define CHIP "/dev/i2c-0"
#define CHIP_ADDR 0x50
int main()
{
printf("hello,this is i2c tester/n");
int fd =open(CHIP, O_RDWR);
if (fd< 0) {
printf("open"CHIP"failed/n");
gotoexit;
}
struct i2c_msg msg;
unsignedchar rddata;
unsignedchar rdaddr[2] = {0, 0};
unsignedchar wrbuf[3] = {0, 0, 0x3c};
printf("inputa char you want to write to E2PROM/n");
wrbuf[2]= getchar();
structi2c_rdwr_ioctl_data ioctl_data;
structi2c_msg msgs[2];
msgs[0].addr= CHIP_ADDR;
msgs[0].len= 3;
msgs[0].buf= wrbuf;
ioctl_data.nmsgs= 1;
ioctl_data.msgs= &msgs[0];
printf("ioctlwrite,return :%d/n", ioctl(fd, I2C_RDWR, &ioctl_data));
sleep(1);
msgs[0].addr= CHIP_ADDR;
msgs[0].len= 2;
msgs[0].buf= rdaddr;
msgs[1].addr= CHIP_ADDR;
msgs[1].flags|= I2C_M_RD;
msgs[1].len= 1;
msgs[1].buf= &rddata;
ioctl_data.nmsgs= 1;
ioctl_data.msgs= msgs;
printf("ioctlwrite address, return :%d/n", ioctl(fd, I2C_RDWR, &ioctl_data));
ioctl_data.msgs= &msgs[1];
printf("ioctlread, return :%d/n", ioctl(fd, I2C_RDWR, &ioctl_data));
printf("rddata:%c/n", rddata);
close:
close(fd);
exit:
return0;
}
用户空间使用i2c_dev的更多相关文章
- 【转】用户空间使用i2c_dev--不错
原文网址:http://blog.csdn.net/yuanlulu/article/details/6161706 ========================================= ...
- 在Linux用户空间做内核空间做的事情
导读 我相信,Linux 最好也是最坏的事情,就是内核空间(kernel space)和用户空间(user space)之间的巨大差别.如果没有这个区别,Linux 可能也不会成为世界上影响力最大的操 ...
- Linux用户空间与内核空间(理解高端内存)
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...
- Linux用户空间与内核空间
源:http://blog.csdn.net/f22jay/article/details/7925531 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针 ...
- 深入理解linux网络技术内幕读书笔记(三)--用户空间与内核的接口
Table of Contents 1 概论 1.1 procfs (/proc 文件系统) 1.1.1 编程接口 1.2 sysctl (/proc/sys目录) 1.2.1 编程接口 1.3 sy ...
- CMA连续物理内存用户空间映射---(一)
背景: 在多媒体和图像处理等应用中,经经常使用到大块内存,尤其是硬件编解码.须要内核分配大块的物理连续内存. 这里希望通过把从内核分配的连续物理内存映射到用户空间.在用户空间经过处理,又能够入队到驱动 ...
- Linux usb子系统(三):通过usbfs操作设备的用户空间驱动
内核中提供了USB设备文件系统(usbdevfs,Linux 2.6改为usbfs,即USB文件系统),它和/proc类似,都是动态产生的.通过在/etc/fstab文件中添加如下一行:none /p ...
- linux 用户空间获得纳秒级时间ns
一.引言 我们在测试程序的性能的时候往往需要获得ns级的精确时间去衡量一个程序的性能,下面介绍下linux中用户空间获得ns级时间的方法 二.用户空间获得ns级时间 使用clock_gettime函数 ...
- 回收进程用户空间资源 exit()函数 _exit()函数 atexit()函数 on_exit()函数
摘要:本文主要讲述进程的终止方式,以及怎样使用exit()函数来终止进程.回收进程用户空间资源:分析了exit()函数与_exit()函数,returnkeyword的差异.同一时候具体解读了怎样使用 ...
随机推荐
- 函数的扩展——箭头函数this的使用
箭头函数中的this指向的是定义时的this,而不是执行时的的this . 举例: 案例中,我们的obj对象中有一个属性x和一个属性show( )方法,show( )通过this打印出x的值,结果是u ...
- Ubuntu16.04+GTX1080配置TensorFlow并实现图像风格转换
1. TensorFlow TensorFlow是谷歌基于DistBelief进行研发的第二代人工智能学习系统,表达了高层次的机器学习计算,大幅简化了第一代系统,并且具备更好的灵活性和可延展性. Te ...
- LeetCode945-使数组唯一的最小增量
问题:使数组唯一的最小增量 给定整数数组 A,每次 move 操作将会选择任意 A[i],并将其递增 1. 返回使 A 中的每个值都是唯一的最少操作次数. 示例 1: 输入:[1,2,2] 输出:1 ...
- mysql按指定顺序排序
select id,name from htms_center_freight_users where id in(114,112,91,223,134) order by find_in_set(i ...
- 【linux】文件默认权限:umask
在默认权限的属性上,目录与文件是不一样的.从第六章我们知道 x 权限对於目录是非常重要的! 但是一般文件的创建则不应该有运行的权限,因为一般文件通常是用在於数据的记录嘛!当然不需要运行的权限了. 因此 ...
- 【kindle】【转发】kindle链接WIFI自动断开问题
在电脑上新建一个新文件,名为“WIFI_NO_NET_PROBE”,同时把后缀名删掉,让它变成一个无格式文件.Kindle 连接电脑,把新建的文件放进Kindle的根目录,断开Kindle之后重启Ki ...
- Django ORM (三) 查询,删除,更新操作
ORM 查询操作 修改 views.py 文件 from django.shortcuts import render, HttpResponse from app01 import models f ...
- Python全栈之jQuery笔记
jQuery runnoob网址: http://www.runoob.com/jquery/jquery-tutorial.html jQuery API手册: http://www.runoob. ...
- matlab callback 数据传递
M文件中内的每个Callback都可以视为一个独立的可执行的接口,因此,任一个Callback触发后所执行的运算值若要在其他Callback中使用,就无法与MATLAB工作空间内的变量继续执行操作,也 ...
- 面试中注意3个javascript的问题
JavaScript 是所有现代浏览器的官方语言.因此,各种语言的开发者面试中都会遇到 JavaScript 问题. 本文不讲最新的 JavaScript 库,通用开发实践,或任何新的 ES6 函数. ...