Using Qt with QSocketNotifier for GPIO attached interrupting buttons.



  • I wanted to use some real buttons connected through GPIO on a Beaglebone Black with interrupts instead of polling.
    After digging around for a while and a few false starts I found something that seems to work pretty well, with almost no processor load.

    The trickiest part of the problem was that the Linux GPIO driver always is "ready" when the value file is read. That means that the select() function returns immediately and the code turns into a CPU driven poll. This was not useful.
    The documentation for the Linux kernel gpio driver indicated that the GPIO value file needed to be in the exception group of file descriptors in order to get interrupt driven behavior.

    So, in short the answer is to set up the GPIO for the button in the usual way that GPIOs are, then set the direction to "in", the edge to "both" (to get the button press and release). and open the GPIO value file and add the GPIO value file to the QSocketNotifier::Exception set of file descriptors.

    The constructor that I have for this uses a C++ library modelled on the Adafruit C code. The commentary for QSocketNotifier discourages using QSocketNotifier::Exception. I'm not exactly certain why.

    @
    gpio_keypad::gpio_keypad(QString sw_pin, QString in_key_name) {

    this_gpio = new Adafruit_bbio_gpio(sw_pin.toStdString());
    if (!this_gpio) {
        abort();
    } else {
        QString gpio_value_path;
        key_name = in_key_name;
        gpio_value_path = QString::fromStdString(this_gpio->gpio_get_path());
        qDebug() << "keypad path=" &lt;&lt; gpio_value_path;
        keypad_value.setFileName(gpio_value_path); /* Set up the value file */
        this_gpio-&gt;gpio_set_direction("in");
        this_gpio->gpio_set_edge("both"); /* Interrupt on rising edge */
        keypad_value.open(QFile::ReadOnly);
        keypad_notifier = new QSocketNotifier(keypad_value.handle(), QSocketNotifier::Exception);
        keypad_notifier->setEnabled(true);
        connect(keypad_notifier, SIGNAL(activated(int)), this, SLOT(ready_read(int)));
    } /* endif */
    

    }
    @



  • use Qtnetwork(server and client).



  • There was no networking involved. QSocketNotifier seems to have been somewhat over-specificly named. It will work with any file descriptor, not just sockets.



  • I am bit new to C++,
    While trying to do above steps i have error at:
    keypad_notifier = new QSocketNotifier(keypad_value.handle(), QSocketNotifier::Exception);
    saying keypad_notifier not declared in the scope.

    What should be the solution for it.

    Also i want to know what should be the contents of below functions:

    1. keypad_value.handle()
    2. ready_read(int).

    What i consider for ready_read(int) is that it will contain the activity that needs to be performed after the interrupt is raised.

    We have configured GPIO0_7 of BeagleBone Black as input with both edges for interrupt.

    Thanks in Advance.
    Harsh



  • Hello Harsh,

    I wasn't sure how general the code I created was. The .h file for my gpio_keypad contains the keypad_notifier and the keypad_value QFile variables. The keypad_value.handle() is the file descriptor of the gpio value file for the keypad.

    @#ifndef GPIO_KEYPAD_H
    #define GPIO_KEYPAD_H

    #include <QObject>
    #include <QSocketNotifier>
    #include <QString>
    #include <QFile>
    #include "adafruit_bbio_gpio.h"

    class gpio_keypad : public QObject {
    Q_OBJECT
    public:
    gpio_keypad(QString sw_pin, QString in_key_name);
    ~gpio_keypad();
    int keypad_fd(void); /* Return the file descriptor for the keypad interrupt device /
    QString const keyname(void); /
    Return the name of the key for this object */
    signals:
    void button_pushed(int on_off);
    private:
    Adafruit_bbio_gpio this_gpio; / GPIO object for this pin /
    QFile keypad_value; /
    GPIO keypad value file for keypad file. /
    QString key_name; /
    User name for this key */
    QSocketNotifier keypad_notifier;
    char current_value; /
    The last value read from the GPIO */
    public slots:
    void ready_read(int);
    };

    -=-=-=-=-=-=-- end of keypad.h -=-=-=-=-=-=-=-=

    void gpio_keypad::ready_read(int) {
    QByteArray line;
    keypad_value.seek(0);
    char last_value = current_value;
    line = keypad_value.readAll();
    qDebug() << "bytes_read=" << line.size();
    if (line.size() < 1) {
    abort();
    } else {
    current_value = line[0];
    emit button_pushed(current_value);
    } /* endif /
    if (current_value != last_value) {
    qDebug() << "key" << key_name << "switch value changed, old=" << last_value << "current_value" << current_value;
    } /
    endif /
    } /
    ready_read */

    @



  • Hello,
    Thanks for your responce, i have dont changes said by you, after that i still have problem saif below

    Error:
    No match for call to (QFile)()
    Error for line:
    keypad_notifier = new QSocketNotifier(keypad_value(), QSocketNotifier::Exception);

    h file

    private:
    Ui::MainWindow ui;
    unsigned int IP_PIN;
    unsigned int OP_PIN;
    QFile keypad_value; /
    GPIO keypad value file for keypad file. /
    QString key_name; /
    User name for this key */
    QSocketNotifier keypad_notifier;
    char current_value; /
    The last value read from the GPIO */

    C File

    keypad_notifier = new QSocketNotifier(keypad_value(), QSocketNotifier::Exception);
    keypad_notifier->setEnabled(true);
    connect(keypad_notifier, SIGNAL(activated(int)), this, SLOT(ready_read(int)));


  • Hello,

    You missed a small piece of code. The keypad_value() should be keypad_value.handle(). The QSocketNotifier constructor needs the Linux file handle as an argument. That should get rid of the error message.

    Ken



  • Thanks Ken,
    I missed handle(), in keypad_value.handle(), so the error is fixed and i can compile my project.

    I am using Beagle Bone Black with Angstrom Linux.
    Linux beaglebone 3.8.13 # 1 SMP Mon 20 17:07:58 CEST 2013 armv7l GNU/linux.

    When i run the project in BeagleBone Black,
    $ ./Intr -qws
    QSocketNotifier: Invalid socket specified.

    Can you please help me.

    Thanks in Advance.
    Harsh



  • That looks like the keypad GPIO file didn't open (and I didn't check for that failure in that piece of code). The file names I wind up using on my bbb look like this

    keypad path= "/sys/class/gpio/gpio76/value"
    keypad path= "/sys/class/gpio/gpio11/value"
    keypad path= "/sys/class/gpio/gpio9/value"
    keypad path= "/sys/class/gpio/gpio81/value"
    keypad path= "/sys/class/gpio/gpio8/value"
    keypad path= "/sys/class/gpio/gpio79/value"
    keypad path= "/sys/class/gpio/gpio78/value"

    Yours will almost certainly be different.

    All the C++ code I have done for the Beaglebone Black is in github at

    https://github.com/kenaaker/BeagleBoneBlack

    Some of the stuff is somewhat redundant, but it does make a functioning application.

    Ken


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.