Как лучше всего программировать GPIO на плате FriendlyARM mini2440?

У меня есть плата mini2440 с Linux 2.6, на которой мне нужно запрограммировать управление установленной солнечной панелью. Алгоритм предоставляется, и мне нужно закодировать его для платы ARM. Графический интерфейс выполнен в Qt, и мне нужно написать код для фактического управления. Я видел метод доступа к GPIO из пользовательского пространства, и это утомительно. Я сомневаюсь в точности, которую я могу получить для ШИМ.

Какие другие методы я могу использовать для программирования GPIO для приложений включения / выключения и ШИМ?

Несколько друзей предложили запрограммировать управляющий код как модуль ядра, но я не совсем уверен, что хочу этим заниматься.


person VedVals    schedule 21.12.2013    source источник
comment
В Linux есть инфраструктура PWM. Вам нужно указать вашу проблему немного более четко. Какова частота PWM и рабочие циклы? Если частота низкая (20 Гц), вы можете сделать это из пользовательского пространства. Вероятно, будет много джиттера, если вы попытаетесь реализовать PWM выше этой частоты. Даже nice задачам придется уступить время задачам ядра и т. д.   -  person artless noise    schedule 03.01.2014


Ответы (1)


Несколько друзей предложили запрограммировать управляющий код как модуль ядра, но я не совсем уверен, что хочу этим заниматься.

Я почти уверен, что если бы это не было тесно связано с аппаратным обеспечением, оно было бы отклонено ядром Linux. Цель пользовательского системного вызова и доступа к sysfs — поместить вашу пользовательскую логику в Linux поверх аппаратной абстракции (модель OSI).

Что вам нужно сделать, так это сначала проверить, обеспечивает ли ядро ​​Linux всю аппаратную поддержку ваших устройств. Тогда вы можете взять мой класс промежуточного программного обеспечения для управления GPIO на языке C++. Наконец, вы можете написать небольшое основное приложение для тестирования ядра и класса C++. Это приложение будет делать что-то простое, например создавать экземпляр класса GPIO, экспортировать GPIO и затем записывать значения.

(Как это связано с вашим вопросом PWM, неясно, но вы, кажется, смешиваете две разные области драйвера ядра)

Вы можете сделать что-то вроде приведенного ниже кода через sysfs в соответствии с документация gpio ядра Linux. Вам, конечно, нужно убедиться, что ваше аппаратное gpio поддерживается ядром Linux.

gpio.h

#ifndef FOOBAR_GENERALPURPOSEIO_H
#define FOOBAR_GENERALPURPOSEIO_H

namespace Foobar
{
   class FOOBAR_EXPORT GeneralPurposeIO
   {
       public:

           enum Direction {
               Input,
               Output
           };

           explicit GeneralPurposeIO(quint32 gpioNumber = 0);
           ~GeneralPurposeIO();

           int gpioExport();
           int gpioUnexport();
           bool isGpioExported();

           quint32 gpioNumber() const;
           void setGpioNumber(quint32 gpioNumber);

           Direction direction() const;
           int setDirection(Direction direction);

           qint32 value() const;
           int setValue(qint32 value);

       private:

           class Private;
           Private *const d;
   };
}

#endif // FOOBAR_GENERALPURPOSEIO_H

gpio.cpp

#include "generalpurposeio.h"

#include <QtCore/QDebug>
#include <QtCore/QFile>

using namespace Foobar;

class GeneralPurposeIO::Private
{
   public:
       Private()
       {
       }

       ~Private()
       {
       }

       static const QString gpioExportFilePath;
       static const QString gpioUnexportFilePath;
       static const QString gpioDirectionFilePath;
       static const QString gpioValueFilePath;
       static const QString gpioFilePath;

       quint32 gpioNumber;
};

const QString GeneralPurposeIO::Private::gpioExportFilePath = "/sys/class/gpio/export";
const QString GeneralPurposeIO::Private::gpioUnexportFilePath = "/sys/class/gpio/unexport";
const QString GeneralPurposeIO::Private::gpioDirectionFilePath = "/sys/class/gpio/gpio%1/direction";
const QString GeneralPurposeIO::Private::gpioValueFilePath = "/sys/class/gpio/gpio%1/value";
const QString GeneralPurposeIO::Private::gpioFilePath = "/sys/class/gpio/gpio%1";

