赞
踩
前言:本文为手把手教学 Linux+QT 的典型基础项目 AP3216C 的数据折线图显示,项目使用正点原子的 IMX6ULL 阿尔法( Cortex-A7 系列)开发板。项目需要实现 AP3216C 在 Linux 系统下的驱动,使用 QT 设计 AP3216C 的数据显示页面作为项目的应用层。该项目属于非常简单的入门级项目,核心目的是帮助大家熟悉 Linux 系统下的项目制作和工程研发过程。希望该项目可以帮助大家学会灵活使用 Linux 系统下的传感器开发!(文末有代码开源!)
硬件实物图:
效果图:
本项目核心是教学 Linux+QT 的工程研发流程,如果专注于应用层工程学习的,可以直接使用正点原子提供的 Linux 镜像文件。本教程是适配正点提供的系统镜像的,可以直接使用!
I.MX6U-ALPHA 开发板上通过 I2C1 连接了一个三合一环境传感器:AP3216C,AP3216C 是由敦南科技推出的一款传感器,其支持环境光强度(ALS)、接近距离(PS)和红外线强度(IR)这三个环境参数检测。该芯片可以通过 IIC 接口与主控制相连,并且支持中断,AP3216C 的特点如下:
①、I2C 接口,快速模式下波特率可以到 400Kbit/S
②、多种工作模式选择:ALS、PS+IR、ALS+PS+IR、PD 等等。
③、内建温度补偿电路。
④、宽工作温度范围(-30°C ~ +80°C)。
⑤、超小封装,4.1mm x 2.4mm x 1.35mm
⑥、环境光传感器具有 16 位分辨率。
⑦、接近传感器和红外传感器具有 10 位分辨率。
这个芯片设计的用途是给手机之类的使用,比如:返回当前环境光强以便调整屏幕亮度;用户接听电话时,将手机放置在耳边后,自动关闭屏幕避免用户误触碰 。
Linux 内核也将 I2C 驱动分为两部分:
①、I2C 总线驱动,I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
②、I2C 设备驱动,I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。
2.1.1 IO修改或添加
需要设置 UART4_TXD 和 UART4_RXD 这两个 IO,NXP 其实已经将他这两个 IO 设置好了,打开 imx6ull-alientek-emmc.dts,然后找到如下内容:
- pinctrl_i2c1: i2c1grp {
- fsl,pins = <
- MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
- MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
- >;
- };
pinctrl_i2c1 就是 I2C1 的 IO 节点,这里将 UART4_TXD 和 UART4_RXD 这两个 IO 分别复用为 I2C1_SCL 和 I2C1_SDA,电气属性都设置为 0x4001b8b0。
2.1.2 在 i2c1 节点追加 ap3216c 子节点
AP3216C 是连接到 I2C1 上的,因此需要在 i2c1 节点下添加 ap3216c 的设备子节点,在 imx6ull-alientek-emmc.dts 文件中找到 i2c1 节点
- &i2c1 {
- clock-frequency = <100000>; /* 时钟频率 */
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c1>; /* pinctrl引脚电气属性 */
- status = "okay";
-
- ap3216c@1e {
- compatible = "alientek,ap3216c";
- reg = <0x1e>;
- };
- };
编译设备树,传到开发板上,重启。此时我们系统的I2C设备有:
下面编写 AP3216 驱动:
ap3216creg.h:
- #ifndef AP3216C_H
- #define AP3216C_H
- /***************************************************************
- 文件名 : ap3216creg.h
- 描述 : AP3216C寄存器地址描述头文件
- ***************************************************************/
- #define AP3216C_ADDR 0X1E /* AP3216C器件地址 */
- /* 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

ap3216.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 <linux/cdev.h>
- #include <linux/device.h>
- #include <linux/of_gpio.h>
- #include <linux/semaphore.h>
- #include <linux/timer.h>
- #include <linux/i2c.h>
- #include <asm/mach/map.h>
- #include <asm/uaccess.h>
- #include <asm/io.h>
- #include "ap3216creg.h"
-
- #define AP3216C_CNT 1
- #define AP3216C_NAME "ap3216c"
-
- struct ap3216c_dev {
- dev_t devid; /* 设备号 */
- struct cdev cdev; /* cdev */
- struct class *class; /* 类 */
- struct device *device; /* 设备 */
- struct device_node *nd; /* 设备节点 */
- int major; /* 主设备号 */
- void *private_data; /* 私有数据 */
- unsigned short ir, als, ps; /* 三个光传感器数据 */
- };
-
- static struct ap3216c_dev ap3216cdev;
-
- /*
- * @description : 从ap3216c读取多个寄存器数据
- * @param - dev: ap3216c设备
- * @param - reg: 要读取的寄存器首地址
- * @param - val: 读取到的数据
- * @param - len: 要读取的数据长度
- * @return : 操作结果
- */
- static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
- {
- int ret;
- struct i2c_msg msg[2];
- struct i2c_client *client = (struct i2c_client *)dev->private_data;
-
- /* 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 = val; /* 读取数据缓冲区 */
- msg[1].len = len; /* 要读取的数据长度*/
-
- ret = i2c_transfer(client->adapter, msg, 2);
- if(ret == 2) {
- ret = 0;
- } else {
- printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
- }
-
- /*
- * @description : 向ap3216c多个寄存器写入数据
- * @param - dev: ap3216c设备
- * @param - reg: 要写入的寄存器首地址
- * @param - val: 要写入的数据缓冲区
- * @param - len: 要写入的数据长度
- * @return : 操作结果
- */
- static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
- {
- u8 b[256];
- struct i2c_msg msg;
- struct i2c_client *client = (struct i2c_client *)dev->private_data;
-
- b[0] = reg; /* 寄存器首地址 */
- memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */
-
- msg.addr = client->addr; /* ap3216c地址 */
- msg.flags = 0; /* 标记为写数据 */
-
- msg.buf = b; /* 要写入的数据缓冲区 */
- msg.len = len + 1; /* 要写入的数据长度 */
-
- return i2c_transfer(client->adapter, &msg, 1);
- }
-
- /*
- * @description : 读取ap3216c指定寄存器值,读取一个寄存器
- * @param - dev: ap3216c设备
- * @param - reg: 要读取的寄存器
- * @return : 读取到的寄存器值
- */
- static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
- {
- u8 data = 0;
-
- ap3216c_read_regs(dev, reg, &data, 1);
- return data;
-
- #if 0
- struct i2c_client *client = (struct i2c_client *)dev->private_data;
- return i2c_smbus_read_byte_data(client, reg);
- #endif
- }
-
- /*
- * @description : 向ap3216c指定寄存器写入指定的值,写一个寄存器
- * @param - dev: ap3216c设备
- * @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);
- }
-
- /*
- * @description : 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!
- * : 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
- * @param - ir : ir数据
- * @param - ps : ps数据
- * @param - ps : als数据
- * @return : 无。
- */
- 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]; /* 读取ALS传感器的数据 */
-
- if(buf[4] & 0x40) /* IR_OF位为1,则数据无效 */
- dev->ps = 0;
- else /* 读取PS传感器的数据 */
- dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
- }
-
- /*
- * @description : 打开设备
- * @param - inode : 传递给驱动的inode
- * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
- * 一般在open的时候将private_data指向设备结构体。
- * @return : 0 成功;其他 失败
- */
- static int ap3216c_open(struct inode *inode, struct file *filp)
- {
- filp->private_data = &ap3216cdev;
-
- /* 初始化AP3216C */
- ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04); /* 复位AP3216C */
- mdelay(50); /* AP3216C复位最少10ms */
- ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03); /* 开启ALS、PS+IR */
- return 0;
- }
-
- /*
- * @description : 从设备读取数据
- * @param - filp : 要打开的设备文件(文件描述符)
- * @param - buf : 返回给用户空间的数据缓冲区
- * @param - cnt : 要读取的数据长度
- * @param - offt : 相对于文件首地址的偏移
- * @return : 读取的字节数,如果为负值,表示读取失败
- */
- static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
- {
- short data[3];
- long err = 0;
-
- struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
-
- ap3216c_readdata(dev);
-
- data[0] = dev->ir;
- data[1] = dev->als;
- data[2] = dev->ps;
- err = copy_to_user(buf, data, sizeof(data));
- return 0;
- }
-
- /*
- * @description : 关闭/释放设备
- * @param - filp : 要关闭的设备文件(文件描述符)
- * @return : 0 成功;其他 失败
- */
- static int ap3216c_release(struct inode *inode, struct file *filp)
- {
- return 0;
- }
-
- /* AP3216C操作函数 */
- static const struct file_operations ap3216c_ops = {
- .owner = THIS_MODULE,
- .open = ap3216c_open,
- .read = ap3216c_read,
- .release = ap3216c_release,
- };
-
- /*
- * @description : i2c驱动的probe函数,当驱动与
- * 设备匹配以后此函数就会执行
- * @param - client : i2c设备
- * @param - id : i2c设备ID
- * @return : 0,成功;其他负值,失败
- */
- static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
- {
- /* 1、构建设备号 */
- if (ap3216cdev.major) {
- ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
- register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
- } else {
- alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
- ap3216cdev.major = MAJOR(ap3216cdev.devid);
- }
-
- /* 2、注册设备 */
- cdev_init(&ap3216cdev.cdev, &ap3216c_ops);
- cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);
-
- /* 3、创建类 */
- ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
- if (IS_ERR(ap3216cdev.class)) {
- return PTR_ERR(ap3216cdev.class);
- }
-
- /* 4、创建设备 */
- ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);
- if (IS_ERR(ap3216cdev.device)) {
- return PTR_ERR(ap3216cdev.device);
- }
-
- ap3216cdev.private_data = client;
-
- return 0;
- }
-
- /*
- * @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
- * @param - client : i2c设备
- * @return : 0,成功;其他负值,失败
- */
- static int ap3216c_remove(struct i2c_client *client)
- {
- /* 删除设备 */
- cdev_del(&ap3216cdev.cdev);
- unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
-
- /* 注销掉类和设备 */
- device_destroy(ap3216cdev.class, ap3216cdev.devid);
- class_destroy(ap3216cdev.class);
- return 0;
- }
-
- /* 传统匹配方式ID列表 */
- static const struct i2c_device_id ap3216c_id[] = {
- {"alientek,ap3216c", 0},
- {}
- };
-
- /* 设备树匹配列表 */
- static const struct of_device_id ap3216c_of_match[] = {
- { .compatible = "alientek,ap3216c" }, /* 需要和设备树下的保持一致 */
- { /* Sentinel */ }
- };
-
- /* i2c驱动结构体 */
- static struct i2c_driver ap3216c_driver = {
- .probe = ap3216c_probe,
- .remove = ap3216c_remove,
- .driver = { /* 设备树的匹配方法 */
- .owner = THIS_MODULE,
- .name = "ap3216c",
- .of_match_table = ap3216c_of_match,
- },
- .id_table = ap3216c_id, /* 传统驱动匹配方法 */
- };
-
- /*
- * @description : 驱动入口函数
- * @param : 无
- * @return : 无
- */
- static int __init ap3216c_init(void)
- {
- int ret = 0;
-
- ret = i2c_add_driver(&ap3216c_driver);
- return ret;
- }
-
- /*
- * @description : 驱动出口函数
- * @param : 无
- * @return : 无
- */
- static void __exit ap3216c_exit(void)
- {
- i2c_del_driver(&ap3216c_driver);
- }
-
- /* module_i2c_driver(ap3216c_driver) */
-
- module_init(ap3216c_init);
- module_exit(ap3216c_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("sneak");

驱动详解可查阅注释及配合上诉的I2C驱动框架的框图及数据手册理解
我们通过 在终端输入 cat 指令可以读取到传感器的数值变量,如下图:
IMX6ULL 的 QT-UI 的部署与实现需要依赖 QT 的很多依赖包,各位读者可以根据实际情况进行移植。或者直接使用正点原子提供的系统镜像!
Qt Charts 很方便的绘制我们常见的曲线图、折线图、柱状图和饼状图等图表。不用自己花精力去了解第三方组件的使用了或者开发第三方组件。Qt 的帮助文档里已经有说明 Qt Charts 主要部件的使用方法。需要用到时我们可以查看 Qt 文档就可以了。
本项目中我们需要使用曲线图去展示 AP3216C 的 ALS 光强度属性。
使用一个 QSplineSeries 对象(曲线),一个 QChart(图表),一个 QChartView(图表视图)。首先我们创建坐 chart 图表,然后创建两条坐标轴 axisX 与 axisY。将两条坐标轴添加到 chart 图表上,再将 splineSeries 曲线与坐标轴连系起来。最后再将 chart 图表添加到 chartView 图表视图中。曲线上的数据由系统产生随机数,使用定时器更新数据。
qtchart.pro:
- QT += core gui charts
-
- greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
-
- CONFIG += c++11
-
- # The following define makes your compiler emit warnings if you use
- # any Qt feature that has been marked deprecated (the exact warnings
- # depend on your compiler). Please consult the documentation of the
- # deprecated API in order to know how to port your code away from it.
- DEFINES += QT_DEPRECATED_WARNINGS
-
- # You can also make your code fail to compile if it uses deprecated APIs.
- # In order to do so, uncomment the following line.
- # You can also select to disable deprecated APIs only up to a certain version of Qt.
- #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
-
- SOURCES += \
- main.cpp \
- mainwindow.cpp
-
- HEADERS += \
- mainwindow.h
-
- FORMS += \
- mainwindow.ui
-
- # Default rules for deployment.
- qnx: target.path = /tmp/$${TARGET}/bin
- else: unix:!android: target.path = /opt/$${TARGET}/bin
- !isEmpty(target.path): INSTALLS += target

mainwindow.h:
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include <QChartView>
- #include <QSplineSeries>
- #include <QScatterSeries>
- #include <QDebug>
- #include <QValueAxis>
- #include <QTimer>
- #include <QMainWindow>
-
- /* 必需添加命名空间 */
- QT_CHARTS_USE_NAMESPACE
-
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
-
- public:
- MainWindow(QWidget *parent = nullptr);
- ~MainWindow();
-
- private:
- /* 接收数据接口 */
- void receivedData(int);
-
- /* 数据最大个数 */
- int maxSize;
-
- /* x轴上的最大值 */
- int maxX;
-
- /* y轴上的最大值 */
- int maxY;
-
- /* y轴 */
- QValueAxis *axisY;
-
- /* x轴 */
- QValueAxis *axisX;
-
- /* QList int类型容器 */
- QList<int> data;
-
- /* QSplineSeries对象(曲线)*/
- QSplineSeries *splineSeries;
-
- /* QChart图表 */
- QChart *chart;
-
- /* 图表视图 */
- QChartView *chartView;
-
- /* 定时器 */
- QTimer *timer;
-
- private slots:
- void timerTimeOut();
- };
- #endif // MAINWINDOW_H

