基于IMX6ULL的AP3216C的QT动态数据曲线图显示

前言:本文为手把手教学 Linux+QT 的典型基础项目 AP3216C 的数据折线图显示,项目使用正点原子的 IMX6ULL 阿尔法( Cortex-A7 系列)开发板。项目需要实现 AP3216C Linux 系统下的驱动,使用 QT 设计 AP3216C 的数据显示页面作为项目的应用层。该项目属于非常简单的入门级项目,核心目的是帮助大家熟悉 Linux 系统下的项目制作和工程研发过程。希望该项目可以帮助大家学会灵活使用 Linux 系统下的传感器开发!(文末有代码开源!

硬件实物图:

效果图:

本项目核心是教学 Linux+QT 的工程研发流程,如果专注于应用层工程学习的,可以直接使用正点原子提供的 Linux 镜像文件。本教程是适配正点提供的系统镜像的,可以直接使用!

一、AP3216C概述

I.MX6U-ALPHA 开发板上通过 I2C1 连接了一个三合一环境传感器:AP3216CAP3216C 是由敦南科技推出的一款传感器,其支持环境光强度(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的AP3216C驱动

2.1 AP3216C的设备树

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 0x4001b8b0MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0 >;};

pinctrl_i2c1 就是 I2C1 的 IO 节点,这里将 UART4_TXDUART4_RXD 这两个 IO 分别复用为 I2C1_SCLI2C1_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设备有:

2.2 AP3216C 驱动编写

下面编写 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 = &reg;					/* 读取的首地址 */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 0struct 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 指令可以读取到传感器的数值变量,如下图:

三、AP3216C的QT程序设计

IMX6ULL 的 QT-UI 的部署与实现需要依赖 QT 的很多依赖包,各位读者可以根据实际情况进行移植。或者直接使用正点原子提供的系统镜像! 

3.1 Chart表制作

Qt Charts 很方便的绘制我们常见的曲线图折线图柱状图饼状图等图表。不用自己花精力去了解第三方组件的使用了或者开发第三方组件。Qt 的帮助文档里已经有说明 Qt Charts 主要部件的使用方法。需要用到时我们可以查看 Qt 文档就可以了。

本项目中我们需要使用曲线图去展示 AP3216CALS 光强度属性。

使用一个 QSplineSeries 对象(曲线),一个 QChart(图表),一个 QChartView(图表视图)。首先我们创建坐 chart 图表,然后创建两条坐标轴 axisXaxisY。将两条坐标轴添加到 chart 图表上,再将 splineSeries 曲线与坐标轴连系起来。最后再将 chart 图表添加到 chartView 图表视图中。曲线上的数据由系统产生随机数,使用定时器更新数据。

qtchart.pro:

QT       += core gui chartsgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += 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.0SOURCES += \main.cpp \mainwindow.cppHEADERS += \mainwindow.hFORMS += \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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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表效果:

3.2 AP3216C的数据读取

我们编写 QT 下的 AP3216C 的应用代码:

ap3216c.h:

#ifndef AP3216C_H
#define AP3216C_H#include <QObject>
#include <QTimer>class Ap3216c : public QObject
{Q_OBJECTpublic: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;          //定时器timerQString 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);elsetimer->stop();
}

“Linux 系统之下一切皆文件”,上述代码就是通过 C 语言库函数去读取系统下的属性数值信息来做到获取 AP3216C 的数据值!

3.3 AP3216C与Chart联动

我们将 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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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);
#endifconnect(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));}}

AP3216CChart 联动的程序主要是进行 QT 的容器布局,通过 layoutInit() 函数进行布局初始化(UI设计部分),之后通过定时 timer 去定时读取 ap3216c 的传感器数据,在读取 ap3216c 的数据之后,通过 Chart 进行绘制出来!

实际项目:

四、项目效果

AP3216C+QT+Chart

五、项目代码

代码地址:基于IMX6ULL的AP3216C的QT动态数据曲线图显示代码资源-CSDN文库

如果积分不够的朋友,点波关注评论区留下邮箱,作者无偿提供源码和后续问题解答。求求啦关注一波吧 !!!

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

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

相关文章

websocket发布到iis之后无法创建连接访问

在做websocket测试时在本地的服务可以正常访问&#xff0c;在调试成功之后发布到了iis中&#xff0c;发现无法连接&#xff0c;这时可能有以下几个原因。 WebSocket无法连接到IIS&#xff1a; IIS版本不支持WebSocket&#xff1a;IIS 8.0及更高版本才支持WebSocket&#xff0…

从小白到大神之路之学习运维第61天--------Ansible自动化运维工具(playbook配置文件深入了解)

第三阶段基础 时 间&#xff1a;2023年7月14日 参加人&#xff1a;全班人员 内 容&#xff1a; playbook配置文件 目录 playbook配置文件 一、playbook配置文件概念 修改hosts文件 建立playbook配置文件 yml脚本写法注释&#xff1a; 二、Playbook的核心元素 三、…

如何破解中小企业数字化转型难点?建议来了!

打开任何一个搜索引擎&#xff0c;只要输入“中小企业数字化转型”&#xff0c;关于痛点、难处的文章就会铺面而来&#xff0c;难在哪里&#xff0c;其实很好解答&#xff0c;关键在于&#xff0c;如何解决这一个个难处。 PS&#xff1a;给大家整理了一份完整版的《中小企业如…

汽车供应链专场对接会 | 8月25日大会同期活动

