一. 简介
经过前面几篇文章的学习,已经完成了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 = ® //寄存器地址
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设备的数据是否正常。