当前位置:   article > 正文

基于IMX6ULL的AP3216C的QT动态数据曲线图显示_百问网imx6ull 显示传感器状态和动态曲线“”

百问网imx6ull 显示传感器状态和动态曲线“”

前言:本文为手把手教学 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,然后找到如下内容:

  1. pinctrl_i2c1: i2c1grp {
  2. fsl,pins = <
  3. MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
  4. MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
  5. >;
  6. };

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 节点

  1. &i2c1 {
  2. clock-frequency = <100000>; /* 时钟频率 */
  3. pinctrl-names = "default";
  4. pinctrl-0 = <&pinctrl_i2c1>; /* pinctrl引脚电气属性 */
  5. status = "okay";
  6. ap3216c@1e {
  7. compatible = "alientek,ap3216c";
  8. reg = <0x1e>;
  9. };
  10. };

编译设备树,传到开发板上,重启。此时我们系统的I2C设备有:

2.2 AP3216C 驱动编写

下面编写 AP3216 驱动:

ap3216creg.h:

  1. #ifndef AP3216C_H
  2. #define AP3216C_H
  3. /***************************************************************
  4. 文件名 : ap3216creg.h
  5. 描述 : AP3216C寄存器地址描述头文件
  6. ***************************************************************/
  7. #define AP3216C_ADDR 0X1E /* AP3216C器件地址 */
  8. /* AP3316C寄存器 */
  9. #define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
  10. #define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
  11. #define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
  12. #define AP3216C_IRDATALOW 0x0A /* IR数据低字节 */
  13. #define AP3216C_IRDATAHIGH 0x0B /* IR数据高字节 */
  14. #define AP3216C_ALSDATALOW 0x0C /* ALS数据低字节 */
  15. #define AP3216C_ALSDATAHIGH 0X0D /* ALS数据高字节 */
  16. #define AP3216C_PSDATALOW 0X0E /* PS数据低字节 */
  17. #define AP3216C_PSDATAHIGH 0X0F /* PS数据高字节 */
  18. #endif

