Мой сын внедряет сервер на Raspberry Pi, который позволяет управлять GPIO через сетевое соединение. Он обнаружил странное поведение, которое сначала показалось ошибкой (но см. Ответ ниже).
Во-первых, используемая ОС - это Raspbian, версия Debian Linux. Он использует стандартный системный файл для управления портами GPIO.
Начнем с вывода GPIO, например вывод 17, в неэкспортированном состоянии. Например,
echo "17" > /sys/class/gpio/unexport
Теперь, если серверу предлагается включить вывод 17, он выполняет следующие действия:
- Открывает
/sys/class/gpio/export
, записывает в него "17" и закрывает файл экспорта. - Откройте
/sys/class/gpio/gpio17/direction
файл для чтения, исследует его, чтобы увидеть, установлен ли он как ввод или вывод. Закрывает файл. Затем, если необходимо, повторно открывает файл для записи и записывает «out» в файл, чтобы установить вывод как выходной вывод, и закрывает файл направления.
На этом этапе мы должны иметь возможность открыть /sys/class/gpio/gpio17/value
для записи и записать в него «1».
Однако разрешения для файла /sys/class/gpio/gpio17/value
существуют, но разрешения группы доступны только для чтения. Если мы перейдем в «спящий режим», чтобы подождать долю секунды, разрешения изменятся, так что разрешение группы будет иметь разрешения на запись.
Я ожидал, что ОС не должна возвращаться из записи в direction
файл, пока она не установит права доступа к файлу значений правильно.
Почему это происходит? Похоже на ошибку. Куда мне сообщить об этом (подробнее ...)? См. Ответ ниже.
Ниже приведены соответствующие фрагменты кода. Код был отредактирован и немного перефразирован, но по сути это то, что используется. (Имейте в виду, что это код ученика 12-го класса, пытающегося изучить концепции C ++ и Unix):
class GpioFileOut
{
private:
const string m_fName;
fstream m_fs;
public:
GpioFileOut(const string& sName)
: m_fName(("/sys/class/gpio/" + sName).c_str())
{
m_fs.open(m_fName.c_str());
if (m_fs.fail())
{
cout<<"ERROR: attempted to open " << m_fName << " but failed" << endl << endl;
}
else
{
cout << m_fName << " opened" << endl;
}
}
~GpioFileOut()
{
m_fs.close();
cout << m_fName << " closed" << endl << endl;
}
void reOpen()
{
m_fs.close();
m_fs.open(m_fName);
if (m_fs.fail())
{
cout<<"ERROR: attempted to re-open " << m_fName << " but failed" << endl << endl;
}
else
{
cout << m_fName << " re-opened" << endl;
}
}
GpioFileOut& operator<<(const string &s)
{
m_fs << s << endl;
cout << s << " sent to " << m_fName << endl;
return *this;
}
GpioFileOut& operator<<(int n)
{
return *this << to_string(n); //ostringstream
}
bool fail()
{
return m_fs.fail();
}
};
class GpioFileIn
{
private:
ifstream m_fs;
string m_fName;
public:
GpioFileIn(const string& sName)
: m_fs( ("/sys/class/gpio/" + sName).c_str())
, m_fName(("/sys/class/gpio/" + sName).c_str())
{
if (m_fs <= 0 || m_fs.fail())
{
cout<<"ERROR: attempted to open " << m_fName << " but failed" << endl;
}
else
{
cout << m_fName << " opened" << endl;
}
}
~GpioFileIn()
{
m_fs.close();
cout << m_fName << " closed" << endl << endl;
}
void reOpen()
{
m_fs.close();
m_fs.open(m_fName);
if (m_fs <= 0 || m_fs.fail())
{
cout<<"ERROR: attempted to re-open " << m_fName << " but failed" << endl;
}
else
{
cout << m_fName << " re-opened" << endl;
}
}
GpioFileIn& operator>>(string &s)
{
m_fs >> s;
cout << s << " read from " << m_fName << endl;
return *this;
}
bool fail()
{
return m_fs.fail();
}
};
class Gpio
{
public:
static const bool OUT = true;
static const bool IN = false;
static const bool ON = true;
static const bool OFF = false;
static bool setPinDirection(const int pinId, const bool direction)
{
GpioFileOut dirFOut(("gpio" + to_string(pinId) + "/direction").c_str());
if (dirFOut.fail())
{
if (!openPin(pinId))
{
cout << "ERROR! Pin direction not set: Failed to export pin" << endl;
return false;
}
dirFOut.reOpen();
}
dirFOut << (direction == OUT ? "out" : "in");
}
static bool setPinValue(const int pinId, const bool pinValue)
{
string s;
{
GpioFileIn dirFIn(("gpio" + to_string(pinId) + "/direction").c_str());
if (dirFIn.fail())
{
if (!openPin(pinId))
{
cout << "ERROR! Pin not set: Failed to export pin"<<endl;
return false;
}
dirFIn.reOpen();
}
dirFIn >> s;
}
if (strncmp(s.c_str(), "out", 3) == 0)
{
struct stat _stat;
int nTries = 0;
string fname("/sys/class/gpio/gpio"+to_string(pinId)+"/value");
for(;;)
{
if (stat(fname.c_str(), &_stat) == 0)
{
cout << _stat.st_mode << endl;
if (_stat.st_mode & 020 )
break;
}
else
{
cout << "stat failed. (Did the pin get exported successfully?)" << endl;
}
cout << "sleeping until value file appears with correct permissions." << endl;
if (++nTries > 10)
{
cout << "giving up!";
return false;
}
usleep(100*1000);
};
GpioFileOut(("gpio" + to_string(pinId) + "/value").c_str()) << pinValue;
return true;
}
return false;
}
static bool openPin(const int pinId)
{
GpioFileOut fOut("export");
if (fOut.fail())
return false;
fOut << to_string(pinId);
return true;
}
}
int main()
{
Gpio::openPin(17);
Gpio::setPinDirection(17, Gpio::OUT)
Gpio::setPinValue(17, Gpio::ON);
}
Ключевой момент заключается в следующем: без for(;;)
цикла, который stat
является файлом, выполнение завершается неудачно, и мы можем видеть изменение разрешений для файла в течение 100 мс.