mainwindow.c:
- #include "mainwindow.h"
- #include <QDateTime>
- MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
- {
- /* 设置最显示位置与大小 */
- this->setGeometry(0, 0, 800, 480);
- /* 最大储存maxSize - 1个数据 */
- maxSize = 51;
- /* x轴上的最大值 */
- maxX = 5000;
- /* y轴最大值 */
- maxY = 40;
-
- /* splineSeries曲线实例化(折线用QLineSeries) */
- splineSeries = new QSplineSeries();
- /* 图表实例化 */
- chart = new QChart();
- /* 图表视图实例化 */
- chartView = new QChartView();
-
- /* 坐标轴 */
- axisY = new QValueAxis();
- axisX = new QValueAxis();
- /* 定时器 */
- timer = new QTimer(this);
-
- /* legend译图例类型,以绘图的颜色区分,本例设置为隐藏 */
- chart->legend()->hide();
- /* chart设置标题 */
- chart->setTitle("实时动态曲线示例");
- /* 添加一条曲线splineSeries */
- chart->addSeries(splineSeries);
-
- /* 设置显示格式 */
- axisY->setLabelFormat("%i");
- /* y轴标题 */
- axisY->setTitleText("温度/℃");
- /* y轴标题位置(设置坐标轴的方向) */
- chart->addAxis(axisY, Qt::AlignLeft);
- /* 设置y轴范围 */
- axisY->setRange(0, maxY);
- /* 将splineSeries附加于y轴上 */
- splineSeries->attachAxis(axisY);
-
- /* 设置显示格式 */
- axisX->setLabelFormat("%i");
- /* x轴标题 */
- axisX->setTitleText("时间/ms");
- /* x轴标题位置(设置坐标轴的方向) */
- chart->addAxis(axisX, Qt::AlignBottom);
- /* 设置x轴范围 */
- axisX->setRange(0, maxX);
- /* 将splineSeries附加于x轴上 */
- splineSeries->attachAxis(axisX);
-
- /* 将图表的内容设置在图表视图上 */
- chartView->setChart(chart);
- /* 设置抗锯齿 */
- chartView->setRenderHint(QPainter::Antialiasing);
-
- /* 设置为图表视图为中心部件 */
- setCentralWidget(chartView);
-
- /* 定时200ms */
- timer->start(200);
- /* 信号槽连接 */
- connect(timer, SIGNAL(timeout()), this, SLOT(timerTimeOut()));
-
- /* 设置随机种子,随机数初始化 */
- qsrand(time(NULL));
- }
-
- MainWindow::~MainWindow()
- {
- }
-
- void MainWindow::timerTimeOut()
- {
- /* 产生随机0~maxY之间的数据 */
- receivedData(qrand() % maxY );
- }
-
- void MainWindow::receivedData(int value)
- {
- /* 将数据添加到data中 */
- data.append(value);
-
- /* 当储存数据的个数大于最大值时,把第一个数据删除 */
- while (data.size() > maxSize) {
- /* 移除data中第一个数据 */
- data.removeFirst();
- }
-
- /* 先清空 */
- splineSeries->clear();
-
- /* 计算x轴上的点与点之间显示的间距 */
- int xSpace = maxX / (maxSize - 1);
-
- /* 添加点,xSpace * i 表示第i个点的x轴的位置 */
- for (int i = 0; i < data.size(); ++i) {
- splineSeries->append(xSpace * i, data.at(i));
- }
- }

