I2C驱动ap3216c的实验

65 篇文章 12 订阅
19 篇文章 1 订阅


ap3216c手册资料

一、设备树

在pinctrl中添加(已经有):

	pinctrl_i2c1: i2c1grp {
			fsl,pins = <
				MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
				MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
			>;
		};

在这里插入图片描述
在&i2c1中添加:


&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

	ap3216c@1e {
		compatible = "luatao,ap3216c";
		reg = <0x1e>;
	};
};

在这里插入图片描述

二、驱动程序

ap3216c.h

#ifndef AP3216C_H
#define AP3216C_H

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


/* AP3216C 寄存器 */
#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

ap3216c.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>

#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/cdev.h>

#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>

#include <linux/of_gpio.h>

#include <linux/platform_device.h>
#include <linux/i2c.h>
#include "ap3216c.h"


/**
 * file name:ap3216c
 * date: 2021-08-15  09:24
 * version:1.0
 * author:luatao
 * describe:ap3216c device drive
 */


#define AP3216C_CNT  1     /* 设备号个数 */
#define AP3216C_NAME        "ap3216c"      /* 设备名*/


/* 设备结构体 自定义 */
struct ap3216c_dev{
    dev_t devid;     /*设备号  */
    struct cdev cdev;  /* cdev */
    struct class *class;  /* 类*/
    struct device *device;  /* 设备 */
    int major;   /* 主设备号 */
    int minor;  /* 次设备号 */

    struct device_node *nd;  /* 设备节点 */

    void *private_date;  /* 私有数据 */
    unsigned short ir, als, ps;   /* 三个光传感器数据 */
 };

/* 定义一个设备结构体 */
struct ap3216c_dev ap3216c;   /*ap3216c 设备 */

/* 从ap3216c读取多个寄存器数据 
 @param - *dev        : ap3216设备 
 @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_date;  /* 私有数据 */

    /* msg[0] 为发送要读取的首地址 */
    msg[0].addr = client->addr;  /* 器件地址 */
    msg[0].flags = 0;                       /* 标记为发送数据 */
    msg[0].buf = &reg;              /* 要读取数据的首地址 */
    msg[0].len = 1;                     /* reg长度 */

    /* msg[1]读取数据 */
    msg[1].addr = client->addr;  /* 器件地址 */
    msg[1].flags = I2C_M_RD;    /* 标记为读取数据 */
    msg[1].buf = val;              /* 读取数据缓冲区  */
    msg[1].len = len;                     /* 读取数据长度 */

    ret = i2c_transfer(client->adapter, msg, 2);  /* 向总线发送2个消息 */
    if(ret == 2){  /* 传输成功 */
        ret = 0;
    }else{
        printk("i2c_transfer failed!\r\n");
        return -EREMOTEIO;
    }

    return ret;
}

/* 从ap3216c多个寄存器写入数据 
 @param - *dev        : ap3216设备 
 @param - reg           : 要写入的寄存器首地址
 @param - *buf         : 写入的数据缓冲区
 @param - len          : 要写入的数据长度 
 @return                    :操作结果
*/
static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, void *buf, int len)
{
    u8 buf1[256];
    struct i2c_msg msg;   /* 传输的消息 */
    struct i2c_client *client = (struct i2c_client *)dev->private_date;  /* 私有数据 */

    buf1[0] = reg;  /* 寄存器首地址 */
    memcpy(&buf1[1], buf, len);  /* 要写入的数据拷贝到数据buf1中 */
    
    /* msg处理数据 */
    msg.addr = client->addr;  /* 器件地址 */
    msg.flags = 0;    /* 标记为写入数据 */
    msg.buf = buf1;              /* 要写入的数据缓冲区  */
    msg.len = len + 1;                     /* 写入的数据长度 */

    return  i2c_transfer(client->adapter, &msg, 1);  /* 向总线发送1个消息 */
}

