how to write and read back GPIO pin states with lgpio

Aug 04, 2024 Last reply: 1 year ago 3 Replies

Hi,



I used to use the sysfs interface to the GPIO pins (/sys/class/gpio) but I understand that is deprecated nowadays. So I tried to switch to lgpio which looks OK. However, I have problems writing and reading back pin states from different programs.



My setup is as follows: I have a couple of relays (solid state and mechanical ones) that control various external devices. I use one program to switch devices on and off and want to use another program to read back the state of the device.



Doing that with sysfs is easy:



1) export the pin: echo $pin > /sys/class/gpio/export echo $direction > /sys/class/gpio/gpio$pin/direction this needs to be done only once.
2) write the state of the pin, thus switching the device on/off: echo $newstate > /sys/class/gpio/gpio$pin/value this is done every time this is required
3) read back the state of the pin value=$(</sys/class/gpio/gpio$pin/value this is done every time I want to check the state of the device

Now I switch a device on/off with lgpio as follows:



1) open the GPIO chip: h = lgGpiochipOpen(0);
2) claim the pin as an output: lgGpioClaimOutput(h, LG_SET_PULL_NONE, pin, value); which, to my understanding, already changes the pin's state?!?
3) write the new state lgGpioWrite(h, pin, value);
4) close the chip lgGpiochipClose(h);

Reading back the state of the pin requires me to



1) open the GPIO chip: h = lgGpiochipOpen(0);
2) claim the pin as an input: lgGpioClaimInput(h, LG_SET_PULL_NONE, pin);
3) read back the pin's state lgGpioRead(h, pin);
4) close the chip lgGpiochipClose(h);

However ... When I set the pin's state to "1", I still read back "0"!



What am I doing wrong? Thanks in advance for any pointers.



Josef


Personally I switched to using the "gpio" command that's one of the example programs included with the bcm2835 library. If you're just trying to build C programs to replace the /sys/class/gpio devices in a shell script, it's an existing option. It does have some bugs though.

formatting link
See examples/gpio/gpio.c in the source code.

At a GUESS, you're reading the input buffer instead of the output buffer. In output mode the input is disabled and always reads zero or is meaningless.

Unfortunately I've forgotten whether this is the case with the Pi, or at least where to look to confirm I'm not mis-remembering, so check for yourself. But this is a common way for IO hardware to work.

TLDR

Here is my C code to drive 4 relays from a Pi Zero W Adpapted frim someone elses that also works It works:

/* relays.h

2023-04-30 Public Domain */ /* zone 1-4, command ON or OFF */ /* All clever stuff is done in relayio, any call to this * will initialise the hardware if it needs it. */

void relay(int zone, int command);

#define RELAY1 6 #define RELAY2 13 #define RELAY3 19 #define RELAY4 26

--------------------------------------------------------------------------------------- /* relayio.c 2023-04-30 Public Domain

*/

#include <stdio.h>

#include <unistd.h>

#include <stdint.h>

#include <string.h>

#include <fcntl.h>

#include <sys/mman.h>

#include <sys/stat.h>

#include <sys/types.h>

#include "relays.h"

#define GPSET0 7 #define GPSET1 8

#define GPCLR0 10 #define GPCLR1 11

#define GPLEV0 13 #define GPLEV1 14

#define GPPUD 37 #define GPPUDCLK0 38 #define GPPUDCLK1 39 // port numbers for relays

unsigned piModel; unsigned piRev;

static volatile uint32_t *gpioReg = MAP_FAILED;

#define PI_BANK (gpio>>5) #define PI_BIT (1<<(gpio&0x1F))

/* gpio modes. */

#define PI_INPUT 0 #define PI_OUTPUT 1 #define PI_ALT0 4 #define PI_ALT1 5 #define PI_ALT2 6 #define PI_ALT3 7 #define PI_ALT4 3 #define PI_ALT5 2

static int initialized=0;

void gpioSetMode(unsigned gpio, unsigned mode) { int reg, shift; reg=gpio/10; shift=(gpio%10) * 3; gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift); }

int gpioGetMode(unsigned gpio) { int reg, shift; reg = gpio/10; shift = (gpio%10) * 3; return (*(gpioReg + reg) >> shift) & 7; }

