Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi gpiochip support for native environment #5743

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

psiegl
Copy link

@psiegl psiegl commented Jan 4, 2025

While tinkering with Meshtastic on the popular Radxa Zero 3E/W, trying to employ an ebyte E22-900M30S I stumbled across the SBCs GPIO partitioning.

However, let's recap what an E22-900M30S requires in regards to SPI/GPIO. Motivated by Xiao BLE/BLE Sense + Ebyte E22-900M30S, an E22-900M30S requires at least:

  1. SPI CS* (NSS)
  2. SPI MISO
  3. SPI MOSI
  4. SPI SCK
  5. GPIO NRST ('reset')
  6. GPIO DIO1 ('irq')
  7. GPIO RXEN
  8. GPIO BUSY

In case DIO2 is driving TXEN, two GPIOs can be saved, which is most often the setup seen and employed by myself.

Even though the Radxa Zero 3E/Ws size and its 40-Pin GPIO header provide a first impression as if the header is RPI compliant: you are to some extend mislead.
Let's start with identifying the Radxas single SPI interface (see below), which employs PIN 19, 21, 23 and 24.
Bildschirmfoto_20250104_210247
As there are just four more GPIOs required (NRST, DIO1, RXEN, BUSY), one could employ any close by GPIOs.

However, the moment one will perform a gpioinfo, one will detect the following (full dump: gpioinfo.log ):

gpiochip0 - 32 lines:
        ... 
        line  24:     "PIN_10"       unused   input  active-high 
        line  25:      "PIN_8"       unused   input  active-high
        ...
gpiochip1 - 32 lines:
        line   0:      "PIN_3"       unused   input  active-high 
        line   1:      "PIN_5"       unused   input  active-high 
        ...
        line   4:     "PIN_37"       unused   input  active-high
        ...
gpiochip2 - 32 lines:
        ...
gpiochip3 - 32 lines:
        line   0:      unnamed       unused   input  active-high 
        line   1:     "PIN_11"       unused   input  active-high 
        line   2:     "PIN_13"       unused   input  active-high 
        line   3:     "PIN_12"       unused   input  active-high 
        line   4:     "PIN_35"       unused   input  active-high 
        line   5:     "PIN_40"       unused   input  active-high 
        line   6:     "PIN_38"       unused   input  active-high 
        line   7:     "PIN_36"       unused   input  active-high 
        line   8:     "PIN_15"       unused   input  active-high 
        line   9:     "PIN_16"       unused   input  active-high 
        line  10:     "PIN_18"       unused   input  active-high 
        line  11:     "PIN_29"       unused   input  active-high 
        line  12:     "PIN_31"       unused   input  active-high 
        ...
        line  17:     "PIN_22"       unused  output  active-high 
        line  18:     "PIN_32"       unused   input  active-high 
        line  19:     "PIN_33"       unused   input  active-high 
        line  20:      "PIN_7"       unused   input  active-high 
        ...
gpiochip4 - 32 lines:
        ...
        line  10:     "PIN_27"       unused  output  active-high 
        line  11:     "PIN_28"       unused   input  active-high 
        ...
        line  18:     "PIN_23"       unused   input  active-high   <- CLK
        line  19:     "PIN_19"       unused   input  active-high   <- MOSI
        line  20:      unnamed       unused   input  active-high 
        line  21:     "PIN_21"       unused   input  active-high   <- MISO
        line  22:     "PIN_24"       unused  output  active-high   <- CS0
        line  23:      unnamed       unused   input  active-high 
        line  24:      unnamed       unused  output  active-high 
        line  25:     "PIN_26"       unused   input  active-high 
        ...
gpiochip5 - 3 lines:
        ...

Note, I annotated already the PINs that enable the sole SPI interface on gpiochip4. No matter which GPIO PINs were chosen previsouly, it is by no means possible to optimise the handling of BUSY, DIO1, RXEN and NRST by the same gpiochip that already handles SPI, as gpiochip4 just handles 3 further GPIOs. Especially, as meshtastic wants to control the CS*, the current config format is limited to specifying the gpiochip that handles CS*.

Now, when specifying a config.yaml, today it can solely support a mapping of PIN name and number, while gpiochip can solely be specified once globally. E.g.:

Lora:
  Module: sx1262
  DIO2_AS_RF_SWITCH: true
  DIO3_TCXO_VOLTAGE: 1.8

  # NSS     PIN_24 -> chip 4, line 22
  CS: 24
  # SCK     PIN_23 -> chip 4, line 18
  SCK: 23
  # BUSY    PIN_29 -> chip 3, line 11 !
  Busy: 29
  # MOSI    PIN_19 -> chip 4, line 19
  MOSI: 19
  # MISO    PIN_21 -> chip 4, line 21
  MISO: 21
  # NRST    PIN_27 -> chip 4, line 10
  Reset: 27
  # DIO1    PIN_28 -> chip 4, line 11
  IRQ: 28

  # RXEN    PIN_22 -> chip 3, line 17 !
  RXen: 22
  # TXEN
  TXen: RADIOLIB_NC

  gpiochip: 4
  spidev: spidev3.0

