Use lgpio as the RPi HAL

This commit is contained in:
jgromes 2024-06-14 19:49:19 +01:00
parent 940eb07674
commit bc36c1e98a
4 changed files with 126 additions and 55 deletions

View file

@ -259,7 +259,12 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y pigpio cmake sudo apt-get install -y cmake wget swig python-dev python3-dev python-setuptools python3-setuptools
wget http://abyz.me.uk/lg/lg.zip
unzip lg.zip
cd lg
make
sudo make install
- name: Install the library - name: Install the library
run: | run: |

View file

@ -15,7 +15,7 @@ add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../../../RadioLib" "${CMAKE_CUR
add_executable(${PROJECT_NAME} main.cpp) add_executable(${PROJECT_NAME} main.cpp)
# link both libraries # link both libraries
target_link_libraries(${PROJECT_NAME} RadioLib pigpio) target_link_libraries(${PROJECT_NAME} RadioLib lgpio)
# you can also specify RadioLib compile-time flags here # you can also specify RadioLib compile-time flags here
#target_compile_definitions(RadioLib PUBLIC RADIOLIB_DEBUG_BASIC RADIOLIB_DEBUG_SPI) #target_compile_definitions(RadioLib PUBLIC RADIOLIB_DEBUG_BASIC RADIOLIB_DEBUG_SPI)

View file

@ -1,55 +1,53 @@
#ifndef PI_HAL_H #ifndef PI_HAL_LGPIO_H
#define PI_HAL_H #define PI_HAL_LGPIO_H
// include RadioLib // include RadioLib
#include <RadioLib.h> #include <RadioLib.h>
// include the library for Raspberry GPIO pins // include the library for Raspberry GPIO pins
#include "pigpio.h" #include <lgpio.h>
// these should really be swapped, but for some reason, #define PI_RISING (LG_RISING_EDGE)
// it seems like the change directions are inverted in gpioSetAlert functions #define PI_FALLING (LG_FALLING_EDGE)
#define PI_RISING (FALLING_EDGE) #define PI_INPUT (0)
#define PI_FALLING (RISING_EDGE) #define PI_OUTPUT (1)
#define PI_MAX_USER_GPIO (31)
// forward declaration of alert handler that will be used to emulate interrupts // forward declaration of alert handler that will be used to emulate interrupts
static void pigpioAlertHandler(int event, int level, uint32_t tick, void *userdata); static void lgpioAlertHandler(int num_alerts, lgGpioAlert_p alerts, void *userdata);
// create a new Raspberry Pi hardware abstraction layer // create a new Raspberry Pi hardware abstraction layer
// using the pigpio library // using the lgpio library
// the HAL must inherit from the base RadioLibHal class // the HAL must inherit from the base RadioLibHal class
// and implement all of its virtual methods // and implement all of its virtual methods
class PiHal : public RadioLibHal { class PiHal : public RadioLibHal {
public: public:
// default constructor - initializes the base HAL and any needed private members // default constructor - initializes the base HAL and any needed private members
PiHal(uint8_t spiChannel, uint32_t spiSpeed = 2000000) PiHal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
: RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING), : RadioLibHal(PI_INPUT, PI_OUTPUT, LG_LOW, LG_HIGH, PI_RISING, PI_FALLING),
_gpioDevice(gpioDevice),
_spiDevice(spiDevice),
_spiChannel(spiChannel), _spiChannel(spiChannel),
_spiSpeed(spiSpeed) { _spiSpeed(spiSpeed) {
} }
void init() override { void init() override {
// first initialise pigpio library // first initialise lgpio library
gpioInitialise(); if((_gpioHandle = lgGpiochipOpen(_gpioDevice)) < 0) {
fprintf(stderr, "Could not open GPIO chip: %s\n", lguErrorText(_gpioHandle));
return;
}
// now the SPI // now the SPI
spiBegin(); spiBegin();
// Waveshare LoRaWAN Hat also needs pin 18 to be pulled high to enable the radio
gpioSetMode(18, PI_OUTPUT);
gpioWrite(18, PI_HIGH);
} }
void term() override { void term() override {
// stop the SPI // stop the SPI
spiEnd(); spiEnd();
// pull the enable pin low // finally, stop the lgpio library
gpioSetMode(18, PI_OUTPUT); lgGpiochipClose(_gpioHandle);
gpioWrite(18, PI_LOW);
// finally, stop the pigpio library
gpioTerminate();
} }
// GPIO-related methods (pinMode, digitalWrite etc.) should check // GPIO-related methods (pinMode, digitalWrite etc.) should check
@ -59,7 +57,24 @@ class PiHal : public RadioLibHal {
return; return;
} }
gpioSetMode(pin, mode); int result;
int flags = 0;
switch(mode) {
case PI_INPUT:
result = lgGpioClaimInput(_gpioHandle, 0, pin);
break;
case PI_OUTPUT:
result = lgGpioClaimOutput(_gpioHandle, flags, pin, LG_HIGH);
break;
default:
fprintf(stderr, "Unknown pinMode mode %" PRIu32 "\n", mode);
return;
}
if(result < 0) {
fprintf(stderr, "Could not claim pin %" PRIu32 " for mode %" PRIu32 ": %s\n",
pin, mode, lguErrorText(result));
}
} }
void digitalWrite(uint32_t pin, uint32_t value) override { void digitalWrite(uint32_t pin, uint32_t value) override {
@ -67,7 +82,10 @@ class PiHal : public RadioLibHal {
return; return;
} }
gpioWrite(pin, value); int result = lgGpioWrite(_gpioHandle, pin, value);
if(result < 0) {
fprintf(stderr, "Error writing value to pin %" PRIu32 ": %s\n", pin, lguErrorText(result));
}
} }
uint32_t digitalRead(uint32_t pin) override { uint32_t digitalRead(uint32_t pin) override {
@ -75,7 +93,11 @@ class PiHal : public RadioLibHal {
return(0); return(0);
} }
return(gpioRead(pin)); int result = lgGpioRead(_gpioHandle, pin);
if(result < 0) {
fprintf(stderr, "Error writing reading from pin %" PRIu32 ": %s\n", pin, lguErrorText(result));
}
return result;
} }
void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override { void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override {
@ -83,13 +105,19 @@ class PiHal : public RadioLibHal {
return; return;
} }
// set lgpio alert callback
int result = lgGpioClaimAlert(_gpioHandle, 0, mode, interruptNum, -1);
if(result < 0) {
fprintf(stderr, "Could not claim pin %" PRIu32 " for alert: %s\n", interruptNum, lguErrorText(result));
return;
}
// enable emulated interrupt // enable emulated interrupt
interruptEnabled[interruptNum] = true; interruptEnabled[interruptNum] = true;
interruptModes[interruptNum] = mode; interruptModes[interruptNum] = mode;
interruptCallbacks[interruptNum] = interruptCb; interruptCallbacks[interruptNum] = interruptCb;
// set pigpio alert callback lgGpioSetAlertsFunc(_gpioHandle, interruptNum, lgpioAlertHandler, (void *)this);
gpioSetAlertFuncEx(interruptNum, pigpioAlertHandler, (void*)this);
} }
void detachInterrupt(uint32_t interruptNum) override { void detachInterrupt(uint32_t interruptNum) override {
@ -102,27 +130,44 @@ class PiHal : public RadioLibHal {
interruptModes[interruptNum] = 0; interruptModes[interruptNum] = 0;
interruptCallbacks[interruptNum] = NULL; interruptCallbacks[interruptNum] = NULL;
// disable pigpio alert callback // disable lgpio alert callback
gpioSetAlertFuncEx(interruptNum, NULL, NULL); lgGpioFree(_gpioHandle, interruptNum);
lgGpioSetAlertsFunc(_gpioHandle, interruptNum, NULL, NULL);
} }
void delay(RadioLibTime_t ms) override { void delay(unsigned long ms) override {
gpioDelay(ms * 1000); if(ms == 0) {
sched_yield();
return;
} }
void delayMicroseconds(RadioLibTime_t us) override { lguSleep(ms / 1000.0);
gpioDelay(us);
} }
RadioLibTime_t millis() override { void delayMicroseconds(unsigned long us) override {
return(gpioTick() / 1000); if(us == 0) {
sched_yield();
return;
} }
RadioLibTime_t micros() override { lguSleep(us / 1000000.0);
return(gpioTick());
} }
long pulseIn(uint32_t pin, uint32_t state, RadioLibTime_t timeout) override { void yield() override {
sched_yield();
}
unsigned long millis() override {
uint32_t time = lguTimestamp() / 1000000UL;
return time;
}
unsigned long micros() override {
uint32_t time = lguTimestamp() / 1000UL;
return time;
}
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
if(pin == RADIOLIB_NC) { if(pin == RADIOLIB_NC) {
return(0); return(0);
} }
@ -142,25 +187,38 @@ class PiHal : public RadioLibHal {
void spiBegin() { void spiBegin() {
if(_spiHandle < 0) { if(_spiHandle < 0) {
_spiHandle = spiOpen(_spiChannel, _spiSpeed, 0); if((_spiHandle = lgSpiOpen(_spiDevice, _spiChannel, _spiSpeed, 0)) < 0) {
fprintf(stderr, "Could not open SPI handle on 0: %s\n", lguErrorText(_spiHandle));
}
} }
} }
void spiBeginTransaction() {} void spiBeginTransaction() {}
void spiTransfer(uint8_t* out, size_t len, uint8_t* in) { void spiTransfer(uint8_t* out, size_t len, uint8_t* in) {
spiXfer(_spiHandle, (char*)out, (char*)in, len); int result = lgSpiXfer(_spiHandle, (char *)out, (char*)in, len);
if(result < 0) {
fprintf(stderr, "Could not perform SPI transfer: %s\n", lguErrorText(result));
}
} }
void spiEndTransaction() {} void spiEndTransaction() {}
void spiEnd() { void spiEnd() {
if(_spiHandle >= 0) { if(_spiHandle >= 0) {
spiClose(_spiHandle); lgSpiClose(_spiHandle);
_spiHandle = -1; _spiHandle = -1;
} }
} }
void tone(uint32_t pin, unsigned int frequency, unsigned long duration = 0) {
lgTxPwm(_gpioHandle, pin, frequency, 50, 0, duration);
}
void noTone(uint32_t pin) {
lgTxPwm(_gpioHandle, pin, 0, 0, 0, 0);
}
// interrupt emulation // interrupt emulation
bool interruptEnabled[PI_MAX_USER_GPIO + 1]; bool interruptEnabled[PI_MAX_USER_GPIO + 1];
uint32_t interruptModes[PI_MAX_USER_GPIO + 1]; uint32_t interruptModes[PI_MAX_USER_GPIO + 1];
@ -170,24 +228,28 @@ class PiHal : public RadioLibHal {
private: private:
// the HAL can contain any additional private members // the HAL can contain any additional private members
const unsigned int _spiSpeed; const unsigned int _spiSpeed;
const uint8_t _gpioDevice;
const uint8_t _spiDevice;
const uint8_t _spiChannel; const uint8_t _spiChannel;
int _gpioHandle = -1;
int _spiHandle = -1; int _spiHandle = -1;
}; };
// this handler emulates interrupts // this handler emulates interrupts
static void pigpioAlertHandler(int event, int level, uint32_t tick, void *userdata) { static void lgpioAlertHandler(int num_alerts, lgGpioAlert_p alerts, void *userdata) {
if((event > PI_MAX_USER_GPIO) || (!userdata)) { if(!userdata)
return; return;
}
// PiHal isntance is passed via the user data // PiHal instance is passed via the user data
PiHal* hal = (PiHal*)userdata; PiHal* hal = (PiHal*)userdata;
// check the interrupt is enabled, the level matches and a callback exists // check the interrupt is enabled, the level matches and a callback exists
if((hal->interruptEnabled[event]) && for(lgGpioAlert_t *alert = alerts; alert < (alerts + num_alerts); alert++) {
(hal->interruptModes[event] == level) && if((hal->interruptEnabled[alert->report.gpio]) &&
(hal->interruptCallbacks[event])) { (hal->interruptModes[alert->report.gpio] == alert->report.level) &&
hal->interruptCallbacks[event](); (hal->interruptCallbacks[alert->report.gpio])) {
hal->interruptCallbacks[alert->report.gpio]();
}
} }
} }

View file

@ -3,7 +3,8 @@
This example shows how to use RadioLib without Arduino. This example shows how to use RadioLib without Arduino.
In this case, a Raspberry Pi with WaveShare SX1302 LoRaWAN Hat In this case, a Raspberry Pi with WaveShare SX1302 LoRaWAN Hat
using the pigpio library. using the lgpio library
https://abyz.me.uk/lg/lgpio.html
Can be used as a starting point to port RadioLib to any platform! Can be used as a starting point to port RadioLib to any platform!
See this API reference page for details on the RadioLib hardware abstraction See this API reference page for details on the RadioLib hardware abstraction
@ -44,10 +45,13 @@ int main(int argc, char** argv) {
printf("success!\n"); printf("success!\n");
// loop forever // loop forever
int count = 0;
for(;;) { for(;;) {
// send a packet // send a packet
printf("[SX1261] Transmitting packet ... "); printf("[SX1261] Transmitting packet ... ");
state = radio.transmit("Hello World!"); char str[64];
sprintf(str, "Hello World! #%d", count++);
state = radio.transmit(str);
if(state == RADIOLIB_ERR_NONE) { if(state == RADIOLIB_ERR_NONE) {
// the packet was successfully transmitted // the packet was successfully transmitted
printf("success!\n"); printf("success!\n");