/* Values for pull-ups/downs off, pull-down and pull-up. */

#define PI_PUD_OFF 0 #define PI_PUD_DOWN 1 #define PI_PUD_UP 2

void gpioSetPullUpDown(unsigned gpio, unsigned pud) { *(gpioReg + GPPUD) = pud; usleep(20); *(gpioReg + GPPUDCLK0 + PI_BANK) = PI_BIT; usleep(20); *(gpioReg + GPPUD) = 0; *(gpioReg + GPPUDCLK0 + PI_BANK) = 0; }

int gpioRead(unsigned gpio) { if ((*(gpioReg + GPLEV0 + PI_BANK) & PI_BIT) != 0) return 1; else return 0; }

void gpioWrite(unsigned gpio, unsigned level) { if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; }

void gpioTrigger(unsigned gpio, unsigned pulseLen, unsigned level) { if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; usleep(pulseLen); if (level != 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; }

/* Bit (1<<x) will be set if gpio x is high. */

uint32_t gpioReadBank1(void) { return (*(gpioReg + GPLEV0)); } uint32_t gpioReadBank2(void) { return (*(gpioReg + GPLEV1)); }

/* To clear gpio x bit or in (1<<x). */

void gpioClearBank1(uint32_t bits) { *(gpioReg + GPCLR0) = bits; } void gpioClearBank2(uint32_t bits) { *(gpioReg + GPCLR1) = bits; }

/* To set gpio x bit or in (1<<x). */

void gpioSetBank1(uint32_t bits) { *(gpioReg + GPSET0) = bits; } void gpioSetBank2(uint32_t bits) { *(gpioReg + GPSET1) = bits; }

unsigned gpioHardwareRevision(void) { static unsigned rev = 0; FILE * filp; char buf[512]; char term; int chars=4; /* number of chars in revision string */

if (rev) return rev; piModel = 0; filp = fopen ("/proc/cpuinfo", "r");

if (filp != NULL) { while (fgets(buf, sizeof(buf), filp) != NULL) { if (piModel == 0) { if (!strncasecmp("model name", buf, 10)) { if (strstr (buf, "ARMv6") != NULL) { piModel = 1; chars = 4; } else if (strstr (buf, "ARMv7") != NULL) { piModel = 2; chars = 6; } else if (strstr (buf, "ARMv8") != NULL) { piModel = 2; chars = 6; } } }

if (!strncasecmp("revision", buf, 8)) { if (sscanf(buf+strlen(buf)-(chars+1), "%x%c", &rev, &term) == 2) { if (term != '\n') rev = 0; } } } fclose(filp); } return rev; }

int gpioInitialise(void) { int fd; piRev = gpioHardwareRevision(); /* sets piModel and piRev */ fd = open("/dev/gpiomem", O_RDWR | O_SYNC) ; if (fd < 0) { fprintf(stderr, "failed to open /dev/gpiomem\n"); return -1; } gpioReg = (uint32_t *)mmap(NULL, 0xB4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (gpioReg == MAP_FAILED) { fprintf(stderr, "Bad, mmap failed\n"); return -1; } return 0; }

void initializeRelays() { if (gpioInitialise() < 0) return; gpioSetMode(RELAY1,PI_OUTPUT); gpioSetMode(RELAY2,PI_OUTPUT); gpioSetMode(RELAY3,PI_OUTPUT); gpioSetMode(RELAY4,PI_OUTPUT); initialized = 1; } void relay(int zone, int command) { int port; if(!initialized) initializeRelays(); switch (zone) { case 1: port=RELAY1; break; case 2: port=RELAY2; break; case 3: port=RELAY3; break; case 4: port=RELAY4; break; default: return; } gpioWrite(port, command); }

I'll have a look at that, thanks!

Well, the documentation

formatting link
says "This command will work for any claimed GPIO (even if a member of a group). For an output GPIO the value returned will be that last written to the GPIO." So, I was thinking that that would happen.

OK, that sounds logical ... at the hardware level. However, I read the documentation such that either it works eg like the ATMega port pins, where you can read back the state of an output pin, OR the kernel driver would keep a copy of the output state and return that upon a read OR the library function would do that, but shame on me I have not checked the latter myself.

Thanks anyway,

Josef

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required