ap3216.c:

  1. #include <linux/types.h>
  2. #include <linux/kernel.h>
  3. #include <linux/delay.h>
  4. #include <linux/ide.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/errno.h>
  8. #include <linux/gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/of_gpio.h>
  12. #include <linux/semaphore.h>
  13. #include <linux/timer.h>
  14. #include <linux/i2c.h>
  15. #include <asm/mach/map.h>
  16. #include <asm/uaccess.h>
  17. #include <asm/io.h>
  18. #include "ap3216creg.h"
  19. #define AP3216C_CNT 1
  20. #define AP3216C_NAME "ap3216c"
  21. struct ap3216c_dev {
  22. dev_t devid; /* 设备号 */
  23. struct cdev cdev; /* cdev */
  24. struct class *class; /* 类 */
  25. struct device *device; /* 设备 */
  26. struct device_node *nd; /* 设备节点 */
  27. int major; /* 主设备号 */
  28. void *private_data; /* 私有数据 */
  29. unsigned short ir, als, ps; /* 三个光传感器数据 */
  30. };
  31. static struct ap3216c_dev ap3216cdev;
  32. /*
  33. * @description : 从ap3216c读取多个寄存器数据
  34. * @param - dev: ap3216c设备
  35. * @param - reg: 要读取的寄存器首地址
  36. * @param - val: 读取到的数据
  37. * @param - len: 要读取的数据长度
  38. * @return : 操作结果
  39. */
  40. static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
  41. {
  42. int ret;
  43. struct i2c_msg msg[2];
  44. struct i2c_client *client = (struct i2c_client *)dev->private_data;
  45. /* msg[0]为发送要读取的首地址 */
  46. msg[0].addr = client->addr; /* ap3216c地址 */
  47. msg[0].flags = 0; /* 标记为发送数据 */
  48. msg[0].buf = &reg; /* 读取的首地址 */
  49. msg[0].len = 1; /* reg长度*/
  50. /* msg[1]读取数据 */
  51. msg[1].addr = client->addr; /* ap3216c地址 */
  52. msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
  53. msg[1].buf = val; /* 读取数据缓冲区 */
  54. msg[1].len = len; /* 要读取的数据长度*/
  55. ret = i2c_transfer(client->adapter, msg, 2);
  56. if(ret == 2) {
  57. ret = 0;
  58. } else {
  59. printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
  60. ret = -EREMOTEIO;
  61. }
  62. return ret;
  63. }
  64. /*
  65. * @description : 向ap3216c多个寄存器写入数据
  66. * @param - dev: ap3216c设备
  67. * @param - reg: 要写入的寄存器首地址
  68. * @param - val: 要写入的数据缓冲区
  69. * @param - len: 要写入的数据长度
  70. * @return : 操作结果
  71. */
  72. static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
  73. {
  74. u8 b[256];
  75. struct i2c_msg msg;
  76. struct i2c_client *client = (struct i2c_client *)dev->private_data;
  77. b[0] = reg; /* 寄存器首地址 */
  78. memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */
  79. msg.addr = client->addr; /* ap3216c地址 */
  80. msg.flags = 0; /* 标记为写数据 */
  81. msg.buf = b; /* 要写入的数据缓冲区 */
  82. msg.len = len + 1; /* 要写入的数据长度 */
  83. return i2c_transfer(client->adapter, &msg, 1);
  84. }
  85. /*
  86. * @description : 读取ap3216c指定寄存器值,读取一个寄存器
  87. * @param - dev: ap3216c设备
  88. * @param - reg: 要读取的寄存器
  89. * @return : 读取到的寄存器值
  90. */
  91. static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
  92. {
  93. u8 data = 0;
  94. ap3216c_read_regs(dev, reg, &data, 1);
  95. return data;
  96. #if 0
  97. struct i2c_client *client = (struct i2c_client *)dev->private_data;
  98. return i2c_smbus_read_byte_data(client, reg);
  99. #endif
  100. }
  101. /*
  102. * @description : 向ap3216c指定寄存器写入指定的值,写一个寄存器
  103. * @param - dev: ap3216c设备
  104. * @param - reg: 要写的寄存器
  105. * @param - data: 要写入的值
  106. * @return : 无
  107. */
  108. static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
  109. {
  110. u8 buf = 0;
  111. buf = data;
  112. ap3216c_write_regs(dev, reg, &buf, 1);
  113. }
  114. /*
  115. * @description : 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!
  116. * : 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
  117. * @param - ir : ir数据
  118. * @param - ps : ps数据
  119. * @param - ps : als数据
  120. * @return : 无。
  121. */
  122. void ap3216c_readdata(struct ap3216c_dev *dev)
  123. {
  124. unsigned char i =0;
  125. unsigned char buf[6];
  126. /* 循环读取所有传感器数据 */
  127. for(i = 0; i < 6; i++)
  128. {
  129. buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
  130. }
  131. if(buf[0] & 0X80) /* IR_OF位为1,则数据无效 */
  132. dev->ir = 0;
  133. else /* 读取IR传感器的数据 */
  134. dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
  135. dev->als = ((unsigned short)buf[3] << 8) | buf[2]; /* 读取ALS传感器的数据 */
  136. if(buf[4] & 0x40) /* IR_OF位为1,则数据无效 */
  137. dev->ps = 0;
  138. else /* 读取PS传感器的数据 */
  139. dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
  140. }
  141. /*
  142. * @description : 打开设备
  143. * @param - inode : 传递给驱动的inode
  144. * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
  145. * 一般在open的时候将private_data指向设备结构体。
  146. * @return : 0 成功;其他 失败
  147. */
  148. static int ap3216c_open(struct inode *inode, struct file *filp)
  149. {
  150. filp->private_data = &ap3216cdev;
  151. /* 初始化AP3216C */
  152. ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04); /* 复位AP3216C */
  153. mdelay(50); /* AP3216C复位最少10ms */
  154. ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03); /* 开启ALS、PS+IR */
  155. return 0;
  156. }
  157. /*
  158. * @description : 从设备读取数据
  159. * @param - filp : 要打开的设备文件(文件描述符)
  160. * @param - buf : 返回给用户空间的数据缓冲区
  161. * @param - cnt : 要读取的数据长度
  162. * @param - offt : 相对于文件首地址的偏移
  163. * @return : 读取的字节数,如果为负值,表示读取失败
  164. */
  165. static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
  166. {
  167. short data[3];
  168. long err = 0;
  169. struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
  170. ap3216c_readdata(dev);
  171. data[0] = dev->ir;
  172. data[1] = dev->als;
  173. data[2] = dev->ps;
  174. err = copy_to_user(buf, data, sizeof(data));
  175. return 0;
  176. }
  177. /*
  178. * @description : 关闭/释放设备
  179. * @param - filp : 要关闭的设备文件(文件描述符)
  180. * @return : 0 成功;其他 失败
  181. */
  182. static int ap3216c_release(struct inode *inode, struct file *filp)
  183. {
  184. return 0;
  185. }
  186. /* AP3216C操作函数 */
  187. static const struct file_operations ap3216c_ops = {
  188. .owner = THIS_MODULE,
  189. .open = ap3216c_open,
  190. .read = ap3216c_read,
  191. .release = ap3216c_release,
  192. };
  193. /*
  194. * @description : i2c驱动的probe函数,当驱动与
  195. * 设备匹配以后此函数就会执行
  196. * @param - client : i2c设备
  197. * @param - id : i2c设备ID
  198. * @return : 0,成功;其他负值,失败
  199. */
  200. static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
  201. {
  202. /* 1、构建设备号 */
  203. if (ap3216cdev.major) {
  204. ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
  205. register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
  206. } else {
  207. alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
  208. ap3216cdev.major = MAJOR(ap3216cdev.devid);
  209. }
  210. /* 2、注册设备 */
  211. cdev_init(&ap3216cdev.cdev, &ap3216c_ops);
  212. cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);
  213. /* 3、创建类 */
  214. ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
  215. if (IS_ERR(ap3216cdev.class)) {
  216. return PTR_ERR(ap3216cdev.class);
  217. }
  218. /* 4、创建设备 */
  219. ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);
  220. if (IS_ERR(ap3216cdev.device)) {
  221. return PTR_ERR(ap3216cdev.device);
  222. }
  223. ap3216cdev.private_data = client;
  224. return 0;
  225. }
  226. /*
  227. * @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
  228. * @param - client : i2c设备
  229. * @return : 0,成功;其他负值,失败
  230. */
  231. static int ap3216c_remove(struct i2c_client *client)
  232. {
  233. /* 删除设备 */
  234. cdev_del(&ap3216cdev.cdev);
  235. unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
  236. /* 注销掉类和设备 */
  237. device_destroy(ap3216cdev.class, ap3216cdev.devid);
  238. class_destroy(ap3216cdev.class);
  239. return 0;
  240. }
  241. /* 传统匹配方式ID列表 */
  242. static const struct i2c_device_id ap3216c_id[] = {
  243. {"alientek,ap3216c", 0},
  244. {}
  245. };
  246. /* 设备树匹配列表 */
  247. static const struct of_device_id ap3216c_of_match[] = {
  248. { .compatible = "alientek,ap3216c" }, /* 需要和设备树下的保持一致 */
  249. { /* Sentinel */ }
  250. };
  251. /* i2c驱动结构体 */
  252. static struct i2c_driver ap3216c_driver = {
  253. .probe = ap3216c_probe,
  254. .remove = ap3216c_remove,
  255. .driver = { /* 设备树的匹配方法 */
  256. .owner = THIS_MODULE,
  257. .name = "ap3216c",
  258. .of_match_table = ap3216c_of_match,
  259. },
  260. .id_table = ap3216c_id, /* 传统驱动匹配方法 */
  261. };
  262. /*
  263. * @description : 驱动入口函数
  264. * @param : 无
  265. * @return : 无
  266. */
  267. static int __init ap3216c_init(void)
  268. {
  269. int ret = 0;
  270. ret = i2c_add_driver(&ap3216c_driver);
  271. return ret;
  272. }
  273. /*
  274. * @description : 驱动出口函数
  275. * @param : 无
  276. * @return : 无
  277. */
  278. static void __exit ap3216c_exit(void)
  279. {
  280. i2c_del_driver(&ap3216c_driver);
  281. }
  282. /* module_i2c_driver(ap3216c_driver) */
  283. module_init(ap3216c_init);
  284. module_exit(ap3216c_exit);
  285. MODULE_LICENSE("GPL");
  286. 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:

  1. QT += core gui charts
  2. greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  3. CONFIG += c++11
  4. # The following define makes your compiler emit warnings if you use
  5. # any Qt feature that has been marked deprecated (the exact warnings
  6. # depend on your compiler). Please consult the documentation of the
  7. # deprecated API in order to know how to port your code away from it.
  8. DEFINES += QT_DEPRECATED_WARNINGS
  9. # You can also make your code fail to compile if it uses deprecated APIs.
  10. # In order to do so, uncomment the following line.
  11. # You can also select to disable deprecated APIs only up to a certain version of Qt.
  12. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
  13. SOURCES += \
  14. main.cpp \
  15. mainwindow.cpp
  16. HEADERS += \
  17. mainwindow.h
  18. FORMS += \
  19. mainwindow.ui
  20. # Default rules for deployment.
  21. qnx: target.path = /tmp/$${TARGET}/bin
  22. else: unix:!android: target.path = /opt/$${TARGET}/bin
  23. !isEmpty(target.path): INSTALLS += target

mainwindow.h:

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QChartView>
  4. #include <QSplineSeries>
  5. #include <QScatterSeries>
  6. #include <QDebug>
  7. #include <QValueAxis>
  8. #include <QTimer>
  9. #include <QMainWindow>
  10. /* 必需添加命名空间 */
  11. QT_CHARTS_USE_NAMESPACE
  12. class MainWindow : public QMainWindow
  13. {
  14. Q_OBJECT
  15. public:
  16. MainWindow(QWidget *parent = nullptr);
  17. ~MainWindow();
  18. private:
  19. /* 接收数据接口 */
  20. void receivedData(int);
  21. /* 数据最大个数 */
  22. int maxSize;
  23. /* x轴上的最大值 */
  24. int maxX;
  25. /* y轴上的最大值 */
  26. int maxY;
  27. /* y轴 */
  28. QValueAxis *axisY;
  29. /* x轴 */
  30. QValueAxis *axisX;
  31. /* QList int类型容器 */
  32. QList<int> data;
  33. /* QSplineSeries对象(曲线)*/
  34. QSplineSeries *splineSeries;
  35. /* QChart图表 */
  36. QChart *chart;
  37. /* 图表视图 */
  38. QChartView *chartView;
  39. /* 定时器 */
  40. QTimer *timer;
  41. private slots:
  42. void timerTimeOut();
  43. };
  44. #endif // MAINWINDOW_H