GeneralPurposeIO::GeneralPurposeIO(quint32 gpioNumber)
   : d(new Private)
{
   d->gpioNumber = gpioNumber;
}

GeneralPurposeIO::~GeneralPurposeIO()
{
}

/*
* Exports the desired gpio number.
*
* Note: Unfortunately, it is not possible to just call this method "export"
* since that is a reserved keyword in C++. Systematically the unexport method
* cannot be called "unexport" either for consistency.
*/

int GeneralPurposeIO::gpioExport()
{
   if (isGpioExported()) {
       // TODO: Proper error mutator mechanism for storing different error
       // enumeration values internally that can be requested by the API user

       qDebug() << "Cannot export the gpio pin since it is already exported:" << d->gpioNumber;
       return -1;
   }

   QFile gpioExportFile(d->gpioExportFilePath);
   if (!gpioExportFile.open(QIODevice::Append)) {
       qDebug() << "Cannot open the gpio export file:" << d->gpioExportFilePath;
       return -1;
   }

   /*
    * Seek to begining of the file
    */

   gpioExportFile.seek(0);

   /*
    * Write our value of "gpioPinNumber" to the file
    */

   if (gpioExportFile.write(QByteArray::number(d->gpioNumber)) == -1) {
       qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioExportFilePath;
       gpioExportFile.close();

       return -1;
   }

   gpioExportFile.close();

   return 0;
}

int GeneralPurposeIO::gpioUnexport()
{
   if (!isGpioExported()) {
       // TODO: Proper error mutator mechanism for storing different error
       // enumeration values internally that can be requested by the API user

       qDebug() << "Cannot unexport the gpio pin since it is not exported yet:" << d->gpioNumber;
       return -1;
   }

   QFile gpioUnexportFile(d->gpioUnexportFilePath);
   if (!gpioUnexportFile.open(QIODevice::Append)) {
       qDebug() << "Cannot open the gpio export file:" << d->gpioUnexportFilePath;
       return -1;
   }

   /*
    * Seek to begining of the file
    */

   gpioUnexportFile.seek(0);

   /*
    * Write our value of "gpioPinNumber" to the file
    */

   if (gpioUnexportFile.write(QByteArray::number(d->gpioNumber)) == -1) {
       qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioUnexportFilePath;
       gpioUnexportFile.close();

       return -1;
   }

   gpioUnexportFile.close();

   return 0;
}

bool GeneralPurposeIO::isGpioExported()
{
   if (!QFile(d->gpioFilePath.arg(d->gpioNumber)).exists()) {
       return false;
   }

   return true;
}

quint32 GeneralPurposeIO::gpioNumber() const
{
   return d->gpioNumber;
}

void GeneralPurposeIO::setGpioNumber(quint32 gpioNumber)
{
   d->gpioNumber = gpioNumber;
}

GeneralPurposeIO::Direction GeneralPurposeIO::direction() const
{
   // TODO: Implement me

   return GeneralPurposeIO::Output;
}

int GeneralPurposeIO::setDirection(Direction direction)
{
   if (!isGpioExported()) {
       if (gpioExport() == -1) {
           return -1;
       }
   }

   /*
    * Set the direction
    */

   QFile gpioDirectionFile(d->gpioDirectionFilePath.arg(d->gpioNumber));

   if (!gpioDirectionFile.open(QIODevice::ReadWrite)) {
       qDebug() << "Cannot open the relevant gpio direction file:" << d->gpioDirectionFilePath;
       return -1;
   }

   int retval = 0;

   /*
    * Seek to begining of the file
    */

   gpioDirectionFile.seek(0);

   switch (direction) {

   case Output:
       if (gpioDirectionFile.write("high") == -1) {
           qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioDirectionFilePath;
           retval = -1;
       }

       break;

   case Input:
       if (gpioDirectionFile.write("low") == -1) {
           qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioDirectionFilePath;
           retval = -1;
       }

       break;

   default:

       break;

   }

   gpioDirectionFile.close();

   return retval;
}