QT Chart表效果:
我们编写 QT 下的 AP3216C 的应用代码:
ap3216c.h:
- #ifndef AP3216C_H
- #define AP3216C_H
-
- #include <QObject>
- #include <QTimer>
-
- class Ap3216c : public QObject
- {
- Q_OBJECT
-
- public:
- explicit Ap3216c(QObject *parent = 0); //构造函数声明,"explicit"是一个C++关键字,用于修饰构造函数或函数,以禁止隐式转换
- ~Ap3216c(); //析构函数
-
- Q_INVOKABLE void setCapture(bool str); //Q_INVOKABLE是一个宏,setCapture函数接受一个bool类型的参数str,用于设置某个对象的捕获状态
-
- QString alsData(); //光强传感器( ALS: Ambient Light Sensor)
- QString psData(); //接近传感器( PS: Proximity Sensor)
- QString irData(); //红外 LED( IR LED)
-
- private:
- QTimer *timer; //定时器timer
- QString alsdata; //光强数据
- QString psdata; //接近数据
- QString irdata; //红外数据
-
- QString readAlsData(); //读取Ais数据
- QString readPsData(); //读取Ps数据
- QString readIrData(); //读取IR数据
-
- Q_PROPERTY(QString alsData READ alsData NOTIFY ap3216cDataChanged)
- Q_PROPERTY(QString psData READ psData NOTIFY ap3216cDataChanged)
- Q_PROPERTY(QString irData READ irData NOTIFY ap3216cDataChanged)
- /*Q_PROPERTY:这是一个宏,用于在类中声明属性。
- *QString:alsData是属性的类型,这里是一个QString类型的变量。
- *READ alsData:这表示该属性可以通过"alsData"方法进行读取。
- *NOTIFY ap3216cDataChanged:这表示当该属性发生变化时,会发出"ap3216cDataChanged"信号
- */
-
- public slots:
- void timer_timeout();
-
- signals:
- void ap3216cDataChanged();
- };
-
- #endif // AP3216C_H