mainwindow.c:

  1. #include "mainwindow.h"
  2. #include <QDateTime>
  3. MainWindow::MainWindow(QWidget *parent)
  4. : QMainWindow(parent)
  5. {
  6. /* 设置最显示位置与大小 */
  7. this->setGeometry(0, 0, 800, 480);
  8. /* 最大储存maxSize - 1个数据 */
  9. maxSize = 51;
  10. /* x轴上的最大值 */
  11. maxX = 5000;
  12. /* y轴最大值 */
  13. maxY = 40;
  14. /* splineSeries曲线实例化(折线用QLineSeries) */
  15. splineSeries = new QSplineSeries();
  16. /* 图表实例化 */
  17. chart = new QChart();
  18. /* 图表视图实例化 */
  19. chartView = new QChartView();
  20. /* 坐标轴 */
  21. axisY = new QValueAxis();
  22. axisX = new QValueAxis();
  23. /* 定时器 */
  24. timer = new QTimer(this);
  25. /* legend译图例类型,以绘图的颜色区分,本例设置为隐藏 */
  26. chart->legend()->hide();
  27. /* chart设置标题 */
  28. chart->setTitle("实时动态曲线示例");
  29. /* 添加一条曲线splineSeries */
  30. chart->addSeries(splineSeries);
  31. /* 设置显示格式 */
  32. axisY->setLabelFormat("%i");
  33. /* y轴标题 */
  34. axisY->setTitleText("温度/℃");
  35. /* y轴标题位置(设置坐标轴的方向) */
  36. chart->addAxis(axisY, Qt::AlignLeft);
  37. /* 设置y轴范围 */
  38. axisY->setRange(0, maxY);
  39. /* 将splineSeries附加于y轴上 */
  40. splineSeries->attachAxis(axisY);
  41. /* 设置显示格式 */
  42. axisX->setLabelFormat("%i");
  43. /* x轴标题 */
  44. axisX->setTitleText("时间/ms");
  45. /* x轴标题位置(设置坐标轴的方向) */
  46. chart->addAxis(axisX, Qt::AlignBottom);
  47. /* 设置x轴范围 */
  48. axisX->setRange(0, maxX);
  49. /* 将splineSeries附加于x轴上 */
  50. splineSeries->attachAxis(axisX);
  51. /* 将图表的内容设置在图表视图上 */
  52. chartView->setChart(chart);
  53. /* 设置抗锯齿 */
  54. chartView->setRenderHint(QPainter::Antialiasing);
  55. /* 设置为图表视图为中心部件 */
  56. setCentralWidget(chartView);
  57. /* 定时200ms */
  58. timer->start(200);
  59. /* 信号槽连接 */
  60. connect(timer, SIGNAL(timeout()), this, SLOT(timerTimeOut()));
  61. /* 设置随机种子,随机数初始化 */
  62. qsrand(time(NULL));
  63. }
  64. MainWindow::~MainWindow()
  65. {
  66. }
  67. void MainWindow::timerTimeOut()
  68. {
  69. /* 产生随机0~maxY之间的数据 */
  70. receivedData(qrand() % maxY );
  71. }
  72. void MainWindow::receivedData(int value)
  73. {
  74. /* 将数据添加到data中 */
  75. data.append(value);
  76. /* 当储存数据的个数大于最大值时,把第一个数据删除 */
  77. while (data.size() > maxSize) {
  78. /* 移除data中第一个数据 */
  79. data.removeFirst();
  80. }
  81. /* 先清空 */
  82. splineSeries->clear();
  83. /* 计算x轴上的点与点之间显示的间距 */
  84. int xSpace = maxX / (maxSize - 1);
  85. /* 添加点,xSpace * i 表示第i个点的x轴的位置 */
  86. for (int i = 0; i < data.size(); ++i) {
  87. splineSeries->append(xSpace * i, data.at(i));
  88. }
  89. }

