第二个Qt开发实例:在Qt中利用GPIO子系统和sysfs伪文件系统实现按钮(Push Button)点击控制GPIO口(效果为LED2灯的灭和亮)

news2025/2/8 3:30:08

引言

本文承接博文 https://blog.csdn.net/wenhao_ir/article/details/145420998 里的代码,在那里面代码的基础上添加上利用sysfs伪文件系统实现按钮(Push Button)点击控制GPIO口的代码,进而实现LED2灯的灭和亮。
最终的效果是点击下面的LED按钮实现LED2的灭和亮。
在这里插入图片描述
本文使用了GPIO子系统和sysfs伪文件系统去操控GPIO口,
关于GPIO子系统的详细介绍见:https://blog.csdn.net/wenhao_ir/article/details/145444452
关于sysfs伪文件系统的详细介绍见:https://blog.csdn.net/wenhao_ir/article/details/145453877

完整源代码

文件led.h中的代码

#ifndef LED_H
#define LED_H

void led_init(void);
void led_control(int on);

#endif // LED_H

文件mainwindow.h中的代码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

文件led.cpp中的代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <QDebug>

void led_init(void)
{
    /*
     * echo 131 > /sys/class/gpio/export
     * echo out > /sys/class/gpio/gpio131/direction
     */

    int fd;

    fd = open("/sys/class/gpio/export", O_WRONLY);
    if (fd < 0)
    {
        qDebug()<<"open /sys/class/gpio/export err";
        return ;
    }

    write(fd, "131\n", 4);

    close(fd);

    fd = open("/sys/class/gpio/gpio131/direction", O_WRONLY);
    if (fd < 0)
    {
        qDebug()<<"open /sys/class/gpio/gpio131/direction err";
        return ;
    }

    write(fd, "out\n", 4);

    close(fd);
}

void led_control(int on)
{
    /*
     * echo 1 > /sys/class/gpio/gpio131/value
     * echo 0 > /sys/class/gpio/gpio131/value
     */
    static int fd = -1;

    if (fd == -1)
    {
        fd = open("/sys/class/gpio/gpio131/value", O_RDWR);
    }
    if (fd < 0)
    {
        qDebug()<<"open /sys/class/gpio/gpio131/value err";
        return ;
    }

    if (on)
    {
        write(fd, "0\n", 2);
    }
    else
    {
        write(fd, "1\n", 2);
    }
}

文件main.cpp中的代码

#include "mainwindow.h"
#include "led.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    /* 1. init LED */
    led_init();

    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

文件mainwindow.cpp中的代码

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "led.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    static int status = 1;

    if (status)
        qDebug()<<"LED clicked on";
    else
        qDebug()<<"LED clicked off";

    /* 2. control LED */
    led_control(status);

    status = !status;
}

书写LED的初始化函数led_init()

在工程中新建一个名为led.cpp的文件:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

然后在其中写出LED的初始化函数led_init()
代码如下:

void led_init(void)
{
    /*
     * echo 131 > /sys/class/gpio/export
     * echo out > /sys/class/gpio/gpio131/direction
     */

    int fd;

    fd = open("/sys/class/gpio/export", O_WRONLY);
    if (fd < 0)
    {
        qDebug()<<"open /sys/class/gpio/export err";
        return ;
    }

    write(fd, "131\n", 4);

    close(fd);

    fd = open("/sys/class/gpio/gpio131/direction", O_WRONLY);
    if (fd < 0)
    {
        qDebug()<<"open /sys/class/gpio/gpio131/direction err";
        return ;
    }

    write(fd, "out\n", 4);

    close(fd);
}

上面这段代码如果把下面两篇博文:
https://blog.csdn.net/wenhao_ir/article/details/145444452
https://blog.csdn.net/wenhao_ir/article/details/145453877
认真看了一遍,那就很好理解了,所以这里就不再赘述。
如果时间紧的话,那就看:
第1篇博文中的“终端中如何利用GPIO子系统(gpiolib)操作GPIO口”
第2篇博文中的第1个标题中的内容。
不过强烈建议把两篇博文浏览一遍。

另外,关于文件描述符的相关知识,可以参考我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/144931005

这里附一张下面这条命令运行结果:
在这里插入图片描述
注意,上面截图中的gpio131是运行了下面这条代码后才有的:

write(fd, "131\n", 4);

具体的过程我在博文 https://blog.csdn.net/wenhao_ir/article/details/145453877 中有描述,打开博文搜索“请求使用 GPIO 131”

书写LED的控制函数led_control()

在文件led.cpp里书写LED的控制函数led_control(),代码如下:

