RadioLib
Universal wireless communication library for Arduino
Loading...
Searching...
No Matches
PicoHal.h
1#ifndef PICO_HAL_H
2#define PICO_HAL_H
3
4#if defined(RADIOLIB_BUILD_RPI_PICO)
5
6// include RadioLib
7#include <RadioLib.h>
8
9// include the necessary Pico libraries
10#include <pico/stdlib.h>
11#include "hardware/spi.h"
12#include "hardware/timer.h"
13#include "hardware/pwm.h"
14#include "hardware/clocks.h"
15#include "hardware/gpio.h"
16#include "pico/multicore.h"
17
18#define PI_PICO_MAX_USER_GPIO (NUM_BANK0_GPIOS)
19
20// because the Pico SDK does not allow to pass user data into interrupt handlers,
21// we keep it as a global here. This is hacky and means that multiple PicoHal
22// instances share the same interrupts, which is weird and will probably break.
23// However, there seems to be no real use case for creating multiple intances of the HAL
24static irq_handler_t picoHalUserCallbacks[PI_PICO_MAX_USER_GPIO] = { 0 };
25static uint32_t picoHalIrqEventMasks[PI_PICO_MAX_USER_GPIO] = { 0 };
26static uint64_t picoHalIrqMask = 0;
27
28static void picoInterruptHandler(void) {
29 for(unsigned int gpio = 0; gpio < PI_PICO_MAX_USER_GPIO; gpio++) {
30 // Skip pins that are not managed by us.
31 if(picoHalIrqEventMasks[gpio] == 0) {
32 continue;
33 }
34 uint32_t activeEvents = gpio_get_irq_event_mask(gpio);
35 if(activeEvents) {
36 gpio_acknowledge_irq(gpio, activeEvents);
37 if((activeEvents & picoHalIrqEventMasks[gpio]) && picoHalUserCallbacks[gpio]) {
38 picoHalUserCallbacks[gpio]();
39 }
40 }
41 }
42}
43
44// create a new Raspberry Pi Pico hardware abstraction
45// layer using the Pico SDK
46// the HAL must inherit from the base RadioLibHal class
47// and implement all of its virtual methods
48class PicoHal : public RadioLibHal {
49public:
50 PicoHal(spi_inst_t *spiChannel, uint32_t misoPin, uint32_t mosiPin, uint32_t sckPin, uint32_t spiSpeed = 500 * 1000)
51 : RadioLibHal(GPIO_IN, GPIO_OUT, 0, 1, GPIO_IRQ_EDGE_RISE, GPIO_IRQ_EDGE_FALL),
52 _spiChannel(spiChannel),
53 _spiSpeed(spiSpeed),
54 _misoPin(misoPin),
55 _mosiPin(mosiPin),
56 _sckPin(sckPin){}
57
58 void init() override {
59 stdio_init_all();
60 spiBegin();
61 }
62
63 void term() override {
64 spiEnd();
65 }
66
67 // GPIO-related methods (pinMode, digitalWrite etc.) should check
68 // RADIOLIB_NC as an alias for non-connected pins
69 void pinMode(uint32_t pin, uint32_t mode) override {
70 if (pin == RADIOLIB_NC) {
71 return;
72 }
73
74 gpio_init(pin);
75 gpio_set_dir(pin, mode);
76 }
77
78 void digitalWrite(uint32_t pin, uint32_t value) override {
79 if (pin == RADIOLIB_NC) {
80 return;
81 }
82
83 gpio_put(pin, (bool)value);
84 }
85
86 uint32_t digitalRead(uint32_t pin) override {
87 if (pin == RADIOLIB_NC) {
88 return 0;
89 }
90
91 return gpio_get(pin);
92 }
93
94 void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override {
95 if (interruptNum == RADIOLIB_NC) {
96 return;
97 }
98
99 // set callbacks
100 picoHalUserCallbacks[interruptNum] = (irq_handler_t)interruptCb;
101 picoHalIrqEventMasks[interruptNum] = mode;
102
103 // is it a new interrupt for us to grab ?
104 if (!(picoHalIrqMask & (1ULL << interruptNum))) {
105 // if we have a handler in place, we must remove it to avoid 'assert' in the PDK
106 // and we must disable interrupt to make sure we don't miss anything during that
107 // shuffling
108 if (picoHalIrqMask) {
109 irq_set_enabled(IO_IRQ_BANK0, false);
110 gpio_remove_raw_irq_handler_masked64(picoHalIrqMask, picoInterruptHandler);
111 }
112
113 // (re-)add shared handler with the new mask
114 picoHalIrqMask |= (1ULL << interruptNum);
115 gpio_add_raw_irq_handler_masked64(picoHalIrqMask, picoInterruptHandler);
116 irq_set_enabled(IO_IRQ_BANK0, true);
117 }
118
119 // enable GPIO to generate interrupt
120 gpio_set_irq_enabled(interruptNum, mode, true);
121 }
122
123 void detachInterrupt(uint32_t interruptNum) override {
124 if (interruptNum == RADIOLIB_NC) {
125 return;
126 }
127
128 // disable the IRQ
129 gpio_set_irq_enabled(interruptNum, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
130
131 // clear callbacks
132 picoHalUserCallbacks[interruptNum] = NULL;
133 picoHalIrqEventMasks[interruptNum] = 0;
134 }
135
136 void delay(unsigned long ms) override {
137 sleep_ms(ms);
138 }
139
140 void delayMicroseconds(unsigned long us) override {
141 sleep_us(us);
142 }
143
144 unsigned long millis() override {
145 return to_ms_since_boot(get_absolute_time());
146 }
147
148 unsigned long micros() override {
149 return to_us_since_boot(get_absolute_time());
150 }
151
152 long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
153 if (pin == RADIOLIB_NC) {
154 return 0;
155 }
156
157 this->pinMode(pin, GPIO_IN);
158 uint32_t start = this->micros();
159 uint32_t curtick = this->micros();
160
161 while (this->digitalRead(pin) == state) {
162 if ((this->micros() - curtick) > timeout) {
163 return 0;
164 }
165 }
166
167 return (this->micros() - start);
168 }
169
170 void tone(uint32_t pin, unsigned int frequency, unsigned long duration = 0) override;
171
172 void noTone(uint32_t pin) override {
173 (void)pin; // avoid unused parameter warning
174 multicore_reset_core1();
175 }
176
177 void spiBegin() {
178 spi_init(_spiChannel, _spiSpeed);
179 spi_set_format(_spiChannel, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
180
181 gpio_set_function(_sckPin, GPIO_FUNC_SPI);
182 gpio_set_function(_mosiPin, GPIO_FUNC_SPI);
183 gpio_set_function(_misoPin, GPIO_FUNC_SPI);
184 }
185
186 void spiBeginTransaction() {}
187
188 void spiTransfer(uint8_t *out, size_t len, uint8_t *in) {
189 spi_write_read_blocking(_spiChannel, out, in, len);
190 }
191
192 void yield() override {
193 tight_loop_contents();
194 }
195
196 void spiEndTransaction() {}
197
198 void spiEnd() {
199 spi_deinit(_spiChannel);
200 }
201
202private:
203 // the HAL can contain any additional private members
204 spi_inst_t *_spiChannel;
205 uint32_t _spiSpeed;
206 uint32_t _misoPin;
207 uint32_t _mosiPin;
208 uint32_t _sckPin;
209};
210
211#endif
212
213#endif
Hardware abstraction library base interface.
Definition Hal.h:16
virtual uint32_t digitalRead(uint32_t pin)=0
Digital read method. Must be implemented by the platform-specific hardware abstraction!
virtual void yield()
Yield method, called from long loops in multi-threaded environment (to prevent blocking other threads...
Definition Hal.cpp:35
virtual void detachInterrupt(uint32_t interruptNum)=0
Method to detach function from an external interrupt. Must be implemented by the platform-specific ha...
virtual long pulseIn(uint32_t pin, uint32_t state, RadioLibTime_t timeout)=0
Measure the length of incoming digital pulse in microseconds. Must be implemented by the platform-spe...
virtual void spiEnd()=0
SPI termination method.
virtual void init()
Module initialization method. This will be called by all radio modules at the beginning of startup....
Definition Hal.cpp:17
virtual RadioLibTime_t millis()=0
Get number of milliseconds since start. Must be implemented by the platform-specific hardware abstrac...
virtual void digitalWrite(uint32_t pin, uint32_t value)=0
Digital write method. Must be implemented by the platform-specific hardware abstraction!
virtual void tone(uint32_t pin, unsigned int frequency, RadioLibTime_t duration=0)
Method to produce a square-wave with 50% duty cycle ("tone") of a given frequency at some pin.
Definition Hal.cpp:25
virtual RadioLibTime_t micros()=0
Get number of microseconds since start. Must be implemented by the platform-specific hardware abstrac...
virtual void spiEndTransaction()=0
Method to end SPI transaction.
virtual void noTone(uint32_t pin)
Method to stop producing a tone.
Definition Hal.cpp:31
virtual void spiBegin()=0
SPI initialization method.
virtual void delay(RadioLibTime_t ms)=0
Blocking wait function. Must be implemented by the platform-specific hardware abstraction!
virtual void term()
Module termination method. This will be called by all radio modules when the destructor is called....
Definition Hal.cpp:21
virtual void delayMicroseconds(RadioLibTime_t us)=0
Blocking microsecond wait function. Must be implemented by the platform-specific hardware abstraction...
virtual void spiBeginTransaction()=0
Method to start SPI transaction.
virtual void spiTransfer(uint8_t *out, size_t len, uint8_t *in)=0
Method to transfer buffer over SPI.
virtual void pinMode(uint32_t pin, uint32_t mode)=0
GPIO pin mode (input/output/...) configuration method. Must be implemented by the platform-specific h...
virtual void attachInterrupt(uint32_t interruptNum, void(*interruptCb)(void), uint32_t mode)=0
Method to attach function to an external interrupt. Must be implemented by the platform-specific hard...