You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
9.5 KiB
345 lines
9.5 KiB
/*
|
|
* Copyright (c) 2022, Erich Styger
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
/* see
|
|
* https://blog.lxsang.me/post/id/33
|
|
* https://embeddedbits.org/new-linux-kernel-gpio-user-space-interface/
|
|
* https://www.ics.com/blog/gpio-programming-exploring-libgpiod-library
|
|
* https://lloydrochester.com/post/hardware/libgpiod-intro-rpi/
|
|
* */
|
|
|
|
#include "gpio.h"
|
|
|
|
/* include API header for the new interface */
|
|
#include <linux/gpio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <getopt.h>
|
|
#include <stdlib.h>
|
|
#include <sys/poll.h>
|
|
|
|
#define GPIO_DEV_NAME "/dev/gpiochip0"
|
|
|
|
/* HAT v7 requires pull-ups enabled. In case if using older headers, define locally the new flags */
|
|
#ifndef GPIOHANDLE_REQUEST_BIAS_PULL_UP /* supported with latest libs: if not defined, maybe using older header files? Let's assume the library is a recent one */
|
|
#define GPIOHANDLE_REQUEST_BIAS_PULL_UP (1UL << 5)
|
|
#define GPIOHANDLE_REQUEST_BIAS_PULL_DOWN (1UL << 6)
|
|
#define GPIOHANDLE_REQUEST_BIAS_DISABLE (1UL << 7)
|
|
#endif
|
|
|
|
typedef struct {
|
|
struct gpiohandle_request rq; /* request data structure */
|
|
struct gpiohandle_data data;
|
|
} Gpio_Desc_t;
|
|
|
|
static Gpio_Desc_t yellowLED, redLED, greenLED;
|
|
#if PL_HAT_VERSION<=6
|
|
static Gpio_Desc_t blueLED;
|
|
#endif
|
|
static Gpio_Desc_t upBTN, downBTN, leftBTN, rightBTN, centerBTN;
|
|
|
|
static void gpio_list(const char *dev_name) {
|
|
struct gpiochip_info info;
|
|
struct gpioline_info line_info;
|
|
int fd, ret;
|
|
|
|
fd = open(dev_name, O_RDONLY);
|
|
if (fd < 0) {
|
|
printf("Unabled to open %s: %s", dev_name, strerror(errno));
|
|
return;
|
|
}
|
|
ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);
|
|
if (ret == -1) {
|
|
printf("Unable to get chip info from ioctl: %s", strerror(errno));
|
|
close(fd);
|
|
return;
|
|
}
|
|
printf("Chip name: %s\n", info.name);
|
|
printf("Chip label: %s\n", info.label);
|
|
printf("Number of lines: %d\n", info.lines);
|
|
for (int i = 0; i < info.lines; i++) {
|
|
line_info.line_offset = i;
|
|
ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line_info);
|
|
if (ret == -1) {
|
|
printf("Unable to get line info from offset %d: %s", i, strerror(errno));
|
|
} else {
|
|
printf("offset: %d, name: %s, consumer: %s. Flags:\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\n",
|
|
i,
|
|
line_info.name,
|
|
line_info.consumer,
|
|
(line_info.flags & GPIOLINE_FLAG_IS_OUT) ? "OUTPUT" : "INPUT",
|
|
(line_info.flags & GPIOLINE_FLAG_ACTIVE_LOW) ? "ACTIVE_LOW" : "ACTIVE_HIGHT",
|
|
(line_info.flags & GPIOLINE_FLAG_OPEN_DRAIN) ? "OPEN_DRAIN" : "...",
|
|
(line_info.flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) ? "PULL-UP" : "...",
|
|
(line_info.flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) ? "PULL-DOWN" : "...",
|
|
(line_info.flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) ? "PULL-DISABLE" : "...",
|
|
(line_info.flags & GPIOLINE_FLAG_OPEN_SOURCE) ? "OPENSOURCE" : "...",
|
|
(line_info.flags & GPIOLINE_FLAG_KERNEL) ? "KERNEL" : "");
|
|
}
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
static void gpio_write(const char *dev_name, int offset, uint8_t value) {
|
|
struct gpiohandle_request rq;
|
|
struct gpiohandle_data data;
|
|
int fd, ret;
|
|
|
|
printf("Write value %d to GPIO at offset %d (OUTPUT mode) on chip %s\n", value, offset, dev_name);
|
|
fd = open(dev_name, O_RDONLY);
|
|
if (fd < 0) {
|
|
printf("Unabled to open %s: %s", dev_name, strerror(errno));
|
|
return;
|
|
}
|
|
rq.lineoffsets[0] = offset;
|
|
rq.flags = GPIOHANDLE_REQUEST_OUTPUT;
|
|
rq.lines = 1;
|
|
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &rq);
|
|
close(fd);
|
|
if (ret == -1) {
|
|
printf("Unable to line handle from ioctl : %s", strerror(errno));
|
|
return;
|
|
}
|
|
data.values[0] = value;
|
|
ret = ioctl(rq.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
|
|
if (ret == -1) {
|
|
printf("Unable to set line value using ioctl : %s", strerror(errno));
|
|
} else {
|
|
usleep(2000000); /* wait 2 sec */
|
|
}
|
|
close(rq.fd);
|
|
}
|
|
|
|
static void writePin(Gpio_Desc_t *io, uint8_t value) {
|
|
int ret;
|
|
|
|
io->data.values[0] = value;
|
|
ret = ioctl(io->rq.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &io->data);
|
|
if (ret == -1) {
|
|
printf("Unable to set line value using ioctl : %s", strerror(errno));
|
|
}
|
|
}
|
|
|
|
static bool readPin(Gpio_Desc_t *io) {
|
|
int ret;
|
|
bool result = false;
|
|
|
|
ret = ioctl(io->rq.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &io->data);
|
|
if (ret == -1) {
|
|
printf("Unable to get line value using ioctl : %s", strerror(errno));
|
|
result = false;
|
|
} else {
|
|
result = io->data.values[0]!=0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int gpio_read(const char *dev_name, int offset)
|
|
{
|
|
struct gpiohandle_request rq;
|
|
struct gpiohandle_data data;
|
|
int fd, ret;
|
|
int result = -1;
|
|
|
|
fd = open(dev_name, O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
printf("Unabled to open %s: %s", dev_name, strerror(errno));
|
|
return -1;
|
|
}
|
|
rq.lineoffsets[0] = offset;
|
|
rq.flags = GPIOHANDLE_REQUEST_INPUT;
|
|
rq.lines = 1;
|
|
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &rq);
|
|
close(fd);
|
|
if (ret == -1)
|
|
{
|
|
printf("Unable to get line handle from ioctl : %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
ret = ioctl(rq.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
|
|
if (ret == -1)
|
|
{
|
|
printf("Unable to get line value using ioctl : %s", strerror(errno));
|
|
result = -1;
|
|
}
|
|
else
|
|
{
|
|
printf("Value of GPIO at offset %d (INPUT mode) on chip %s: %d\n", offset, dev_name, data.values[0]);
|
|
result = data.values[0];
|
|
}
|
|
close(rq.fd);
|
|
return result;
|
|
}
|
|
|
|
static void gpio_poll(const char *dev_name, int offset)
|
|
{
|
|
struct gpioevent_request rq;
|
|
struct pollfd pfd;
|
|
int fd, ret;
|
|
|
|
fd = open(dev_name, O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
printf("Unabled to open %s: %s", dev_name, strerror(errno));
|
|
return;
|
|
}
|
|
rq.lineoffset = offset;
|
|
rq.eventflags = GPIOEVENT_EVENT_RISING_EDGE;
|
|
ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
|
|
close(fd);
|
|
if (ret == -1)
|
|
{
|
|
printf("Unable to get line event from ioctl : %s", strerror(errno));
|
|
close(fd);
|
|
return;
|
|
}
|
|
pfd.fd = rq.fd;
|
|
pfd.events = POLLIN;
|
|
ret = poll(&pfd, 1, -1);
|
|
if (ret == -1)
|
|
{
|
|
printf("Error while polling event from GPIO: %s", strerror(errno));
|
|
}
|
|
else if (pfd.revents & POLLIN)
|
|
{
|
|
printf("Rising edge event on GPIO offset: %d, of %s\n", offset, dev_name);
|
|
}
|
|
close(rq.fd);
|
|
}
|
|
|
|
bool Gpio_ReadPin(Gpio_Pins_e pin) {
|
|
bool val;
|
|
//return gpio_read(GPIO_DEV_NAME, pin);
|
|
switch(pin) {
|
|
case Gpio_Pins_BTN_UP_BCM:
|
|
val = readPin(&upBTN);
|
|
break;
|
|
case Gpio_Pins_BTN_DOWN_BCM:
|
|
val = readPin(&downBTN);
|
|
break;
|
|
case Gpio_Pins_BTN_LEFT_BCM:
|
|
val = readPin(&leftBTN);
|
|
break;
|
|
case Gpio_Pins_BTN_RIGHT_BCM:
|
|
val = readPin(&rightBTN);
|
|
break;
|
|
case Gpio_Pins_BTN_CENTER_BCM:
|
|
val = readPin(¢erBTN);
|
|
break;
|
|
default:
|
|
val = false;
|
|
break;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
void Gpio_WritePin(Gpio_Pins_e pin, bool val) {
|
|
switch(pin) {
|
|
#if PL_HAT_VERSION<=6
|
|
case Gpio_Pins_LED_BLUE_PIN_BCM:
|
|
writePin(&blueLED, val);
|
|
break;
|
|
#endif
|
|
case Gpio_Pins_LED_RED_PIN_BCM:
|
|
writePin(&redLED, val);
|
|
break;
|
|
case Gpio_Pins_LED_GREEN_PIN_BCM:
|
|
writePin(&greenLED, val);
|
|
break;
|
|
case Gpio_Pins_LED_YELLOW_PIN_BCM:
|
|
writePin(&yellowLED, val);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int Gpio_Deinit(void) {
|
|
#if PL_HAT_VERSION<=6
|
|
close(blueLED.rq.fd);
|
|
#endif
|
|
close(redLED.rq.fd);
|
|
close(greenLED.rq.fd);
|
|
close(yellowLED.rq.fd);
|
|
|
|
close(upBTN.rq.fd);
|
|
close(downBTN.rq.fd);
|
|
close(leftBTN.rq.fd);
|
|
close(rightBTN.rq.fd);
|
|
close(centerBTN.rq.fd);
|
|
return 0;
|
|
}
|
|
|
|
static int RequestPin(Gpio_Desc_t *desc, Gpio_Pins_e pin, int lineRequestFlags, const char *label) {
|
|
int fd, ret;
|
|
|
|
fd = open(GPIO_DEV_NAME, O_RDONLY);
|
|
if (fd < 0) {
|
|
printf("Unabled to open %s: %s", GPIO_DEV_NAME, strerror(errno));
|
|
return -1;
|
|
}
|
|
desc->rq.lineoffsets[0] = pin;
|
|
desc->rq.flags = lineRequestFlags;
|
|
memcpy(desc->rq.default_values, &desc->data, sizeof(desc->rq.default_values));
|
|
strcpy(desc->rq.consumer_label, label);
|
|
desc->rq.lines = 1;
|
|
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &desc->rq);
|
|
close(fd);
|
|
if (ret == -1) {
|
|
printf("Unable to get line handle from ioctl : %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Gpio_Init(void) {
|
|
#if GPIO_HAT_VERSION<=6
|
|
if (RequestPin(&blueLED, Gpio_Pins_LED_BLUE_PIN_BCM, GPIOHANDLE_REQUEST_OUTPUT, "led_blue")!=0) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
if (RequestPin(&redLED, Gpio_Pins_LED_RED_PIN_BCM, GPIOHANDLE_REQUEST_OUTPUT, "led_red")!=0) {
|
|
return -1;
|
|
}
|
|
if (RequestPin(&greenLED, Gpio_Pins_LED_GREEN_PIN_BCM, GPIOHANDLE_REQUEST_OUTPUT, "led_green")!=0) {
|
|
return -1;
|
|
}
|
|
|
|
if (RequestPin(&yellowLED, Gpio_Pins_LED_YELLOW_PIN_BCM, GPIOHANDLE_REQUEST_OUTPUT, "led_yellow")!=0) {
|
|
return -1;
|
|
}
|
|
|
|
int inputPinFlags = GPIOHANDLE_REQUEST_INPUT;
|
|
#if PL_HAT_VERSION>6
|
|
inputPinFlags |= GPIOHANDLE_REQUEST_BIAS_PULL_UP;
|
|
#endif
|
|
if (RequestPin(&upBTN, Gpio_Pins_BTN_UP_BCM, inputPinFlags, "button_up")!=0) {
|
|
return -1;
|
|
}
|
|
if (RequestPin(&downBTN, Gpio_Pins_BTN_DOWN_BCM, inputPinFlags, "button_down")!=0) {
|
|
return -1;
|
|
}
|
|
if (RequestPin(&leftBTN, Gpio_Pins_BTN_LEFT_BCM, inputPinFlags, "button_left")!=0) {
|
|
return -1;
|
|
}
|
|
if (RequestPin(&rightBTN, Gpio_Pins_BTN_RIGHT_BCM, inputPinFlags, "button_right")!=0) {
|
|
return -1;
|
|
}
|
|
if (RequestPin(¢erBTN, Gpio_Pins_BTN_CENTER_BCM, inputPinFlags, "button_center")!=0) {
|
|
return -1;
|
|
}
|
|
#if 0 /* testing only */
|
|
gpio_list(GPIO_DEV_NAME);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|