#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 - 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;
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 - 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;
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的数据,读取原始数据,包括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 : 要读取的数据长度
@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;
}
/*
/* 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;
}
/*
/* 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** 文档就可以了。
本项目中我们需要使用**曲线图**去展示 **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
DEFINES += QT_DEPRECATED_WARNINGS
#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
qnx: target.path = /tmp/
T
A
R
G
E
T
/
b
i
n
e
l
s
e
:
u
n
i
x
:
!
a
n
d
r
o
i
d
:
t
a
r
g
e
t
.
p
a
t
h
=
/
o
p
t
/
{TARGET}/bin else: unix:!android: target.path = /opt/
TARGET/binelse:unix:!android:target.path=/opt/{TARGET}/bin
!isEmpty(target.path): INSTALLS += target
**mainwindow.h:**
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include
/* 必需添加命名空间 */
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
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
#include
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
/* 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 的数据值!
>
>
>

### 3.3 AP3216C与Chart联动
>
> 我们将 **AP3216C** 与 **Chart** 进行联动配合,通过 **Chart** 实时读取显示 **AP3216C** 的 **ALS** 属性。
>
>
>
**mainwindow.h:**
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!
QVBoxLayout *vboxLayout[4];
/* 界面右侧区域容器 */
QWidget *rightWidget;
/* 用一个 QLabel 对象用于显示字符串 */
QLabel *labelString;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-Op31CTxx-1715682585950)]
[外链图片转存中…(img-8eIjbCGe-1715682585952)]
[外链图片转存中…(img-oWG3fGvR-1715682585954)]
[外链图片转存中…(img-MNnZjfjC-1715682585955)]
[外链图片转存中…(img-X8BOlzbN-1715682585956)]
[外链图片转存中…(img-Sl9aIp0H-1715682585957)]
[外链图片转存中…(img-Zo0pVKtx-1715682585958)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!
因篇幅问题不能全部显示,请点此查看更多更全内容