[嵌入式Linux+QT]基于QT的AP3216+ICM20608传感器数据采集

1 篇文章 0 订阅

目录

一、传感器驱动

1、AP3216驱动

2、ICM20608驱动

 二、QT程序编写

1、AP3216类编写

2、ICM20608类编写

3、QT显示界面

三、最终效果

四、 结语


一、传感器驱动

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 = &reg; /* 读取的首地址 */
	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 = &reg; /* 读取的首地址 */
	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项目。遇到问题可以私信或评论。

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
I2C接口距离传感器ap3216c读写Verilog驱动源码Quartus工程文件,FPGA型号Cyclone4E系列中的EP4CE10F17C8,Quartus版本18.0。 module ap3216c_top( //global clock input sys_clk , // 系统时钟 input sys_rst_n , // 系统复位 //ap3216c interface output ap_scl , // i2c时钟线 inout ap_sda , // i2c数据线 //user interface output [3:0] led , // led灯接口 output [5:0] sel , // 数码管位选 output [7:0] seg_led // 数码管段选 ); //parameter define parameter SLAVE_ADDR = 7'h1e ; // 器件地址 parameter BIT_CTRL = 1'b0 ; // 字地址位控制参数(16b/8b) parameter CLK_FREQ = 26'd50_000_000; // i2c_dri模块的驱动时钟频率(CLK_FREQ) parameter I2C_FREQ = 18'd250_000 ; // I2C的SCL时钟频率 //wire define wire clk ; // I2C操作时钟 wire i2c_exec ; // i2c触发控制 wire [15:0] i2c_addr ; // i2c操作地址 wire [ 7:0] i2c_data_w; // i2c写入的数据 wire i2c_done ; // i2c操作结束标志 wire i2c_ack ; // I2C应答标志 0:应答 1:未应答 wire i2c_rh_wl ; // i2c读写控制 wire [ 7:0] i2c_data_r; // i2c读出的数据 wire [15:0] als_data ; // ALS的数据 wire [ 9:0] ps_data ; // PS的数据 //***************************************************** //** main code //***************************************************** //例化i2c_dri,调用IIC协议 i2c_dri #( .SLAVE_ADDR (SLAVE_ADDR), // slave address从机地址,放此处方便参数传递 .CLK_FREQ (CLK_FREQ ), // i2c_dri模块的驱动时钟频率(CLK_FREQ) .I2C_FREQ (I2C_FREQ ) // I2C的SCL时钟频率 ) u_i2c_dri( //global clock .clk (sys_clk ), // i2c_dri模块的驱动时钟(CLK_FREQ) .rst_n (sys_rst_n ), // 复位信号 //i2c interface .i2c_exec
下面是一个简单的 Linux SPI 驱动程序,用于读取 ICM20608 传感器的数据: ```c #include <linux/spi/spi.h> #include <linux/module.h> #define ICM20608_REG_WHO_AM_I 0x75 #define ICM20608_REG_ACCEL_XOUT_H 0x3B #define ICM20608_REG_ACCEL_XOUT_L 0x3C #define ICM20608_REG_ACCEL_YOUT_H 0x3D #define ICM20608_REG_ACCEL_YOUT_L 0x3E #define ICM20608_REG_ACCEL_ZOUT_H 0x3F #define ICM20608_REG_ACCEL_ZOUT_L 0x40 static struct spi_device *icm20608_spi_device; static int icm20608_spi_probe(struct spi_device *spi) { printk(KERN_INFO "ICM20608 SPI driver probe\n"); icm20608_spi_device = spi; // 检查设备 ID u8 who_am_i; spi_read(spi, &who_am_i, 1); if (who_am_i != 0xAF) { printk(KERN_ERR "ICM20608 device ID mismatch\n"); return -ENODEV; } printk(KERN_INFO "ICM20608 device ID match\n"); return 0; } static int icm20608_spi_remove(struct spi_device *spi) { printk(KERN_INFO "ICM20608 SPI driver remove\n"); return 0; } static struct spi_driver icm20608_spi_driver = { .driver = { .name = "icm20608", .owner = THIS_MODULE, }, .probe = icm20608_spi_probe, .remove = icm20608_spi_remove, }; static int __init icm20608_init(void) { printk(KERN_INFO "ICM20608 driver init\n"); // 注册 SPI 设备 spi_register_driver(&icm20608_spi_driver); return 0; } static void __exit icm20608_exit(void) { printk(KERN_INFO "ICM20608 driver exit\n"); // 注销 SPI 设备 spi_unregister_driver(&icm20608_spi_driver); } module_init(icm20608_init); module_exit(icm20608_exit); MODULE_LICENSE("GPL"); ``` 上面的代码只是一个简单的示例,它只检查了 ICM20608 的设备 ID,并没有实际读取传感器数据。你需要根据 ICM20608 的数据手册,编写适当的代码来读取传感器数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值