Linux I2C 驱动实验(2)

1 I2C 设备和驱动匹配过程

I2C 设备和驱动的匹配过程是由 I2C 核心来完成的, drivers/i2c/i2c-core.c 就是 I2C 的核心
部分, I2C 核心提供了一些与具体硬件无关的 API 函数,比如前面讲过的:
1 i2c_adapter 注册 / 注销函数
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)
2 i2c_driver 注册 / 注销函数
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
设备和驱动的匹配过程也是由 I2C 总线完成的, I2C 总线的数据结构为 i2c_bus_type ,定义
drivers/i2c/i2c-core.c 文件, i2c_bus_type 内容如下:
736 struct bus_type i2c_bus_type = {
737 .name = "i2c",
738 .match = i2c_device_match,
739 .probe = i2c_device_probe,
740 .remove = i2c_device_remove,
741 .shutdown = i2c_device_shutdown,
742 };
.match 就是 I2C 总线的设备和驱动匹配函数,在这里就是 i2c_device_match 这个函数,此
函数内容如下:
457 static int i2c_device_match(struct device *dev, struct
device_driver *drv)
458 {
459 struct i2c_client *client = i2c_verify_client(dev);
460 struct i2c_driver *driver;
461
462 if (!client)
463 return 0;
464
465 /* Attempt an OF style match */
466 if (of_driver_match_device(dev, drv))
467 return 1;
468
469 /* Then ACPI style match */
470 if (acpi_driver_match_device(dev, drv))
471 return 1;
472
473 driver = to_i2c_driver(drv);
474 /* match on an id table if there is one */
475 if (driver->id_table)
476 return i2c_match_id(driver->id_table, client) != NULL;
477
478 return 0;
479 }
466 行, of_driver_match_device 函数用于完成设备树设备和驱动匹配。比较 I2C 设备节
点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 I2C
设备和驱动匹配。
470 行, acpi_driver_match_device 函数用于 ACPI 形式的匹配。
476 行, i2c_match_id 函数用于传统的、无设备树的 I2C 设备和驱动匹配过程。比较 I2C
设备名字和 i2c_device_id name 字段是否相等,相等的话就说明 I2C 设备和驱动匹配。

2 I2C 设备驱动编写流程

   1IO 修改或添加

 首先肯定是要修改 IOAP3216C 用到了 I2C1 接口,I.MX6U-ALPHA 开发板上的 I2C1 接 口使用到了 UART4_TXD UART4_RXD,因此肯定要在设备树里面设置这两个 IO。如果要用 到 AP3216C 的中断功能的话还需要初始化 AP_INT 对应的 GIO1_IO01 这个 IO,本章实验我们 不使用中断功能。因此只需要设置 UART4_TXD UART4_RXD 这两个 IONXP 其实已经将 他这两个 IO 设置好了,打开 imx6ull-alientek-emmc.dts,然后找到如下内容:

		pinctrl_i2c1: i2c1grp {
			fsl,pins = <
				MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
				MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
			>;
		};
pinctrl_i2c1 就是 I2C1 IO 节点,这里将 UART4_TXD UART4_RXD 这两个 IO 分别
复用为 I2C1_SCL I2C1_SDA ,电气属性都设置为 0x4001b8b0
2 、在 i2c1 节点追加 ap3216c 子节点
i2c1: i2c@021a0000 {
				#address-cells = <1>;
				#size-cells = <0>;
				compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
				reg = <0x021a0000 0x4000>;
				interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
				clocks = <&clks IMX6UL_CLK_I2C1>;
				status = "disabled";
			};
