• how to write and read back GPIO pin states with lgpio

    From =?UTF-8?Q?Josef_M=C3=B6llers?=@3:770/3 to All on Sunday, August 04, 2024 22:13:55
    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

    --- SoupGate-Win32 v1.05
    * Origin: Agency HUB, Dunedin - New Zealand | Fido<>Usenet Gateway (3:770/3)
  • From Computer Nerd Kev@3:770/3 to josef@invalid.invalid on Monday, August 05, 2024 09:04:21
    Josef M?llers <josef@invalid.invalid> wrote:
    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.

    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.

    https://www.airspayce.com/mikem/bcm2835/

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

    However, I have problems writing and reading back pin
    states from different programs.
    [snip]
    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.

    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.

    --
    __ __
    #_ < |\| |< _#

    --- SoupGate-Win32 v1.05
    * Origin: Agency HUB, Dunedin - New Zealand | Fido<>Usenet Gateway (3:770/3)
  • From The Natural Philosopher@3:770/3 to All on Monday, August 05, 2024 07:53:45
    On 04/08/2024 21:13, Josef Möllers wrote:
    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

    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);
    }



    --
    "The most difficult subjects can be explained to the most slow witted
    man if he has not formed any idea of them already; but the simplest
    thing cannot be made clear to the most intelligent man if he is firmly persuaded that he knows already, without a shadow of doubt, what is laid
    before him."

    - Leo Tolstoy

    --- SoupGate-Win32 v1.05
    * Origin: Agency HUB, Dunedin - New Zealand | Fido<>Usenet Gateway (3:770/3)
  • From =?UTF-8?Q?Josef_M=C3=B6llers?=@3:770/3 to Computer Nerd Kev on Thursday, August 08, 2024 12:35:28
    On 05.08.24 01:04, Computer Nerd Kev wrote:
    Josef M?llers <josef@invalid.invalid> wrote:
    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.

    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.

    https://www.airspayce.com/mikem/bcm2835/

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


    I'll have a look at that, thanks!

    However, I have problems writing and reading back pin
    states from different programs.
    [snip]
    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.

    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.

    Well, the documentation (https://abyz.me.uk/lg/py_lgpio.html#gpio_read)
    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.

    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.

    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

    --- SoupGate-Win32 v1.05
    * Origin: Agency HUB, Dunedin - New Zealand | Fido<>Usenet Gateway (3:770/3)