爱普搜汽车供应链对接会&#xff0c;是根据采购商的项目需求&#xff0c;有针对性地组织全国各地采购商与供应商&#xff0c;进行面对面交流与沟通&#xff0c;促成实质性交易。参会群体为汽车行业制造型企业、主机厂、Tier1/2。 供应商在参加对接会前已做足功课&#xff0c;现…

C语言-ubuntu下的命令

目录 linux命令 【1】打开关闭终端 【2】终端 【3】ls命令 【4】cd 切换路径 【5】新建 【6】删除 【7】复制 【8】移动 【9】常用快捷键 【10】vi编辑器 【11】简单编程步骤 任务&#xff1a; linux命令 【1】打开关闭终端 打开终端&#xff1a; 1. 直接点击 …

File格式转换MultipartFile格式的例子

首先&#xff1a;需要先引入依赖包 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.9</version> </dependency> 1.Multipartfile转File类型 //创建一…

一零六四、世界杯数据可视化分析(阿里云天池赛)

目录 赛制官方链接 活动背景 活动时间&#xff1a;即日起-12月31日17点 数据说明 世界杯成绩信息表&#xff1a;WorldCupsSummary 世界杯比赛比分汇总表&#xff1a;WorldCupMatches.csv 世界杯球员信息表&#xff1a;WorldCupPlayers.csv 代码实现 赛制官方链接 世界杯…

视频融合平台EasyCVR登录后通道数据及菜单栏页面显示异常的排查与解决

EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。 有用…

docker基础1——架构组成、安装配置

文章目录 一、发展起源1.1 传统虚拟化与容器虚拟化1.2 docker底层核心技术1.2.1 命名空间1.2.2 控制组 1.3 docker工作方式1.4 docker容器编排1.5 docker优劣势1.6 docker架构组成 二、yum安装docker三、配置docker加速器 一、发展起源 背景了解&#xff1a; 容器是内核里的一项…

上市公司前端开发规范参考

上市公司前端开发规范参考 命名规则通用约定文件与目录命名HTML命名CSS命名JS命名 代码格式通用约定HTML格式CSS格式JS格式注释 组件组件大小单文件组件容器组件组件使用说明Prop指令缩写组件通讯组件的挂载和销毁按需加载第三方组件库的规定 脚手架使用规范移动端脚手架PC端脚…

前端 Jenkins 自动化部署

由于公司使用自己搭建的 svn 服务器来进行代码管理&#xff0c;因此这里 Jenkins 是针对 svn 服务器来进行的配置&#xff0c;其实跟Git 配置基本一致。 在没有自动化部署前 之前项目每次修改之后都需要本地 ​​npm run build ​​一次手动发布到服务器上方便测试和产品查看…

Jenkins | 获取凭证密码

目录 方法一&#xff1a;查看所有账号及密码 方法二&#xff1a;查看指定账号密码 方法一&#xff1a;查看所有账号及密码 Jenkins > 系统管理 > 脚本命令行 com.cloudbees.plugins.credentials.SystemCredentialsProvider.getInstance().getCredentials().forEach{i…

nginx基础1——工作原理、安装配置、命令参数

文章目录 一、基本了解1.1 特性优点1.2 功能应用1.3 工作模块分类1.4 模块配置方法 二、工作原理三、安装与配置四、常用命令 一、基本了解 nginx简介&#xff1a; nginx是一款轻量级的Web服务器、反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器&#x…

前端学习——ajax (Day1)

AJAX 概念和 axios 使用 axios 使用 练习 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" …

SpringBoot 整合 RabbitMQ demo

Rabbit Windows安装教程 本文只做Demo案例的分享&#xff0c;具体只是需自行百度 一、生产者 1.application.properties 配置Rabbit的基本信息 #rabbit 主机IP spring.rabbitmq.host127.0.0.1 #rabbit 端口 spring.rabbitmq.port5672 #rabbit 账号 可自行创建 这里是默认的 …

网络知识整合——Web页面请求的历程

Web页面请求的历程 内部涉及知识&#xff1a;一、准备:DHCP、UDP、IP 和以太网二、仍在准备&#xff1a;DNS和ARP三、仍在准备&#xff1a;域内路由选择到DNS服务器四、Web客户-服务器交互&#xff1a;TCP和HTTP五、HTTP请求响应格式Requests部分Responses 部分 下载一个Web页面…

ES6迭代器、Set、Map集合和async异步函数

目录 迭代器 Iterator 的作用 Iterator 的遍历过程 Set Map集合 map和对象区别? async异步函数 迭代器 迭代器&#xff08;Iterator&#xff09;就是这样一种机制。它是一种接口&#xff0c;为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口&…

介绍性能压力测试的重要性

在当今数字化时代&#xff0c;软件和应用程序的性能对于用户体验和业务成功至关重要。为了确保系统在面临高负载和压力时能够正常运行&#xff0c;性能压力测试成为一项不可或缺的活动。本文将介绍性能压力测试的重要性。 性能压力测试是一种通过模拟实际场景中的负荷和用户访问…

2023-7-19-第二十式迭代器模式

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f4a5;&#x1f4a5;&#x1f4a5;欢迎来到&#x1f91e;汤姆&#x1f91e;的csdn博文&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f49f;&#x1f49f;喜欢的朋友可以关注一下&#xf…

React native 已有项目升级兼容web

基础 概念 | webpack 中文文档 | webpack 中文文档 | webpack 中文网 深入理解Webpack及Babel的使用 - 掘金 Introduction to React Native for Web // React Native for Web Webpack 是一个现代的 JavaScript 应用程序的静态模块打包工具&#xff0c;它将应用程序所依赖的各…