void led_control(int on)
{
    /*
     * echo 1 > /sys/class/gpio/gpio131/value
     * echo 0 > /sys/class/gpio/gpio131/value
     */
    static int fd = -1;

    if (fd == -1)
    {
        fd = open("/sys/class/gpio/gpio131/value", O_RDWR);
    }
    if (fd < 0)
    {
        qDebug()<<"open /sys/class/gpio/gpio131/value err";
        return ;
    }

    if (on)
    {
        write(fd, "0\n", 2);
    }
    else
    {
        write(fd, "1\n", 2);
    }
}

如果理解了初始化函数led_init()里的代码,那么这段代码也就很简单了,这里就不再赘述了。

这里附一张下面这条命令运行结果:
在这里插入图片描述
注意,上面截图中的gpio131是运行了下面这条代码后才有的:

write(fd, "131\n", 4);

具体的过程我在博文 https://blog.csdn.net/wenhao_ir/article/details/145453877 中有描述,打开博文搜索“请求使用 GPIO 131”

解决led.cpp因缺少头文件而导致的各种报错

上面的函数led_init()led_control()写到文件led.cpp后,会有很多报错:
在这里插入图片描述
这些报错本质上都是因为工程的代码编辑器没有正确设置头文件目录,具体的设置方法见 https://blog.csdn.net/wenhao_ir/article/details/145479279

注意:这些报错只是代码编辑器的报错,并不是编译时的报错,我实测过,不解决这个问题,也能成功编译,因为Makefile中有相关的路径设置。

新建头文件led.h并书写头文件的内容

初始化函数led_init()和控制函数led_control()已经写完了,接下来我们要写个头文件对这两个函数声明,以便其它文件中的代码能引用这两个函数。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
写入led.h的代码如下:

#ifndef LED_H
#define LED_H

void led_init(void);
void led_control(int on);

#endif // LED_H

在这里插入图片描述

main函数里调用初始化函数led_init()

在这里插入图片描述

类MainWindow的成员函数on_pushButton_clicked里调用控制函数led_control()

代码如下:

void MainWindow::on_pushButton_clicked()
{
    static int status = 1;

    if (status)
        qDebug()<<"LED clicked on";
    else
        qDebug()<<"LED clicked off";

    /* 2. control LED */
    led_control(status);

    status = !status;
}

这段代码很简单,没啥好说的。

值得注意的是哪里与按钮事件联结起来的?这涉及到Qt的信号槽机制,就是下面这些东西:
在这里插入图片描述
上面红框中的东西我已经在博文 https://blog.csdn.net/wenhao_ir/article/details/145420998 中介绍过了,这里就不再赘述了。

编译工程

编译方法

在这里插入图片描述
成功编译,顺利生成ELF可执行文件:test_01
在这里插入图片描述
把生成的这个ELF可执行文件test_01复制到NFS网络文件目录中,备用。

这里要说明下,其实在 led.cpp中,用到了Linux系统的很多用户空间的函数,但是似乎我在配置QtCreator时没有去配置用户空间编译时需要的头文件和库呀,配置QtCreator的博文链接 https://blog.csdn.net/wenhao_ir/article/details/145367198

那是怎么回事呢?其实只要有sysroot位置就能有这些需要的头文件和库,关于sysroot的详细介绍,见 https://blog.csdn.net/wenhao_ir/article/details/145468785
这里QtCreator能根据qmake的位置找到sysroot的位置,所以自然就能编译啦。
我们查看QtCreator工程中的Makefile文件:
在这里插入图片描述
能发现sysroot的路径为:

/home/book/100ask_imx6ull-sdk/Buildroot_2020.02.x/output/host/arm-buildroot-linux-gnueabihf/sysroot

如下图所示:
在这里插入图片描述
所以是能正确构建的。

重要概念:Qt程序是运行于用户空间的

这里要注意:其实Qt工程中的代码都是运行于用户空间的,Qt本身就是在用户空间运行的程序,所以只需要提供sysroot,就能正常编译啦。

上板测试

复制可执行文件到NFS网络文件目录

把之前编译生成个ELF可执行文件test_01复制到NFS网络文件目录中。
在这里插入图片描述
打开串口终端→打开开发板→挂载网络文件系统:

mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt

关掉开发板上自带的QT的GUI

经实测,如果不关闭开发板自带的QT的GUI,虽然你自己写的Qt界面能加载出来,但是当你用手划动屏幕时,开发板上自带的QT的GUI有可能会弹出来。

参考博文 https://blog.csdn.net/wenhao_ir/article/details/144591685 用一次性有效的方法(即不是永久有效的方法)关掉自带的QT的GUI界面。

这里用一次性的方法,即不是永久有效的方法:
执行下面这条命令:

