-
Notifications
You must be signed in to change notification settings - Fork 376
/
PiHal.h
256 lines (209 loc) · 7.14 KB
/
PiHal.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#ifndef PI_HAL_LGPIO_H
#define PI_HAL_LGPIO_H
// include RadioLib
#include <RadioLib.h>
// include the library for Raspberry GPIO pins
#include <lgpio.h>
#define PI_RISING (LG_RISING_EDGE)
#define PI_FALLING (LG_FALLING_EDGE)
#define PI_INPUT (0)
#define PI_OUTPUT (1)
#define PI_MAX_USER_GPIO (31)
// forward declaration of alert handler that will be used to emulate interrupts
static void lgpioAlertHandler(int num_alerts, lgGpioAlert_p alerts, void *userdata);
// create a new Raspberry Pi hardware abstraction layer
// using the lgpio library
// the HAL must inherit from the base RadioLibHal class
// and implement all of its virtual methods
class PiHal : public RadioLibHal {
public:
// default constructor - initializes the base HAL and any needed private members
PiHal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
: RadioLibHal(PI_INPUT, PI_OUTPUT, LG_LOW, LG_HIGH, PI_RISING, PI_FALLING),
_gpioDevice(gpioDevice),
_spiDevice(spiDevice),
_spiChannel(spiChannel),
_spiSpeed(spiSpeed) {
}
void init() override {
// first initialise lgpio library
if((_gpioHandle = lgGpiochipOpen(_gpioDevice)) < 0) {
fprintf(stderr, "Could not open GPIO chip: %s\n", lguErrorText(_gpioHandle));
return;
}
// now the SPI
spiBegin();
}
void term() override {
// stop the SPI
spiEnd();
// finally, stop the lgpio library
lgGpiochipClose(_gpioHandle);
}
// GPIO-related methods (pinMode, digitalWrite etc.) should check
// RADIOLIB_NC as an alias for non-connected pins
void pinMode(uint32_t pin, uint32_t mode) override {
if(pin == RADIOLIB_NC) {
return;
}
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 {
if(pin == RADIOLIB_NC) {
return;
}
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 {
if(pin == RADIOLIB_NC) {
return(0);
}
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 {
if((interruptNum == RADIOLIB_NC) || (interruptNum > PI_MAX_USER_GPIO)) {
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
interruptEnabled[interruptNum] = true;
interruptModes[interruptNum] = mode;
interruptCallbacks[interruptNum] = interruptCb;
lgGpioSetAlertsFunc(_gpioHandle, interruptNum, lgpioAlertHandler, (void *)this);
}
void detachInterrupt(uint32_t interruptNum) override {
if((interruptNum == RADIOLIB_NC) || (interruptNum > PI_MAX_USER_GPIO)) {
return;
}
// clear emulated interrupt
interruptEnabled[interruptNum] = false;
interruptModes[interruptNum] = 0;
interruptCallbacks[interruptNum] = NULL;
// disable lgpio alert callback
lgGpioFree(_gpioHandle, interruptNum);
lgGpioSetAlertsFunc(_gpioHandle, interruptNum, NULL, NULL);
}
void delay(unsigned long ms) override {
if(ms == 0) {
sched_yield();
return;
}
lguSleep(ms / 1000.0);
}
void delayMicroseconds(unsigned long us) override {
if(us == 0) {
sched_yield();
return;
}
lguSleep(us / 1000000.0);
}
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) {
return(0);
}
this->pinMode(pin, PI_INPUT);
uint32_t start = this->micros();
uint32_t curtick = this->micros();
while(this->digitalRead(pin) == state) {
if((this->micros() - curtick) > timeout) {
return(0);
}
}
return(this->micros() - start);
}
void spiBegin() {
if(_spiHandle < 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 spiTransfer(uint8_t* out, size_t len, uint8_t* in) {
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 spiEnd() {
if(_spiHandle >= 0) {
lgSpiClose(_spiHandle);
_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
bool interruptEnabled[PI_MAX_USER_GPIO + 1];
uint32_t interruptModes[PI_MAX_USER_GPIO + 1];
typedef void (*RadioLibISR)(void);
RadioLibISR interruptCallbacks[PI_MAX_USER_GPIO + 1];
private:
// the HAL can contain any additional private members
const unsigned int _spiSpeed;
const uint8_t _gpioDevice;
const uint8_t _spiDevice;
const uint8_t _spiChannel;
int _gpioHandle = -1;
int _spiHandle = -1;
};
// this handler emulates interrupts
static void lgpioAlertHandler(int num_alerts, lgGpioAlert_p alerts, void *userdata) {
if(!userdata)
return;
// PiHal instance is passed via the user data
PiHal* hal = (PiHal*)userdata;
// check the interrupt is enabled, the level matches and a callback exists
for(lgGpioAlert_t *alert = alerts; alert < (alerts + num_alerts); alert++) {
if((hal->interruptEnabled[alert->report.gpio]) &&
(hal->interruptModes[alert->report.gpio] == alert->report.level) &&
(hal->interruptCallbacks[alert->report.gpio])) {
hal->interruptCallbacks[alert->report.gpio]();
}
}
}
#endif