Logging:
  LogLevel: trace
  AsciiLogs: true

General:
  MACAddressSource: wlan0

The PIN number in case of native is anyway a bit odd, as it does not really mean PIN no. but rather line. Thus, if there is a mismatch between both, one has to use line in Linux (i.e. Meshtastic native environment).

With this patch, I propose to extend this format to allow for each PIN to specify its gpiochip and line. The old format is kept for backwards compatibility, while the current gpiochip can be considered as 'fallback' or 'globally gpiochip'. Thus, with this given patch, the above format is supported as well as the below:

Lora:
  Module: sx1262
  DIO2_AS_RF_SWITCH: true
  DIO3_TCXO_VOLTAGE: 1.8

  # NSS     PIN_24 -> chip 4, line 22
  CS: 
    pin: 24
    gpiochip: 4
    line: 22
  # SCK     PIN_23 -> chip 4, line 18
  SCK: 
    pin: 23
    gpiochip: 4
    line: 18
  # BUSY    PIN_29 -> chip 3, line 11 !
  Busy:
    pin: 29
    gpiochip: 3
    line: 11
  # MOSI    PIN_19 -> chip 4, line 19
  MOSI: 
    pin: 19
    gpiochip: 4
    line: 19
  # MISO    PIN_21 -> chip 4, line 21
  MISO: 
    pin: 21
    gpiochip: 4
    line: 21
  # NRST    PIN_27 -> chip 4, line 10
  Reset: 
    pin: 27
    gpiochip: 4
    line: 10
  # DIO1    PIN_28 -> chip 4, line 11
  IRQ: 
    pin: 28
    gpiochip: 4
    line: 11

  # RXEN    PIN_22 -> chip 3, line 17 !
  RXen: 
    pin: 22
    gpiochip: 3
    line: 17
  # TXEN
  TXen: RADIOLIB_NC

  gpiochip: 4
  spidev: spidev3.0

Logging:
  LogLevel: trace
  AsciiLogs: true

General:
  MACAddressSource: wlan0

You can notice, that TXen still employs the old format, which the YAML parser handles fine.

But how does that work, and why is it just a matter of parsing a YAML and enabling it? Already today, the portduino framework supports the handling of gpiochip and line per PIN. In particular the functions LinuxGPIOPin(pin_size_t n, const char *chipLabel, const int linuxPinNum, const char *portduinoPinName); (https://github.com/meshtastic/framework-portduino/blob/master/cores/portduino/linux/gpio/LinuxGPIOPin.h#L40), where n is employed for the PIN, linuxPinNum used for the line within Linux, while chipLabel obviously is meant for the gpiochip name. The last parameter portduinoPinName is an artificial name to make portduino happy, which could be n as string.
The magic of setting portduino properly up in regards to the mapping is subsequently taken care of in portduinos void gpioBind(GPIOPinIf *p) (https://github.com/meshtastic/framework-portduino/blob/master/cores/portduino/PortduinoGPIO.cpp#L38). Luckily, this is handled already well in src/platform/portduino/PortduinoGlue.cpp -> int initGPIOPin(int pinNum, const std::string gpioChipName).

Thus it is just a matter of enhancing src/platform/portduino/PortduinoGlue.cpp -> int initGPIOPin(int pinNum, const std::string gpioChipName). As one can see, I extended initGPIOPin with the parameter line and reworked the YAML parsing, while keeping the backwards compatibility. To ensure, that all affected spots were detected, the individual enum such as cs was reworked to cs_pin, cs_line and cs_gpiochip to follow the current implementation pattern.

In light of Meshtastic, the patch and the new config format mean, that Meshtastic internally uses the PIN no., which must not be the true line number. While portduino was made aware of the mapping of PIN no. to gpiochip and line, and thus can perform a seemless mapping. For the new config, it is solely crucial that the PIN no. is unique, otherwise the mapping won't work properly in portduino. Meanwhile, the PIN no. could be even artificially chosen.

Last, I reworked code that was multiple times copied into structs and foreachs.

With this, not only Radxa Zero 3E/W can perform a native meshtastic with the above (2nd) config.yaml, but one could also employ long term multiple gpiochips of other sorts for a chosen LoRa chip.

@CLAassistant
Copy link

CLAassistant commented Jan 4, 2025

CLA assistant check
All committers have signed the CLA.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@psiegl
Copy link
Author

psiegl commented Jan 4, 2025

Signed the CLA

src/main.cpp Show resolved Hide resolved
@psiegl
Copy link
Author

psiegl commented Jan 5, 2025

Sadly I can't get the build green right now. Seems some like to push to main broken code/tests :-( PRs would be better, instead of pushing to main^^

@jp-bennett
Copy link
Collaborator

Sadly I can't get the build green right now. Seems some like to push to main broken code/tests :-( PRs would be better, instead of pushing to main^^

Sorry about that. It's Github CI work. Pushing to master is the only way to test it.

The PR here looks great. I'll test drive it on my various rigs as soon as I can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants