I2C设备驱动实验:实现读/写I2C设备寄存器的函数

一. 简介

经过前面几篇文章的学习,实现了简单的 I2C设备驱动框架,文章如下:

I2C驱动实验:向I2C驱动框架中添加字符设备驱动框架代码-CSDN博客

本文在此基础上,编写读写I2C设备寄存器的函数,具体就是 读取I2C设备中寄存器的数据函数,向I2C设备的寄存器写数据的函数。

二.  I2C设备驱动实验:实现读/写I2C设备寄存器的函数

1.  读写 I2C设备中数据的实现思路

I2C控制器向设备的寄存器读写数据,是可以调用 i2c_transfer函数来实现的,i2c_transfer函数是内核提供的I2C函数。

注意:可以在内核源码中搜索 i2c_transfer函数进行参考,学习i2c_transfer如何使用来实现 I2C读写设备寄存器的数据。

涉及的结构体:

adap:IIC设备对应的适配器,也就是IIC接口,当IIC设备和驱动匹配以后,probe函数执行,probe函数传递进来的第一个参数就是i2c_client,在i2c_client里面保存了此I2C设备所对应的i2c_adapter。

msgs:就是构成的I2C传输数据。

2. 添加 读/写I2C设备寄存器的函数

打开 17_i2c工程,向 probe函数中,字符设备驱动框架代码之后,添加如下代码:

    /*将 i2c_client传递给 字符设备的私有数据指针  */
    ap3216c_dev.private_data = client; 

这里字符设备结构体中添加了一个私有数据指针,这里设置为了 i2c_client。为了后面 读/写AP3216C设备中寄存器值时会用到。

ap3216c.c添加 读/写I2C设备寄存器函数后,内容如下:

#include <linux/kernel.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/module.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/delay.h>

#include "ap3216c.h"

#define  AP3216C_NANE   "ap3216c"
#define  AP3216C_CNT    1

//设备结构体
struct ap3216c_Dev{
    dev_t devid;  //设备号
    int major;     //主设备号
    int minor;     //次设备号 
    struct cdev led_cdev; 
    struct class * class;   //类(自动创建设备节点用)
    struct device * device; //设备(自动创建设备节点用) 
    void* private_data;   /* 私有数据,一般会设置为 i2c_client */
};

struct ap3216c_Dev ap3216c_dev;


/*读取AP3216C设备(I2C设备)中多个寄存器的值
* @param – dev : I2C 设备
* @param – reg : 要读取的寄存器首地址
* @param – buffer : 读取到的数据
* @param – len : 要读取的数据长度
*/
static int ap3216C_i2c_read_regs(struct ap3216c_Dev* dev, u8 reg, u8* buffer, int len)
{
    int ret = 0;
    struct i2c_msg msg[2];
    struct i2c_client* client = (struct i2c_client*)dev->private_data;

    // msg[0],第一条写消息,发送要读取的寄存器首地址 
    msg[0].addr = client->addr;//ap321316c器件地址
    msg[0].flags = 0;          //标志为写数据
    msg[0].buf = &reg;          //寄存器地址
    msg[0].len = 1;            //寄存器地址长度

    //msg[1],第二条读消息,读取寄存器数据
    msg[1].addr = client->addr;//ap321316c器件地址
    msg[1].flags = I2C_M_RD;   //标志为读取数据 
    msg[1].buf = buffer;       //存放数据缓存区
    msg[1].len = len;        //所读取的数据长度

    //向AP3216C设备传输数据
    ret = i2c_transfer(client->adapter, msg, 2);
    if(ret == 2)
    {
        ret = 0;
    }else{
        ret = -EIO;
    }
    return ret;
}

/*向AP3216C(I2C设备)中的多个寄存器写入数据*/
static int ap3216c_i2c_write_regs(struct ap3216c_Dev* dev, u8 reg, u8* data, int len)
{   
    int ret = 0;
    struct i2c_msg msg;
    struct i2c_client* client = (struct i2c_client*)dev->private_data;
    u8 buffer[256] = {0};

    //填充缓冲区数据:寄存器首地址+实际写入的数据
    buffer[0] = reg;
    memcpy(&buffer[1], data, len);

    msg.addr = client->addr; //器件地址(AP3216C地址)
    msg.flags = 0;           //标志为写数据
    msg.buf = buffer;        //缓冲区
    msg.len = len+1;         //要发送的数据长度:有一字节的寄存器长度

    ret = i2c_transfer(client->adapter, &msg, 1);
    if(ret == 1)
    {
        ret = 0;
    }
    else
    {
        ret = -EIO;
    }
    return ret;
}

/*读取AP3216C设备(I2C设备)中一个寄存器的值 */
unsigned char ap3216c_i2c_read_reg(struct ap3216c_Dev* dev, u8 reg)
{
    u8 data;
    ap3216C_i2c_read_regs(dev, reg, &data, 1);
    return data;
}

/*向AP3216C的一个寄存器中写入数据 */
int ap3216c_i2c_write_reg(struct ap3216c_Dev* dev, u8 reg, u8 data)
{
    u8 value;
    value = data;
    return ap3216c_i2c_write_regs(dev, reg, &value, 1);
}

/*打开设备函数 */
static int ap3216c_open(struct inode *inode, struct file *filp)
{
    u8 value = 0;
    filp->private_data = (struct ap3216c_Dev*)&ap3216c_dev;
    printk("ap3216c_open\n");
    
    return 0;
}
/*读数据函数*/
ssize_t ap3216c_read(struct file * filp, char __user * buf, size_t count, loff_t * ppos)
{
    printk("ap3216c_read\n");

    return 0;
}
/*关闭设备函数*/
int ap3216c_release(struct inode * inode, struct file * filp)
{
    printk("led_release\n");
    return 0;
}