ap3216c.c:
- #include "ap3216c.h"
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <QDebug>
-
- /* AP3216C的构造函数 */
- Ap3216c::Ap3216c(QObject *parent) : QObject (parent)
- {
- timer = new QTimer(); //定义一个定时器容器
- connect(timer, SIGNAL(timeout()),this,SLOT(timer_timeout())); //将超时信号,与timer_timeout链接起来
- }
-
- /* 析构函数 */
- Ap3216c::~Ap3216c()
- {
-
- }
-
- /* 超时函数 */
- void Ap3216c::timer_timeout()
- {
- alsdata = readAlsData();
- psdata = readPsData();
- irdata = readIrData();
- emit ap3216cDataChanged(); //发送ap3216c数值变化信息
- }
-
- QString Ap3216c::readIrData()
- {
- char const *filename = "/sys/class/misc/ap3216c/ir";
- int err = 0;
- int fd;
- char buf[10];
-
- fd = open(filename,O_RDONLY); //只读模式打开文件
- if(fd < 0){
- close(fd);
- return "open file error!";
- }
-
- err = read(fd,buf,sizeof(buf));
- if(err < 0){
- close(fd);
- return "read data error!";
- }
- close(fd);
-
- QString irValue = buf;
- QStringList list = irValue.split("\n"); //将irValue按照换行符"\n"分割成一个字符串列表,并将结果存储在list对象中
- return list[0];
- }
-
- QString Ap3216c::readPsData()
- {
- char const *filename = "/sys/class/misc/ap3216c/ps";
- int err = 0;
- int fd;
- char buf[10];
-
- fd = open(filename,O_RDONLY); //只读模式打开文件
- if(fd < 0){
- close(fd);
- return "open file error!";
- }
-
- err = read(fd,buf,sizeof(buf));
- if(err < 0){
- close(fd);
- return "read data error!";
- }
- close(fd);
-
- QString irValue = buf;
- QStringList list = irValue.split("\n"); //将irValue按照换行符"\n"分割成一个字符串列表,并将结果存储在list对象中
- return list[0];
- }
-
- QString Ap3216c::readAlsData()
- {
- char const *filename = "/sys/class/misc/ap3216c/als";
- int err = 0;
- int fd;
- char buf[10];
-
- fd = open(filename,O_RDONLY); //只读模式打开文件
- if(fd < 0){
- close(fd);
- return "open file error!";
- }
-
- err = read(fd,buf,sizeof(buf));
- if(err < 0){
- close(fd);
- return "read data error!";
- }
- close(fd);
-
- QString irValue = buf;
- QStringList list = irValue.split("\n"); //将irValue按照换行符"\n"分割成一个字符串列表,并将结果存储在list对象中
- return list[0];
- }
-
- QString Ap3216c::alsData()
- {
- return alsdata;
- }
-
- QString Ap3216c::irData()
- {
- return irdata;
- }
-
- QString Ap3216c::psData()
- {
- return psdata;
- }
-
- void Ap3216c::setCapture(bool str)
- {
- if(str)
- timer->start(500);
- else
- timer->stop();
- }