AP3216C 是连接到 I2C1 上的,因此需要在 i2c1 节点下添加 ap3216c 的设备子节点,在
imx6ull-alientek-emmc.dts 文件中找到 i2c1 节点,此节点默认内容如下:
1 &i2c1 { 
2 clock-frequency = <100000>;
3 pinctrl-names = "default"; 
4 pinctrl-0 = <&pinctrl_i2c1>;
5 status = "okay"; 
6 
7 mag3110@0e { 
8 compatible = "fsl,mag3110"; 
9 reg = <0x0e>;
10 position = <2>;
11 };
12
13 fxls8471@1e {
14 compatible = "fsl,fxls8471";
15 reg = <0x1e>;
16 position = <0>;
17 interrupt-parent = <&gpio5>;
18 interrupts = <0 8>;
19 };
20 };
2 行, clock-frequency 属性为 I2C 频率,这里设置为 100KHz
4 行, pinctrl-0 属性指定 I2C 所使用的 IO 为示例代码 61.5.1.1 中的 pinctrl_i2c1 子节
点。
7~11 行, mag3110 是个磁力计, NXP 官方的 EVK 开发板上接了 mag3110 ,因此 NXP
i2c1 节点下添加了 mag3110 这个子节点。
13~19 行, NXP 官方 EVK 开发板也接了一个 fxls8471。
i2c1 节点里面原有的 mag3110 fxls8471 这两个 I2C 子节点删除,然后添加 ap3216c
子节点信息,完成以后的 i2c1 节点内容如下所示:
&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

	ap3216c@1e{
		compatible = "alientek,ap3216c";
		reg = <0x1e>;
	};
};
7 行, ap3216c 子节点, @ 后面的“ 1e ”是 ap3216c 的器件地址。
8 行,设置 compatible 值为“ alientek,ap3216c ”。
9 行, reg 属性也是设置 ap3216c 器件地址的,因此 reg 设置为 0x1e
设备树修改完成以后使用“ make dtbs ”重新编译一下,然后使用新的设备树启动 Linux
核。 /sys/bus/i2c/devices 目录下存放着所有 I2C 设备,如果设备树修改正确的话,会在
/sys/bus/i2c/devices 目录下看到一个名为“ 0-001e ”的子目录,如图 61.5.1.1 所示:

3 编写驱动框架,

 3.1I2C设备驱动框架和字符设备驱动框架

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/input.h>
#include <linux/i2c.h>


struct ap3216c_dev{
    dev_t devid;
    int major;
    int minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node  *nd;
};
struct ap3216c_dev ap3216cdev;
#define ap3216c_cnt 1
#define ap3216c__name "ap3216c"

static int ap3216cdev_open(struct inode *inode, struct file *filp)
{
    printk("ap3216c open\r\n");
    return 0;
}

static int ap3216cdev_release(struct inode *inode, struct file *filp)
{
    printk("ap3216c release\r\n");

    return 0;
}
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    printk("ap3216c read\r\n");
    return 0;
}
static const struct file_operations ap3216cdev_fops = {
    .owner = THIS_MODULE,
	.read = ap3216c_read ,
	.open = ap3216cdev_open,
	.release = ap3216cdev_release,
};
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    printk("ap3216 probe\n");
    ap3216cdev.major = 0; 
    if(ap3216cdev.major) {
        ap3216cdev.devid = MKDEV(ap3216cdev.major,0);
        ret = register_chrdev_region(ap3216cdev.devid, ap3216c_cnt, ap3216c__name);
    } else {
        ret = alloc_chrdev_region(&ap3216cdev.devid, 0, ap3216c_cnt, ap3216c__name);
        ap3216cdev.major = MAJOR(ap3216cdev.devid);
        ap3216cdev.minor = MINOR(ap3216cdev.devid);
    }
    if(ret < 0) {
        goto fail_devid;
    }
    printk("major = %d,minor = %d\r\n",ap3216cdev.major,ap3216cdev.minor);

    ap3216cdev.cdev.owner = THIS_MODULE;

    cdev_init(&ap3216cdev.cdev, &ap3216cdev_fops);
    ret = cdev_add(&ap3216cdev.cdev, ap3216cdev.devid,  ap3216c_cnt);
    if(ret < 0) {
        goto fail_cdev;
    }

    ap3216cdev.class = class_create (THIS_MODULE, ap3216c__name);
    if (IS_ERR(ap3216cdev.class)) {
		ret = PTR_ERR(ap3216cdev.class);
		goto fail_class;
	}
    ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL,ap3216c__name);
    if (IS_ERR(ap3216cdev.device)) {
		ret = PTR_ERR(ap3216cdev.device);
		goto fail_device;
	}
    return 0;
fail_device:
    class_destroy(ap3216cdev.class);
fail_class:
    cdev_del(&ap3216cdev.cdev);
fail_cdev:
    unregister_chrdev_region(ap3216cdev.devid, ap3216c_cnt);