QT Chart表效果:

3.2 AP3216C的数据读取

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

ap3216c.h:

  1. #ifndef AP3216C_H
  2. #define AP3216C_H
  3. #include <QObject>
  4. #include <QTimer>
  5. class Ap3216c : public QObject
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit Ap3216c(QObject *parent = 0); //构造函数声明,"explicit"是一个C++关键字,用于修饰构造函数或函数,以禁止隐式转换
  10. ~Ap3216c(); //析构函数
  11. Q_INVOKABLE void setCapture(bool str); //Q_INVOKABLE是一个宏,setCapture函数接受一个bool类型的参数str,用于设置某个对象的捕获状态
  12. QString alsData(); //光强传感器( ALS: Ambient Light Sensor)
  13. QString psData(); //接近传感器( PS: Proximity Sensor)
  14. QString irData(); //红外 LED( IR LED)
  15. private:
  16. QTimer *timer; //定时器timer
  17. QString alsdata; //光强数据
  18. QString psdata; //接近数据
  19. QString irdata; //红外数据
  20. QString readAlsData(); //读取Ais数据
  21. QString readPsData(); //读取Ps数据
  22. QString readIrData(); //读取IR数据
  23. Q_PROPERTY(QString alsData READ alsData NOTIFY ap3216cDataChanged)
  24. Q_PROPERTY(QString psData READ psData NOTIFY ap3216cDataChanged)
  25. Q_PROPERTY(QString irData READ irData NOTIFY ap3216cDataChanged)
  26. /*Q_PROPERTY:这是一个宏,用于在类中声明属性。
  27. *QString:alsData是属性的类型,这里是一个QString类型的变量。
  28. *READ alsData:这表示该属性可以通过"alsData"方法进行读取。
  29. *NOTIFY ap3216cDataChanged:这表示当该属性发生变化时,会发出"ap3216cDataChanged"信号
  30. */
  31. public slots:
  32. void timer_timeout();
  33. signals:
  34. void ap3216cDataChanged();
  35. };
  36. #endif // AP3216C_H

