RadioLib
Universal wireless communication library for Arduino
Loading...
Searching...
No Matches
PiHal.h
1#ifndef PI_HAL_LGPIO_H
2#define PI_HAL_LGPIO_H
3
4// include RadioLib
5#include <RadioLib.h>
6
7// include the library for Raspberry GPIO pins
8#include <lgpio.h>
9
10#if LGPIO_VERSION < 0x00020200
11 #warning "lgpio version is lower than 0.2.2 - some functionality (e.g. pull-up control) may be unavailable!"
12#endif
13
14#define PI_RISING (LG_RISING_EDGE)
15#define PI_FALLING (LG_FALLING_EDGE)
16#define PI_INPUT (0)
17#define PI_OUTPUT (1)
18#define PI_MAX_USER_GPIO (31)
19
20// custom error codes
21#define RADIOLIB_ERR_HAL_PI_GPIO_HANDLE_FAILED RADIOLIB_ERR_HAL(-1)
22#define RADIOLIB_ERR_HAL_PI_SPI_HANDLE_FAILED RADIOLIB_ERR_HAL(-2)
23#define RADIOLIB_ERR_HAL_PI_SPI_XFER_FAILED RADIOLIB_ERR_HAL(-3)
24
25// forward declaration of alert handler that will be used to emulate interrupts
26static void lgpioAlertHandler(int num_alerts, lgGpioAlert_p alerts, void *userdata);
27
28// create a new Raspberry Pi hardware abstraction layer
29// using the lgpio library
30// the HAL must inherit from the base RadioLibHal class
31// and implement all of its virtual methods
32class PiHal : public RadioLibHal {
33 public:
34 // default constructor - initializes the base HAL and any needed private members
35 PiHal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
36 : RadioLibHal(PI_INPUT, PI_OUTPUT, LG_LOW, LG_HIGH, PI_RISING, PI_FALLING),
37 _gpioDevice(gpioDevice),
38 _spiDevice(spiDevice),
39 _spiSpeed(spiSpeed),
40 _spiChannel(spiChannel) {
41 }
42
43 int16_t init() override {
44 if(_gpioHandle != -1) {
45 // initialized already
46 return(RADIOLIB_ERR_NONE);
47 }
48
49 // first initialise lgpio library
50 if((_gpioHandle = lgGpiochipOpen(_gpioDevice)) < 0) {
51 fprintf(stderr, "Could not open GPIO chip: %s\n", lguErrorText(_gpioHandle));
52 return(RADIOLIB_ERR_HAL_PI_GPIO_HANDLE_FAILED);
53 }
54
55 // now the SPI
56 return(spiBegin());
57 }
58
59 int16_t term() override {
60 // stop the SPI
61 int16_t state = spiEnd();
62
63 // finally, stop the lgpio library
64 lgGpiochipClose(_gpioHandle);
65 _gpioHandle = -1;
66 return(state);
67 }
68
69 // GPIO-related methods (pinMode, digitalWrite etc.) should check
70 // RADIOLIB_NC as an alias for non-connected pins
71 void pinMode(uint32_t pin, uint32_t mode) override {
72 if(pin == RADIOLIB_NC) {
73 return;
74 }
75
76 int result;
77 switch(mode) {
78 case PI_INPUT:
79 result = lgGpioClaimInput(_gpioHandle, pinFlags[pin], pin);
80 break;
81 case PI_OUTPUT:
82 result = lgGpioClaimOutput(_gpioHandle, pinFlags[pin], pin, LG_HIGH);
83 break;
84 default:
85 fprintf(stderr, "Unknown pinMode mode %" PRIu32 "\n", mode);
86 return;
87 }
88
89 if(result < 0) {
90 fprintf(stderr, "Could not claim pin %" PRIu32 " for mode %" PRIu32 ": %s\n",
91 pin, mode, lguErrorText(result));
92 }
93 }
94
95 void digitalWrite(uint32_t pin, uint32_t value) override {
96 if(pin == RADIOLIB_NC) {
97 return;
98 }
99
100 int result = lgGpioWrite(_gpioHandle, pin, value);
101 if(result < 0) {
102 fprintf(stderr, "Error writing value to pin %" PRIu32 ": %s\n", pin, lguErrorText(result));
103 }
104 }
105
106 uint32_t digitalRead(uint32_t pin) override {
107 if(pin == RADIOLIB_NC) {
108 return(0);
109 }
110
111 int result = lgGpioRead(_gpioHandle, pin);
112 if(result < 0) {
113 fprintf(stderr, "Error writing reading from pin %" PRIu32 ": %s\n", pin, lguErrorText(result));
114 }
115 return result;
116 }
117
118 void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override {
119 if((interruptNum == RADIOLIB_NC) || (interruptNum > PI_MAX_USER_GPIO)) {
120 return;
121 }
122
123 // set lgpio alert callback
124 int result = lgGpioClaimAlert(_gpioHandle, 0, mode, interruptNum, -1);
125 if(result < 0) {
126 fprintf(stderr, "Could not claim pin %" PRIu32 " for alert: %s\n", interruptNum, lguErrorText(result));
127 return;
128 }
129
130 // enable emulated interrupt
131 interruptEnabled[interruptNum] = true;
132 interruptCallbacks[interruptNum] = interruptCb;
133
134 // lpgio reports the value of level after an interrupt, not the actual direction
135 interruptModes[interruptNum] = (mode == this->GpioInterruptFalling) ? LG_LOW : LG_HIGH;
136
137 lgGpioSetAlertsFunc(_gpioHandle, interruptNum, lgpioAlertHandler, (void *)this);
138 }
139
140 void detachInterrupt(uint32_t interruptNum) override {
141 if((interruptNum == RADIOLIB_NC) || (interruptNum > PI_MAX_USER_GPIO)) {
142 return;
143 }
144
145 // clear emulated interrupt
146 interruptEnabled[interruptNum] = false;
147 interruptModes[interruptNum] = 0;
148 interruptCallbacks[interruptNum] = NULL;
149
150 // disable lgpio alert callback
151 lgGpioFree(_gpioHandle, interruptNum);
152 lgGpioSetAlertsFunc(_gpioHandle, interruptNum, NULL, NULL);
153 }
154
155 void delay(unsigned long ms) override {
156 if(ms == 0) {
157 sched_yield();
158 return;
159 }
160
161 lguSleep(ms / 1000.0);
162 }
163
164 void delayMicroseconds(unsigned long us) override {
165 if(us == 0) {
166 sched_yield();
167 return;
168 }
169
170 lguSleep(us / 1000000.0);
171 }
172
173 void yield() override {
174 sched_yield();
175 }
176
177 unsigned long millis() override {
178 uint32_t time = lguTimestamp() / 1000000UL;
179 return time;
180 }
181
182 unsigned long micros() override {
183 uint32_t time = lguTimestamp() / 1000UL;
184 return time;
185 }
186
187 long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
188 if(pin == RADIOLIB_NC) {
189 return(0);
190 }
191
192 this->pinMode(pin, PI_INPUT);
193 uint32_t start = this->micros();
194 uint32_t curtick = this->micros();
195
196 while(this->digitalRead(pin) == state) {
197 if((this->micros() - curtick) > timeout) {
198 return(0);
199 }
200 }
201
202 return(this->micros() - start);
203 }
204
205 int16_t spiBegin() {
206 if(_spiHandle < 0) {
207 if((_spiHandle = lgSpiOpen(_spiDevice, _spiChannel, _spiSpeed, 0)) < 0) {
208 fprintf(stderr, "Could not open SPI handle on 0: %s\n", lguErrorText(_spiHandle));
209 return(RADIOLIB_ERR_HAL_PI_SPI_HANDLE_FAILED);
210 }
211 }
212
213 return(RADIOLIB_ERR_NONE);
214 }
215
217
218 int16_t spiTransfer(uint8_t* out, size_t len, uint8_t* in) {
219 int result = lgSpiXfer(_spiHandle, (char *)out, (char*)in, len);
220 if(result < 0) {
221 fprintf(stderr, "Could not perform SPI transfer: %s\n", lguErrorText(result));
222 return(RADIOLIB_ERR_HAL_PI_SPI_XFER_FAILED);
223 }
224 return(RADIOLIB_ERR_NONE);
225 }
226
228
229 int16_t spiEnd() {
230 if(_spiHandle >= 0) {
231 lgSpiClose(_spiHandle);
232 _spiHandle = -1;
233 }
234 return(RADIOLIB_ERR_NONE);
235 }
236
237 void tone(uint32_t pin, unsigned int frequency, unsigned long duration = 0) {
238 lgTxPwm(_gpioHandle, pin, frequency, 50, 0, duration);
239 }
240
241 void noTone(uint32_t pin) {
242 lgTxPwm(_gpioHandle, pin, 0, 0, 0, 0);
243 }
244
245#if LGPIO_VERSION > 0x00010000
246 void pullUpDown(uint32_t pin, bool enable, bool up) {
247 if((pin == RADIOLIB_NC) || (pin > PI_MAX_USER_GPIO)) {
248 return;
249 }
250
251 pinFlags[pin] = enable ? (up ? LG_SET_PULL_UP : LG_SET_PULL_DOWN) : LG_SET_PULL_NONE;
252 }
253#endif /* LGPIO_VERSION */
254
255 // interrupt emulation
256 bool interruptEnabled[PI_MAX_USER_GPIO + 1];
257 uint32_t interruptModes[PI_MAX_USER_GPIO + 1];
258 typedef void (*RadioLibISR)(void);
259 RadioLibISR interruptCallbacks[PI_MAX_USER_GPIO + 1];
260
261 private:
262 // the HAL can contain any additional private members
263 const uint8_t _gpioDevice;
264 const uint8_t _spiDevice;
265 const unsigned int _spiSpeed;
266 const uint8_t _spiChannel;
267 int _gpioHandle = -1;
268 int _spiHandle = -1;
269
270 int pinFlags[PI_MAX_USER_GPIO + 1] = { 0 };
271};
272
273// this handler emulates interrupts
274static void lgpioAlertHandler(int num_alerts, lgGpioAlert_p alerts, void *userdata) {
275 if(!userdata)
276 return;
277
278 // PiHal instance is passed via the user data
279 PiHal* hal = (PiHal*)userdata;
280
281 // check the interrupt is enabled, the level matches and a callback exists
282 for(lgGpioAlert_t *alert = alerts; alert < (alerts + num_alerts); alert++) {
283 if((hal->interruptEnabled[alert->report.gpio]) &&
284 (hal->interruptModes[alert->report.gpio] == alert->report.level) &&
285 (hal->interruptCallbacks[alert->report.gpio])) {
286 hal->interruptCallbacks[alert->report.gpio]();
287 }
288 }
289}
290
291#endif
Definition PiHal.h:32
void noTone(uint32_t pin)
Method to stop producing a tone.
Definition PiHal.h:241
unsigned long millis() override
Get number of milliseconds since start. Must be implemented by the platform-specific hardware abstrac...
Definition PiHal.h:177
int16_t term() override
Module termination method. This will be called by all radio modules when the destructor is called....
Definition PiHal.h:59
void digitalWrite(uint32_t pin, uint32_t value) override
Digital write method. Must be implemented by the platform-specific hardware abstraction!
Definition PiHal.h:95
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override
Measure the length of incoming digital pulse in microseconds. Must be implemented by the platform-spe...
Definition PiHal.h:187
int16_t init() override
Module initialization method. This will be called by all radio modules at the beginning of startup....
Definition PiHal.h:43
int16_t spiTransfer(uint8_t *out, size_t len, uint8_t *in)
Method to transfer buffer over SPI.
Definition PiHal.h:218
void spiEndTransaction()
Method to end SPI transaction.
Definition PiHal.h:227
int16_t spiEnd()
SPI termination method.
Definition PiHal.h:229
int16_t spiBegin()
SPI initialization method.
Definition PiHal.h:205
void tone(uint32_t pin, unsigned int frequency, unsigned long duration=0)
Method to produce a square-wave with 50% duty cycle ("tone") of a given frequency at some pin.
Definition PiHal.h:237
unsigned long micros() override
Get number of microseconds since start. Must be implemented by the platform-specific hardware abstrac...
Definition PiHal.h:182
void spiBeginTransaction()
Method to start SPI transaction.
Definition PiHal.h:216
uint32_t digitalRead(uint32_t pin) override
Digital read method. Must be implemented by the platform-specific hardware abstraction!
Definition PiHal.h:106
void delayMicroseconds(unsigned long us) override
Blocking microsecond wait function. Must be implemented by the platform-specific hardware abstraction...
Definition PiHal.h:164
void attachInterrupt(uint32_t interruptNum, void(*interruptCb)(void), uint32_t mode) override
Method to attach function to an external interrupt. Must be implemented by the platform-specific hard...
Definition PiHal.h:118
void pinMode(uint32_t pin, uint32_t mode) override
GPIO pin mode (input/output/...) configuration method. Must be implemented by the platform-specific h...
Definition PiHal.h:71
void delay(unsigned long ms) override
Blocking wait function. Must be implemented by the platform-specific hardware abstraction!
Definition PiHal.h:155
void yield() override
Yield method, called from long loops in multi-threaded environment (to prevent blocking other threads...
Definition PiHal.h:173
void detachInterrupt(uint32_t interruptNum) override
Method to detach function from an external interrupt. Must be implemented by the platform-specific ha...
Definition PiHal.h:140
const uint32_t GpioInterruptFalling
Value to be used as the "falling" GPIO level change direction.
Definition Hal.h:52
virtual void pullUpDown(uint32_t pin, bool enable, bool up)
Enable or disable pull up or pull down for a specific pin.
Definition Hal.cpp:45
RadioLibHal(const uint32_t input, const uint32_t output, const uint32_t low, const uint32_t high, const uint32_t rising, const uint32_t falling)
Default constructor.
Definition Hal.cpp:5
#define RADIOLIB_ERR_NONE
No error, method executed successfully.
Definition TypeDef.h:109