目录
一、传感器驱动
一、传感器驱动
1、AP3216驱动
AP3216C是集光照强度、距离、红外三合一传感器。采用I2C接口。
我们的AP3216C芯片是连接到I.MX6ULL的I2C1接口上,首先我们编写设备树:
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
ap3216c@1e {
compatible = "alientek,ap3216c", "misc-ap3216c";
reg = <0x1e>;
};
};
编译后,启动开发板我们可以在设备树映射文件中找到节点:
然后我们开始编写驱动。
(1)编写 i2c_client 结构体
static const struct i2c_device_id ap3216_id[] = {
{.name = "ap3216c", 0},
{/* Sentinel*/},
};
static const struct of_device_id misc_ap3216_of_match[] = {
{.compatible = "misc-ap3216c"},
{/* Sentinel*/},
};
static struct i2c_driver misc_ap3216_driver = {
.driver = {
.name = "misc-ap3216",
.of_match_table = misc_ap3216_of_match, //兼容性匹配列表
},
.id_table = ap3216_id, //名字匹配列表
.probe = misc_ap3216_probe,
.remove = misc_ap3216_remove,
};
(2)声明模块出入口
这里我们使用宏定义 module_i2c_driver(misc_ap3216_driver) 直接声明。
(3)编写 i2c 读写函数
static int ap3216c_read_regs(struct i2c_client *client, u8 reg, void* data, int len)
{
struct i2c_msg msg[2];
/* msg[0]为发送要读取的首地址 */
msg[0].addr = client->addr; /* ap3216c 地址 */
msg[0].flags = 0; /* 标记为发送数据 */
msg[0].buf = ® /* 读取的首地址 */
msg[0].len = 1; /* reg 长度 */
/* msg[1]读取数据 */
msg[1].addr = client->addr; /* ap3216c 地址 */
msg[1].flags = I2C_M_RD; /* 标记为读取数据 */
msg[1].buf = data; /* 读取数据缓冲区 */
msg[1].len = len; /* 要读取的数据长度 */
//int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (2 != i2c_transfer(client->adapter, msg, 2))
return -1;
return 0;
}
static int ap3216c_write_regs(struct i2c_client *client, u8 reg, void* data, int len)
{
u8 buf[256];
struct i2c_msg msg;
buf[0] = reg;
memcpy(buf+1, data, len);
msg.addr = client->addr; /* ap3216c 地址 */
msg.flags = 0; /* 标记为发送数据 */
msg.buf = buf; /* 发送数据缓冲区 */
msg.len = len + 1; /* reg 长度 */
if (1 != i2c_transfer(client->adapter, &msg, 1))
return -1;
return 0;
}
(4)注册ap3216为杂项设备(misc)
我们不仅需要将ap3216注册为一个字符设备,我们还需要将ap3216采集的三个属性映射到文件,从而方便我们的读写。因此我们需要使用到DEVICE_ATTR()宏定义。
使用DEVICE_ATTR,可以实现驱动在sys目录自动创建文件,我们只需要实现show和store函数即可.然后在应用层就能通过cat和echo命令来对sys创建出来的文件进行读写驱动设备,实现交互.具体内容请参考https://www.cnblogs.com/lifexy/p/9799778.html。
同时在struct miscdevice 结构体中恰好存在 const struct attribute_group **groups 变量,为了方便,我们直接将ap3216注册为一个杂项设备。与正常注册字符设备不同,注册misc设备只需要调用misc_register()函数,使注册设备变得更加简单。我们在这里也回顾一下正常注册字符设备的过程:
1、申请设备号:
alloc_chrdev_region(&ap3216c.devid, 0, DEV_CNT, DEV_NAME);
2、注册字符设备:
cdev_init(&ap3216c.cdev, &fops);
ret = cdev_add(&ap3216c.cdev, ap3216c.devid, DEV_CNT);
3、添加设备节点
ap3216c.class = class_create(THIS_MODULE, DEV_NAME);
ap3216c.device = device_create(ap3216c.class, NULL, ap3216c.devid, NULL, DEV_NAME);
(5) 使用DEVICE_ATTR()宏定义 ,并编写对应show函数。store函数用于写,我们这里只需读取传感器数据,不需要写,因此不需要编写store函数。
static ssize_t show_als(struct device *dev, struct device_attribute *attr, char *buf)
{
u16 data;
struct misc_ap3216c_dev *ap3216c = (struct misc_ap3216c_dev *)dev_get_drvdata(dev);
if (ap3216c_read_als_data(ap3216c->client, &data))
return sprintf(buf, "%d\n", -1);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_ir(struct device *dev, struct device_attribute *attr, char *buf)
{
u16 data;
struct misc_ap3216c_dev *ap3216c = (struct misc_ap3216c_dev *)dev_get_drvdata(dev);
if (ap3216c_read_ir_data(ap3216c->client, &data))
return sprintf(buf, "%d\n", -1);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_ps(struct device *dev, struct device_attribute *attr, char *buf)
{
u16 data;
struct misc_ap3216c_dev *ap3216c = (struct misc_ap3216c_dev *)dev_get_drvdata(dev);
if (ap3216c_read_ps_data(ap3216c->client, &data))
return sprintf(buf, "%d\n", -1);
return sprintf(buf, "%d\n", data);
}
static DEVICE_ATTR(als, S_IRUGO, show_als, NULL);
static DEVICE_ATTR(ir, S_IRUGO, show_ir, NULL);
static DEVICE_ATTR(ps, S_IRUGO, show_ps, NULL);
(6)完整代码
做完上述步骤,驱动编写基本完成。 下面附上完整代码。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <linux/of_address.h>
#include <linux/cdev.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/spi/spi.h>
#include <linux/input.h>
#include <linux/regmap.h>
#include <linux/miscdevice.h>
#include <linux/of_platform.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "ap3216reg.h"
#define DEV_NAME "ap3216"
#define DEV_COUNT 1
#define DEV_MINOR 144 //子设备号
struct misc_ap3216c_dev{
struct miscdevice miscdev;
struct i2c_client *client;
};
static int ap3216c_open(struct inode *nd, struct file *fp);
static int ap3216c_release(struct inode *nd, struct file *fp);
static ssize_t ap3216c_read(struct file *fp, char __user *buf, size_t cnt, loff_t *offt);
static int ap3216c_read_regs(struct i2c_client *client, u8 reg, void* data, int len)
{
struct i2c_msg msg[2];
/* msg[0]为发送要读取的首地址 */
msg[0].addr = client->addr; /* ap3216c 地址 */
msg[0].flags = 0; /* 标记为发送数据 */
msg[0].buf = ® /* 读取的首地址 */
msg[0].len = 1; /* reg 长度 */
/* msg[1]读取数据 */
msg[1].addr = client->addr; /* ap3216c 地址 */
msg[1].flags = I2C_M_RD; /* 标记为读取数据 */
msg[1].buf = data; /* 读取数据缓冲区 */
msg[1].len = len; /* 要读取的数据长度 */
//int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (2 != i2c_transfer(client->adapter, msg, 2))
return -1;
return 0;
}
static int ap3216c_write_regs(struct i2c_client *client, u8 reg, void* data, int len)
{
u8 buf[256];
struct i2c_msg msg;
buf[0] = reg;
memcpy(buf+1, data, len);
msg.addr = client->addr; /* ap3216c 地址 */
msg.flags = 0; /* 标记为发送数据 */
msg.buf = buf; /* 发送数据缓冲区 */
msg.len = len + 1; /* reg 长度 */
//int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (1 != i2c_transfer(client->adapter, &msg, 1))
return -1;
return 0;
}
static int ap3216c_write_one_reg(struct i2c_client *client, u8 reg, u8 data)
{
return ap3216c_write_regs(client, reg, &data, 1);
}
static int ap3216c_read_one_reg(struct i2c_client *client, u8 reg, u8* data)
{
return ap3216c_read_regs(client, reg, data, 1);
}
/* static void ap3216c_read_data(struct misc_ap3216c_dev *dev)
{
u8 buf[6];
int i = 0;
for(i = 0 ;i < 6; i++) ap3216c_read_one_reg(dev->client, AP3216C_IRDATALOW + i, &buf[i]);
if(buf[0] & 0x80) misc_ap3216c.ir = 0;
else misc_ap3216c.ir = ((unsigned short) buf[1] << 2) | (buf[0] & 0x03);
misc_ap3216c.light = ((unsigned short) buf[3] << 8) | buf[2];
if(buf[4] & 0x40) misc_ap3216c.ps = 0;
else misc_ap3216c.ps = ((unsigned short) (buf[5] & 0x3f) << 4) | (buf[4] & 0x0f);
}*/
static int ap3216c_read_ps_data(struct i2c_client *client, u16 *data)
{
u8 high, low;
if(ap3216c_read_one_reg(client, AP3216C_PSDATAHIGH, &high) ||
ap3216c_read_one_reg(client, AP3216C_PSDATALOW, &low)){
return -1;
}
if (low & 0x40U)
return -1;
else{
*data = (((u32)high & 0x3FU) << 4) | ((u32)low & 0x0FU);
}
return 0;
}
static int ap3216c_read_ir_data(struct i2c_client *client, u16 *data)
{
u8 high, low;
if(ap3216c_read_one_reg(client, AP3216C_IRDATAHIGH, &high) ||
ap3216c_read_one_reg(client, AP3216C_IRDATALOW, &low)){
return -1;
}
if (low & 0x80U)
return -1;
else{
*data = ((u32)high << 2) | ((u32)low & 0x03U);
}
return 0;
}
static int ap3216c_read_als_data(struct i2c_client *client, u16 *data)
{
u8 high, low;
if(ap3216c_read_one_reg(client, AP3216C_ALSDATAHIGH, &high) ||
ap3216c_read_one_reg(client, AP3216C_ALSDATALOW, &low)){
return -1;
}
if ((u32)low & 0x40U)
return -1;
else{
*data = (((u32)high & 0x3FU) << 4) | ((u32)low & 0x0FU);
}
return 0;
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = ap3216c_open,
.release = ap3216c_release,
.read = ap3216c_read,
};
static ssize_t show_als(struct device *dev, struct device_attribute *attr, char *buf)
{
u16 data;
struct misc_ap3216c_dev *ap3216c = (struct misc_ap3216c_dev *)dev_get_drvdata(dev);
if (ap3216c_read_als_data(ap3216c->client, &data))
return sprintf(buf, "%d\n", -1);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_ir(struct device *dev, struct device_attribute *attr, char *buf)
{
u16 data;
struct misc_ap3216c_dev *ap3216c = (struct misc_ap3216c_dev *)dev_get_drvdata(dev);
if (ap3216c_read_ir_data(ap3216c->client, &data))
return sprintf(buf, "%d\n", -1);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_ps(struct device *dev, struct device_attribute *attr, char *buf)
{
u16 data;
struct misc_ap3216c_dev *ap3216c = (struct misc_ap3216c_dev *)dev_get_drvdata(dev);
if (ap3216c_read_ps_data(ap3216c->client, &data))
return sprintf(buf, "%d\n", -1);
return sprintf(buf, "%d\n", data);
}
static DEVICE_ATTR(als, S_IRUGO, show_als, NULL);
static DEVICE_ATTR(ir, S_IRUGO, show_ir, NULL);
static DEVICE_ATTR(ps, S_IRUGO, show_ps, NULL);
static struct attribute *ap3216c_attrs[] = {
&dev_attr_ir.attr,
&dev_attr_als.attr,
&dev_attr_ps.attr,
NULL
};
const struct attribute_group misc_ap3216_group={
.attrs = ap3216c_attrs,
};
const struct attribute_group *misc_ap3216_groups[]={
&misc_ap3216_group,
NULL,
};
static struct misc_ap3216c_dev misc_ap3216c = {
.miscdev = {
.minor = DEV_MINOR,
.name = DEV_NAME,
.fops = &fops,
.groups = misc_ap3216_groups,
},
.client = NULL,
};
static ssize_t ap3216c_read(struct file *fp, char __user *buf, size_t cnt, loff_t *offt)
{
short data[3];
long err = 0;
struct misc_ap3216c_dev *dev = (struct misc_ap3216c_dev *)fp->private_data;
ap3216c_read_ir_data(dev->client, &data[0]);
ap3216c_read_als_data(dev->client, &data[1]);
ap3216c_read_ps_data(dev->client, &data[2]);
err = copy_to_user(buf, data, sizeof(data));
return err;
}
static int ap3216c_open(struct inode *nd, struct file *fp)
{
fp->private_data = &misc_ap3216c;
return 0;
}
static int ap3216c_release(struct inode *nd, struct file *fp)
{
fp->private_data = NULL;
return 0;
}
static int misc_ap3216_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
u8 data;
//misc设备注册
misc_register(&misc_ap3216c.miscdev);
//结构体初始化
misc_ap3216c.client = client;
dev_set_drvdata(misc_ap3216c.miscdev.this_device, (void *)&misc_ap3216c);
//ap3216c初始化
ap3216c_write_one_reg(client, AP3216C_SYSTEMCONG, 0x04);
mdelay(50); /* AP3216C 复位最少 10ms */
ap3216c_write_one_reg(client, AP3216C_SYSTEMCONG, 0X03); //开启ALS+PS+IR
ap3216c_read_one_reg(client, AP3216C_SYSTEMCONG, &data);
if(data == 0x03)
printk("ap3216c init success!\n");
return 0;
}
static int misc_ap3216_remove(struct i2c_client *client)
{
misc_ap3216c.client = NULL;
//misc设备注销
return misc_deregister(&misc_ap3216c.miscdev);
}
static const struct i2c_device_id ap3216_id[] = {
{.name = "ap3216c", 0},
{/* Sentinel*/},
};
static const struct of_device_id misc_ap3216_of_match[] = {
{.compatible = "misc-ap3216c"},
{/* Sentinel*/},
};
static struct i2c_driver misc_ap3216_driver = {
.driver = {
.name = "misc-ap3216",
.of_match_table = misc_ap3216_of_match,
},
.id_table = ap3216_id,
.probe = misc_ap3216_probe,
.remove = misc_ap3216_remove,
};
module_i2c_driver(misc_ap3216_driver)
MODULE_LICENSE("GPL");
2、ICM20608驱动
ICM20608驱动为六轴加速度传感器,采用SPI接口,编写流程与AP3216非常相似,就不重复赘述。直接上代码:
&ecspi3 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
spidev0: icm20608@0 {
compatible = "alientek,icm20608", "misc-icm20608";
spi-max-frequency = <8000000>;
reg = <0>;
};
};
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <linux/of_address.h>
#include <linux/cdev.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/spi/spi.h>
#include <linux/input.h>
#include <linux/regmap.h>
#include <linux/miscdevice.h>
#include <linux/of_platform.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "icm20608reg.h"
#define DEV_NAME "icm20608"
#define DEV_COUNT 1
#define DEV_MINOR 145 //子设备号
struct misc_icm20608_dev{
struct miscdevice miscdev;
struct spi_device *device;
};
static int icm20608_open(struct inode *nd, struct file *fp);
static int icm20608_release(struct inode *nd, struct file *fp);
static long icm20608_ioctl(struct file *, unsigned int, unsigned long);
static int icm20608_read_regs(struct spi_device *dev, u8 reg, void* data, int len)
{
int err;
u8 *txbuf, *rxbuf;
txbuf = (u8 *)kcalloc(1, sizeof(u8), GFP_KERNEL);
*txbuf = reg | 0x80; //读寄存器时首位要置一
rxbuf = (u8 *)kcalloc(len, sizeof(u8), GFP_KERNEL);
err = spi_write_then_read(dev, txbuf, 1, rxbuf, len);
if(err < 0){
return -1;
}
else{
memcpy(data, rxbuf, len);
}
return 0;
}
static int icm20608_write_regs(struct spi_device *dev, u8 reg, void* data, int len)
{
int err;
u8 *txbuf;
txbuf = (u8 *)kcalloc(len + 1, sizeof(u8), GFP_KERNEL);
*txbuf = reg & ~0x80; //写寄存器时首位要清零
memcpy(txbuf+1, data, len);
err = spi_write_then_read(dev, txbuf, len+1, NULL, 0);
if(err < 0){
return -1;
}
return 0;
}
static int icm20608_write_one_reg(struct spi_device *dev, u8 reg, u8 data)
{
return icm20608_write_regs(dev, reg, &data, 1);
}
static int icm20608_read_one_reg(struct spi_device *dev, u8 reg, u8* data)
{
return icm20608_read_regs(dev, reg, data, 1);
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = icm20608_open,
.release = icm20608_release,
.unlocked_ioctl = icm20608_ioctl,
};
static ssize_t show_xg(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
short data;
u8 tmp[2];
struct misc_icm20608_dev *icm20608 = (struct misc_icm20608_dev *)dev_get_drvdata(dev);
ret = icm20608_read_regs(icm20608->device, ICM20_GYRO_XOUT_H, tmp, 2);
if(ret < 0) return sprintf(buf, "%d\n", -1);
data = (signed short)(tmp[0]<<8 | tmp[1]);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_yg(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
short data;
u8 tmp[2];
struct misc_icm20608_dev *icm20608 = (struct misc_icm20608_dev *)dev_get_drvdata(dev);
ret = icm20608_read_regs(icm20608->device, ICM20_GYRO_YOUT_H, tmp, 2);
if(ret < 0) return sprintf(buf, "%d\n", -1);
data = (signed short)(tmp[0]<<8 | tmp[1]);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_zg(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
short data;
u8 tmp[2];
struct misc_icm20608_dev *icm20608 = (struct misc_icm20608_dev *)dev_get_drvdata(dev);
ret = icm20608_read_regs(icm20608->device, ICM20_GYRO_ZOUT_H, tmp, 2);
if(ret < 0) return sprintf(buf, "%d\n", -1);
data = (signed short)(tmp[0]<<8 | tmp[1]);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_xa(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
short data;
u8 tmp[2];
struct misc_icm20608_dev *icm20608 = (struct misc_icm20608_dev *)dev_get_drvdata(dev);
ret = icm20608_read_regs(icm20608->device, ICM20_ACCEL_XOUT_H, tmp, 2);
if(ret < 0) return sprintf(buf, "%d\n", -1);
data = (signed short)(tmp[0]<<8 | tmp[1]);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_ya(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
short data;
u8 tmp[2];
struct misc_icm20608_dev *icm20608 = (struct misc_icm20608_dev *)dev_get_drvdata(dev);
ret = icm20608_read_regs(icm20608->device, ICM20_ACCEL_YOUT_H, tmp, 2);
if(ret < 0) return sprintf(buf, "%d\n", -1);
data = (signed short)(tmp[0]<<8 | tmp[1]);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_za(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
short data;
u8 tmp[2];
struct misc_icm20608_dev *icm20608 = (struct misc_icm20608_dev *)dev_get_drvdata(dev);
ret = icm20608_read_regs(icm20608->device, ICM20_ACCEL_ZOUT_H, tmp, 2);
if(ret < 0) return sprintf(buf, "%d\n", -1);
data = (signed short)(tmp[0]<<8 | tmp[1]);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
short data;
u8 tmp[2];
struct misc_icm20608_dev *icm20608 = (struct misc_icm20608_dev *)dev_get_drvdata(dev);
ret = icm20608_read_regs(icm20608->device, ICM20_TEMP_OUT_H, tmp, 2);
if(ret < 0) return sprintf(buf, "%d\n", -1);
data = (signed short)(tmp[0]<<8 | tmp[1]);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_accel_scale(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
u8 data;
// u16 as;
struct misc_icm20608_dev *icm20608 = (struct misc_icm20608_dev *)dev_get_drvdata(dev);
ret = icm20608_read_one_reg(icm20608->device, ICM20_ACCEL_CONFIG, &data);
if(ret < 0) return sprintf(buf, "%d\n", -1);
data = (data >> 3) & 0x03;
// as = icm20608_accel_scaleget(data);
return sprintf(buf, "%d\n", data);
}
static ssize_t show_gyro_scale(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
u8 data;
// float gs;
struct misc_icm20608_dev *icm20608 = (struct misc_icm20608_dev *)dev_get_drvdata(dev);
ret = icm20608_read_one_reg(icm20608->device, ICM20_GYRO_CONFIG, &data);
if(ret < 0) return sprintf(buf, "%d\n", -1);
data = (data >> 3) & 0x03;
// gs = icm20608_gyro_scaleget(data);
return sprintf(buf, "%d\n", data);
}
static DEVICE_ATTR(xg, S_IRUGO, show_xg, NULL);
static DEVICE_ATTR(yg, S_IRUGO, show_yg, NULL);
static DEVICE_ATTR(zg, S_IRUGO, show_zg, NULL);
static DEVICE_ATTR(xa, S_IRUGO, show_xa, NULL);
static DEVICE_ATTR(ya, S_IRUGO, show_ya, NULL);
static DEVICE_ATTR(za, S_IRUGO, show_za, NULL);
static DEVICE_ATTR(temp, S_IRUGO, show_temp, NULL);
static DEVICE_ATTR(accel_scale, S_IRUGO, show_accel_scale, NULL);
static DEVICE_ATTR(gyro_scale, S_IRUGO, show_gyro_scale, NULL);
static struct attribute *icm20608_attrs[] = {
&dev_attr_xg.attr,
&dev_attr_yg.attr,
&dev_attr_zg.attr,
&dev_attr_xa.attr,
&dev_attr_ya.attr,
&dev_attr_za.attr,
&dev_attr_temp.attr,
&dev_attr_accel_scale.attr,
&dev_attr_gyro_scale.attr,
NULL
};
const struct attribute_group misc_icm20608_group={
.attrs = icm20608_attrs,
};
const struct attribute_group *misc_icm20608_groups[]={
&misc_icm20608_group,
NULL,
};
static struct misc_icm20608_dev misc_icm20608 = {
.miscdev = {
.minor = DEV_MINOR,
.name = DEV_NAME,
.fops = &fops,
.groups = misc_icm20608_groups,
},
.device = NULL,
};
static long icm20608_ioctl(struct file *fp, unsigned int cmd, unsigned long data_address)
{
u8 buf[2], data;
signed short tmp = 0;
long ret = 0;
struct misc_icm20608_dev *dev = (struct misc_icm20608_dev *)fp->private_data;
switch(cmd){
case 3:
ret = icm20608_read_regs(dev->device, ICM20_ACCEL_XOUT_H, buf, 2);
break;
case 4:
ret = icm20608_read_regs(dev->device, ICM20_ACCEL_YOUT_H, buf, 2);
break;
case 5:
ret = icm20608_read_regs(dev->device, ICM20_ACCEL_ZOUT_H, buf, 2);
break;
case 6:
ret = icm20608_read_regs(dev->device, ICM20_GYRO_XOUT_H, buf, 2);
break;
case 7:
ret = icm20608_read_regs(dev->device, ICM20_GYRO_YOUT_H, buf, 2);
break;
case 8:
ret = icm20608_read_regs(dev->device, ICM20_GYRO_ZOUT_H, buf, 2);
break;
case 9:
ret = icm20608_read_regs(dev->device, ICM20_TEMP_OUT_H, buf, 2);
break;
case 10://获取accel scale
ret = icm20608_read_one_reg(dev->device, ICM20_ACCEL_CONFIG, &data);
if(ret < 0) return -1;
data = (data >> 3) & 0x03;
if (copy_to_user((void *)data_address, &data, sizeof(data))) return -1;
return 0;
case 11://获取gyro scale
ret = icm20608_read_one_reg(dev->device, ICM20_GYRO_CONFIG, &data);
if(ret < 0) return -1;
data = (data >> 3) & 0x03;
if (copy_to_user((void *)data_address, &data, sizeof(data))) return -1;
return 0;
default:
ret = -1;
break;
}
if(ret < 0){
return -1;
}
tmp = (signed short)(buf[0] << 8 | buf[1]);
// printk("read data[%d]: %d\r\n", cmd, tmp);
if (copy_to_user((void *)data_address, &tmp, sizeof(tmp)))
return -1;
return 0;
}
static int icm20608_open(struct inode *nd, struct file *fp)
{
fp->private_data = &misc_icm20608;
return 0;
}
static int icm20608_release(struct inode *nd, struct file *fp)
{
fp->private_data = NULL;
return 0;
}
static int misc_icm20608_probe(struct spi_device *dev)
{
u8 value;
//misc设备注册
misc_register(&misc_icm20608.miscdev);
//结构体初始化
misc_icm20608.device = dev;
//icm20608初始化
icm20608_write_one_reg(dev, ICM20_PWR_MGMT_1, 0x80); /* 复位,复位后为0x40,睡眠模式 */
mdelay(50);
icm20608_write_one_reg(dev, ICM20_PWR_MGMT_1, 0x01); /* 关闭睡眠,自动选择时钟 */
mdelay(50);
icm20608_read_one_reg(dev, ICM20_WHO_AM_I, &value);
printk("ICM20608 ID = %#X\r\n", value);
icm20608_write_one_reg(dev, ICM20_SMPLRT_DIV, 0x00); /* 输出速率是内部采样率 */
icm20608_write_one_reg(dev, ICM20_GYRO_CONFIG, 0x18); /* 陀螺仪±2000dps量程 */
icm20608_write_one_reg(dev, ICM20_ACCEL_CONFIG, 0x18); /* 加速度计±16G量程 */
icm20608_write_one_reg(dev, ICM20_CONFIG, 0x04); /* 陀螺仪低通滤波BW=20Hz */
icm20608_write_one_reg(dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz */
icm20608_write_one_reg(dev, ICM20_PWR_MGMT_2, 0x00); /* 打开加速度计和陀螺仪所有轴 */
icm20608_write_one_reg(dev, ICM20_LP_MODE_CFG, 0x00); /* 关闭低功耗 */
icm20608_write_one_reg(dev, ICM20_FIFO_EN, 0x00); /* 关闭FIFO */
return 0;
}
static int misc_icm20608_remove(struct spi_device *dev)
{
misc_icm20608.device = NULL;
//misc设备注销
return misc_deregister(&misc_icm20608.miscdev);
}
static const struct spi_device_id icm20608_id[] = {
{.name = "icm20608", 0},
{/* Sentinel*/},
};
static const struct of_device_id misc_icm20608_of_match[] = {
{.compatible = "misc-icm20608"},
{/* Sentinel*/},
};
static struct spi_driver misc_icm20608_driver = {
.driver = {
.name = "misc-icm20608", //通过兼容性进行匹配
.of_match_table = misc_icm20608_of_match,
},
.id_table = icm20608_id, //通过名字进行匹配
.probe = misc_icm20608_probe,
.remove = misc_icm20608_remove,
};
module_spi_driver(misc_icm20608_driver)
MODULE_LICENSE("GPL");
二、QT程序编写
1、AP3216类编写
class ap3216 : public QObject
{
Q_OBJECT
public:
explicit ap3216(QObject *parent = nullptr);
~ap3216();
float getAls(void);
int getPs(void);
int getIr(void);
int read_file_ap3216(QString filename);
void updateDta();
void startTimer(int msec);
private slots:
void timerHandle();
private:
QTimer *timer;
float als;
int ps;
int ir;
};
可以看到 class ap3216 主要负责ap3216c传感器数据的读取。
我们从 /sys/class/misc/ap3216/** 文件中读取到的数据是传感器的ADC值,我们需要将其转化为实际值,并将数据存放在私有成员变量 als、ps、ir 中。
void ap3216::updateDta()
{
int ta = read_file_ap3216("/sys/class/misc/ap3216/als");
int tp = read_file_ap3216("/sys/class/misc/ap3216/ps");
int ti = read_file_ap3216("/sys/class/misc/ap3216/ir");
als = (float)ta * 0.35;
ps = tp;
ir = ti;
}
完整 ap3216.cpp 代码:
#include "ap3216.h"
ap3216::ap3216(QObject *parent) : QObject(parent)
{
timer = new QTimer;
connect(timer, SIGNAL(timeout()), this, SLOT(timerHandle()));
}
ap3216::~ap3216()
{
delete timer;
}
int ap3216::read_file_ap3216(QString filename)
{
QFile file;
int tmp = -1;
file.setFileName(filename);
if(!file.exists()){
qDebug() << filename << "is not exist" << Qt::endl;
return -1;
}
if(!file.open(QIODevice::ReadOnly)){
qDebug() << filename << "can not open" << Qt::endl;
return -1;
}
QString str = file.readAll();
tmp = str.toInt();
return tmp;
}
void ap3216::updateDta()
{
int ta = read_file_ap3216("/sys/class/misc/ap3216/als");
int tp = read_file_ap3216("/sys/class/misc/ap3216/ps");
int ti = read_file_ap3216("/sys/class/misc/ap3216/ir");
als = (float)ta * 0.35;
ps = tp;
ir = ti;
}
void ap3216::startTimer(int msec)
{
timer->start(msec);
}
void ap3216::timerHandle()
{
updateDta();
}
float ap3216::getAls()
{
return als;
}
int ap3216::getPs()
{
return ps;
}
int ap3216::getIr()
{
return ir;
}
2、ICM20608类编写
与ap3216类似,直接附上代码:
icm20608.h:
#ifndef ICM20608_H
#define ICM20608_H
#include <QObject>
#include <qfile.h>
#include <qdebug.h>
#include <QScreen>
#include <QGuiApplication>
#include <qtimer.h>
class icm20608 : public QObject
{
Q_OBJECT
public:
explicit icm20608(QObject *parent = nullptr);
~icm20608();
int read_file_icm20608(QString filename);
void updateData(void);
float icm20608_gyro_scaleget(int data);
unsigned short icm20608_accel_scaleget(int data);
void startTimer(int msec);
float getAx(void);
float getAy(void);
float getAz(void);
float getGx(void);
float getGy(void);
float getGz(void);
float getTemp(void);
float getAs(void);
float getGs(void);
signals:
private slots:
void timerHandle(void);
private:
QTimer *timer;
float ax,ay,az;
float gx,gy,gz;
float temp;
float aScale, gScale;
};
#endif // ICM20608_H
icm20608.cpp:
#include "icm20608.h"
icm20608::icm20608(QObject *parent) : QObject(parent)
{
timer = new QTimer;
connect(timer, SIGNAL(timeout()), this, SLOT(timerHandle()));
}
icm20608::~icm20608()
{
delete timer;
}
int icm20608::read_file_icm20608(QString filename)
{
QFile file;
int tmp = -1;
file.setFileName(filename);
if(!file.exists()){
qDebug() << filename << "is not exist" << Qt::endl;
return -1;
}
if(!file.open(QIODevice::ReadOnly)){
qDebug() << filename << "can not open" << Qt::endl;
return -1;
}
QString str = file.readAll();
tmp = str.toInt();
return tmp;
}
/*
* @description : 获取陀螺仪的分辨率
* @param : 无
* @return : 获取到的分辨率
*/
float icm20608::icm20608_gyro_scaleget(int data)
{
float gyroscale = 0;
switch(data) {
case 0:
gyroscale = 131;
break;
case 1:
gyroscale = 65.5;
break;
case 2:
gyroscale = 32.8;
break;
case 3:
gyroscale = 16.4;
break;
}
return gyroscale;
}
/*
* @description : 获取加速度计的分辨率
* @param : 无
* @return : 获取到的分辨率
*/
unsigned short icm20608::icm20608_accel_scaleget(int data)
{
unsigned short accelscale = 0;
switch(data) {
case 0:
accelscale = 16384;
break;
case 1:
accelscale = 8192;
break;
case 2:
accelscale = 4096;
break;
case 3:
accelscale = 2048;
break;
}
return accelscale;
}
void icm20608::startTimer(int msec)
{
timer->start(msec);
}
float icm20608::getAx()
{
return ax;
}
float icm20608::getAy()
{
return ay;
}
float icm20608::getAz()
{
return az;
}
float icm20608::getGx()
{
return gx;
}
float icm20608::getGy()
{
return gy;
}
float icm20608::getGz()
{
return gz;
}
float icm20608::getTemp()
{
return temp;
}
float icm20608::getAs()
{
return aScale;
}
float icm20608::getGs()
{
return gScale;
}
void icm20608::updateData()
{
int t_ax = read_file_icm20608("/sys/class/misc/icm20608/xa");
int t_ay = read_file_icm20608("/sys/class/misc/icm20608/ya");
int t_az = read_file_icm20608("/sys/class/misc/icm20608/za");
int t_gx = read_file_icm20608("/sys/class/misc/icm20608/xg");
int t_gy = read_file_icm20608("/sys/class/misc/icm20608/yg");
int t_gz = read_file_icm20608("/sys/class/misc/icm20608/zg");
int t_temp = read_file_icm20608("/sys/class/misc/icm20608/temp");
int t_as = read_file_icm20608("/sys/class/misc/icm20608/accel_scale");
int t_gs = read_file_icm20608("/sys/class/misc/icm20608/gyro_scale");
aScale = icm20608_accel_scaleget(t_as);
gScale = icm20608_gyro_scaleget(t_gs);
t_ax = ((float)t_ax/aScale * 100); //乘100是为了保留两位小数
t_ay = ((float)t_ay/aScale * 100);
t_az = ((float)t_az/aScale * 100);
t_gx = ((float)t_gx/gScale * 100);
t_gy = ((float)t_gy/gScale * 100);
t_gz = ((float)t_gz/gScale * 100);
t_temp = (((float)(t_temp) - 25 ) / 326.8 + 25) *100;
ax = (float)t_ax/100;
ay = (float)t_ay/100;
az = (float)t_az/100;
gx = (float)t_gx/100;
gy = (float)t_gy/100;
gy = (float)t_gz/100;
temp = (float)t_temp/100;
}
void icm20608::timerHandle()
{
updateData();
}
3、QT显示界面
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QScreen>
#include <QGuiApplication>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// system("insmod /home/gmj/ap3216/misc_ap3216.ko");
// system("insmod /home/gmj/icm20608/misc_icm20608.ko");
ui->setupUi(this);
this->setLayout(ui->verticalLayout_2);
QList <QScreen *> list_screen = QGuiApplication::screens();
/* 重设大小 */
this->resize(list_screen.at(0)->geometry().width(),
list_screen.at(0)->geometry().height());
ap = new ap3216;
icm = new icm20608;
ap->startTimer(500);
icm->startTimer(500);
timer = new QTimer;
connect(timer, SIGNAL(timeout()), this, SLOT(showData()));
timer->start(200);
}
Widget::~Widget()
{
delete ui;
// system("rmmod /home/gmj/ap3216/misc_ap3216.ko");
// system("rmmod /home/gmj/icm20608/misc_icm20608.ko");
}
void Widget::showData()
{
ui->lineEdit_temp->setText(QString::number(icm->getTemp()));
ui->lineEdit_ax->setText(QString::number(icm->getAx()));
ui->lineEdit_ay->setText(QString::number(icm->getAy()));
ui->lineEdit_az->setText(QString::number(icm->getAz()));
ui->lineEdit_gx->setText(QString::number(icm->getGx()));
ui->lineEdit_gy->setText(QString::number(icm->getGy()));
ui->lineEdit_gz->setText(QString::number(icm->getGz()));
ui->lineEdit_lr->setText(QString::number(ap->getAls()));
ui->lineEdit_ps->setText(QString::number(ap->getPs()));
ui->lineEdit_ir->setText(QString::number(ap->getIr()));
}
三、最终效果
四、 结语
这个项目包含 I2C、SPI通信协议,linux驱动编写、QT编程三大块的知识,是一个简单有趣的嵌入式Linux+QT项目。遇到问题可以私信或评论。