【转】用户空间使用i2c_dev--不错

原文网址:http://blog.csdn.net/yuanlulu/article/details/6161706

============================================
作者: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>

这两个头文件中定义了之后需要用到的结构体和宏。

然后就可以打开设备节点了。但是打开哪一个呢?因为适配器的编号并不固定。为此我们在中端中运行以下命令:

[[email protected] /]# cat /sys/class/i2c-dev/i2c-0/name

PNX4008-I2C0

[[email protected] /]# cat /sys/class/i2c-dev/i2c-1/name

PNX4008-I2C1

[[email protected] /]# 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;

}

时间: 2024-10-14 06:26:04

【转】用户空间使用i2c_dev--不错的相关文章

Operating System-Thread(3)用户空间和内核空间实现线程

本文主要内容: 操作系统用户空间和内核空间简介 在用户空间实现线程 在内核空间实现线程 用户空间和内核空间线程混合使用 一.用户空间和内核空间简介 用户空间:User space,内核空间:Kernel Space.这两个是操作系统的重要概念之一,今天为了线程做一下简单的介绍: 内核空间用于运行操作系统核心组件,比如内存管理组件,IO交互组件,文件管理.中断管理组件等,同时驱动程序(Driver)也运行在内核空间. 用户空间,用于运行普通应用程序. 示意图: 二,在用户空间实现线程 用户空间的线

内核态和用户态,内核空间和用户空间

内核态与用户态是操作系统的两种运行级别,intel cpu提供Ring0-Ring3三种级别的运行模式.Ring0级别最高,Ring3最低 内核态可以拥有比用户态更大的权限 处于内核态的进程,可以访问用户进程空间(是虚拟地址空间),就是通过进程的页表(进程本身就是一个4G虚拟地址空间.其中用户空间的3G是独立的,内核空间是共享的)来访问用户地址空间对应的物理地址,从而访问用户空间 相反用户态的进程,只能访问用户地址空间(虚拟地址空间) 在内核态下,利用内核地址空间中的高端内存地址空间,来映射高端

用户空间与内核空间,进程上下文与中断上下文[总结]

用户空间与内核空间,进程上下文与中断上下文[总结] 最近有研究到zabbix监控,就得清楚cpu各个指标的含义, 1,简单回顾下cpu及计算机组成: 计算机五大部件: 运算器 控制器 存储器 输入/输出设备. 2,cpu 进程的内核态和用户态 我们知道现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操心系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限.为 了保证用户进程不能直接操作内核,

内核空间与用户空间的通信方式

内核空间与用户空间的通信方式 下面总结了7种方式,主要对以前不是很熟悉的方式做了编程实现,以便加深印象. 1.使用API:这是最常使用的一种方式了 A.get_user(x,ptr):在内核中被调用,获取用户空间指定地址的数值并保存到内核变量x中. B.put_user(x,ptr):在内核中被调用,将内核空间的变量x的数值保存到到用户空间指定地址处. C.Copy_from_user()/copy_to_user():主要应用于设备驱动读写函数中,通过系统调用触发. 2.使用proc文件系统:

Linux Malloc分析-从用户空间到内核空间

本文介绍malloc的实现及其malloc在进行堆扩展操作,并分析了虚拟地址到物理地址是如何实现映射关系. ordeder原创,原文链接: http://blog.csdn.net/ordeder/article/details/41654509 1背景知识 1.1 进程的用户空间 图1:来源 http://www.open-open.com/lib/view/open1409716051963.html 该结构是由进程task_struct.mm_struct进行管理的mm_struct的定义

操作系统--用户空间和内核空间,用户态和内核态

内核空间和用户空间,内核态和用户态(转载) 内核空间和用户空间Linux简化了分段机制,使得虚拟地址与线性地址总是一致,因此,Linux的虚拟地址空间也为0-4G.Linux内核将这4G字节的空间分为两部分.将最高的1G字节(从虚拟地址 0xC0000000到0xFFFFFFFF),供内核使用,称为“内核空间”.而将较低的 3G字节(从虚拟地址 0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间).因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有

rhcl7的用户空间父进程systemd

Sysmted systemd是什么? 大家知道当内核完成初始化,以只读方式挂载rootfs以后,运行了用户空间的第一个进程叫init,我们以centos系列为视角来看看: CentOS 5: SysV init CentOS 6:Upstart CentOS 7:Systemd     Systemd的新特性: 系统引导时实现服务并行启动: 按需激活进程:注:访问时候才激活进程 系统状态快照: 基于依赖关系定义服务控制逻辑: 核心概念:unit unit是什么?         Unit 文件

内核空间和用户空间的分界 PAGE_OFFSET

PAGE_OFFSET 首先看看PAGE_OFFSET的功能 内存映射 |            用户空间                  |   内核空间   | |——————+——————+——————+———————| 物理 A:0        1G                         B:3G          C:4G B: 定义为    PAGE_OFFSET 0-1G: 和内核空间又有丰富的内容,我还没有整理,以后再说. ---------------------

Linux内核工程导论——用户空间设备管理

用户空间设备管理 用户空间所能见到的所有设备都放在/dev目录下(当然,只是一个目录,是可以变化的),文件系统所在的分区被当成一个单独的设备也放在该目录下.以前的2.4版本的曾经出现过devfs,这个思路非常好,在内核态实现对磁盘设备的动态管理.可以做到当用户访问一个设备的设备的时候,devfs驱动才会去加载该设备的驱动.甚至每个节点的设备号都是动态获得的.但是该机制的作者不再维护他的代码,linux成员经过讨论,使用用户态的udev代替内核态的devfs,所以现在的devfs已经废弃了.用户态