ap3216c.c:

  1. #include "ap3216c.h"
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. #include <unistd.h>
  8. #include <QDebug>
  9. /* AP3216C的构造函数 */
  10. Ap3216c::Ap3216c(QObject *parent) : QObject (parent)
  11. {
  12. timer = new QTimer(); //定义一个定时器容器
  13. connect(timer, SIGNAL(timeout()),this,SLOT(timer_timeout())); //将超时信号,与timer_timeout链接起来
  14. }
  15. /* 析构函数 */
  16. Ap3216c::~Ap3216c()
  17. {
  18. }
  19. /* 超时函数 */
  20. void Ap3216c::timer_timeout()
  21. {
  22. alsdata = readAlsData();
  23. psdata = readPsData();
  24. irdata = readIrData();
  25. emit ap3216cDataChanged(); //发送ap3216c数值变化信息
  26. }
  27. QString Ap3216c::readIrData()
  28. {
  29. char const *filename = "/sys/class/misc/ap3216c/ir";
  30. int err = 0;
  31. int fd;
  32. char buf[10];
  33. fd = open(filename,O_RDONLY); //只读模式打开文件
  34. if(fd < 0){
  35. close(fd);
  36. return "open file error!";
  37. }
  38. err = read(fd,buf,sizeof(buf));
  39. if(err < 0){
  40. close(fd);
  41. return "read data error!";
  42. }
  43. close(fd);
  44. QString irValue = buf;
  45. QStringList list = irValue.split("\n"); //将irValue按照换行符"\n"分割成一个字符串列表,并将结果存储在list对象中
  46. return list[0];
  47. }
  48. QString Ap3216c::readPsData()
  49. {
  50. char const *filename = "/sys/class/misc/ap3216c/ps";
  51. int err = 0;
  52. int fd;
  53. char buf[10];
  54. fd = open(filename,O_RDONLY); //只读模式打开文件
  55. if(fd < 0){
  56. close(fd);
  57. return "open file error!";
  58. }
  59. err = read(fd,buf,sizeof(buf));
  60. if(err < 0){
  61. close(fd);
  62. return "read data error!";
  63. }
  64. close(fd);
  65. QString irValue = buf;
  66. QStringList list = irValue.split("\n"); //将irValue按照换行符"\n"分割成一个字符串列表,并将结果存储在list对象中
  67. return list[0];
  68. }
  69. QString Ap3216c::readAlsData()
  70. {
  71. char const *filename = "/sys/class/misc/ap3216c/als";
  72. int err = 0;
  73. int fd;
  74. char buf[10];
  75. fd = open(filename,O_RDONLY); //只读模式打开文件
  76. if(fd < 0){
  77. close(fd);
  78. return "open file error!";
  79. }
  80. err = read(fd,buf,sizeof(buf));
  81. if(err < 0){
  82. close(fd);
  83. return "read data error!";
  84. }
  85. close(fd);
  86. QString irValue = buf;
  87. QStringList list = irValue.split("\n"); //将irValue按照换行符"\n"分割成一个字符串列表,并将结果存储在list对象中
  88. return list[0];
  89. }
  90. QString Ap3216c::alsData()
  91. {
  92. return alsdata;
  93. }
  94. QString Ap3216c::irData()
  95. {
  96. return irdata;
  97. }
  98. QString Ap3216c::psData()
  99. {
  100. return psdata;
  101. }
  102. void Ap3216c::setCapture(bool str)
  103. {
  104. if(str)
  105. timer->start(500);
  106. else
  107. timer->stop();
  108. }

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

3.3 AP3216C与Chart联动

我们将 AP3216C Chart 进行联动配合,通过 Chart 实时读取显示 AP3216C ALS 属性。

mainwindow.h:

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QMainWindow>
  4. #include <QVBoxLayout>
  5. #include <QHBoxLayout>
  6. #include <QComboBox>
  7. #include <QPushButton>
  8. #include <QVBoxLayout>
  9. #include <QLabel>
  10. #include <QScrollArea>
  11. #include <QDebug>
  12. #include <QChartView>
  13. #include <QSplineSeries>
  14. #include <QScatterSeries>
  15. #include <QDebug>
  16. #include <QValueAxis>
  17. #include "ap3216c.h"
  18. /* 必需添加命名空间 */
  19. QT_CHARTS_USE_NAMESPACE
  20. class MainWindow : public QMainWindow
  21. {
  22. Q_OBJECT
  23. public:
  24. MainWindow(QWidget *parent = nullptr);
  25. ~MainWindow();
  26. private:
  27. /* 主容器,Widget也可以当作一种容器 */
  28. QWidget *mainWidget;
  29. /* 界面水平区域布局 */
  30. QHBoxLayout *hboxLayout[2];
  31. /* 界面右侧区域布局 */
  32. QVBoxLayout *vboxLayout[4];
  33. /* 界面右侧区域容器 */
  34. QWidget *rightWidget;
  35. /* 用一个 QLabel 对象用于显示字符串 */
  36. QLabel *labelString;
  37. /* 容器作用,用于布局 */
  38. QWidget *widget[5];
  39. /* 标签文本 */
  40. QLabel *label[3];
  41. /* 数据标签 */
  42. QLabel *my_label[3];
  43. /* 数据最大个数 */
  44. int maxSize;
  45. /* x轴上的最大值 */
  46. int maxX;
  47. /* y轴上的最大值 */
  48. int maxY;
  49. /* y轴 */
  50. QValueAxis *axisY;
  51. /* x轴 */
  52. QValueAxis *axisX;
  53. /* QList int类型容器 */
  54. QList<int> data;
  55. /* QSplineSeries对象(曲线)*/
  56. QSplineSeries *splineSeries;
  57. /* QChart图表 */
  58. QChart *chart;
  59. /* 图表视图 */
  60. QChartView *chartView;
  61. /* ii2传感器类 */
  62. Ap3216c *ap3216c;
  63. /* 接收数据接口 */
  64. void receivedData(int);
  65. /* 布局初始化 */
  66. void layoutInit();
  67. private slots:
  68. /* 获取ap3216传感器数据 */
  69. void getAp3216cData();
  70. };
  71. #endif // MAINWINDOW_H

