вторник, 31 января 2012 г.

Работа с Linux GPIO из User Space

Ядро Linux предоставляет для доступа к входам/выходам GPIO (general purpose input/output) специальный интерфейс, который позволяет работать с GPIO как с символьными устройствами.

Прежде всего, сошлюсь на полезный пост Linux - Accessing GPIO from User Space и дополню его некоторыми комментариями, а также описанием работы с прерываниями.

Получение значения порта.

Итак, мы экспортировали GPIO

int exportfd;
exportfd = open("/sys/class/gpio/export", O_WRONLY);
write(exportfd, "149", 4);
close(exportfd);

задали направление

int directionfd;
directionfd = open("/sys/class/gpio/gpio149/direction", O_RDWR);
write(directionfd, "in", 4);
close(directionfd);

и открыли файл со значением выхода для чтения (как производить запись, полностью описано в указанной статье)

int valuefd;
valuefd = open("/sys/class/gpio/gpio149/value", O_RDWR);

Файл valuefd является символьным устройством, в котором в строковом виде содержится значение входного порта. Чтобы прочитать текущее значение, необходимо установить смещение в файле, и прочитать строку со значением.

char buffer[2];
lseek(valuefd , 0, SEEK_SET);
r = read(valuefd, buffer, 2);

Строка значения обычно имеет формат "0\n" или "1\n".

Обработка прерываний.

Получение прерываний от GPIO в общем случае асинхронно. В User Space невозможно стандартным образом установить непосредственный обработчик прерывания. Но для GPIO возможно получать информацию о том, произошло ли прерывание или нет, и если произошло, то произвести чтение значения порта и осуществить необходимые вам действия. Для этого используется системный вызов poll(), в этом случае работа ведется, как с обычным символьным устройством (с помощью poll() можно получать и информацию о поступлении символов на консоль, и другие подобные задачи).

Необходимо учесть, что поступление прерывания по фронту не гарантирует, что ко времени чтения значения с порта с помощью read() оно не изменится, т.к. может пройти произвольное время. Linux не является операционной системой реального времени.

Для получения прерываний необходимо настроить фронт, на который сработает прерывание: "none", "rising", "falling", or "both".

int edgefd;
edgefd = open("/sys/class/gpio/gpio149/edge", O_WRONLY);
r = write(edgefd, "rising", 7);
close(edgefd);

Теперь мы открываем файл значения порта и можем ловить сигналы с помощью вызова poll():

#include <poll.h>
...
int valuefd;
struct pollfd poll_fd;
int r;
int timeout_ms = 1000;

valuefd = open("/sys/class/gpio/gpio149/value", O_RDWR);
    
poll_fd.fd = gpio_fd;
poll_fd.events = POLLPRI;

r = poll(&poll_fd, 1, timeout_ms);      

if (r < 0) 
{
    printf("poll() failed!\n");

}else if (poll_fd.revents & POLLPRI) 
{
    printf("it was interrupt event on gpio\n");
}

На этом всё. Описание интерфейса gpiolib можно посмотреть на kernel.org.

Комментариев нет:

Отправить комментарий