/etc/init.d/S99myirhmi2 stop

执行完成后再用手去操作屏幕上的UI界面,UI界面就没有任何反应了,说明QT的GUI界面被关掉了

设置Qt环境变量

export QT_QPA_GENERIC_PLUGINS=tslib:/dev/input/event1
export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0
export QT_QPA_FONTDIR=/usr/lib/fonts/

这三条命令的详细解释见 https://blog.csdn.net/wenhao_ir/article/details/145433648

运行编译生成的Qt程序

注意:运行前请确保Qt运行的环境变量设置好了。
注意:运行前请确保Qt运行的环境变量设置好了。

/mnt/qt_sys_led/test_01

屏幕如下图所示:
在这里插入图片描述
点击LED按钮1次:
终端运行结果如下:
在这里插入图片描述
此时LED2灯是亮着的。

再点LED按钮1次:
终端运行结果如下:
在这里插入图片描述
此是LED2灯灭了。

这样测试就成功了。

附编译完成后完整的工程目录

https://pan.baidu.com/s/1j7DVGZaZj0WtK3J-K2xnsQ?pwd=h5sp
注意:QtCreator的工程换位置后一定要更换一下Build directory的位置,因为在QtCreator中Build directory是一个绝对路径,详情见 https://blog.csdn.net/wenhao_ir/article/details/145458743

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

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

相关文章

Day37-【13003】短文,串的基本概念,匹配算法,算法时间复杂度,真题训练

文章目录 第二节 串串的基本概念串的模式匹配朴素的模式匹配算法(BF算法)算法最坏时间复杂度O(n x m) 改进的模式匹配算法(KMP算法)特征向量next&#xff0c;来确定k值特征向量next的算法实现 算法最坏时间复杂度O(n)进一步改进next值的计算&#xff0c;简化步骤 第四章真题真题…

陷入闭包:理解 React 状态管理中的怪癖

TLDR 闭包就像函数随身携带的背包&#xff0c;包含它们创建时的数据React 组件使用闭包来记住它们的状态和属性过时的闭包可能导致状态更新不如预期时的错误函数式更新提供了一个可靠的方式来处理最新状态 简介 你是否曾经疑惑过&#xff0c;为什么有时你的 React 状态更新不…

【SRC排名】安全应急响应中心SRC上榜记录

2023年 新氧第三 https://security.soyoung.com/top 合合第四 https://security.intsig.com/index.php?m&chall&aindex 2024年 好未来第一 https://src.100tal.com/index.php?m&chall&aindex&#xff08;官网是总榜&#xff0c;年榜只有海报&#xff09;…

Linux——基础命令1

$&#xff1a;普通用户 #&#xff1a;超级用户 cd 切换目录 cd 目录 &#xff08;进入目录&#xff09; cd ../ &#xff08;返回上一级目录&#xff09; cd ~ &#xff08;切换到当前用户的家目录&#xff09; cd - &#xff08;返回上次目录&#xff09; pwd 输出当前目录…

OSPF基础(1):工作过程、状态机、更新

OSPF基础 1、技术背景&#xff08;与RIP密不可分&#xff0c;因为RIP中存在的问题&#xff09; RIP中存在最大跳数为15的限制&#xff0c;不能适应大规模组网周期性发送全部路由信息&#xff0c;占用大量的带宽资源以路由收敛速度慢以跳数作为度量值存在路由环路可能性每隔30秒…

【目标检测】模型验证:K-Fold 交叉验证

K-Fold 交叉验证 1、引言1.1 K 折交叉验证概述 2、配置2.1 数据集2.2 安装包 3、 实战3.1 生成物体检测数据集的特征向量3.2 K 折数据集拆分3.3 保存记录3.4 使用 K 折数据分割训练YOLO 4、总结 1、引言 我们将利用YOLO 检测格式和关键的Python 库&#xff08;如 sklearn、pan…

Unity 2D实战小游戏开发跳跳鸟 - 计分逻辑开发

上文对障碍物的碰撞逻辑进行了开发,接下来就是进行跳跳鸟成功穿越过障碍物进行计分的逻辑开发,同时将对应的分数以UI的形式显示告诉玩家。 计分逻辑 在跳跳鸟通过障碍物的一瞬间就进行一次计分,计分后会同步更新分数的UI显示来告知玩家当前获得的分数。 首先我们创建一个用…

京准:NTP卫星时钟服务器对于DeepSeek安全的重要性

京准&#xff1a;NTP卫星时钟服务器对于DeepSeek安全的重要性 京准&#xff1a;NTP卫星时钟服务器对于DeepSeek安全的重要性 在网络安全领域&#xff0c;分布式拒绝服务&#xff08;DDoS&#xff09;攻击一直是企业和网络服务商面临的重大威胁之一。随着攻击技术的不断演化…

