5#ifndef RADIOLIB_ESP_IDF_HAL
6#define RADIOLIB_ESP_IDF_HAL
8#if defined(ESP_PLATFORM)
10#include "driver/gpio.h"
11#include "driver/ledc.h"
12#include "driver/spi_master.h"
14#include "esp_rom_sys.h"
16#include "freertos/FreeRTOS.h"
17#include "freertos/task.h"
28#define INPUT (GPIO_MODE_INPUT)
29#define OUTPUT (GPIO_MODE_OUTPUT)
30#define RISING (GPIO_INTR_POSEDGE)
31#define FALLING (GPIO_INTR_NEGEDGE)
34#if !defined(RADIOLIB_ESP32)
37#if !defined(RADIOLIB_TONE_ESP32_CHANNEL)
38#define RADIOLIB_TONE_ESP32_CHANNEL (LEDC_CHANNEL_0)
58 EspHal(int8_t sck, int8_t miso, int8_t mosi,
59 spi_host_device_t host = SPI2_HOST, uint32_t clockHz = 2000000)
60 :
RadioLibHal(INPUT, OUTPUT, LOW, HIGH, RISING, FALLING), spiSCK(sck),
61 spiMISO(miso), spiMOSI(mosi), spiHost(host), spiClockHz(clockHz),
62 spiDevice(nullptr), busInitialized(false), deviceAdded(false),
63 halInitialized(false), tonePrevFreq(-1) {}
65 virtual ~EspHal() {
term(); }
67 void init()
override {
68 if (!halInitialized) {
72 esp_err_t ret = gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
73 if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
74 ESP_LOGE(
"EspHal",
"Failed to install GPIO ISR service: %s",
75 esp_err_to_name(ret));
77 halInitialized =
true;
81 void term()
override {
84 halInitialized =
false;
89 void pinMode(uint32_t pin, uint32_t mode)
override {
90 if (pin == RADIOLIB_NC) {
94 gpio_config_t conf = {
95 .pin_bit_mask = (1ULL << pin),
96 .mode = (mode == OUTPUT) ? GPIO_MODE_OUTPUT : GPIO_MODE_INPUT,
97 .pull_up_en = GPIO_PULLUP_DISABLE,
98 .pull_down_en = GPIO_PULLDOWN_DISABLE,
99 .intr_type = GPIO_INTR_DISABLE,
104 void digitalWrite(uint32_t pin, uint32_t value)
override {
105 if (pin == RADIOLIB_NC) {
108 gpio_set_level((gpio_num_t)pin, value);
112 if (pin == RADIOLIB_NC) {
115 return gpio_get_level((gpio_num_t)pin);
119 uint32_t mode)
override {
120 if (interruptNum == RADIOLIB_NC) {
124 gpio_set_intr_type((gpio_num_t)interruptNum, (gpio_int_type_t)mode);
125 gpio_isr_handler_add((gpio_num_t)interruptNum, (gpio_isr_t)interruptCb,
130 if (interruptNum == RADIOLIB_NC) {
133 gpio_isr_handler_remove((gpio_num_t)interruptNum);
137 void delay(
unsigned long ms)
override { vTaskDelay(pdMS_TO_TICKS(ms)); }
141 unsigned long millis()
override {
142 return (
unsigned long)(esp_timer_get_time() / 1000ULL);
145 unsigned long micros()
override {
146 return (
unsigned long)esp_timer_get_time();
149 long pulseIn(uint32_t pin, uint32_t state,
unsigned long timeout)
override {
150 if (pin == RADIOLIB_NC) {
154 unsigned long startMicros =
micros();
155 unsigned long currentMicros;
160 if (currentMicros - startMicros >= timeout) {
166 unsigned long pulseStart =
micros();
169 if (currentMicros - pulseStart >= timeout) {
174 return micros() - pulseStart;
178 void pullUpDown(uint32_t pin,
bool enable,
bool up)
override {
179 if (pin == RADIOLIB_NC) {
184 gpio_set_pull_mode((gpio_num_t)pin,
185 up ? GPIO_PULLUP_ONLY : GPIO_PULLDOWN_ONLY);
187 gpio_set_pull_mode((gpio_num_t)pin, GPIO_FLOATING);
192 void tone(uint32_t pin,
unsigned int frequency,
194 if (pin == RADIOLIB_NC) {
200 if (tonePrevFreq == -1) {
201 ledc_timer_config_t timer_config = {};
202 timer_config.speed_mode = LEDC_LOW_SPEED_MODE;
203 timer_config.duty_resolution = LEDC_TIMER_10_BIT;
204 timer_config.timer_num = LEDC_TIMER_0;
205 timer_config.freq_hz = (frequency > 0) ? frequency : 1000;
206 timer_config.clk_cfg = LEDC_AUTO_CLK;
207 ledc_timer_config(&timer_config);
209 ledc_channel_config_t channel_config = {};
210 channel_config.gpio_num = (int)pin;
211 channel_config.speed_mode = LEDC_LOW_SPEED_MODE;
212 channel_config.channel = (ledc_channel_t)RADIOLIB_TONE_ESP32_CHANNEL;
213 channel_config.timer_sel = LEDC_TIMER_0;
214 channel_config.duty = 512;
215 channel_config.hpoint = 0;
216 ledc_channel_config(&channel_config);
220 if ((int32_t)frequency != tonePrevFreq) {
221 ledc_set_freq(LEDC_LOW_SPEED_MODE, LEDC_TIMER_0, frequency);
222 ledc_set_duty(LEDC_LOW_SPEED_MODE,
223 (ledc_channel_t)RADIOLIB_TONE_ESP32_CHANNEL, 512);
224 ledc_update_duty(LEDC_LOW_SPEED_MODE,
225 (ledc_channel_t)RADIOLIB_TONE_ESP32_CHANNEL);
226 tonePrevFreq = (int32_t)frequency;
230 void noTone(uint32_t pin)
override {
231 if (pin == RADIOLIB_NC) {
234 if (tonePrevFreq == -1) {
237 ledc_stop(LEDC_LOW_SPEED_MODE,
238 (ledc_channel_t)RADIOLIB_TONE_ESP32_CHANNEL, 0);
244 ESP_LOGD(
"EspHal",
"Initializing SPI on host %d (SCK=%d, MISO=%d, MOSI=%d)",
245 spiHost, spiSCK, spiMISO, spiMOSI);
250 spi_bus_config_t bus_config = {};
251 bus_config.mosi_io_num = spiMOSI;
252 bus_config.miso_io_num = spiMISO;
253 bus_config.sclk_io_num = spiSCK;
254 bus_config.quadwp_io_num = -1;
255 bus_config.quadhd_io_num = -1;
256 bus_config.data4_io_num = -1;
257 bus_config.data5_io_num = -1;
258 bus_config.data6_io_num = -1;
259 bus_config.data7_io_num = -1;
260 bus_config.max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;
261 bus_config.flags = 0;
262 bus_config.isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO;
263 bus_config.intr_flags = 0;
265 esp_err_t ret = spi_bus_initialize(spiHost, &bus_config, SPI_DMA_DISABLED);
267 busInitialized =
true;
268 ESP_LOGD(
"EspHal",
"SPI bus initialized successfully");
269 }
else if (ret == ESP_ERR_INVALID_STATE) {
271 busInitialized =
false;
272 ESP_LOGD(
"EspHal",
"SPI bus already initialized (shared bus)");
274 ESP_LOGE(
"EspHal",
"Failed to initialize SPI bus: %s",
275 esp_err_to_name(ret));
281 spi_device_interface_config_t dev_config = {};
282 dev_config.command_bits = 0;
283 dev_config.address_bits = 0;
284 dev_config.dummy_bits = 0;
286 dev_config.clock_source = SPI_CLK_SRC_DEFAULT;
287 dev_config.duty_cycle_pos = 128;
288 dev_config.cs_ena_pretrans = 0;
289 dev_config.cs_ena_posttrans = 0;
290 dev_config.clock_speed_hz = (int)spiClockHz;
291 dev_config.input_delay_ns = 0;
292 dev_config.spics_io_num = -1;
293 dev_config.flags = 0;
294 dev_config.queue_size = 1;
295 dev_config.pre_cb =
nullptr;
296 dev_config.post_cb =
nullptr;
298 ret = spi_bus_add_device(spiHost, &dev_config, &spiDevice);
300 ESP_LOGE(
"EspHal",
"Failed to add SPI device: %s", esp_err_to_name(ret));
304 ESP_LOGD(
"EspHal",
"SPI device added (clock=%" PRIu32
" Hz, software CS)",
310 if (spiDevice !=
nullptr) {
311 spi_device_acquire_bus(spiDevice, portMAX_DELAY);
315 void spiTransfer(uint8_t *out,
size_t len, uint8_t *in) {
316 if (spiDevice ==
nullptr) {
317 ESP_LOGE(
"EspHal",
"SPI device not initialized!");
325 spi_transaction_t trans = {};
326 trans.length = len * 8;
327 trans.tx_buffer = out;
328 trans.rx_buffer = in;
330 esp_err_t ret = spi_device_polling_transmit(spiDevice, &trans);
332 ESP_LOGE(
"EspHal",
"SPI transfer failed: %s", esp_err_to_name(ret));
338 if (spiDevice !=
nullptr) {
339 spi_device_release_bus(spiDevice);
345 if (deviceAdded && spiDevice !=
nullptr) {
346 spi_bus_remove_device(spiDevice);
349 ESP_LOGD(
"EspHal",
"SPI device removed");
353 if (busInitialized) {
354 spi_bus_free(spiHost);
355 busInitialized =
false;
356 ESP_LOGD(
"EspHal",
"SPI bus freed");
364 spi_host_device_t spiHost;
366 spi_device_handle_t spiDevice;
370 int32_t tonePrevFreq;
Hardware abstraction library base interface.
Definition Hal.h:18
virtual uint32_t digitalRead(uint32_t pin)=0
Digital read method. Must be implemented by the platform-specific hardware abstraction!
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 pullUpDown(uint32_t pin, bool enable, bool up)
Enable or disable pull up or pull down for a specific pin.
Definition Hal.cpp:43
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...
unsigned long RadioLibTime_t
Type used for durations in RadioLib.
Definition TypeDef.h:679