Linux应用程序直接调用I2C控制器实现的设备驱动

一、简介

i2c设备驱动有两种模式:一种是用户模式设备驱动,这种驱动依赖于i2c子系统中i2c-dev驱动,这种驱动对应用程序员的要求很高,要求应用程序员了解硬件的一些东西,了解时序、地址等;另一种是普通的设备驱动,应用程序员在使用的时候就像读写文件一样。

下文介绍怎么在应用层通过设备节点操作IIC。

二、操作步骤

1、内核配置里的dev-interface选项

make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
Device Drivers  --->
    <*> I2C support  --->
        <*>   I2C device interface

选上此项,更新内核镜像重启系统后,应就可以在/dev目录下出现i2c-*的设备文件,应用程序就是通过这些设备文件调用控制器的。
设备文件的命名方式, 如序号为0的控制器,对应的设备文件应为/dev/i2c-0.

2、打开要调用的控制器设备文件

    int fd = open("/dev/i2c-0", O_RDWR);


3、设置超时和传输失败时的重试次数(非必须)

    ioctl(fd, I2C_TIMEOUT, 10);
    ioctl(fd, I2C_RETRIES, 2);


4、准备好需要传输的数据. 

应用层操作IIC是以数据包进行交流的,对应的结构体:

//include/linux/i2c-dev.h

/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
	struct i2c_msg __user *msgs;	/* pointers to i2c_msgs */
	__u32 nmsgs;			/* number of i2c_msgs */
};


//include/linux/i2c.h
struct i2c_msg {
	__u16 addr;	/* iic从机地址	*/
	__u16 flags; //iic的标志,见如下宏定义  1为读操作,0为写操作(写的没有宏定义,但是可以直接用)
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	__u16 len;		/* 单位为字节				*/
	__u8 *buf;		/* 读操作的时候存取读的信息,写操作要提前给buffer赋值			*/
};

5、调用API函数读写数据

ioctl(file, I2C_RDWR, struct i2c_rdwr_ioctl_data *msgset); 

调用此ioctl函数类似设备驱动里调用i2c_transfer函数。调用一次,只会在完成时发出一个停止信号,不管发出了多少个i2c_msg。 而且每一个i2c_msg都会有一个开始信号. 

三、实例:

/*
C library for reading/writing I2C slave device registers from Raspberry Pi 1 Model B


*/

#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#include <linux/i2c-dev.h>
// Terrible portability hack between arm-linux-gnueabihf-gcc on Mac OS X and native gcc on raspbian.
#ifndef I2C_M_RD
#include <linux/i2c.h>
#endif

typedef unsigned char   u8;
int i2c_fd = -1;
const char *i2c_fname = "/dev/i2c-1";

// Returns a new file descriptor for communicating with the I2C bus:
int i2c_init(void) {
    if ((i2c_fd = open(i2c_fname, O_RDWR)) < 0) {
        char err[200];
        sprintf(err, "open('%s') in i2c_init", i2c_fname);
        perror(err);
        return -1;
    }

    return i2c_fd;
}

void i2c_close(void) {
    close(i2c_fd);
}

// Write to an I2C slave device's register:
int i2c_write(u8 slave_addr, u8 reg, u8 data) {
    int retval;
    u8 outbuf[2];

    struct i2c_msg msgs[1];
    struct i2c_rdwr_ioctl_data msgset[1];

    outbuf[0] = reg;
    outbuf[1] = data;

    msgs[0].addr = slave_addr;
    msgs[0].flags = 0;
    msgs[0].len = 2;
    msgs[0].buf = outbuf;

    msgset[0].msgs = msgs;
    msgset[0].nmsgs = 1;

    if (ioctl(i2c_fd, I2C_RDWR, &msgset) < 0) {
        perror("ioctl(I2C_RDWR) in i2c_write");
        return -1;
    }

    return 0;
}