Android学习20 -- 手搓App2(Gradle)

1 前言 昨天写了一个完全手搓的&#xff1a;Android学习19 -- 手搓App-CSDN博客 后面谷歌说不要用aapt&#xff0c;d8这些来搞。其实不想弄Gradle的&#xff0c;不过想着既然开始了&#xff0c;就多看一些。之前写过一篇Gradle&#xff0c;不过是最简单的编译&#xff0c;不涉…

车型检测7种YOLOV8

车型检测7种YOLOV8&#xff0c;采用YOLOV8NANO训练&#xff0c;得到PT模型&#xff0c;转换成ONNX&#xff0c;然后OPENCV的DNN调用&#xff0c;支持C&#xff0c;python,android开发 车型检测7种YOLOV8

IDEA 中集成 Maven,配置环境、创建以及导入项目

目录 在 IntelliJ IDEA 中集成 Maven 并配置环境 1. 打开 IDEA 设置 2. 定位 Maven 配置选项 3. 配置 Maven 路径 4. 应用配置 创建 Maven 项目 1. 新建项目 2. 选择项目类型 3. 配置项目信息 4. 确认 Maven 设置 5. 完成项目创建 导入 Maven 项目 1. 打开导入窗口…

react关于手搓antd pro面包屑的经验(写的不好请见谅)

我们先上代码&#xff0c;代码里面都有注释&#xff0c;我是单独写了一个组件&#xff0c;方便使用&#xff0c;在其他页面引入就行了 还使用了官方的Breadcrumb组件 import React, { useEffect, useState } from react; import { Breadcrumb, Button } from antd; import { …

[含文档+PPT+源码等]精品大数据项目-Django基于大数据实现的心血管疾病分析系统

大数据项目-Django基于大数据实现的心血管疾病分析系统背景可以从以下几个方面进行阐述&#xff1a; 一、项目背景与意义 1. 心血管疾病现状 心血管疾病是当前全球面临的主要健康挑战之一&#xff0c;其高发病率、高致残率和高死亡率严重威胁着人类的生命健康。根据权威机构…

【Rust自学】19.5. 高级类型

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 19.5.1.使用newtype模式实现类型安全和抽象 在 19.2. 高级trait 中&#xff08;具体来说是…

113,【5】 功防世界 web unseping

进入靶场 代码审计 <?php // 高亮显示当前 PHP 文件的源代码&#xff0c;方便开发者查看代码结构和内容 highlight_file(__FILE__);// 定义一个名为 ease 的类 class ease {// 私有属性 $method&#xff0c;用于存储要调用的方法名private $method;// 私有属性 $args&…

leetCode刷题-图、回溯相关

岛屿数量 class Solution { private:int mi;int mj; public:int numIslands(vector<vector<char>>& grid) {mi grid.size() - 1; // i的范围 0~mimj grid[0].size() - 1; // j的范围 0~mjint landnum 0;bool sea false;do {pair<int, int> res …

Windows编程:下载与安装 Visual Studio 2010

本节前言 在写作本节的时候&#xff0c;本来呢&#xff0c;我正在写的专栏&#xff0c;是 MFC 专栏。而 VS2010 和 VS2019&#xff0c;正是 MFC 学习与开发中&#xff0c;可以使用的两款软件。然而呢&#xff0c;如果你去学习 Windows API 知识的话&#xff0c;那么&#xff0…

OpenEuler学习笔记(十八):搭建企业云盘服务

要在 OpenEuler 上搭建企业云盘&#xff0c;可借助一些开源软件来实现&#xff0c;以下以 Nextcloud 为例详细介绍搭建步骤。Nextcloud 是一款功能丰富的开源云存储解决方案&#xff0c;支持文件共享、同步、协作等多种功能。 1. 系统环境准备 确保 OpenEuler 系统已更新到最…

什么是三层交换技术?与二层有什么区别?

什么是三层交换技术&#xff1f;让你的网络飞起来&#xff01; 一. 什么是三层交换技术&#xff1f;二. 工作原理三. 优点四. 应用场景五. 总结 前言 点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有惊喜。 作者&#xff1a;神的孩子都在歌唱 大家好…

Ollama+deepseek+Docker+Open WebUI实现与AI聊天

1、下载并安装Ollama 官方网址&#xff1a;Ollama 安装好后&#xff0c;在命令行输入&#xff0c; ollama --version 返回以下信息&#xff0c;则表明安装成功&#xff0c; 2、 下载AI大模型 这里以deepseek-r1:1.5b模型为例&#xff0c; 在命令行中&#xff0c;执行&…