qint32 GeneralPurposeIO::value() const
{
   // TODO: Implement me

   return 0;
}

int GeneralPurposeIO::setValue(qint32 value)
{
   if (direction() != GeneralPurposeIO::Output) {
       qDebug() << "Cannot set the value for an input gpio pin:" << d->gpioNumber;
       return -1;
   }

   /*
    * Set the value
    */

   QFile gpioValueFile(d->gpioValueFilePath.arg(d->gpioNumber));
   if (!gpioValueFile.open(QIODevice::ReadWrite)) {
       qDebug() << "Cannot open the relevant gpio value file:" << d->gpioValueFilePath.arg(d->gpioNumber);
       gpioValueFile.close();
       return -1;
   }

   /*
    * Seek to begining of the file
    */

   gpioValueFile.seek(0);

   if (gpioValueFile.write(QByteArray::number(value)) == -1) {
       qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioValueFilePath.arg(d->gpioNumber);
       gpioValueFile.close();
       return -1;
   }

   gpioValueFile.close();

   return 0;
}
person lpapp    schedule 22.12.2013
comment
Я видел образцы вашей программы общего назначения и сам пытался написать для нее код на C. Это доставляло некоторые неудобства, такие как невозможность создания определенных файлов/папок в /sys/class/gpio/, даже если они запускались пользователем root. Также я хочу подать ШИМ-сигнал с помощью этих GPIO, поэтому я спросил, существует ли какой-либо другой метод, который облегчил бы это. - person VedVals; 22.12.2013
comment
Прежде всего, вы не создаете файлы, но класс сделает это за вас. Что еще более важно, вы не указали никаких конкретных ошибок. Я действительно понятия не имею, почему вы продолжаете смешивать эти два понятия. Тахометр и т. д. отличаются от переключаемого GPIO. Во всяком случае, этот класс отлично работает для меня и некоторых других людей. Вероятно, у вас могут быть большие проблемы с ядром Linux. - person lpapp; 22.12.2013
comment
Цитата: Неясно, как это связано с вашим вопросом о ШИМ, но вы, кажется, смешиваете две разные области драйвера ядра... Вы можете реализовать ШИМ, переключая высокий/низкий уровень GPIO в фиксированный период. . Я думаю, вы пропустили эту идею. Это загрузит систему и потребует хорошей реакции системы на пространство пользователя. - person artless noise; 03.01.2014
comment
@artlessnoise: нет, не видел. Однако вы, похоже, упустили из виду, что в конце концов я дал полную реализацию gpio на C++ для sysfs, так что эта часть была сделана некоторое время назад. Если вам нужно что-то еще, это не связано с GPIO, но, поскольку вопрос ОП был не совсем ясен, это лучший ответ, который я мог дать. - person lpapp; 03.01.2014
comment
Я попробовал код, но он по-прежнему не может открывать/записывать файлы. Я не могу получить доступ к каким-либо файлам даже при запуске кода от имени пользователя root. Также я не понял, что такое FOOBAR_EXPORT. Я даже пытался изменить разрешения для каждого файла в /sys/devices/virtual/gpio/. - person VedVals; 25.01.2014
comment
@VedVals: возможно, вы делаете что-то не так. Если файл доступен, его необходимо открыть. Другого пути к этому нет. :) - person lpapp; 25.01.2014
comment
@VedVals: также это не /sys/devices/virtual/gpio, а /sys/class/gpio. Вы решили это с тех пор? - person lpapp; 16.02.2014
comment
@LaszloPapp /sys/slass/gpio/<whatever> — это символическая ссылка на /sys/devices/virtual/gpio/<same whatever>, за исключением файлов export и unexport. Я могу получить к нему доступ из чистого кода C, но не из Qt - person VedVals; 17.02.2014
comment
@VedVals: вы должны использовать тот, который я упомянул (/sys/class/gpio). Я не могу сказать, в чем проблема, потому что вы еще не поделились ни соответствующим кодом, ни разрешениями, ни ошибкой и т. д. - person lpapp; 17.02.2014