/* 从ap3216c读取指定寄存器值 读取一个寄存器
 @param - *dev        : ap3216设备 
 @param - reg           : 要读取的寄存器
 @return                    :读取到的寄存器值
*/
static unsigned char  ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
   struct i2c_client *client = (struct i2c_client *)dev->private_date;  /* 私有数据 */

	return i2c_smbus_read_byte_data(client, reg);  /* 读取一个字节数据 */
}

/* 向ap3216c指定寄存器写入指定的值,写一个寄存器 
 @param - *dev        : ap3216设备 
 @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);  /* 调用写入多个寄存器的方法 */
}

/* 读取ap3216c的数据 读取原始数据  包括als  ps 和 ir
 @param - *dev        : ap3216设备 
 @return                    :无
*/
static void ap3216c_readdata(struct ap3216c_dev *dev)
{
    unsigned char i = 0;
	unsigned char buf[6];

    /* 循环读取所有传感器数据 */
    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;  
    }else{   /* 读取ir传感器的数据 */
        dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0x03);   
    }
	dev->als =  ((unsigned short)buf[3] << 8) | (buf[2]);   

    if(buf[4] & 0x40){ /* IR_OF 为1  则数据位无效 */
        dev->ps = 0;
    }else{
        dev->ps = ((unsigned short)(buf[5] & 0x3F)  << 4)| (buf[4] & 0x0F);  
    }

}

/* 打开设备 */
static int ap3216c_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &ap3216c;  /* 设置私有数据 */

    /* 初始化ap3216c */
    ap3216c_write_reg(&ap3216c, AP3216C_SYSTEMCONG, 0x04);   /* 复位ap3216c */
    mdelay(50);  /* ap3216c复位最少10ms */
    ap3216c_write_reg(&ap3216c, AP3216C_SYSTEMCONG, 0x03);   /* 开启als  ps+ir */

    return 0;
}

/* 从设备读取数据 */
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    short data[3];  /* 存放读取到的数据 */
    long ret = 0;

    struct ap3216c_dev *dev = filp->private_data;  // 获取私有数据 

    ap3216c_readdata(dev);  // 读取数据 

    data[0] = dev->ir;
    data[1] = dev->als;
    data[2] = dev->ps;

    ret = copy_to_user(buf, data, sizeof(data));  // 发送给用户空间  

    return 0;
}

/* 往设备写数据 */
static ssize_t ap3216c_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    printk("no write operation!\r\n");
    return 0;
}

/* 释放设备 */
static int ap3216c_release(struct inode *inode, struct file *filp)
{
    //printk("ap3216c release!\r\n");
    return 0;
}


/* 设备操作函数结构体  */
static struct file_operations ap3216c_fops = {
    .owner = THIS_MODULE,
    .open =ap3216c_open,
    .read =ap3216c_read,
    .write =ap3216c_write,
    .release =ap3216c_release,
};

/* i2C驱动的probe函数 ,当驱动与设备匹配以后此函数就会执行 */
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    printk("ap3216c driver and device has match!\r\n");  // 提示信息 

    /* 1. 创建设备号 */
    if(ap3216c.major){  // 定义了设备号 
        ap3216c.devid = MKDEV(ap3216c.major, 0 );  // 根据主设备号和次设备号合成设备号 
        register_chrdev_region(ap3216c.devid, AP3216C_CNT, AP3216C_NAME);  // 注册设备号 
    }else{  // 没有定义设备号 动态生成 
        alloc_chrdev_region(&ap3216c.devid,0,AP3216C_CNT, AP3216C_NAME ); // 申请设备号
        ap3216c.major = MAJOR(ap3216c.devid);  // 获取主设备号
        ap3216c.minor = MINOR(ap3216c.devid);  // 获取次设备号
    }
    printk("ap3216c major = %d,minor = %d\r\n",ap3216c.major, ap3216c.minor);  // 打印主设备号和次设备号

    /* 2. 初始化 cdev */
    ap3216c.cdev.owner = THIS_MODULE;  
    cdev_init(&ap3216c.cdev, &ap3216c_fops);  // 初始化cdev
    /* 3. 添加cdev */
    cdev_add(&ap3216c.cdev, ap3216c.devid, AP3216C_CNT ); // 向linux系统添加cdev

     /* 自动创建设备节点文件 */
    /* 4. 创建类 */
    ap3216c.class = class_create(THIS_MODULE, AP3216C_NAME);  // 创建类 
    if(IS_ERR(ap3216c.class)){
        return PTR_ERR(ap3216c.class);
    }
    /* 创建设备 */
    ap3216c.device = device_create(ap3216c.class, NULL, ap3216c.devid, NULL, AP3216C_NAME);
      if(IS_ERR(ap3216c.device)){
        return PTR_ERR(ap3216c.device);
    }

    ap3216c.private_date = client;  /* 私有数据为当前设备 */

    return 0;
}