“Linux 系统之下一切皆文件”,上述代码就是通过 C 语言库函数去读取系统下的属性数值信息来做到获取 AP3216C 的数据值!
我们将 AP3216C 与 Chart 进行联动配合,通过 Chart 实时读取显示 AP3216C 的 ALS 属性。
mainwindow.h:
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
-
- #include <QMainWindow>
- #include <QVBoxLayout>
- #include <QHBoxLayout>
- #include <QComboBox>
- #include <QPushButton>
- #include <QVBoxLayout>
- #include <QLabel>
- #include <QScrollArea>
- #include <QDebug>
- #include <QChartView>
- #include <QSplineSeries>
- #include <QScatterSeries>
- #include <QDebug>
- #include <QValueAxis>
- #include "ap3216c.h"
-
- /* 必需添加命名空间 */
- QT_CHARTS_USE_NAMESPACE
-
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
-
- public:
- MainWindow(QWidget *parent = nullptr);
- ~MainWindow();
-
- private:
- /* 主容器,Widget也可以当作一种容器 */
- QWidget *mainWidget;
-
- /* 界面水平区域布局 */
- QHBoxLayout *hboxLayout[2];
-
- /* 界面右侧区域布局 */
- QVBoxLayout *vboxLayout[4];
-
- /* 界面右侧区域容器 */
- QWidget *rightWidget;
-
- /* 用一个 QLabel 对象用于显示字符串 */
- QLabel *labelString;
-
- /* 容器作用,用于布局 */
- QWidget *widget[5];
-
- /* 标签文本 */
- QLabel *label[3];
-
- /* 数据标签 */
- QLabel *my_label[3];
-
- /* 数据最大个数 */
- int maxSize;
-
- /* x轴上的最大值 */
- int maxX;
-
- /* y轴上的最大值 */
- int maxY;
-
- /* y轴 */
- QValueAxis *axisY;
-
- /* x轴 */
- QValueAxis *axisX;
-
- /* QList int类型容器 */
- QList<int> data;
-
- /* QSplineSeries对象(曲线)*/
- QSplineSeries *splineSeries;
-
- /* QChart图表 */
- QChart *chart;
-
- /* 图表视图 */
- QChartView *chartView;
-
- /* ii2传感器类 */
- Ap3216c *ap3216c;
-
- /* 接收数据接口 */
- void receivedData(int);
-
- /* 布局初始化 */
- void layoutInit();
-
-
- private slots:
- /* 获取ap3216传感器数据 */
- void getAp3216cData();
- };
- #endif // MAINWINDOW_H

