首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-03-05 03:01
  • 2579
  • 6222
blog.csdn.net

引言

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

完整源代码

文件mainwindow.ui 中的代码

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>90</x>
      <y>130</y>
      <width>89</width>
      <height>25</height>
     </rect>
    </property>
    <property name="text">
     <string>LED</string>
    </property>
   </widget>
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>260</x>
      <y>80</y>
      <width>71</width>
      <height>41</height>
     </rect>
    </property>
    <property name="text">
     <string>TextLabel</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_2">
    <property name="geometry">
     <rect>
      <x>260</x>
      <y>160</y>
      <width>71</width>
      <height>31</height>
     </rect>
    </property>
    <property name="text">
     <string>TextLabel</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>22</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

文件led.h中的代码

#ifndef LED_H
#define LED_H

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

#endif // LED_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

文件mainwindow.h中的代码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

文件led.cpp中的代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

文件main.cpp中的代码

#include "mainwindow.h"
#include "led.h"
#include 

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

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

文件mainwindow.cpp中的代码

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "led.h"
#include 

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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

书写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);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

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

另外,关于文件描述符的相关知识,可以参考我的另一篇博文 http://iyenn.com/rec/1709313.html

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

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

具体的过程我在博文 http://iyenn.com/rec/1709312.html 中有描述,打开博文搜索“请求使用 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);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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

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

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

具体的过程我在博文 http://iyenn.com/rec/1709312.html 中有描述,打开博文搜索“请求使用 GPIO 131”

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

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

注意:这些报错只是代码编辑器的报错,并不是编译时的报错,我实测过,不解决这个问题,也能成功编译,因为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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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

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

编译工程

编译方法

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

这里要说明下,其实在 led.cpp中,用到了Linux系统的很多用户空间的函数,但是似乎我在配置QtCreator时没有去配置用户空间编译时需要的头文件和库呀,配置QtCreator的博文链接 http://iyenn.com/rec/1709264.html

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

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

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

重要概念: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
  • 1

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

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

参考博文 http://iyenn.com/rec/1709315.html 用一次性有效的方法(即不是永久有效的方法)关掉自带的QT的GUI界面。

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

/etc/init.d/S99myirhmi2 stop
  • 1

执行完成后再用手去操作屏幕上的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/

  • 1
  • 2
  • 3
  • 4

这三条命令的详细解释见 http://iyenn.com/rec/1709052.html

运行编译生成的Qt程序

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

/mnt/qt_sys_led/test_01
  • 1

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

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

这样测试就成功了。

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

https://pan.baidu.com/s/1j7DVGZaZj0WtK3J-K2xnsQ?pwd=h5sp
注意:QtCreator的工程换位置后一定要更换一下Build directory的位置,因为在QtCreator中Build directory是一个绝对路径,详情见 http://iyenn.com/rec/1709289.html

昊虹嵌入式技术交流群
QQ群名片
注:本文转载自blog.csdn.net的昊虹AI笔记的文章"https://blog.csdn.net/wenhao_ir/article/details/145459006"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top