fail_devid:
    return ret;
}
static int ap3216c_remove(struct i2c_client *client)
{
    printk("ap3216 remove\r\n");
    device_destroy(ap3216cdev.class, ap3216cdev.devid);
    class_destroy(ap3216cdev.class);
    cdev_del(&ap3216cdev.cdev);
    unregister_chrdev_region(ap3216cdev.devid, ap3216c_cnt);
    return 0;

}
static const struct i2c_device_id ap3216c_id_table[] = {
	{ "ysy_ap3216c", 0 },
	{ },
};
static const struct of_device_id ap3216c_of_match_table[] = {
   {.compatible = "alientek,ap3216c"},
    { /*sentinel*/},
};

static struct i2c_driver i2c_driver = {
	.driver = {
		.name	= "ap3216c",
		.owner	= THIS_MODULE,
		.of_match_table = ap3216c_of_match_table,
	},
	.probe		= ap3216c_probe,
	.remove		= ap3216c_remove,
	.id_table	= ap3216c_id_table,
};

static int __init ap3216c_init(void)
{
    
    return i2c_add_driver(&i2c_driver);
}

static void __exit ap3216c_exit(void)
{   

    i2c_del_driver(&i2c_driver);
}

module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YSY");

3.2 驱动编写

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/input.h>
#include <linux/i2c.h>

#include "ap3216creg.h"

struct ap3216c_dev{
    dev_t devid;
    int major;
    int minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node  *nd;
    struct i2c_client *private;
    short ir,ps,als;
};
struct ap3216c_dev ap3216cdev;
#define ap3216c_cnt 1
#define ap3216c__name "ap3216c"


/*读取ap3216c的N个寄存器值*/
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
    struct i2c_client *client = (struct i2c_client *)dev->private;
    //struct i2c_msg msg[2];
    /*msg[0]发送要读取的寄存器的首地址   msg[1]读取数据*/
    struct i2c_msg msg[2] = {
        {
            .addr = client->addr,
            .flags = 0,
            .buf = &reg,
            .len = 1,
        },
        {
            .addr = client->addr,
            .flags = I2C_M_RD,
            .buf = val,
            .len = len,
        },
    };
#if 0
    msg[0].addr = client->addr;
    msg[0].flags = 0;
    msg[0].buf = &reg;
    msg[0].len = 1;

    msg[1].addr = client->addr;
    msg[1].flags = I2C_M_RD;
    msg[1].buf = val;
    msg[1].len = len;
#endif
    return i2c_transfer(client->adapter, msg, 2);
}
/*向ap3216写N个寄存器值*/
static int ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, int len)
{
    u8 b[256];
    struct i2c_client *client = (struct i2c_client *)dev->private;
    //struct i2c_msg msg;
    /*msg[0]发送要读取的寄存器的首地址   msg[1]读取数据*/ 
    struct i2c_msg msg[1] = {
        {
            .addr = client->addr,
            .flags = 0,
            .buf = b,
            .len = len+1,
        },
    };
    b[0]=reg;
    memcpy(&b[1], buf, len);
#if 0
    b[0]=reg;
    memcpy(&b[1], buf, len);
    msg.addr = client->addr;
    msg.flags = 0;
    msg.buf = b;
    msg.len = len+1;
#endif
    return i2c_transfer(client->adapter, msg, 1);
}
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{

    u8 data = 0; 
    ap3216c_read_regs(dev, reg, &data, 1);
    return data;
}
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
    u8 buf = 0;
    buf = data;
    ap3216c_write_regs(dev, reg, &data, 1);
}
static int ap3216cdev_open(struct inode *inode, struct file *filp)
{
    int value = 0;
    filp->private_data = &ap3216cdev;
    
    ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X04);	
	mdelay(50);												
	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03);	
	value = ap3216c_read_reg(&ap3216cdev, AP3216C_SYSTEMCONG);	
    printk("value = %#x\r\n",value);
    return 0;
}
void ap3216c_readdata(struct ap3216c_dev *dev)
{
    unsigned char buf[6];
    unsigned char i;

	/* 循环读取所有传感器数据 */
    for(i = 0; i < 6; i++)	
    {
        buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);	
    }
	
    if(buf[0] & 0X80) 	/* IR_OF位为1,则数据无效 */
    {
        dev->ir = 0;	
        dev->ps = 0	;
    } else {
		dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 			
		dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F); 	
    }	
	dev->als = ((unsigned short)buf[3] << 8) | buf[2];	/* 读取ALS传感器的数据 			 */  
}
static int ap3216cdev_release(struct inode *inode, struct file *filp)
{
    printk("ap3216c release\r\n");

    return 0;
}
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    short data[3];
    int err = 0;
    struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
    //printk("ap3216c read\r\n");
    ap3216c_readdata(dev);
    data[0] = dev->ir;
    data[1] = dev->als;
    data[2] = dev->ps;
    err = copy_to_user(buf, data, sizeof(data));

    return 0;
}
static const struct file_operations ap3216cdev_fops = {
    .owner = THIS_MODULE,
	.read = ap3216c_read ,
	.open = ap3216cdev_open,
	.release = ap3216cdev_release,
};
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    printk("ap3216 probe\n");
    ap3216cdev.major = 0; 
    if(ap3216cdev.major) {
        ap3216cdev.devid = MKDEV(ap3216cdev.major,0);
        ret = register_chrdev_region(ap3216cdev.devid, ap3216c_cnt, ap3216c__name);
    } else {
        ret = alloc_chrdev_region(&ap3216cdev.devid, 0, ap3216c_cnt, ap3216c__name);
        ap3216cdev.major = MAJOR(ap3216cdev.devid);
        ap3216cdev.minor = MINOR(ap3216cdev.devid);
    }
    if(ret < 0) {
        goto fail_devid;
    }
    printk("major = %d,minor = %d\r\n",ap3216cdev.major,ap3216cdev.minor);

    ap3216cdev.cdev.owner = THIS_MODULE;

    cdev_init(&ap3216cdev.cdev, &ap3216cdev_fops);
    ret = cdev_add(&ap3216cdev.cdev, ap3216cdev.devid,  ap3216c_cnt);
    if(ret < 0) {
        goto fail_cdev;
    }

    ap3216cdev.class = class_create (THIS_MODULE, ap3216c__name);
    if (IS_ERR(ap3216cdev.class)) {
		ret = PTR_ERR(ap3216cdev.class);
		goto fail_class;
	}
    ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL,ap3216c__name);
    if (IS_ERR(ap3216cdev.device)) {
		ret = PTR_ERR(ap3216cdev.device);
		goto fail_device;
	}
    ap3216cdev.private = client;
    return 0;
