Я хочу использовать Linux GPIO API для получения сигнала при изменении уровня GPIO.
Поскольку poll()
получит событие POLLPRI
, я предположил, что могу просто зарегистрировать обработчик дляSIGURG
и используйте fcntl()
, чтобы заставить fd, связанный с GPIO, отправлять сигнал при возникновении события.
На Raspberry PI с подключенными контактами GPIO 13 и 26 следующий код должен напечатать текущий уровеньс последующим «повышением» или «падением» каждую секунду:
#include <fcntl.h>
#include <linux/gpio.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define PIN_IN (13)
#define PIN_OUT (26)
static int fd_in; /* to be read in the signal handler */
static int setup_pin_int(int fd, int pin, int mode, int flank) {
struct gpioevent_request req = {
.lineoffset = pin,
.handleflags = mode,
.eventflags = flank
};
int res = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &req);
if (res < 0) {
return res;
}
return req.fd;
}
static int setup_pin_out(int fd, int pin, int mode) {
struct gpiohandle_request req = {
.lineoffsets[0] = pin,
.flags = mode,
.lines = 1
};
int res = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (res < 0) {
return res;
}
return req.fd;
}
static void pin_set(int fd, uint8_t val) {
ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &val);
}
static void sigurg_handler(void) {
struct gpioevent_data event;
read(fd_in, &event, sizeof(event));
if (event.id == GPIOEVENT_EVENT_RISING_EDGE) {
puts("rising");
}
if (event.id == GPIOEVENT_EVENT_FALLING_EDGE) {
puts("falling");
}
}
static void signal_handler(int signal) {
printf("got signal: %x\n", signal);
if (signal == SIGURG) {
sigurg_handler();
}
}
static void register_signal(int signal, void (*handler)(int)) {
struct sigaction sa = {
.sa_handler = handler
};
sigaction(signal, &sa, NULL);
}
static void _do_poll(int fd) {
struct pollfd pfd = {
.fd = fd,
.events = POLLIN | POLLPRI
};
if (poll(&pfd, 1, 1000) > 0) {
sigurg_handler();
}
}
int main(void) {
int pins[2];
int fd = open("/dev/gpiochip0", O_RDWR);
pins[0] = setup_pin_int(fd, PIN_IN, GPIOHANDLE_REQUEST_INPUT, GPIOEVENT_REQUEST_BOTH_EDGES);
pins[1] = setup_pin_out(fd, PIN_OUT, GPIOHANDLE_REQUEST_OUTPUT);
fd_in = pins[0];
/* register signal handler */
register_signal(SIGIO, signal_handler);
register_signal(SIGURG, signal_handler);
/* make the fd generate signals */
fcntl(fd_in, F_SETOWN, getpid());
fcntl(fd_in, F_SETFL, O_NONBLOCK | O_ASYNC);
/* toggle the output pin each second */
int state;
while (1) {
state = !state;
sleep(1);
printf("set %d\n", state);
pin_set(pins[1], state);
/* poll() is working on the fd */
// _do_poll(fd_in);
}
close(fd);
return 0;
}
Однако, это происходит сейчас.
Я добавил функцию _do_poll()
, которая вручную опрашивает GPIO fd, чтобы подтвердить, чтоPOLLPRI
событие генерируется.
Но как мне превратить это в сигнал? Другим решением было бы fork()
и использование poll()
в дочернем элементе, посылая надлежащий сигнал родителю при возврате poll()
, но я бы хотел избежать этих накладных расходов.