I2C驱动实验:读取AP3216C设备中寄存器的数据

news2024/10/6 6:41:06

一. 简介

经过前面几篇文章的学习,已经完成了I2C驱动框架,字符设备驱动框架,编写了 读写 I2C设备中寄存器的数据的代码,文章如下:

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

本文在此基础上,实现 AP3216C设备的初始化,初始化过程中会复位,使能 ALS、PS、IR三个功能。会涉及向 AP3216C设备的寄存器写数据。

二. I2C驱动实验:读取AP3216C设备中寄存器的数据

1.  实现思路

(1) AP3216C设备的初始化工作,可以在字符设备驱动框架的 ap3216c_open函数中实现。

(2) 读取 AP3216C设备中寄存器的数据,可以在 字符设备驱动框架的 ap3216c_read函数中实现。

2. 读取AP3216C设备中寄存器的数据

打开 17_i2c工程代码,因为要操作 ap3216c设备中寄存器的地址,所以,可以在 ap3216c.h头文件中添加(可以从 I2C裸机实验中查找或者查看 AP3216C芯片的数据手册)。

ap3216c.h文件代码如下:

#ifndef  AP3216C_H
#define  AP3216C_H

/* 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

添加初始化AP3216C设备,读取AP3216C设备中寄存器数据功能后,ap3216c.c文件代码如下:

#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 */
    unsigned short ir,ps,als; //AP3216C设备的数据
};

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

/*读取AP3216C设备中寄存器的数据 */
void i2c_read_ap3216c(struct ap3216c_Dev* dev)
{
    int i = 0;
    unsigned char buffer[8] = {0};

    for(i=0; i<6; i++)
    {
        buffer[i] = ap3216c_i2c_read_reg(dev, AP3216C_IRDATALOW+i);
    }
    if(buffer[0] & 0x80) //IR数据与PS数据无效
    {
        dev->ir = 0;
        dev->ps = 0;
    }
    else
    {
        dev->ir = (buffer[1] << 2) | (buffer[0] & 0x03);
        dev->ps = ((buffer[5] & 0x3F) << 4) | (buffer[4] & 0x0F);
    }
    dev->als = (buffer[3] << 8) | buffer[2];

}

/*打开设字符备函数 */
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");
    /*AP3216C设备初始化 */
    ap3216c_i2c_write_reg(&ap3216c_dev, AP3216C_SYSTEMCONG, 0x04); //复位
    mdelay(50);
    ap3216c_i2c_write_reg(&ap3216c_dev, AP3216C_SYSTEMCONG, 0x03); //开启 ALS、PS+IR

    value = ap3216c_i2c_read_reg(&ap3216c_dev, AP3216C_SYSTEMCONG);
    printk("SYSTEMCONG: %#x\n", value);
    
    return 0;
}
/*读字符设备中数据的函数*/
ssize_t ap3216c_read(struct file * filp, char __user * buf, size_t count, loff_t * ppos)
{  
    int ret = 0; 
    short data[4] = {0};
    
    struct ap3216c_Dev* dev = (struct ap3216c_Dev*) filp->private_data;
    printk("ap3216c_read\n");

    i2c_read_ap3216c(dev);
    data[0] = dev->ir;
    data[1] = dev->ps;
    data[2] = dev->als;
    ret = copy_to_user(buf, data, count);
    if(ret != 0)
    {
        ret = -1;
    }
    return ret;
}

/*关闭设备函数*/
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"); //作者

三.  编译驱动,加载测试

1.  编译驱动

输入 "make"命令,对上面驱动代码进行编译:

make

这里可以编译通过,生成驱动模块 ap3216c.ko

2. 加载驱动模块

测试方法:AP3216C设备的初始化,初始化过程中会复位,使能 ALS、PS、IR三个功能。会涉及向 AP3216C设备的寄存器写数据。open函数中最后读取了所写入寄存器的值,通过写入数据可以确定 I2C读写I2C设备(AP3216C设备)函数是否正常。

运行应用程序后, 应用层 open函数会调用到 驱动程序中 对应的 open函数。这时可以打印出寄存器的值。通过代码中写入寄存器的值与开发板打印的值比较,可以判断出I2C读写I2C设备函数是否正常。

开发板上电后,进入系统 /lib/modules/4.1.15/目录下。加载驱动模块:

运行应用程序:

可以看出,寄存器 SYSTEMCONG 的值为 0x03,而代码中是写入的 0x03。