//字符设备的函数集
const struct file_operations fops = {  
    .owner = THIS_MODULE,
    .open = ap3216c_open,
    .read = ap3216c_read,
    .release = ap3216c_release,
};

//传统驱动匹配表
static const struct i2c_device_id ap3216c_i2c_id[] = {
    {"ap3216c", 0},
    {},
};

//设备树驱动匹配
static const struct of_device_id ap3216c_dt_ids[]={
    {.compatible = "alientek,ap3216c"},
    { },
};

static int ap3216c_i2c_probe(struct i2c_client* client, const struct i2c_device_id * id)
{
    int ret = 0;
    printk("ap3216c_i2c_probe\n");
    /* 2. 注册字符设备 */

    //设备号的分配
    ap3216c_dev.major = 0;
    if(ap3216c_dev.major) //给定主设备号
    {
        ap3216c_dev.devid = MKDEV(ap3216c_dev.major, 0);
        ret = register_chrdev_region(ap3216c_dev.devid, AP3216C_CNT, AP3216C_NANE);
    }
    else{ //没有给定主设备号
        ret = alloc_chrdev_region(&(ap3216c_dev.devid), 0, AP3216C_CNT, AP3216C_NANE);	
        ap3216c_dev.major = MAJOR(ap3216c_dev.devid);
        ap3216c_dev.minor = MINOR(ap3216c_dev.devid);
    }
    printk("dev.major: %d\n", ap3216c_dev.major);
    printk("dev.minor: %d\n", ap3216c_dev.minor);
	if (ret < 0) {
		printk("register-chrdev failed!\n");
		goto fail_devid;
	}
    //初始化设备
    ap3216c_dev.led_cdev.owner = THIS_MODULE;
    cdev_init(&ap3216c_dev.led_cdev, &fops);
    
    //注册设备
    ret = cdev_add(&ap3216c_dev.led_cdev, ap3216c_dev.devid, AP3216C_CNT);
    if(ret < 0)
    {
        printk("cdev_add failed!\r\n");
        goto fail_adddev;
    }

/* 3.自动创建设备节点 */
    //创建类
    ap3216c_dev.class = class_create(THIS_MODULE, AP3216C_NANE); 
    if (IS_ERR(ap3216c_dev.class)) {
		ret = PTR_ERR(ap3216c_dev.class);
		goto fail_class;
	}   
    //创建设备
    ap3216c_dev.device = device_create(ap3216c_dev.class, NULL, ap3216c_dev.devid, NULL, AP3216C_NANE);
    if (IS_ERR(ap3216c_dev.device)) {
		ret = PTR_ERR(ap3216c_dev.device);
		goto fail_dev_create;
	}  

    /*将 i2c_client传递给 字符设备的私有数据指针  */
    ap3216c_dev.private_data = client;  

    return 0;

fail_dev_create:
    class_destroy(ap3216c_dev.class);
fail_class:
    cdev_del(&ap3216c_dev.led_cdev);
fail_adddev:
    unregister_chrdev_region(ap3216c_dev.devid, AP3216C_CNT);
fail_devid:
    return ret;
}

static int ap3216c_i2c_remove(struct i2c_client* client)
{
    printk("ap3216c_i2c_remove!\n");
    /*注销字符设备*/
    //1. 删除设备
    cdev_del(&ap3216c_dev.led_cdev);
    //2. 注销设备号
    unregister_chrdev_region(ap3216c_dev.devid, AP3216C_CNT);

    /*摧毁类与设备(自动创建设备节点时用) */
    //3. 摧毁设备
    device_destroy(ap3216c_dev.class, ap3216c_dev.devid);
    //4. 摧毁类
    class_destroy(ap3216c_dev.class);

    return 0;
}

//i2c_driver结构体
static struct i2c_driver ap3216c_i2c_driver = {
    .driver = {
        .name = "ap3216c",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(ap3216c_dt_ids), //设备树驱动匹配
    },
    .id_table = ap3216c_i2c_id, //传统驱动匹配表
    .probe = ap3216c_i2c_probe,
    .remove = ap3216c_i2c_remove,
};

/*模块加载 */
static int __init ap3216c_init(void)
{
    int ret = 0;
    //注册I2C设备驱动
    ret = i2c_add_driver(&ap3216c_i2c_driver);
    if (ret != 0) {
		printk(KERN_ERR "Failed to register WM8737 I2C driver: %d\n",ret);
	}
    return ret;
}

/*模块卸载 */
static void __exit ap3216c_exit(void)
{
    //删除I2C驱动
    i2c_del_driver(&ap3216c_i2c_driver);
}

/*驱动加载与卸载 */
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL"); //模块 Licence
MODULE_AUTHOR("WeiWuXian"); //作者

三.  编译驱动

对上面驱动代码进行模块化编译:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/17_i2c$ make
make -C /home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/wangtian/zhengdian_Linux/Linux_Drivers/17_i2c modules
make[1]: 进入目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
  CC [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/17_i2c/ap3216c.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/wangtian/zhengdian_Linux/Linux_Drivers/17_i2c/ap3216c.mod.o
  LD [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/17_i2c/ap3216c.ko
make[1]: 离开目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”

可以看出,驱动模块正常编译通过。接下来继续开始初始化 AP3216C,读取 AP3216C设备中的数据。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值