From 7c62df071fa07669cccc70efda1739d5904af588 Mon Sep 17 00:00:00 2001 From: lumapu Date: Mon, 4 Dec 2023 08:45:51 +0100 Subject: [PATCH] started implementation of ethernet for opendtufusion board --- scripts/applyPatches.py | 5 +- src/defines.h | 2 +- src/eth/ahoyeth.cpp | 10 +- src/eth/ahoyeth.h | 6 +- src/eth/ethSpi.h | 141 ++++++++++++++++++++++ src/hm/hmRadio.h | 102 +++++++++------- src/hm/nrfHal.h | 241 ++++++++++++++++++++++++++++++++++++++ src/hm/spiPatcher.h | 78 ++++++++++++ src/hm/spiPatcherHandle.h | 17 +++ src/platformio.ini | 6 +- 10 files changed, 557 insertions(+), 51 deletions(-) create mode 100644 src/eth/ethSpi.h create mode 100644 src/hm/nrfHal.h create mode 100644 src/hm/spiPatcher.h create mode 100644 src/hm/spiPatcherHandle.h diff --git a/scripts/applyPatches.py b/scripts/applyPatches.py index 657525e6..131572fd 100644 --- a/scripts/applyPatches.py +++ b/scripts/applyPatches.py @@ -26,7 +26,10 @@ def applyPatch(libName, patchFile): # list of patches to apply (relative to /src) -applyPatch("ESP Async WebServer", "../patches/AsyncWeb_Prometheus.patch") +if env['PIOENV'][:22] != "opendtufusion-ethernet": + applyPatch("ESP Async WebServer", "../patches/AsyncWeb_Prometheus.patch") if env['PIOENV'][:13] == "opendtufusion": applyPatch("GxEPD2", "../patches/GxEPD2_SW_SPI.patch") +if env['PIOENV'][:22] == "opendtufusion-ethernet": + applyPatch("RF24", "../patches/RF24_Hal.patch") diff --git a/src/defines.h b/src/defines.h index d471a5ef..80b94687 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 8 -#define VERSION_PATCH 13 +#define VERSION_PATCH 14 //------------------------------------- typedef struct { diff --git a/src/eth/ahoyeth.cpp b/src/eth/ahoyeth.cpp index 21fa0821..85b9994c 100644 --- a/src/eth/ahoyeth.cpp +++ b/src/eth/ahoyeth.cpp @@ -11,7 +11,6 @@ #endif #include "ahoyeth.h" - //----------------------------------------------------------------------------- ahoyeth::ahoyeth() { @@ -41,8 +40,11 @@ void ahoyeth::setup(settings_t *config, uint32_t *utcTimestamp, OnNetworkCB onNe if(!ETH.config(ip, gateway, mask, dns1, dns2)) DPRINTLN(DBG_ERROR, F("failed to set static IP!")); } + #if defined(CONFIG_IDF_TARGET_ESP32S3) + mEthSpi.begin(DEF_ETH_MISO_PIN, DEF_ETH_MOSI_PIN, DEF_ETH_SCK_PIN, DEF_ETH_CS_PIN, DEF_ETH_IRQ_PIN, DEF_ETH_RST_PIN); + #else ETH.begin(DEF_ETH_MISO_PIN, DEF_ETH_MOSI_PIN, DEF_ETH_SCK_PIN, DEF_ETH_CS_PIN, DEF_ETH_IRQ_PIN, ETH_SPI_CLOCK_MHZ, ETH_SPI_HOST); - + #endif } @@ -155,7 +157,11 @@ void ahoyeth::onEthernetEvent(WiFiEvent_t event, arduino_event_info_t info) case ARDUINO_EVENT_ETH_GOT_IP: if (!ESP32_W5500_eth_connected) { + #if defined (CONFIG_IDF_TARGET_ESP32S3) + AWS_LOG3(F("ETH MAC: "), mEthSpi.macAddress(), F(", IPv4: "), ETH.localIP()); + #else AWS_LOG3(F("ETH MAC: "), ETH.macAddress(), F(", IPv4: "), ETH.localIP()); + #endif if (ETH.fullDuplex()) { diff --git a/src/eth/ahoyeth.h b/src/eth/ahoyeth.h index 9f5123fb..157a9c76 100644 --- a/src/eth/ahoyeth.h +++ b/src/eth/ahoyeth.h @@ -13,6 +13,7 @@ #include #include +#include "ethSpi.h" #include "../utils/dbg.h" #include "../config/config.h" #include "../config/settings.h" @@ -45,6 +46,9 @@ class ahoyeth { void onEthernetEvent(WiFiEvent_t event, arduino_event_info_t info); private: + #if defined(CONFIG_IDF_TARGET_ESP32S3) + EthSpi mEthSpi; + #endif settings_t *mConfig; uint32_t *mUtcTimestamp; @@ -57,4 +61,4 @@ class ahoyeth { }; #endif /*__AHOYETH_H__*/ -#endif /* defined(ETHERNET) */ \ No newline at end of file +#endif /* defined(ETHERNET) */ diff --git a/src/eth/ethSpi.h b/src/eth/ethSpi.h new file mode 100644 index 00000000..d0ef9487 --- /dev/null +++ b/src/eth/ethSpi.h @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + + +#if defined(CONFIG_IDF_TARGET_ESP32S3) +#if defined(ETHERNET) +#ifndef __ETH_SPI_H__ +#define __ETH_SPI_H__ + +#pragma once + +#include +#include +#include +#include + +// Functions from WiFiGeneric +void tcpipInit(); +void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif); + +class EthSpi { + public: + + EthSpi() : + eth_handle(nullptr), + eth_netif(nullptr) {} + + void begin(int8_t pin_miso, int8_t pin_mosi, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst) { + gpio_reset_pin(static_cast(pin_rst)); + gpio_set_direction(static_cast(pin_rst), GPIO_MODE_OUTPUT); + gpio_set_level(static_cast(pin_rst), 0); + + gpio_reset_pin(static_cast(pin_sclk)); + gpio_reset_pin(static_cast(pin_mosi)); + gpio_reset_pin(static_cast(pin_miso)); + gpio_reset_pin(static_cast(pin_cs)); + gpio_set_pull_mode(static_cast(pin_miso), GPIO_PULLUP_ONLY); + + // Workaround, because calling gpio_install_isr_service directly causes issues with attachInterrupt later + attachInterrupt(digitalPinToInterrupt(pin_int), nullptr, CHANGE); + detachInterrupt(digitalPinToInterrupt(pin_int)); + gpio_reset_pin(static_cast(pin_int)); + gpio_set_pull_mode(static_cast(pin_int), GPIO_PULLUP_ONLY); + + spi_bus_config_t buscfg = { + .mosi_io_num = pin_mosi, + .miso_io_num = pin_miso, + .sclk_io_num = pin_sclk, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .data4_io_num = -1, + .data5_io_num = -1, + .data6_io_num = -1, + .data7_io_num = -1, + .max_transfer_sz = 0, // uses default value internally + .flags = 0, + .intr_flags = 0 + }; + + ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + spi_device_interface_config_t devcfg = { + .command_bits = 16, // actually address phase + .address_bits = 8, // actually command phase + .dummy_bits = 0, + .mode = 0, + .duty_cycle_pos = 0, + .cs_ena_pretrans = 0, // only 0 supported + .cs_ena_posttrans = 0, // only 0 supported + .clock_speed_hz = 20000000, // stable with on OpenDTU Fusion Shield + .input_delay_ns = 0, + .spics_io_num = pin_cs, + .flags = 0, + .queue_size = 20, + .pre_cb = nullptr, + .post_cb = nullptr + }; + + spi_device_handle_t spi; + ESP_ERROR_CHECK(spi_bus_add_device(SPI3_HOST, &devcfg, &spi)); + + // Reset sequence + delayMicroseconds(500); + gpio_set_level(static_cast(pin_rst), 1); + delayMicroseconds(1000); + + // Arduino function to start networking stack if not already started + tcpipInit(); + + ESP_ERROR_CHECK(tcpip_adapter_set_default_eth_handlers()); // ? + + eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi); + w5500_config.int_gpio_num = pin_int; + + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + mac_config.rx_task_stack_size = 4096; + esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); + + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + phy_config.reset_gpio_num = -1; + esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config); + + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + ESP_ERROR_CHECK(esp_eth_driver_install(ð_config, ð_handle)); + + // Configure MAC address + uint8_t mac_addr[6]; + ESP_ERROR_CHECK(esp_efuse_mac_get_default(mac_addr)); + mac_addr[5] |= 0x03; // derive ethernet MAC address from base MAC address + ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_addr)); + + esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_ETH(); + eth_netif = esp_netif_new(&netif_config); + + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle))); + + // Add to Arduino + add_esp_interface_netif(ESP_IF_ETH, eth_netif); + + ESP_ERROR_CHECK(esp_eth_start(eth_handle)); + } + + String macAddress() { + uint8_t mac_addr[6] = {0, 0, 0, 0, 0, 0}; + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + char mac_addr_str[24]; + snprintf(mac_addr_str, sizeof(mac_addr_str), "%02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + return String(mac_addr_str); + } + + + private: + esp_eth_handle_t eth_handle; + esp_netif_t *eth_netif; +}; + +#endif /*__ETH_SPI_H__*/ +#endif /*ETHERNET*/ +#endif /*CONFIG_IDF_TARGET_ESP32S3*/ diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index 18ae549e..82eb85ff 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -9,6 +9,7 @@ #include #include "SPI.h" #include "radio.h" +#include "nrfHal.h" #define SPI_SPEED 1000000 @@ -28,7 +29,7 @@ const char* const rf24AmpPowerNames[] = {"MIN", "LOW", "HIGH", "MAX"}; template class HmRadio : public Radio { public: - HmRadio() : mNrf24(CE_PIN, CS_PIN, SPI_SPEED) { + HmRadio() { mDtuSn = DTU_SN; mIrqRcvd = false; } @@ -36,6 +37,14 @@ class HmRadio : public Radio { void setup(bool *serialDebug, bool *privacyMode, bool *printWholeTrace, uint8_t irq = IRQ_PIN, uint8_t ce = CE_PIN, uint8_t cs = CS_PIN, uint8_t sclk = SCLK_PIN, uint8_t mosi = MOSI_PIN, uint8_t miso = MISO_PIN) { DPRINTLN(DBG_VERBOSE, F("hmRadio.h:setup")); + #if defined(CONFIG_IDF_TARGET_ESP32S3) + DBGPRINTLN("1"); + delay(300); + mNrfHal->init(mosi, miso, sclk, cs, ce); + mNrf24 = new RF24(mNrfHal); + #else + mNrf24 = new RF24(CE_PIN, CS_PIN, SPI_SPEED); + #endif pinMode(irq, INPUT_PULLUP); mSerialDebug = serialDebug; @@ -56,39 +65,45 @@ class HmRadio : public Radio { DTU_RADIO_ID = ((uint64_t)(((mDtuSn >> 24) & 0xFF) | ((mDtuSn >> 8) & 0xFF00) | ((mDtuSn << 8) & 0xFF0000) | ((mDtuSn << 24) & 0xFF000000)) << 8) | 0x01; #ifdef ESP32 - #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 - mSpi = new SPIClass(HSPI); - #else - mSpi = new SPIClass(VSPI); + #if !defined(CONFIG_IDF_TARGET_ESP32S3) + #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 + mSpi = new SPIClass(HSPI); + #else + mSpi = new SPIClass(VSPI); + #endif + mSpi->begin(sclk, miso, mosi, cs); #endif - mSpi->begin(sclk, miso, mosi, cs); #else //the old ESP82xx cannot freely place their SPI pins mSpi = new SPIClass(); mSpi->begin(); #endif - mNrf24.begin(mSpi, ce, cs); - mNrf24.setRetries(3, 15); // 3*250us + 250us and 15 loops -> 15ms - - mNrf24.setChannel(mRfChLst[mRxChIdx]); - mNrf24.startListening(); - mNrf24.setDataRate(RF24_250KBPS); - mNrf24.setAutoAck(true); - mNrf24.enableDynamicAck(); - mNrf24.enableDynamicPayloads(); - mNrf24.setCRCLength(RF24_CRC_16); - mNrf24.setAddressWidth(5); - mNrf24.openReadingPipe(1, reinterpret_cast(&DTU_RADIO_ID)); + #if defined(CONFIG_IDF_TARGET_ESP32S3) + mNrf24->begin(); + #else + mNrf24->begin(mSpi, ce, cs); + #endif + mNrf24->setRetries(3, 15); // 3*250us + 250us and 15 loops -> 15ms + + mNrf24->setChannel(mRfChLst[mRxChIdx]); + mNrf24->startListening(); + mNrf24->setDataRate(RF24_250KBPS); + mNrf24->setAutoAck(true); + mNrf24->enableDynamicAck(); + mNrf24->enableDynamicPayloads(); + mNrf24->setCRCLength(RF24_CRC_16); + mNrf24->setAddressWidth(5); + mNrf24->openReadingPipe(1, reinterpret_cast(&DTU_RADIO_ID)); // enable all receiving interrupts - mNrf24.maskIRQ(false, false, false); + mNrf24->maskIRQ(false, false, false); - mNrf24.setPALevel(1); // low is default + mNrf24->setPALevel(1); // low is default - if(mNrf24.isChipConnected()) { + if(mNrf24->isChipConnected()) { DPRINTLN(DBG_INFO, F("Radio Config:")); - mNrf24.printPrettyDetails(); + mNrf24->printPrettyDetails(); DPRINT(DBG_INFO, F("DTU_SN: 0x")); DBGPRINTLN(String(mDtuSn, HEX)); } else @@ -100,14 +115,14 @@ class HmRadio : public Radio { return; // nothing to do mIrqRcvd = false; bool tx_ok, tx_fail, rx_ready; - mNrf24.whatHappened(tx_ok, tx_fail, rx_ready); // resets the IRQ pin to HIGH - mNrf24.flush_tx(); // empty TX FIFO + mNrf24->whatHappened(tx_ok, tx_fail, rx_ready); // resets the IRQ pin to HIGH + mNrf24->flush_tx(); // empty TX FIFO // start listening - //mNrf24.setChannel(23); + //mNrf24->setChannel(23); //mRxChIdx = 0; - mNrf24.setChannel(mRfChLst[mRxChIdx]); - mNrf24.startListening(); + mNrf24->setChannel(mRfChLst[mRxChIdx]); + mNrf24->startListening(); if(NULL == mLastIv) // prevent reading on NULL object! return; @@ -128,7 +143,7 @@ class HmRadio : public Radio { // switch to next RX channel if(++mRxChIdx >= RF_CHANNELS) mRxChIdx = 0; - mNrf24.setChannel(mRfChLst[mRxChIdx]); + mNrf24->setChannel(mRfChLst[mRxChIdx]); startMicros = micros() + 5110; } // not finished but time is over @@ -140,7 +155,7 @@ class HmRadio : public Radio { bool isChipConnected(void) { //DPRINTLN(DBG_VERBOSE, F("hmRadio.h:isChipConnected")); - return mNrf24.isChipConnected(); + return mNrf24->isChipConnected(); } void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) { @@ -229,31 +244,31 @@ class HmRadio : public Radio { } uint8_t getDataRate(void) { - if(!mNrf24.isChipConnected()) + if(!mNrf24->isChipConnected()) return 3; // unknown - return mNrf24.getDataRate(); + return mNrf24->getDataRate(); } bool isPVariant(void) { - return mNrf24.isPVariant(); + return mNrf24->isPVariant(); } private: inline bool getReceived(void) { bool tx_ok, tx_fail, rx_ready; - mNrf24.whatHappened(tx_ok, tx_fail, rx_ready); // resets the IRQ pin to HIGH + mNrf24->whatHappened(tx_ok, tx_fail, rx_ready); // resets the IRQ pin to HIGH bool isLastPackage = false; - while(mNrf24.available()) { + while(mNrf24->available()) { uint8_t len; - len = mNrf24.getDynamicPayloadSize(); // if payload size > 32, corrupt payload has been flushed + len = mNrf24->getDynamicPayloadSize(); // if payload size > 32, corrupt payload has been flushed if (len > 0) { packet_t p; p.ch = mRfChLst[mRxChIdx]; p.len = (len > MAX_RF_PAYLOAD_SIZE) ? MAX_RF_PAYLOAD_SIZE : len; - p.rssi = mNrf24.testRPD() ? -64 : -75; + p.rssi = mNrf24->testRPD() ? -64 : -75; p.millis = millis() - mMillis; - mNrf24.read(p.packet, p.len); + mNrf24->read(p.packet, p.len); if (p.packet[0] != 0x00) { if(!checkIvSerial(p.packet, mLastIv)) { DPRINT(DBG_WARN, "RX other inverter "); @@ -281,7 +296,7 @@ class HmRadio : public Radio { } void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) { - mNrf24.setPALevel(iv->config->powerLevel & 0x03); + mNrf24->setPALevel(iv->config->powerLevel & 0x03); updateCrcs(&len, appendCrc16); // set TX and RX channels @@ -303,10 +318,10 @@ class HmRadio : public Radio { DBGHEXLN(mTxBuf[9]); } - mNrf24.stopListening(); - mNrf24.setChannel(mTxChIdx); - mNrf24.openWritingPipe(reinterpret_cast(&iv->radioId.u64)); - mNrf24.startWrite(mTxBuf, len, false); // false = request ACK response + mNrf24->stopListening(); + mNrf24->setChannel(mTxChIdx); + mNrf24->openWritingPipe(reinterpret_cast(&iv->radioId.u64)); + mNrf24->startWrite(mTxBuf, len, false); // false = request ACK response mMillis = millis(); mLastIv = iv; @@ -336,7 +351,8 @@ class HmRadio : public Radio { uint32_t mMillis; SPIClass* mSpi; - RF24 mNrf24; + RF24 *mNrf24; + nrfHal *mNrfHal; Inverter<> *mLastIv = NULL; }; diff --git a/src/hm/nrfHal.h b/src/hm/nrfHal.h new file mode 100644 index 00000000..be476a76 --- /dev/null +++ b/src/hm/nrfHal.h @@ -0,0 +1,241 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __NRF_HAL_H__ +#define __NRF_HAL_H__ + +#pragma once + +#include "spiPatcher.h" + +#include +#include + +#define NRF_MAX_TRANSFER_SZ 64 +#define NRF_DEFAULT_SPI_SPEED 10000000 // 10 MHz + +class nrfHal: public RF24_hal, public SpiPatcherHandle { + public: + nrfHal() : mSpiPatcher(SPI2_HOST) {} + + void patch() override { + esp_rom_gpio_connect_out_signal(mPinMosi, spi_periph_signal[host_device].spid_out, false, false); + esp_rom_gpio_connect_in_signal(mPinMiso, spi_periph_signal[host_device].spiq_in, false); + esp_rom_gpio_connect_out_signal(mPinClk, spi_periph_signal[host_device].spiclk_out, false, false); + } + + void unpatch() override { + esp_rom_gpio_connect_out_signal(mPinMosi, SIG_GPIO_OUT_IDX, false, false); + esp_rom_gpio_connect_in_signal(mPinMiso, GPIO_MATRIX_CONST_ZERO_INPUT, false); + esp_rom_gpio_connect_out_signal(mPinClk, SIG_GPIO_OUT_IDX, false, false); + } + + void init(int8_t mosi, int8_t miso, int8_t sclk, int8_t cs, int8_t en, int32_t speed = 0) { + DHEX(mosi); + DBGPRINT(" "); + DHEX(miso); + DBGPRINT(" "); + DHEX(sclk); + DBGPRINT(" "); + DHEX(cs); + DBGPRINT(" "); + DHEX(en); + DBGPRINTLN(" "); + delay(300); + mPinMosi = static_cast<>(mosi); + DBGPRINTLN("21"); + delay(300); + mPinMiso = static_cast(miso); + DBGPRINTLN("22"); + delay(300); + mPinClk = static_cast(sclk); + DBGPRINTLN("23"); + delay(300); + mPinCs = static_cast(cs); + DBGPRINTLN("24"); + delay(300); + mPinEn = static_cast(en); + DBGPRINTLN("25"); + delay(300); + mSpiSpeed = speed; + + DBGPRINTLN("3"); + delay(300); + host_device = mSpiPatcher.init(); + DBGPRINTLN("4"); + delay(300); + + gpio_reset_pin(mPinMosi); + gpio_set_direction(mPinMosi, GPIO_MODE_OUTPUT); + gpio_set_level(mPinMosi, 1); + + gpio_reset_pin(mPinMiso); + gpio_set_direction(mPinMiso, GPIO_MODE_INPUT); + + gpio_reset_pin(mPinClk); + gpio_set_direction(mPinClk, GPIO_MODE_OUTPUT); + gpio_set_level(mPinClk, 0); + + gpio_reset_pin(mPinCs); + spi_device_interface_config_t devcfg = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = 0, + .duty_cycle_pos = 0, + .cs_ena_pretrans = 0, + .cs_ena_posttrans = 0, + .clock_speed_hz = mSpiSpeed, + .input_delay_ns = 0, + .spics_io_num = mPinCs, + .flags = 0, + .queue_size = 1, + .pre_cb = nullptr, + .post_cb = nullptr + }; + ESP_ERROR_CHECK(spi_bus_add_device(host_device, &devcfg, &spi)); + + gpio_reset_pin(mPinEn); + gpio_set_direction(mPinEn, GPIO_MODE_OUTPUT); + gpio_set_level(mPinEn, 0); + } + + + bool begin() override { + return true; + } + + void end() override {} + + void ce(bool level) override { + gpio_set_level(mPinEn, level); + } + + uint8_t write(uint8_t cmd, const uint8_t* buf, uint8_t len) override { + uint8_t data[NRF_MAX_TRANSFER_SZ]; + data[0] = cmd; + std::copy(&buf[0], &buf[len], &data[1]); + + request_spi(); + + size_t spiLen = (static_cast(len) + 1u) << 3; + spi_transaction_t t = { + .flags = 0, + .cmd = 0, + .addr = 0, + .length = spiLen, + .rxlength = spiLen, + .user = NULL, + .tx_buffer = data, + .rx_buffer = data + }; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t)); + + release_spi(); + + return data[0]; // status + } + + uint8_t write(uint8_t cmd, const uint8_t* buf, uint8_t data_len, uint8_t blank_len) override { + uint8_t data[NRF_MAX_TRANSFER_SZ]; + data[0] = cmd; + memset(data, 0, NRF_MAX_TRANSFER_SZ); + std::copy(&buf[0], &buf[data_len], &data[1]); + + request_spi(); + + size_t spiLen = (static_cast(data_len) + static_cast(blank_len) + 1u) << 3; + spi_transaction_t t = { + .flags = 0, + .cmd = 0, + .addr = 0, + .length = spiLen, + .rxlength = spiLen, + .user = NULL, + .tx_buffer = data, + .rx_buffer = data + }; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t)); + + release_spi(); + + return data[0]; // status + } + + uint8_t read(uint8_t cmd, uint8_t* buf, uint8_t len) override { + uint8_t data[NRF_MAX_TRANSFER_SZ]; + data[0] = cmd; + memset(&data[1], 0xff, len); + + request_spi(); + + size_t spiLen = (static_cast(len) + 1u) << 3; + spi_transaction_t t = { + .flags = 0, + .cmd = 0, + .addr = 0, + .length = spiLen, + .rxlength = spiLen, + .user = NULL, + .tx_buffer = data, + .rx_buffer = data + }; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t)); + + release_spi(); + + std::copy(&data[1], &data[len+1], buf); + return data[0]; // status + } + + uint8_t read(uint8_t cmd, uint8_t* buf, uint8_t data_len, uint8_t blank_len) override { + uint8_t data[NRF_MAX_TRANSFER_SZ]; + data[0] = cmd; + memset(&data[1], 0xff, (data_len + blank_len)); + + request_spi(); + + size_t spiLen = (static_cast(data_len) + static_cast(blank_len) + 1u) << 3; + spi_transaction_t t = { + .flags = 0, + .cmd = 0, + .addr = 0, + .length = spiLen, + .rxlength = spiLen, + .user = NULL, + .tx_buffer = data, + .rx_buffer = data + }; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t)); + + release_spi(); + + std::copy(&data[1], &data[data_len+1], buf); + return data[0]; // status + } + + private: + inline void request_spi() { + mSpiPatcher.request(this); + } + + inline void release_spi() { + mSpiPatcher.release(); + } + + private: + gpio_num_t mPinMosi = GPIO_NUM_NC; + gpio_num_t mPinMiso = GPIO_NUM_NC; + gpio_num_t mPinClk = GPIO_NUM_NC; + gpio_num_t mPinCs = GPIO_NUM_NC; + gpio_num_t mPinEn = GPIO_NUM_NC; + int32_t mSpiSpeed = NRF_DEFAULT_SPI_SPEED; + + spi_host_device_t host_device; + spi_device_handle_t spi; + SpiPatcher mSpiPatcher; +}; + +#endif /*__NRF_HAL_H__*/ diff --git a/src/hm/spiPatcher.h b/src/hm/spiPatcher.h new file mode 100644 index 00000000..4188fa22 --- /dev/null +++ b/src/hm/spiPatcher.h @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __SPI_PATCHER_H__ +#define __SPI_PATCHER_H__ +#pragma once + +#include "spiPatcherHandle.h" + +#include +#include + +class SpiPatcher { + public: + explicit SpiPatcher(spi_host_device_t host_device) : + host_device(host_device), initialized(false), cur_handle(nullptr) { + // Use binary semaphore instead of mutex for performance reasons + mutex = xSemaphoreCreateBinaryStatic(&mutex_buffer); + xSemaphoreGive(mutex); + } + + ~SpiPatcher() { vSemaphoreDelete(mutex); } + + spi_host_device_t init() { + if (!initialized) { + initialized = true; + + spi_bus_config_t buscfg = { + .mosi_io_num = -1, + .miso_io_num = -1, + .sclk_io_num = -1, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .data4_io_num = -1, + .data5_io_num = -1, + .data6_io_num = -1, + .data7_io_num = -1, + .max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE, + .flags = 0, + .intr_flags = 0 + }; + ESP_ERROR_CHECK(spi_bus_initialize(host_device, &buscfg, SPI_DMA_DISABLED)); + } + + return host_device; + } + + inline void request(SpiPatcherHandle* handle) { + xSemaphoreTake(mutex, portMAX_DELAY); + + if (cur_handle != handle) { + if (cur_handle) { + cur_handle->unpatch(); + } + cur_handle = handle; + if (cur_handle) { + cur_handle->patch(); + } + } + } + + inline void release() { + xSemaphoreGive(mutex); + } + + private: + const spi_host_device_t host_device; + bool initialized; + + SpiPatcherHandle* cur_handle; + + SemaphoreHandle_t mutex; + StaticSemaphore_t mutex_buffer; +}; + +#endif /*__SPI_PATCHER_H__*/ diff --git a/src/hm/spiPatcherHandle.h b/src/hm/spiPatcherHandle.h new file mode 100644 index 00000000..69aa14df --- /dev/null +++ b/src/hm/spiPatcherHandle.h @@ -0,0 +1,17 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __SPI_PATCHER_HANDLE_H__ +#define __SPI_PATCHER_HANDLE_H__ +#pragma once + +class SpiPatcherHandle { + public: + virtual ~SpiPatcherHandle() {} + virtual void patch() = 0; + virtual void unpatch() = 0; +}; + +#endif /*__SPI_PATCHER_HANDLE_H__*/ diff --git a/src/platformio.ini b/src/platformio.ini index 643a77a0..da7b1dc5 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -91,8 +91,7 @@ monitor_filters = platform = espressif32 board = esp32dev lib_deps = - khoih-prog/AsyncWebServer_ESP32_W5500 - khoih-prog/AsyncUDP_ESP32_W5500 + https://github.com/yubox-node-org/ESPAsyncWebServer nrf24/RF24 @ ^1.4.8 paulstoffregen/Time @ ^1.6.1 https://github.com/bertmelis/espMqttClient#v1.5.0 @@ -152,7 +151,7 @@ board = esp32-s3-devkitc-1 lib_deps = khoih-prog/AsyncWebServer_ESP32_W5500 khoih-prog/AsyncUDP_ESP32_W5500 - nrf24/RF24 @ ^1.4.8 + https://github.com/nrf24/RF24 @ ^1.4.8 paulstoffregen/Time @ ^1.6.1 https://github.com/bertmelis/espMqttClient#v1.5.0 bblanchon/ArduinoJson @ ^6.21.3 @@ -184,6 +183,7 @@ build_flags = ${env.build_flags} -DDEF_LED1=17 -DLED_ACTIVE_HIGH -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 monitor_filters = esp32_exception_decoder, colorize