说明 I2C从AP3216C设备的寄存器读取数据,I2C向AP3216C设备的寄存器中写入数据函数都是正常的。

测试完后,卸载驱动模块:

接下来对读取设备寄存器数据的功能进行测试。这里虽然 驱动中 read函数实现了读取AP2316C设备中寄存器的数据,但是,没有经过测试。

接下来编写测试程序,对 read函数进行测试,确定获取到 AP3216C设备的数据是否正常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1573690.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java | Leetcode Java题解之第14题最长公共前缀

题目&#xff1a; 题解&#xff1a; class Solution {public String longestCommonPrefix(String[] strs) {if (strs null || strs.length 0) {return "";}int minLength Integer.MAX_VALUE;for (String str : strs) {minLength Math.min(minLength, str.length…

加入酷开会员 酷开系统带你一起开启看电视的美好时光!

看电视对孩子和大人来说&#xff0c;都是有好处的。英国的《星期日泰晤士报》曾刊登报道&#xff1a;“看电视可以让小孩增长见闻&#xff0c;学习各种良好的社交和学习技巧&#xff0c;从而为他们今后的学习打下良好的基础。”而对于成年人来说&#xff0c;看电视也是一种娱乐…

Flutter开发进阶之错误信息

Flutter开发进阶之错误信息 在Flutter开发中错误信息通常是由Exception和Error表示&#xff0c;Error表示严重且不可恢复的错误&#xff0c;一般会导致程序直接终止&#xff0c;而Exception可以被显式抛出&#xff0c;一般为代码逻辑错误&#xff0c;根据Flutter的解释说Excep…

基于单片机放大电路程控放大特性参数设计

**单片机设计介绍&#xff0c;基于单片机放大电路程控放大特性参数设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机放大电路程控放大特性参数设计是一个结合了单片机编程和放大电路技术的综合性项目。以下是对该设计项目的概…

qgis加载天地图

一、点击Tile Server (XYZ)&#xff0c;选择New Connection 二、在弹出的输入框中输入天地图的url 此处以影像底图为例&#xff0c;url为http://t0.tianditu.gov.cn/img_w/wmts?SERVICEWMTS&REQUESTGetTile&VERSION1.0.0&LAYERimg&STYLEdefault&TILEMATR…

浅析智能数据采集技术在数字化转型中的核心作用|电商数据采集API接口的核心应用

随着科技的飞速发展和全球化的深入推进&#xff0c;数字化转型已经成为企业和社会发展的必然趋势。在这一背景下&#xff0c;智能数据采集技术作为数字化转型的核心驱动力&#xff0c;正发挥着越来越重要的作用。本文将从智能数据采集技术的定义、特点、应用场景以及对企业的影…

京东云服务器幻兽帕鲁4核16G/8核32G配置价格和选择攻略

京东云幻兽帕鲁Palworld游戏多人联机服务器&#xff0c;配置可选4核16G、4核32G、8核32G、16C64G&#xff0c;京东云幻兽帕鲁服务器优惠价格26元1个月起&#xff0c;可购买1个月、3个月、6个月和一年时长&#xff0c;云服务器吧yunfuwuqiba.com整理京东云幻兽帕鲁服务器配置价格…

【话题】如何看待那些速成并精通软件书籍的神器

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章&#xff0c;这是《话题》系列文章 目录 背景1. 神话与现实1.1 理论与实践之间的鸿沟1.2 一劳永逸的错觉 2. 速成书籍的优势与局限2.1 优势&#xff1a;2.2 局限&#xff1a; 3. 如何有效利用速成书籍3.1 量力而…

机器学习(五) -- 监督学习(3) -- 朴素贝叶斯

系列文章目录及链接 目录 前言 一、朴素贝叶斯通俗理解及定义 二、原理理解及公式 1、概率基础 2、贝叶斯公式 3、拉普拉斯平滑系数 三、**算法实现 四、接口实现 1、新闻数据集介绍 2、API 3、流程 3.1、获取数据 3.2、数据预处理 3.3、特征工程 3.4、朴素贝叶…

芯课堂 | JScope虚拟示波器使用说明

​1. 首先需要安装Jlink的驱动&#xff0c;即安装JLink_Windows_V634e之后才能安装JScope&#xff0c;一般这个能正常使用Jlink下载、仿真说明你的Jlink驱动已经正常安装 2. 需要安装Jscope&#xff0c;即安装Setup_JScope_V611m&#xff0c;安装完成之后能看到以下画面 3. 新建…