mainwindow.c:
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include <QGuiApplication>
- #include <QScreen>
- #include <QDebug>
-
- MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
- {
- /* 布局初始化 */
- layoutInit();
- }
-
- MainWindow::~MainWindow()
- {
- }
-
- /* 布局初始化 */
- void MainWindow::layoutInit()
- {
- /* 获取屏幕的分辨率,Qt官方建议使用这
- * 种方法获取屏幕分辨率,防上多屏设备导致对应不上
- * 注意,这是获取整个桌面系统的分辨率
- */
- QList <QScreen *> list_screen = QGuiApplication::screens();
-
- /* 如果是ARM平台,直接设置大小为屏幕的大小 */
- #if __arm__
- /* 重设大小 */
- this->resize(list_screen.at(0)->geometry().width(),
- list_screen.at(0)->geometry().height());
- #else
- /* 否则则设置主窗体大小为800x480 */
- this->resize(800,400);
- #endif
-
- /* 实例化与布局,常规操作 */
- mainWidget = new QWidget();
- rightWidget = new QWidget();
-
- for(int i = 0; i < 2; i++) //水平布局
- hboxLayout[i] = new QHBoxLayout();
-
- for(int i = 0; i < 5; i++) //容器布局
- widget[i] = new QWidget();
-
- for(int i = 0; i < 4; i++) //垂直布局
- vboxLayout[i] = new QVBoxLayout();
-
- for(int i = 0; i < 3; i++) //数据变量
- my_label[i] = new QLabel();
-
-
- /********************** chart表格 *****************************/
- /* 最大储存maxSize - 1个数据 */
- maxSize = 51;
- /* x轴上的最大值 */
- maxX = 5000;
- /* y轴最大值 */
- maxY = 40;
-
- /* splineSeries曲线实例化(折线用QLineSeries) */
- splineSeries = new QSplineSeries();
- /* 图表实例化 */
- chart = new QChart();
- /* 图表视图实例化 */
- chartView = new QChartView();
-
- /* 坐标轴 */
- axisY = new QValueAxis();
- axisX = new QValueAxis();
-
- /* legend译图例类型,以绘图的颜色区分,本例设置为隐藏 */
- chart->legend()->hide();
- /* chart设置标题 */
- chart->setTitle("环境光强度");
- /* 添加一条曲线splineSeries */
- chart->addSeries(splineSeries);
-
- /* 设置显示格式 */
- axisY->setLabelFormat("%i");
- /* y轴标题 */
- axisY->setTitleText("光通亮/Lux");
- /* y轴标题位置(设置坐标轴的方向) */
- chart->addAxis(axisY, Qt::AlignLeft);
- /* 设置y轴范围 */
- axisY->setRange(0, maxY);
- /* 将splineSeries附加于y轴上 */
- splineSeries->attachAxis(axisY);
-
- /* 设置显示格式 */
- axisX->setLabelFormat("%i");
- /* x轴标题 */
- axisX->setTitleText("时间/ms");
- /* x轴标题位置(设置坐标轴的方向) */
- chart->addAxis(axisX, Qt::AlignBottom);
- /* 设置x轴范围 */
- axisX->setRange(0, maxX);
- /* 将splineSeries附加于x轴上 */
- splineSeries->attachAxis(axisX);
-
- /* 将图表的内容设置在图表视图上 */
- chartView->setChart(chart);
- /* 设置抗锯齿 */
- chartView->setRenderHint(QPainter::Antialiasing);
- /********************** chart表格 *****************************/
-
- /* 设置传感器数据标签 */
- QFont font;
- font.setPixelSize(18);
-
- QPalette pal;
- pal.setColor(QPalette::WindowText, Qt::black);
-
- QStringList list;
- list<<"环境光强度:"<<"接近距离:"<<"红外强度:";
- for (int i = 0; i < 3; i++) {
- label[i] = new QLabel();
- label[i]->setText(list[i]);
- label[i]->setFont(font);
- label[i]->setPalette(pal);
- label[i]->adjustSize();
- }
-
- /* 垂直容器布局 */
- vboxLayout[3]->addWidget(widget[0]);
- vboxLayout[3]->addWidget(widget[1]);
- vboxLayout[3]->addWidget(widget[2]);
-
- hboxLayout[0]->addWidget(chartView); //将chart表格添加到水平布局中
-
- widget[3]->setLayout(hboxLayout[0]);
- widget[4]->setLayout(vboxLayout[3]);
-
- hboxLayout[1]->addWidget(widget[3]);
- hboxLayout[1]->addWidget(widget[4]);
-
- mainWidget->setLayout(hboxLayout[1]);
-
- this->setCentralWidget(mainWidget);
-
- /* als布局 */
- vboxLayout[0]->addWidget(label[0]);
- vboxLayout[0]->addWidget(my_label[0]);
- vboxLayout[0]->setAlignment(Qt::AlignTop | Qt::AlignVCenter);
- widget[0]->setLayout(vboxLayout[0]);
-
- /* ps布局 */
- vboxLayout[1]->addWidget(label[1]);
- vboxLayout[1]->addWidget(my_label[1]);
- vboxLayout[1]->setAlignment(Qt::AlignTop | Qt::AlignVCenter);
- widget[1]->setLayout(vboxLayout[1]);
-
- /* ir布局 */
- vboxLayout[2]->addWidget(label[2]);
- vboxLayout[2]->addWidget(my_label[2]);
- vboxLayout[2]->setAlignment(Qt::AlignTop | Qt::AlignVCenter);
- widget[2]->setLayout(vboxLayout[2]);
-
- ap3216c = new Ap3216c(this);
-
- /* 只能在开发板上开启获取数据,Ubuntu上是没有ap3216c传感器的 */
- #if __arm__
- ap3216c->setCapture(true);
- #endif
-
- connect(ap3216c,SIGNAL(ap3216cDataChanged()),this,SLOT(getAp3216cData()));
- }
-
- void MainWindow::getAp3216cData()
- {
- static QString als = ap3216c->alsData();
- // if (als != ap3216c->alsData()) {
- // als = ap3216c->alsData();
- // }
- als = ap3216c->alsData();
-
- static QString ps = ap3216c->psData();
- // if (ps != ap3216c->psData()) {
- // ps = ap3216c->psData();
- // }
-
- ps = ap3216c->psData();
-
- static QString ir = ap3216c->irData();
- // if (ir != ap3216c->irData()) {
- // ir = ap3216c->irData();
- // }
-
- ir = ap3216c->irData();
-
- my_label[0]->setText(als);
- my_label[1]->setText(ps);
- my_label[2]->setText(ir);
-
-
- /**************** chart表数据 ******************/
-
- int num = als.toInt(); //字符串转int类型数据
-
- /* 将数据添加到data中 */
- data.append(num);
-
- /* 当储存数据的个数大于最大值时,把第一个数据删除 */
- while (data.size() > maxSize) {
- /* 移除data中第一个数据 */
- data.removeFirst();
- }
-
- /* 先清空 */
- splineSeries->clear();
-
- /* 计算x轴上的点与点之间显示的间距 */
- int xSpace = maxX / (maxSize - 1);
-
- /* 添加点,xSpace * i 表示第i个点的x轴的位置 */
- for (int i = 0; i < data.size(); ++i) {
- splineSeries->append(xSpace * i, data.at(i));
- }
-
- }

AP3216C 与 Chart 联动的程序主要是进行 QT 的容器布局,通过 layoutInit() 函数进行布局初始化(UI设计部分),之后通过定时 timer 去定时读取 ap3216c 的传感器数据,在读取 ap3216c 的数据之后,通过 Chart 进行绘制出来!
实际项目:
AP3216C+QT+Chart
代码地址:基于IMX6ULL的AP3216C的QT动态数据曲线图显示代码资源-CSDN文库
如果积分不够的朋友,点波关注,评论区留下邮箱,作者无偿提供源码和后续问题解答。求求啦关注一波吧 !!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。