Я пытаюсь использовать GPIO Raspberry Pi через sysfs, используя язык C, но я продолжаю получать fopen: Permission denied при попытке изменить направление линии GPIO.
Проблема возникает при попытке открыть файл "/sys/class/gpio/gpio18/direction".
Примечания:
Обычно мне нужно вставить булавку, которую я хочу использовать в "/sys/class/gpio/export".
А затем мне нужно задать его направление (выход/ввод), написав 1 или 0 в "/sys/class/gpio/gpio18/direction".
Чтобы добиться этого программно, я использую следующий код
#include "io.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc, char const* argv[]) {
// export line
FILE* p_gpio_line;
if ((p_gpio_line = fopen("/sys/class/gpio/export", "w")) == NULL) {
printf("Cannot open export file.\n");
perror("fopen");
exit(1);
}
rewind(p_gpio_line);
fwrite("18", sizeof(char), 2, p_gpio_line);
fclose(p_gpio_line);
// set direction
FILE* p_gpio_direction;
if ((p_gpio_direction = fopen("/sys/class/gpio/gpio18/direction", "r+")) == NULL) {
printf("Cannot open direction file.\n");
perror("fopen");
exit(1);
}
return 0;
}
В первый раз, когда он выполняется, я получаю
Cannot open direction file.
fopen: Permission denied
Второй раз работает нормально, потому что линия GPIO экспортируется до выполнения программы (из первого исполнения)
Он также отлично работает, если я вручную экспортирую линию GPIO из терминала.
Используйте libgpiod и его ресурсы, не используйте sysfs для GPIO.





Мой комментарий о perror() остается в силе, но действительно в вашем примере fopen() возвращает EACCES («Отказано в доступе»).
Группа gpio и разрешения для /sys/class/gpio специфичны для дистрибутива Raspberry Pi. Они устанавливаются правилом udev в /etc/udev/rules.d/99-com.rules:
SUBSYSTEM= = "gpio", ACTION= = "add", PROGRAM = "/bin/sh -c 'chgrp -R gpio /sys%p && chmod -R g=u /sys%p'"
Правило Udev применяется асинхронно после появления файла direction. Вы пытаетесь открыть файл, пока разрешения еще не установлены.
Я бы предложил использовать интерфейс GPIO символьного устройства с libgpiod вместо устаревшего интерфейса sysfs.
Если вы все еще хотите использовать интерфейс sysfs, простое решение — повторять попытку fopen() до тех пор, пока она не увенчается успехом (или с ошибкой, отличной от EACCES, или тайм-аутом). Например:
// Open timeout, ms
#define DIR_OPEN_TIMEOUT_MS 3000
// Delay between retries, ms
#define DIR_POLL_DELAY_MS 100
...
// set direction
FILE* p_gpio_direction;
unsigned int retries_left = DIR_OPEN_TIMEOUT_MS / DIR_POLL_DELAY_MS;
while (1)
{
p_gpio_direction = fopen("/sys/class/gpio/gpio18/direction", "r+");
if (p_gpio_direction)
break;
if ((errno != EACCES) || (retries_left-- == 0)) {
printf("Cannot open direction file: %m\n");
exit(1);
}
const struct timespec rqt = {
.tv_sec = DIR_POLL_DELAY_MS / 1000,
.tv_nsec = 1000000L * (DIR_POLL_DELAY_MS % 1000)
};
if (nanosleep(&rqt, NULL) == -1)
perror("nanosleep()");
}
...
Это объясняет это, спасибо, один вопрос: если я хочу по-прежнему использовать sysfs, как я могу дождаться применения правила перед доступом к файлу direction?
Это не лучшее направление для решения проблемы. Нам нужно включить libgpiod для GPIO, sysfs использовать нельзя.
Не используйте
perror(), если только сразу после неудачной функции.printf()может изменитьсяerrno, поэтомуperror()сообщение не имеет смысла.