TypeScript 泛型工具类型

文章目录 前言Partial(可选)代码解读 Readonly代码解读 Pick代码解读 Record代码解读 ---上面是常用的&#xff0c;下面是不常用的&#xff0c;需要的话往下看 ( 进阶 )---Required<T>&#xff1a;Required与Partial相反&#xff0c;作用是将所有属性变成必选属性&#x…

路径规划——曲线拟合详解(二):贝塞尔曲线、B样条曲线与QP优化( Fast-Planner算法核心部分)

1. 贝塞尔曲线 (1). 贝塞尔曲线的作用 贝塞尔曲线的作用是给定控制点&#xff0c;通过控制点生成对应的曲线进行轨迹拟合&#xff0c;输入为点&#xff0c;输出为受到控制点约束而产生的轨迹。 (2). 贝塞尔曲线的数学表达式 假设给定N个控制点&#xff0c;得到的为N-1阶的贝…

GA-SVM,基于GA遗传算法优化SVM支持向量机回归预测(多输入单输出)

基于遗传算法&#xff08;Genetic Algorithm, GA&#xff09;优化支持向量机&#xff08;Support Vector Machine, SVM&#xff09;用于回归预测是一个常见的任务。在这个任务中&#xff0c;我们使用GA来寻找SVM的最佳超参数配置&#xff0c;以最大化回归性能指标&#xff0c;例…

PyCharm使用指南(个性化设置、开发必备插件、常用快捷键)

&#x1f947;作者简介&#xff1a;CSDN内容合伙人、新星计划第三季Python赛道Top1 &#x1f525;本文已收录于Python系列专栏&#xff1a; 零基础学Python &#x1f4ac;订阅专栏后可私信博主进入Python学习交流群&#xff0c;进群可领取Python视频教程以及Python相关电子书合…

【Ambari】Ansible自动化部署大数据集群

目录 一&#xff0e;版本说明和介绍信息 1.1 大数据组件版本 1.2 Apache Components 1.3 Databases支持版本 二&#xff0e;安装包上传和说明 三&#xff0e;服务器基础环境配置 3.1global配置修改 3.2主机名映射配置 3.3免密用户名密码配置 3.4 ansible安装 四. 安…

虚幻引擎像素流源码解读

前言 假期在家把虚幻引擎像素流的源码有研究了一下&#xff0c;进行了一下总结&#xff0c;本文适合有一定使用虚幻引擎像素流经验的人阅读。 源码地址 这里研究的是UE5.1的版本&#xff0c;源码位置如下 C:\Program Files\Epic Games\UE_5.1\Engine\Plugins\Media\PixelStream…

AIGC实战——ProGAN(Progressive Growing Generative Adversarial Network)

AIGC实战——ProGAN 0. 前言1. ProGAN2. 渐进式训练3. 其他技术3.1 小批标准差3.2 均等学习率3.3 逐像素归一化 4. 图像生成小结系列链接 0. 前言 我们已经学习了使用生成对抗网络 (Generative Adversarial Network, GAN) 解决各种图像生成任务。GAN 的模型架构和训练过程具有…

pytest的时候输出一个F后面跟很多绿色的点解读

使用pytest来测试pyramid和kotti项目&#xff0c;在kotti项目测试的时候&#xff0c;输出一个F后面跟很多绿色的点&#xff0c;是什么意思呢&#xff1f; 原来在使用pytest进行测试时&#xff0c;输出中的“F”代表一个失败的测试&#xff08;Failed&#xff09;&#xff0c;而…

Spring源码分析(@Configuration)

文章目录 Spring源码分析&#xff08;Configuration&#xff09;一、ConfigurationClassPostProcessor1、主要作用和特点2、执行的时机3、BeanFactoryPostProcessor4、BeanDefinitionRegistryPostProcessor5、ConfigurationClassPostProcessor1&#xff09;postProcessBeanDefi…

ebpf+perfetto实现调度延迟记录与展示

1.背景 需要分析生产环境的调度问题,如线程的调度延迟有多少,在哪些时间点延迟比较明显,影响其调度的主要原因是什么?其次,我们希望可以比较直观的展示调度延迟情况。最好能对接perfetto的UI和后处理,因为perfetto已经用于分析比较多的性能数据,可以和调度数据进行整合.我们…