/* i2c驱动后的remove函数 */
static int ap3216c_remove(struct i2c_client *client)
{

      /*  注销设备驱动 */
    cdev_del(&ap3216c.cdev);  /* 删除 cdev */
    unregister_chrdev_region(ap3216c.devid, AP3216C_CNT ); /* 注销设备号 */

    device_destroy(ap3216c.class, ap3216c.devid);  /* 注销设备  */
    class_destroy(ap3216c.class);  /* 注销类 */


    printk("ap3216c drive unregsister ok !\r\n");
    return 0;
}

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

/* i2c驱动结构体 */
static struct i2c_driver ap3216c_driver = {
        .driver = {
            .owner = THIS_MODULE,
            .name = "ap3216c",   /* 驱动名字 用于和设备匹配  适用于没有设备树的情况*/
            .of_match_table =ap3216c_of_match,  /* 设备树匹配列表 */
        },
        .probe =ap3216c_probe,
        .remove =ap3216c_remove,
        .id_table = ap3216c_id, /* id配置列表 */
};

/* 驱动入口函数 */
static int __init ap3216c_init(void)
{
    return i2c_add_driver(&ap3216c_driver);
}


/* 驱动出口函数 */
static void __exit ap3216c_exit(void)
{
   i2c_del_driver(&ap3216c_driver);
}

/* 加载驱动入口和出口函数 */
module_init(ap3216c_init);
module_exit(ap3216c_exit);

/* 上面4步可以直接合成下面一步 */
// module_i2c_driver(ap3216c_driver);

/* LICENSE 和 AUTHOR 信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("luatao");

三、应用程序

ap3216cApp.c

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

/**
 * file name:ap3216cApp
 * date: 2021-08-15  10:42
 * version:1.0
 * author:luatao
 * describe:ap3216c测试APP
 * 执行命令:./ap3216cApp 读取按键值 
 */


/* 主程序 */
int main(int argc, char *argv[])
{
    char *filename;  // 可执行文件名
    int fd,ret = 0;  //  fd: 文件句柄 ret:函数操作返回值
    unsigned short databuf[3];  // 读出来的数据 
    unsigned short ir, als, ps;  // 实际的数据 

    /* 先判断输入的参数 */
    if(argc !=  2){  // 本身文件名带1个 执行文件1个  
       printf("parameter error!\r\n");
       return -1;
    }

    /* 分析参数 ,提取有用的信息 */
    filename = argv[1];  // 可执行文件名 
    
    /* 打开key文件 */
    fd = open(filename, O_RDWR);  // 可读可写 
    if(fd < 0){
        printf("can't open file:%s\r\n",filename);
        return -1;
    }

    /* 循环读取值 */
    while(1){
        ret =  read(fd, &databuf, sizeof(databuf));  // 从驱动中读取实际读出的值
        if(ret == 0){  // 数据读取成功 
            ir =          databuf[0];  // ir 传感器数据
            als =       databuf[1];  // als 传感器数据
            ps =        databuf[2];  // ps 传感器数据

            printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps);
        }
        sleep(1);  /* 延时1s*/
    }

    /* 关闭文件 */
    ret = close(fd);
    if(ret < 0){
        printf("can't close file %s \r\n", filename);
        return -1;
    }

    return 0;
}
 

四、测试

加载驱动:
运行程序:
卸载驱动:
在这里插入图片描述

在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值