linux驱动开发 | iic

一、基础知识

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后编译设备树,
make dtbs
sudo cp arch/arm/boot/dts/imx6ull-alientek-nand.dtb /home/zys/linux/tftpboot/ -f
然后以新的设备树启动linux
查看/sys/bus/i2c/devices下 会有地址为1e的设备,就是我们刚刚添加的设备
在这里插入图片描述
在这里插入图片描述

二、驱动编写

#include <linux/ide.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/of_irq.h>
#include <linux/poll.h>
#include <linux/input.h>
#include "ap3216creg.h"
#include <linux/i2c.h>
#include <linux/delay.h>
/********************************************************************
 * ap3216c.c
 * 作者:张亚胜
 * 版本:V1.0
 * 描述:ap3216c驱动程序
 * 其他:无
 * 网站:www.s123.xyz
 * 日志:出版V1.0 2020/8/15 张亚胜创建
 * ******************************************************************/
#define AP3216C_CNT   1               /* 设备号个数 */
#define AP3216C_NAME  "ap3216c"       /* 设备名字 */

/* 设备结构体 */
struct ap3216c_dev{
    dev_t devid;           /* 设备号 */
    struct cdev cdev;       /* 字符设备 */
    struct class *class;    /* mdev 自动节点文件使用 */
    struct device *device;  /* class使用 */
    struct device_node *nd; /* 设备节点 */
    int major;              /* 主设备号 */
    void *private_data;     /* 私有数据 */
    unsigned short ir,als,ps;   /* 三个光传感器数据 */
};

struct ap3216c_dev ap3216cdev;  /* 设备 */

/*
 * @description	: 从ap3216c读取多个寄存器数据
 * @param - dev:  ap3216c设备
 * @param - reg:  要读取的寄存器首地址
 * @param - val:  读取到的数据
 * @param - len:  要读取的数据长度
 * @return 		: 操作结果
 */
static int ap3216c_read_regs(struct ap3216c_dev *dev,u8 reg,void *val,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;//ap3216c地址
    msg[0].flags = 0;          //标记为发送数据 
    msg[0].buf = &reg;         //读取的首地址 
    msg[0].len = 1;            //reg长度 
    /* msg[1]读取数据 */
    msg[1].addr = client->addr;//ap3216c地址
    msg[1].flags = I2C_M_RD;   //标记为读取数据 
    msg[1].buf = val;          //读取数据缓冲区 
    msg[1].len = len;          //读取的数据长度 

    ret = i2c_transfer(client->adapter,msg,2);
    if(ret == 2){
        ret = 0;
    } else {
        printk("i2c_transfer failed=%d reg=%06x len=%d\r\n",ret,reg,len);
        ret = -EREMOTEIO;
    }
    return ret;
}

/*
 * @description	: 向ap3216c多个寄存器写入数据
 * @param - dev:  ap3216c设备
 * @param - reg:  要写入的寄存器首地址
 * @param - val:  要写入的数据缓冲区
 * @param - len:  要写入的数据长度
 * @return 	  :   操作结果
 */
static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{
    u8 b[256];
    struct i2c_msg msg;
    struct i2c_client *client = (struct i2c_client *)dev->private_data;

    b[0] = reg;             //寄存器首地址
    memcpy(&b[1],buf,len);  //将要写入的数据拷贝到数组b里面
    msg.addr = client->addr;//设备地址  
    msg.flags = 0;          //标记为写数据

    msg.buf = b;            //要写的数据缓冲区
    msg.len = len+1;        //要写入的数据长度
    return i2c_transfer(client->adapter,&msg,1); 
}
/*
 * @description	: 读取ap3216c指定寄存器值,读取一个寄存器
 * @param - dev:  ap3216c设备
 * @param - reg:  要读取的寄存器
 * @return 	  :   读取到的寄存器值
 */
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
    u8 data=0;
    ap3216c_read_regs(dev,reg,&data,1);
    return data;
}
/*
 * @description	: 向ap3216c指定寄存器写入指定的值,写一个寄存器
 * @param - dev:  ap3216c设备
 * @param - reg:  要写的寄存器
 * @param - data: 要写入的值
 * @return   :    无
 */
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
    u8 buf=0;
    buf = data;
    ap3216c_write_regs(dev,reg,&buf,1);
}
/*
 * @description	: 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!
 *				: 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
 * @param - ir	: ir数据
 * @param - ps 	: ps数据
 * @param - ps 	: als数据 
 * @return 		: 无。
 */
void ap3216c_readdata(struct ap3216c_dev *dev)
{
    u8 i=0;
    u8 buf[6];
    /* 循环获取所有传感器数据 */
    for (i = 0; i < 6; i++)
    {
        buf[i] = ap3216c_read_reg(dev,AP3216C_IRDATALOW + i);//寄存器见驱动开发指南P601
    }
    if(buf[0] & 0x80)
        dev->ir = 0;
    else 
        dev->ir = ((unsigned short)(buf[1]<<2))|(buf[0]&0x03);

    dev->als = ((unsigned short)buf[3]<<8)|buf[2];

    if(buf[4]&0x40)
        dev->ps = 0;
    else 
        dev->ps = ((unsigned short)(buf[5]&0x3f)<<4) | (buf[4]&0x0f);
}
/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int ap3216c_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &ap3216cdev;
    /* 初始化 */
    ap3216c_write_reg(&ap3216cdev,AP3216C_SYSTEMCONG,0X04);
    mdelay(50);
    ap3216c_write_reg(&ap3216cdev,AP3216C_SYSTEMCONG,0X03);//寄存器见驱动开发指南P601
    return 0;
}
/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    short data[3];
    long err=0;
    struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;

    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;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int ap3216c_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static const struct file_operations ap3216c_ops = {
    .owner = THIS_MODULE,
    .open = ap3216c_open,
    .read = ap3216c_read,
    .release = ap3216c_release,
};

 /*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0,成功;其他负值,失败
  */
static int ap3216c_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
    /* 构建设备号 */
    if(ap3216cdev.major){
        ap3216cdev.devid = MKDEV(ap3216cdev.major,0);
    } else {
        alloc_chrdev_region(&ap3216cdev.devid,0,AP3216C_CNT,AP3216C_NAME);
        ap3216cdev.major = MAJOR(ap3216cdev.devid);
    }
    /* 2.注册设备 */
    cdev_init(&ap3216cdev.cdev,&ap3216c_ops);
    cdev_add(&ap3216cdev.cdev,ap3216cdev.devid,AP3216C_CNT);
    
    /* 创建类 */
    ap3216cdev.class = class_create(THIS_MODULE,AP3216C_NAME);
    if(IS_ERR(ap3216cdev.class)){
        return PTR_ERR(ap3216cdev.class);
    }

    /* 创建设备 */
    ap3216cdev.device = device_create(ap3216cdev.class,NULL,ap3216cdev.devid,NULL,AP3216C_NAME);
    if(IS_ERR(ap3216cdev.device)){
        return PTR_ERR(ap3216cdev.device);
    }

    ap3216cdev.private_data = client;
    return 0;
}

static int ap3216c_remove(struct i2c_client *client)
{
    /* 删除设备 */
    cdev_del(&ap3216cdev.cdev);
    unregister_chrdev_region(ap3216cdev.devid,AP3216C_CNT);
    /* 注销类和设备 */
    device_destroy(ap3216cdev.class,ap3216cdev.devid);
    class_destroy(ap3216cdev.class);
    return 0;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {
    {"alientek,ap3216c",0},
    {}
};
/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[]={
    {.compatible = "alientek,ap3216c"},
    { /* Sentinel */ }
};

/* i2c驱动结构体 */
static struct i2c_driver ap3216c_driver = {
    .probe = ap3216c_probe,
    .remove = ap3216c_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "ap3216c",
        .of_match_table = ap3216c_of_match,
    },
    .id_table = ap3216c_id,
};

/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init ap3216c_init(void)
{
    int ret = 0;
    ret = i2c_add_driver(&ap3216c_driver);
    return ret;
}
/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit ap3216c_exit(void)
{
    i2c_del_driver(&ap3216c_driver);
}

module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zys");
#ifndef AP3216C_H
#define AP3216C_H
/***************************************************************
文件名		: ap3216creg.h
作者	  	: 张亚胜
版本	   	: V1.0
描述	   	: AP3216C寄存器地址描述头文件
其他	   	: 无
日志	   	: 初版V1.0 2020/8/15 张亚胜创建
***************************************************************/

#define AP3216C_ADDR    	0X1E	/* AP3216C器件地址  */

/* AP3316C寄存器 */
#define AP3216C_SYSTEMCONG	0x00	/* 配置寄存器       */
#define AP3216C_INTSTATUS	0X01	/* 中断状态寄存器   */
#define AP3216C_INTCLEAR	0X02	/* 中断清除寄存器   */
#define AP3216C_IRDATALOW	0x0A	/* IR数据低字节     */
#define AP3216C_IRDATAHIGH	0x0B	/* IR数据高字节     */
#define AP3216C_ALSDATALOW	0x0C	/* ALS数据低字节    */
#define AP3216C_ALSDATAHIGH	0X0D	/* ALS数据高字节    */
#define AP3216C_PSDATALOW	0X0E	/* PS数据低字节     */
#define AP3216C_PSDATAHIGH	0X0F	/* PS数据高字节     */

#endif


三、app编写

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <linux/input.h>
/********************************************************************
 * 文件名:ap3216cApp.c
 * 作者:张亚胜
 * 版本:V1.0
 * 描述:ap3216c设备测试程序
 * 其他:使用方法:./ap3216cApp /dev/ap3216c
 * 网站:www.s123.xyz
 * 日志:出版V1.0 2020/8/17 张亚胜创建
 * ******************************************************************/


/*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
    int fd;
    char *filename;
    unsigned short databuf[3];
    unsigned short ir,als,ps;
    int ret = 0;

    if(argc != 2){
        printf("params err!\r\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename,O_RDWR);//阻塞打开
    if(fd<0){
        printf("file %s open failed!\r\n", argv[1]);
        return -1;
    }

    while(1){
        ret = read(fd,databuf,sizeof(databuf));
        if(ret == 0){//读取成功
            ir = databuf[0];
            als = databuf[1];
            ps = databuf[2];
            printf("ir = %d, als = %d, ps = %d\r\n",ir,als,ps);
        }
        usleep(200000); //200ms
    }

    ret = close(fd);
    if(ret < 0){
       printf("file %s close failed!\r\n", argv[1]);
       return -1;
    }
    return ret;
}

四、测试

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值