1

I am writing software that needs to control some of the Raspberry Pi's GPIO pins.

The Internet tells me that sysfs is deprecated and anyhow doesn't work on the Raspberry Pi 5. Apparently the "correct" solution is to use libgpiod.

The libgpiod documentation tells me that the first step is to call gpio_chip_open and pass the path and filename of a gpio chip device.

However, I can find no explanation as to how one is to know the correct filename to use. On the Raspberry Pi 5, at this moment in time, it is apparently "/dev/gpiochip4"; but it will almost certainly be something else on a Pi 4, for instance. And my software has to work on all Pis.

In the shell, I can invoke gpiofind GPIO22 and get the chip name and gpio index for GPIO 22 on the standard header. But in C++ or Python, there is no obvious way to determine this.

Unless the official wisdom is that I should execute a shell command from C++ and capture its output? Surely not...

Sod Almighty
  • 121
  • 7

2 Answers2

1

'Apparently the "correct" solution is to use libgpiod'; this may be true BUT the version in Debian & Raspberry Pi OS is deprecated, at least 3 years old, has minimal documentation and no examples.

It does work if you are prepared to put in the work or install an up-to-date version.

The recommended Raspberry Pi OS solution for Python is gpiozero which uses lgpio which has c & python support and is reasonably documented and has examples. There is no need to worry about which gpiochip to use in gpiozero as these use normal BCM numbering.

At the moment if using Pi5/BCM2712 you need to use gpiochip4, but this may change in the future to gpiochip0 like all earlier models.
You can access the Revision Code to determine the Pi Model. This is easy to access in c at /proc/device-tree/system/linux,revision

Milliways
  • 62,573
  • 32
  • 113
  • 225
1

It was a minor change to my Pi revision code to get processor and thus determine correct gpiochip which I include for the benefit of others encountering problems.

The code to determine gpiochip is independent of any other libraries so could easily be included in any program requiring it.

Below I have listed c & python versions of my revision code and examples of a modification to a couple of simple lgpio examples

#! /usr/bin/env python3
"""
Raspberry Pi Processor & Type
"""
import sys
# 2024-07-13

def get_revision(): """ Returns Raspberry Pi Revision Code """ with open("/proc/device-tree/system/linux,revision", "rb") as fp: return int.from_bytes(fp.read(4), 'big')

def processor(): """ Raspberry Pi SOC returns 0: BCM2835 1: BCM2836 2: BCM2837 3: BCM2711 4: BCM2712 """ return int((get_revision()>>12)&7)

def type(): """ Raspberry Pi Type returns 0: A 1: B 2: A+ 3: B+ 4: 2B 6: CM1 8: 3B 9: Zero a: CM3 c: Zero W d: 3B+ e: 3A+ 10: CM3+ 11: 4B 12: Zero 2 W 13: 400 14: CM4 15: CM4S 17: 5 """ return int((get_revision()>>4)&0xff)

def main(): print(f"{get_revision():08x}") print(f"{processor()}") print(f"{type():02x}")

if name == 'main': main()

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
// Raspberry Pi Revision, Processor & Type
// 2024-07-13

unsigned get_revision(void) {
  FILE *fp;
  uint32_t n = 0;

  if ((fp = fopen("/proc/device-tree/system/linux,revision", "r"))) {
    if (fread(&n, sizeof(n), 1, fp) != 1) {
      fclose(fp);
      return 0;
    }
  }
  fclose(fp);
  return ntohl(n);
}

unsigned processor(void) {
     return (get_revision()>>12)&7;
}

unsigned type(void) {
    return (get_revision()>>4)&0xff;
}

int main(int argc, char *argv[]) {
  printf("Version =\t%08x\n", get_revision());
  printf("Processor =\t%d\n", processor());
  printf("Type =\t\t%02x\n", type());
  return 0;
}
#! /usr/bin/env python3
"""
This program toggles a GPIO pin and measures rate.
Using lgpio Python Interface
"""
# 2024-04-02
# 2024-04-24    commas in number
# 2024-07-13  modified for Pi5/BCBM2712

import lgpio
import time
gpiochip = 0
SigOUT = 12
LOOPS = 20000

def get_revision():
    """
    Returns Raspberry Pi Revision Code
    """
    with open("/proc/device-tree/system/linux,revision", "rb") as fp:
        return int.from_bytes(fp.read(4), 'big')

def processor():
    """
    Raspberry Pi SOC
    returns
        0: BCM2835
        1: BCM2836
        2: BCM2837
        3: BCM2711
        4: BCM2712
    """
    return int((get_revision()>>12)&7)

if(processor()==4):
    gpiochip = 4

h = lgpio.gpiochip_open(gpiochip)
if h < 0:
   print('open error')

lgpio.gpio_claim_output(h, SigOUT)

t0 = time.time()

for i in range(LOOPS):
    lgpio.gpio_write(h, SigOUT, 1)
    lgpio.gpio_write(h, SigOUT, 0)

t1 = time.time()
nloops = (1.0 * LOOPS) / (t1 - t0)

print(f"lgpio Python\t{nloops:10,.0f} toggles per second")
lgpio.gpio_free(h, SigOUT)

/*
bench.c
This program toggles a GPIO pin and measures rate.
Using lgpio C API

gcc -Wall -o bench-lgpio bench-lgpio.c -llgpio

*/
// 2024-04-24   commas in number
// 2024-07-13 Support for Pi5

#include <stdio.h>
#include <locale.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <arpa/inet.h>

#include <lgpio.h>

#define LFLAGS 0

#define SigOUT 12
#define LOOPS 20000

unsigned get_revision(void) {
  FILE *fp;
  uint32_t n = 0;

  if ((fp = fopen("/proc/device-tree/system/linux,revision", "r"))) {
    if (fread(&n, sizeof(n), 1, fp) != 1) {
      fclose(fp);
      return 0;
    }
  }
  fclose(fp);
  return ntohl(n);
}

unsigned processor(void) {
     return (get_revision()>>12)&7;
}

int main(int argc, char *argv[]) {
  int h;
  int i;
  double t0, t1;
  int status;

    if(processor()==4)
            h = lgGpiochipOpen(4);
    else
            h = lgGpiochipOpen(0);

  if (h >= 0) {
    if ((status = lgGpioClaimOutput(h, LFLAGS, SigOUT, 0)) == LG_OKAY) {
      t0 = lguTime();

      for (i = 0; i < LOOPS; i++) {
        lgGpioWrite(h, SigOUT, 0);
        lgGpioWrite(h, SigOUT, 1);
      }

      t1 = lguTime();

            int nloops = (1.0 * LOOPS) / (t1 - t0);
            setlocale(LC_ALL,"");
            printf("lgpio\tC\t%'10d toggles per second\n", nloops);
    } else {
      printf("lgGpioClaimSigOUTput FAILED on Pin %d\n", SigOUT);
      lgLineInfo_t lInfo;

      status = lgGpioGetLineInfo(h, SigOUT, &lInfo);

      if (status == LG_OKAY) {
        if (lInfo.lFlags & 1) {
          printf("GPIO in use by the kernel ");
          printf("name=%s  user=%s\n", lInfo.name, lInfo.user);
        }
      }
    }
        lgGpioFree(h, SigOUT);
    lgGpiochipClose(h);
  } else
    printf("Unable to open gpiochip 0\n");
}
Milliways
  • 62,573
  • 32
  • 113
  • 225