fail_device:
    class_destroy(ap3216cdev.class);
fail_class:
    cdev_del(&ap3216cdev.cdev);
fail_cdev:
    unregister_chrdev_region(ap3216cdev.devid, ap3216c_cnt);
fail_devid:
    return ret;
}
static int ap3216c_remove(struct i2c_client *client)
{
    printk("ap3216 remove\r\n");
    device_destroy(ap3216cdev.class, ap3216cdev.devid);
    class_destroy(ap3216cdev.class);
    cdev_del(&ap3216cdev.cdev);
    unregister_chrdev_region(ap3216cdev.devid, ap3216c_cnt);
    return 0;

}
static const struct i2c_device_id ap3216c_id_table[] = {
	{ "ysy_ap3216c", 0 },
	{ },
};
static const struct of_device_id ap3216c_of_match_table[] = {
   {.compatible = "alientek,ap3216c"},
    { /*sentinel*/},
};

static struct i2c_driver i2c_driver = {
	.driver = {
		.name	= "ap3216c",
		.owner	= THIS_MODULE,
		.of_match_table = ap3216c_of_match_table,
	},
	.probe		= ap3216c_probe,
	.remove		= ap3216c_remove,
	.id_table	= ap3216c_id_table,
};

static int __init ap3216c_init(void)
{
    
    return i2c_add_driver(&i2c_driver);
}

static void __exit ap3216c_exit(void)
{   

    i2c_del_driver(&i2c_driver);
}

module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YSY");

3.3编写测试 APP

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <linux/input.h>


int main(int argc,char *argv[])
{
    int fd;
    int ret;
    unsigned short data[3];
    char *filename;
    unsigned short ir,ps,als;
    filename = argv[1];
    if(argc != 2)
    {
        printf("Error usage!\r\n");
        return -1;
    }
    fd=open(filename,O_RDWR);
    if(fd<0)
    {
        printf("file %s open fialed !\r\n",filename);
        return -1;
    }
    while(1) 
    {
        ret = read(fd, &data, sizeof(data));
        if(ret == 0) {
            ir = data[0];
            als = data[1];
            ps = data[2];
            printf("ir=%d, ps=%d, als=%d\r\n",ir,ps,als);
        }
        usleep(200000);

    }
    close(fd);
    return 0;
}

ap3216cApp.c 文件内容很简单,就是在 while 循环中不断的读取 AP3216C 的设备文件,从
而得到 ir als ps 这三个数据值,然后将其输出到终端上。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值