// Read the given I2C slave device's register and return the read value in `*result`:
int i2c_read(u8 slave_addr, u8 reg, u8 *result) {
    int retval;
    u8 outbuf[1], inbuf[1];
    struct i2c_msg msgs[2];//这个地方要封两个包。因为要先写入需要读的地址,然后才是读操作
    struct i2c_rdwr_ioctl_data msgset[1];

    msgs[0].addr = slave_addr;
    msgs[0].flags = 0;//表示写
    msgs[0].len = 1;
    msgs[0].buf = outbuf;

    msgs[1].addr = slave_addr;
    msgs[1].flags = I2C_M_RD | I2C_M_NOSTART;//读
    msgs[1].len = 1;
    msgs[1].buf = inbuf;

    msgset[0].msgs = msgs;
    msgset[0].nmsgs = 2;

    outbuf[0] = reg;

    inbuf[0] = 0;

    *result = 0;
    if (ioctl(i2c_fd, I2C_RDWR, &msgset) < 0) {
        perror("ioctl(I2C_RDWR) in i2c_read");
        return -1;
    }

    *result = inbuf[0];
    return 0;
}

ref:

Pololu - 12.8. Example I²C code for Linux in C

i2c-dev.h - include/linux/i2c-dev.h - Linux source code (v3.3) - Bootlin

【北京迅为】嵌入式学习之Linux驱动篇_哔哩哔哩_bilibili

https://gist.github.com/JamesDunne/9b7fbedb74c22ccc833059623f47beb7

[Linux驱动炼成记] 01-用户空间操作IIC - 云+社区 - 腾讯云

Linux应用程序直接调用I2C控制器实现的设备驱动_jklinux的博客-程序员ITS301_i2c控制器驱动 - 程序员ITS301

i2c驱动之调用ioctl函数进行读写at24c08_luckywang1103的专栏-CSDN博客_i2c_rdwr_ioctl_data

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
I2C控制器驱动程序是用于与I2C设备进行通信的软件模块。它允许应用程序通过I2C总线与外部设备进行数据交换。根据引用和引用的描述,I2C控制器驱动程序可以分为两种模式:用户模式设备驱动和普通设备驱动。 用户模式设备驱动是一种依赖于i2c-dev驱动驱动程序。它要求应用程序员具备一定的硬件知识,了解I2C设备的时序、地址等信息。使用用户模式设备驱动时,应用程序员需要直接调用I2C控制器实现设备的读写操作。 普通设备驱动是一种更简单的驱动程序,应用程序员可以像读写文件一样使用它。普通设备驱动隐藏了底层I2C控制器的细节,提供了一组简单的API供应用程序使用。应用程序员不需要了解I2C设备的底层细节,只需要调用相应的API来进行数据交换。 下面是一个示例,演示了如何使用普通设备驱动程序进行I2C设备的读写操作: ```c #include <linux/i2c-dev.h> #include <fcntl.h> #include <unistd.h> int main() { int file; char *filename = "/dev/i2c-0"; // I2C设备文件路径 int addr = 0x50; // I2C设备地址 // 打开I2C设备文件 if ((file = open(filename, O_RDWR)) < 0) { printf("Failed to open the bus.\n"); return 1; } // 设置I2C设备地址 if (ioctl(file, I2C_SLAVE, addr) < 0) { printf("Failed to acquire bus access and/or talk to slave.\n"); return 1; } // 向I2C设备写入数据 char buffer[2] = {0x01, 0x02}; if (write(file, buffer, 2) != 2) { printf("Failed to write to the I2C device.\n"); return 1; } // 从I2C设备读取数据 char data[2]; if (read(file, data, 2) != 2) { printf("Failed to read from the I2C device.\n"); return 1; } // 关闭I2C设备文件 close(file); return 0; } ``` 这是一个使用C语言编写的简单示例,演示了如何使用普通设备驱动程序进行I2C设备的读写操作。在示例中,我们首先打开I2C设备文件,然后设置I2C设备地址。接下来,我们向I2C设备写入数据,并从I2C设备读取数据。最后,我们关闭I2C设备文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值