mainwindow.c:

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include <QGuiApplication>
  4. #include <QScreen>
  5. #include <QDebug>
  6. MainWindow::MainWindow(QWidget *parent)
  7. : QMainWindow(parent)
  8. {
  9. /* 布局初始化 */
  10. layoutInit();
  11. }
  12. MainWindow::~MainWindow()
  13. {
  14. }
  15. /* 布局初始化 */
  16. void MainWindow::layoutInit()
  17. {
  18. /* 获取屏幕的分辨率,Qt官方建议使用这
  19. * 种方法获取屏幕分辨率,防上多屏设备导致对应不上
  20. * 注意,这是获取整个桌面系统的分辨率
  21. */
  22. QList <QScreen *> list_screen = QGuiApplication::screens();
  23. /* 如果是ARM平台,直接设置大小为屏幕的大小 */
  24. #if __arm__
  25. /* 重设大小 */
  26. this->resize(list_screen.at(0)->geometry().width(),
  27. list_screen.at(0)->geometry().height());
  28. #else
  29. /* 否则则设置主窗体大小为800x480 */
  30. this->resize(800,400);
  31. #endif
  32. /* 实例化与布局,常规操作 */
  33. mainWidget = new QWidget();
  34. rightWidget = new QWidget();
  35. for(int i = 0; i < 2; i++) //水平布局
  36. hboxLayout[i] = new QHBoxLayout();
  37. for(int i = 0; i < 5; i++) //容器布局
  38. widget[i] = new QWidget();
  39. for(int i = 0; i < 4; i++) //垂直布局
  40. vboxLayout[i] = new QVBoxLayout();
  41. for(int i = 0; i < 3; i++) //数据变量
  42. my_label[i] = new QLabel();
  43. /********************** chart表格 *****************************/
  44. /* 最大储存maxSize - 1个数据 */
  45. maxSize = 51;
  46. /* x轴上的最大值 */
  47. maxX = 5000;
  48. /* y轴最大值 */
  49. maxY = 40;
  50. /* splineSeries曲线实例化(折线用QLineSeries) */
  51. splineSeries = new QSplineSeries();
  52. /* 图表实例化 */
  53. chart = new QChart();
  54. /* 图表视图实例化 */
  55. chartView = new QChartView();
  56. /* 坐标轴 */
  57. axisY = new QValueAxis();
  58. axisX = new QValueAxis();
  59. /* legend译图例类型,以绘图的颜色区分,本例设置为隐藏 */
  60. chart->legend()->hide();
  61. /* chart设置标题 */
  62. chart->setTitle("环境光强度");
  63. /* 添加一条曲线splineSeries */
  64. chart->addSeries(splineSeries);
  65. /* 设置显示格式 */
  66. axisY->setLabelFormat("%i");
  67. /* y轴标题 */
  68. axisY->setTitleText("光通亮/Lux");
  69. /* y轴标题位置(设置坐标轴的方向) */
  70. chart->addAxis(axisY, Qt::AlignLeft);
  71. /* 设置y轴范围 */
  72. axisY->setRange(0, maxY);
  73. /* 将splineSeries附加于y轴上 */
  74. splineSeries->attachAxis(axisY);
  75. /* 设置显示格式 */
  76. axisX->setLabelFormat("%i");
  77. /* x轴标题 */
  78. axisX->setTitleText("时间/ms");
  79. /* x轴标题位置(设置坐标轴的方向) */
  80. chart->addAxis(axisX, Qt::AlignBottom);
  81. /* 设置x轴范围 */
  82. axisX->setRange(0, maxX);
  83. /* 将splineSeries附加于x轴上 */
  84. splineSeries->attachAxis(axisX);
  85. /* 将图表的内容设置在图表视图上 */
  86. chartView->setChart(chart);
  87. /* 设置抗锯齿 */
  88. chartView->setRenderHint(QPainter::Antialiasing);
  89. /********************** chart表格 *****************************/
  90. /* 设置传感器数据标签 */
  91. QFont font;
  92. font.setPixelSize(18);
  93. QPalette pal;
  94. pal.setColor(QPalette::WindowText, Qt::black);
  95. QStringList list;
  96. list<<"环境光强度:"<<"接近距离:"<<"红外强度:";
  97. for (int i = 0; i < 3; i++) {
  98. label[i] = new QLabel();
  99. label[i]->setText(list[i]);
  100. label[i]->setFont(font);
  101. label[i]->setPalette(pal);
  102. label[i]->adjustSize();
  103. }
  104. /* 垂直容器布局 */
  105. vboxLayout[3]->addWidget(widget[0]);
  106. vboxLayout[3]->addWidget(widget[1]);
  107. vboxLayout[3]->addWidget(widget[2]);
  108. hboxLayout[0]->addWidget(chartView); //将chart表格添加到水平布局中
  109. widget[3]->setLayout(hboxLayout[0]);
  110. widget[4]->setLayout(vboxLayout[3]);
  111. hboxLayout[1]->addWidget(widget[3]);
  112. hboxLayout[1]->addWidget(widget[4]);
  113. mainWidget->setLayout(hboxLayout[1]);
  114. this->setCentralWidget(mainWidget);
  115. /* als布局 */
  116. vboxLayout[0]->addWidget(label[0]);
  117. vboxLayout[0]->addWidget(my_label[0]);
  118. vboxLayout[0]->setAlignment(Qt::AlignTop | Qt::AlignVCenter);
  119. widget[0]->setLayout(vboxLayout[0]);
  120. /* ps布局 */
  121. vboxLayout[1]->addWidget(label[1]);
  122. vboxLayout[1]->addWidget(my_label[1]);
  123. vboxLayout[1]->setAlignment(Qt::AlignTop | Qt::AlignVCenter);
  124. widget[1]->setLayout(vboxLayout[1]);
  125. /* ir布局 */
  126. vboxLayout[2]->addWidget(label[2]);
  127. vboxLayout[2]->addWidget(my_label[2]);
  128. vboxLayout[2]->setAlignment(Qt::AlignTop | Qt::AlignVCenter);
  129. widget[2]->setLayout(vboxLayout[2]);
  130. ap3216c = new Ap3216c(this);
  131. /* 只能在开发板上开启获取数据,Ubuntu上是没有ap3216c传感器的 */
  132. #if __arm__
  133. ap3216c->setCapture(true);
  134. #endif
  135. connect(ap3216c,SIGNAL(ap3216cDataChanged()),this,SLOT(getAp3216cData()));
  136. }
  137. void MainWindow::getAp3216cData()
  138. {
  139. static QString als = ap3216c->alsData();
  140. // if (als != ap3216c->alsData()) {
  141. // als = ap3216c->alsData();
  142. // }
  143. als = ap3216c->alsData();
  144. static QString ps = ap3216c->psData();
  145. // if (ps != ap3216c->psData()) {
  146. // ps = ap3216c->psData();
  147. // }
  148. ps = ap3216c->psData();
  149. static QString ir = ap3216c->irData();
  150. // if (ir != ap3216c->irData()) {
  151. // ir = ap3216c->irData();
  152. // }
  153. ir = ap3216c->irData();
  154. my_label[0]->setText(als);
  155. my_label[1]->setText(ps);
  156. my_label[2]->setText(ir);
  157. /**************** chart表数据 ******************/
  158. int num = als.toInt(); //字符串转int类型数据
  159. /* 将数据添加到data中 */
  160. data.append(num);
  161. /* 当储存数据的个数大于最大值时,把第一个数据删除 */
  162. while (data.size() > maxSize) {
  163. /* 移除data中第一个数据 */
  164. data.removeFirst();
  165. }
  166. /* 先清空 */
  167. splineSeries->clear();
  168. /* 计算x轴上的点与点之间显示的间距 */
  169. int xSpace = maxX / (maxSize - 1);
  170. /* 添加点,xSpace * i 表示第i个点的x轴的位置 */
  171. for (int i = 0; i < data.size(); ++i) {
  172. splineSeries->append(xSpace * i, data.at(i));
  173. }
  174. }

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

实际项目:

四、项目效果

AP3216C+QT+Chart

五、项目代码

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/385543
推荐阅读
相关标签
  

闽ICP备14008679号