From a02ad8b52cb467c8c14365321e17b30f73d35b54 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:47:37 +0200 Subject: [PATCH 01/11] Remove unnecessary CMT SPI inversions --- lib/CMT2300a/cmt_spi3.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index 59aad36f..b0194761 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -1,7 +1,6 @@ #include "cmt_spi3.h" #include #include -#include // for esp_rom_gpio_connect_out_signal SemaphoreHandle_t paramLock = NULL; #define SPI_PARAM_LOCK() \ @@ -63,19 +62,16 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin }; ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); - esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[SPI_CMT].spid_out, true, false); delay(100); } -void cmt_spi3_write(const uint8_t addr, const uint8_t dat) +void cmt_spi3_write(const uint8_t addr, const uint8_t data) { - uint8_t tx_data; - tx_data = ~dat; spi_transaction_t t = { - .cmd = 1, - .addr = ~addr, + .cmd = 0, + .addr = addr, .length = 8, - .tx_buffer = &tx_data, + .tx_buffer = &data, .rx_buffer = NULL }; SPI_PARAM_LOCK(); @@ -86,35 +82,31 @@ void cmt_spi3_write(const uint8_t addr, const uint8_t dat) uint8_t cmt_spi3_read(const uint8_t addr) { - uint8_t rx_data; + uint8_t data; spi_transaction_t t = { - .cmd = 0, - .addr = ~addr, - .length = 8, + .cmd = 1, + .addr = addr, .rxlength = 8, .tx_buffer = NULL, - .rx_buffer = &rx_data + .rx_buffer = &data }; SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); SPI_PARAM_UNLOCK(); delayMicroseconds(100); - return rx_data; + return data; } void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) { - uint8_t tx_data; - spi_transaction_t t = { .length = 8, - .tx_buffer = &tx_data, // reference to write data .rx_buffer = NULL }; SPI_PARAM_LOCK(); for (uint8_t i = 0; i < len; i++) { - tx_data = ~buf[i]; // negate buffer contents + t.tx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); delayMicroseconds(4); // > 4 us } @@ -123,20 +115,16 @@ void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) { - uint8_t rx_data; - spi_transaction_t t = { - .length = 8, .rxlength = 8, - .tx_buffer = NULL, - .rx_buffer = &rx_data + .tx_buffer = NULL }; SPI_PARAM_LOCK(); for (uint8_t i = 0; i < len; i++) { + t.rx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); delayMicroseconds(4); // > 4 us - buf[i] = rx_data; } SPI_PARAM_UNLOCK(); } From ec47e8978f5e941f712c8a28657af0495539f706 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:27:02 +0200 Subject: [PATCH 02/11] Fix cs_ena_posttrans calculation --- lib/CMT2300a/cmt_spi3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index b0194761..604706ab 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -52,7 +52,7 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .dummy_bits = 0, .mode = 0, // SPI mode 0 .cs_ena_pretrans = 2, - .cs_ena_posttrans = (uint8_t)(1 / (spi_speed * 10e6 * 2) + 2), // >2 us + .cs_ena_posttrans = (uint8_t)(2 * spi_speed / 1000000), // >2 us .clock_speed_hz = spi_speed, .spics_io_num = pin_fcs, .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, From 992e174bb2ea74414954ecc32716c04e29285e32 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:22:58 +0200 Subject: [PATCH 03/11] Remove unnecessary delays --- lib/CMT2300a/cmt_spi3.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index 604706ab..80d31035 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -61,8 +61,6 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .post_cb = NULL, }; ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); - - delay(100); } void cmt_spi3_write(const uint8_t addr, const uint8_t data) @@ -77,7 +75,6 @@ void cmt_spi3_write(const uint8_t addr, const uint8_t data) SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); SPI_PARAM_UNLOCK(); - delayMicroseconds(100); } uint8_t cmt_spi3_read(const uint8_t addr) @@ -93,7 +90,6 @@ uint8_t cmt_spi3_read(const uint8_t addr) SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); SPI_PARAM_UNLOCK(); - delayMicroseconds(100); return data; } @@ -108,7 +104,6 @@ void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) for (uint8_t i = 0; i < len; i++) { t.tx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); - delayMicroseconds(4); // > 4 us } SPI_PARAM_UNLOCK(); } @@ -124,7 +119,6 @@ void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) for (uint8_t i = 0; i < len; i++) { t.rx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); - delayMicroseconds(4); // > 4 us } SPI_PARAM_UNLOCK(); } From 851190dbccc0bbbf5aa7fa6580b72712b9b48079 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Fri, 21 Jul 2023 16:57:00 +0200 Subject: [PATCH 04/11] Implement W5500 support --- include/NetworkSettings.h | 2 + include/PinMapping.h | 10 +++- include/W5500.h | 19 ++++++ platformio.ini | 30 ++++++++++ src/NetworkSettings.cpp | 16 +++-- src/PinMapping.cpp | 48 +++++++++++++++ src/W5500.cpp | 119 ++++++++++++++++++++++++++++++++++++++ src/WebApi_device.cpp | 8 +++ 8 files changed, 246 insertions(+), 6 deletions(-) create mode 100644 include/W5500.h create mode 100644 src/W5500.cpp diff --git a/include/NetworkSettings.h b/include/NetworkSettings.h index 433867e9..51ec1075 100644 --- a/include/NetworkSettings.h +++ b/include/NetworkSettings.h @@ -5,6 +5,7 @@ #include #include #include +#include "W5500.h" enum class network_mode { WiFi, @@ -83,6 +84,7 @@ private: bool _ethConnected = false; std::vector _cbEventList; bool _lastMdnsEnabled = false; + std::unique_ptr _w5500; }; extern NetworkSettingsClass NetworkSettings; diff --git a/include/PinMapping.h b/include/PinMapping.h index e0db88b6..de94654c 100644 --- a/include/PinMapping.h +++ b/include/PinMapping.h @@ -26,6 +26,13 @@ struct PinMapping_t { int8_t cmt_gpio3; int8_t cmt_sdio; + int8_t w5500_mosi; + int8_t w5500_miso; + int8_t w5500_sclk; + int8_t w5500_cs; + int8_t w5500_int; + int8_t w5500_rst; + int8_t eth_phy_addr; bool eth_enabled; int eth_power; @@ -49,10 +56,11 @@ public: bool isValidNrf24Config() const; bool isValidCmt2300Config() const; + bool isValidW5500Config() const; bool isValidEthConfig() const; private: PinMapping_t _pinMapping; }; -extern PinMappingClass PinMapping; \ No newline at end of file +extern PinMappingClass PinMapping; diff --git a/include/W5500.h b/include/W5500.h new file mode 100644 index 00000000..f4ee9d2a --- /dev/null +++ b/include/W5500.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +class W5500 { +public: + explicit W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst); + W5500(const W5500&) = delete; + W5500 &operator=(const W5500&) = delete; + ~W5500(); + + String macAddress(); + +private: + esp_eth_handle_t eth_handle; + esp_netif_t *eth_netif; +}; diff --git a/platformio.ini b/platformio.ini index 99b67634..02919348 100644 --- a/platformio.ini +++ b/platformio.ini @@ -227,6 +227,7 @@ build_flags = ${env.build_flags} -DLED0=17 -DLED1=18 -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 [env:opendtufusionv2] board = esp32-s3-devkitc-1 @@ -250,3 +251,32 @@ build_flags = ${env.build_flags} -DCMT_SDIO=5 -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 + +[env:opendtufusionv2_shield] +board = esp32-s3-devkitc-1 +upload_protocol = esp-builtin +debug_tool = esp-builtin +debug_speed = 12000 +build_flags = ${env.build_flags} + -DHOYMILES_PIN_MISO=48 + -DHOYMILES_PIN_MOSI=35 + -DHOYMILES_PIN_SCLK=36 + -DHOYMILES_PIN_IRQ=47 + -DHOYMILES_PIN_CE=38 + -DHOYMILES_PIN_CS=37 + -DLED0=17 + -DLED1=18 + -DCMT_CLK=6 + -DCMT_CS=4 + -DCMT_FCS=21 + -DCMT_GPIO2=3 + -DCMT_GPIO3=8 + -DCMT_SDIO=5 + -DW5500_MOSI=40 + -DW5500_MISO=41 + -DW5500_SCLK=39 + -DW5500_CS=42 + -DW5500_INT=44 + -DW5500_RST=43 + -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index 31313feb..cab3c7e4 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -31,6 +31,15 @@ void NetworkSettingsClass::init(Scheduler& scheduler) WiFi.disconnect(true, true); WiFi.onEvent(std::bind(&NetworkSettingsClass::NetworkEvent, this, _1, _2)); + + if (PinMapping.isValidW5500Config()) { + PinMapping_t& pin = PinMapping.get(); + _w5500 = std::make_unique(pin.w5500_mosi, pin.w5500_miso, pin.w5500_sclk, pin.w5500_cs, pin.w5500_int, pin.w5500_rst); + } else if (PinMapping.isValidEthConfig()) { + PinMapping_t& pin = PinMapping.get(); + ETH.begin(pin.eth_phy_addr, pin.eth_power, pin.eth_mdc, pin.eth_mdio, pin.eth_type, pin.eth_clk_mode); + } + setupMode(); scheduler.addTask(_loopTask); @@ -169,11 +178,6 @@ void NetworkSettingsClass::setupMode() WiFi.mode(WIFI_MODE_NULL); } } - - if (PinMapping.isValidEthConfig()) { - PinMapping_t& pin = PinMapping.get(); - ETH.begin(pin.eth_phy_addr, pin.eth_power, pin.eth_mdc, pin.eth_mdio, pin.eth_type, pin.eth_clk_mode); - } } void NetworkSettingsClass::enableAdminMode() @@ -401,6 +405,8 @@ String NetworkSettingsClass::macAddress() const { switch (_networkMode) { case network_mode::Ethernet: + if (_w5500) + return _w5500->macAddress(); return ETH.macAddress(); break; case network_mode::WiFi: diff --git a/src/PinMapping.cpp b/src/PinMapping.cpp index 74f28285..7514af5f 100644 --- a/src/PinMapping.cpp +++ b/src/PinMapping.cpp @@ -84,6 +84,30 @@ #define CMT_SDIO -1 #endif +#ifndef W5500_MOSI +#define W5500_MOSI -1 +#endif + +#ifndef W5500_MISO +#define W5500_MISO -1 +#endif + +#ifndef W5500_SCLK +#define W5500_SCLK -1 +#endif + +#ifndef W5500_CS +#define W5500_CS -1 +#endif + +#ifndef W5500_INT +#define W5500_INT -1 +#endif + +#ifndef W5500_RST +#define W5500_RST -1 +#endif + PinMappingClass PinMapping; PinMappingClass::PinMappingClass() @@ -103,6 +127,13 @@ PinMappingClass::PinMappingClass() _pinMapping.cmt_gpio3 = CMT_GPIO3; _pinMapping.cmt_sdio = CMT_SDIO; + _pinMapping.w5500_mosi = W5500_MOSI; + _pinMapping.w5500_miso = W5500_MISO; + _pinMapping.w5500_sclk = W5500_SCLK; + _pinMapping.w5500_cs = W5500_CS; + _pinMapping.w5500_int = W5500_INT; + _pinMapping.w5500_rst = W5500_RST; + #ifdef OPENDTU_ETHERNET _pinMapping.eth_enabled = true; #else @@ -164,6 +195,13 @@ bool PinMappingClass::init(const String& deviceMapping) _pinMapping.cmt_gpio3 = doc[i]["cmt"]["gpio3"] | CMT_GPIO3; _pinMapping.cmt_sdio = doc[i]["cmt"]["sdio"] | CMT_SDIO; + _pinMapping.w5500_mosi = doc[i]["w5500"]["mosi"] | W5500_MOSI; + _pinMapping.w5500_miso = doc[i]["w5500"]["miso"] | W5500_MISO; + _pinMapping.w5500_sclk = doc[i]["w5500"]["sclk"] | W5500_SCLK; + _pinMapping.w5500_cs = doc[i]["w5500"]["cs"] | W5500_CS; + _pinMapping.w5500_int = doc[i]["w5500"]["int"] | W5500_INT; + _pinMapping.w5500_rst = doc[i]["w5500"]["rst"] | W5500_RST; + #ifdef OPENDTU_ETHERNET _pinMapping.eth_enabled = doc[i]["eth"]["enabled"] | true; #else @@ -211,6 +249,16 @@ bool PinMappingClass::isValidCmt2300Config() const && _pinMapping.cmt_sdio >= 0; } +bool PinMappingClass::isValidW5500Config() const +{ + return _pinMapping.w5500_mosi >= 0 + && _pinMapping.w5500_miso >= 0 + && _pinMapping.w5500_sclk >= 0 + && _pinMapping.w5500_cs >= 0 + && _pinMapping.w5500_int >= 0 + && _pinMapping.w5500_rst >= 0; +} + bool PinMappingClass::isValidEthConfig() const { return _pinMapping.eth_enabled; diff --git a/src/W5500.cpp b/src/W5500.cpp new file mode 100644 index 00000000..3ce60b8d --- /dev/null +++ b/src/W5500.cpp @@ -0,0 +1,119 @@ +#include "W5500.h" + +#include + +// Internal Arduino functions from WiFiGeneric +void tcpipInit(); +void add_esp_interface_netif(esp_interface_t interface, esp_netif_t *esp_netif); + +W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst) : + eth_handle(nullptr), + eth_netif(nullptr) +{ + gpio_reset_pin(static_cast(pin_rst)); + gpio_set_level(static_cast(pin_rst), 0); + gpio_set_direction(static_cast(pin_rst), GPIO_MODE_OUTPUT); + + gpio_reset_pin(static_cast(pin_mosi)); + gpio_reset_pin(static_cast(pin_miso)); + gpio_reset_pin(static_cast(pin_sclk)); + gpio_reset_pin(static_cast(pin_cs)); + + gpio_reset_pin(static_cast(pin_int)); + + esp_err_t err = gpio_install_isr_service(ARDUINO_ISR_FLAG); + if (err != ESP_ERR_INVALID_STATE) // don't raise an error when ISR service is already installed + ESP_ERROR_CHECK(err); + + spi_bus_config_t bus_config { + .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, &bus_config, SPI_DMA_CH_AUTO)); + + spi_device_interface_config_t device_config { + .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 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, &device_config, &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_read_mac(mac_addr, ESP_MAC_ETH)); + 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)); +} + +W5500::~W5500() { + // TODO(LennartF22): support cleanup at some point? +} + +String W5500::macAddress() { + uint8_t mac_addr[6] = {}; + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + + char mac_addr_str[18]; + 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); +} diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index 42c175f7..b4878817 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -50,6 +50,14 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) cmtPinObj["gpio2"] = pin.cmt_gpio2; cmtPinObj["gpio3"] = pin.cmt_gpio3; + auto w5500PinObj = curPin["w5500"].to(); + w5500PinObj["sclk"] = pin.w5500_sclk; + w5500PinObj["mosi"] = pin.w5500_mosi; + w5500PinObj["miso"] = pin.w5500_miso; + w5500PinObj["cs"] = pin.w5500_cs; + w5500PinObj["int"] = pin.w5500_int; + w5500PinObj["rst"] = pin.w5500_rst; + auto ethPinObj = curPin["eth"].to(); ethPinObj["enabled"] = pin.eth_enabled; ethPinObj["phy_addr"] = pin.eth_phy_addr; From 9b9c1e29f15e44b85bc29ff79468bc171e47d522 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:57:01 +0200 Subject: [PATCH 05/11] Add SpiManager library --- lib/SpiManager/library.json | 13 ++++++ lib/SpiManager/src/SpiBus.cpp | 49 +++++++++++++++++++++ lib/SpiManager/src/SpiBus.h | 45 +++++++++++++++++++ lib/SpiManager/src/SpiBusConfig.cpp | 67 +++++++++++++++++++++++++++++ lib/SpiManager/src/SpiBusConfig.h | 20 +++++++++ lib/SpiManager/src/SpiCallback.cpp | 65 ++++++++++++++++++++++++++++ lib/SpiManager/src/SpiCallback.h | 15 +++++++ lib/SpiManager/src/SpiManager.cpp | 64 +++++++++++++++++++++++++++ lib/SpiManager/src/SpiManager.h | 33 ++++++++++++++ 9 files changed, 371 insertions(+) create mode 100644 lib/SpiManager/library.json create mode 100644 lib/SpiManager/src/SpiBus.cpp create mode 100644 lib/SpiManager/src/SpiBus.h create mode 100644 lib/SpiManager/src/SpiBusConfig.cpp create mode 100644 lib/SpiManager/src/SpiBusConfig.h create mode 100644 lib/SpiManager/src/SpiCallback.cpp create mode 100644 lib/SpiManager/src/SpiCallback.h create mode 100644 lib/SpiManager/src/SpiManager.cpp create mode 100644 lib/SpiManager/src/SpiManager.h diff --git a/lib/SpiManager/library.json b/lib/SpiManager/library.json new file mode 100644 index 00000000..22e5ddc9 --- /dev/null +++ b/lib/SpiManager/library.json @@ -0,0 +1,13 @@ +{ + "name": "SpiManager", + "keywords": "spi", + "description": "Library for managing the allocation of dedicated or shared SPI buses on the ESP32.", + "authors": { + "name": "Lennart Ferlemann" + }, + "version": "0.0.1", + "frameworks": "arduino", + "platforms": [ + "espressif32" + ] +} diff --git a/lib/SpiManager/src/SpiBus.cpp b/lib/SpiManager/src/SpiBus.cpp new file mode 100644 index 00000000..26b361cc --- /dev/null +++ b/lib/SpiManager/src/SpiBus.cpp @@ -0,0 +1,49 @@ +#include "SpiBus.h" + +#include "SpiBusConfig.h" +#include "SpiCallback.h" + +SpiBus::SpiBus(const std::string &_id, spi_host_device_t _host_device) : + id(_id), + host_device(_host_device), + cur_config(nullptr) +{ + spi_bus_config_t bus_config { + .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 = SPI_MAX_DMA_LEN, + .flags = 0, + .intr_flags = 0 + }; + ESP_ERROR_CHECK(spi_bus_initialize(host_device, &bus_config, SPI_DMA_CH_AUTO)); +} + +SpiBus::~SpiBus() { + ESP_ERROR_CHECK(spi_bus_free(host_device)); +} + +spi_device_handle_t SpiBus::add_device(const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { + if (!SpiCallback::patch(shared_from_this(), bus_config, device_config)) + return nullptr; + + spi_device_handle_t device; + ESP_ERROR_CHECK(spi_bus_add_device(host_device, &device_config, &device)); + return device; +} + +// TODO: add remove_device (with spi_device_acquire_bus) + +void SpiBus::apply_config(SpiBusConfig *config) { + if (cur_config) + cur_config->unpatch(host_device); + cur_config = config; + if (cur_config) + cur_config->patch(host_device); +} diff --git a/lib/SpiManager/src/SpiBus.h b/lib/SpiManager/src/SpiBus.h new file mode 100644 index 00000000..a5fde06c --- /dev/null +++ b/lib/SpiManager/src/SpiBus.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include +#include + +class SpiBusConfig; + +class SpiBus : public std::enable_shared_from_this { +public: + explicit SpiBus(const std::string &id, spi_host_device_t host_device); + SpiBus(const SpiBus&) = delete; + SpiBus &operator=(const SpiBus&) = delete; + ~SpiBus(); + + inline __attribute__((always_inline)) void require_config(SpiBusConfig *config) { + if (config == cur_config) + return; + apply_config(config); + } + + inline __attribute__((always_inline)) void free_config(SpiBusConfig *config) { + if (config != cur_config) + return; + apply_config(nullptr); + } + + inline const std::string &get_id() const { + return id; + } + + inline spi_host_device_t get_host_device() const { + return host_device; + } + + spi_device_handle_t add_device(const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); + +private: + void apply_config(SpiBusConfig *config); + + std::string id; + spi_host_device_t host_device; + SpiBusConfig *cur_config; +}; diff --git a/lib/SpiManager/src/SpiBusConfig.cpp b/lib/SpiManager/src/SpiBusConfig.cpp new file mode 100644 index 00000000..c3cc0196 --- /dev/null +++ b/lib/SpiManager/src/SpiBusConfig.cpp @@ -0,0 +1,67 @@ +#include "SpiBusConfig.h" + +#include +#include +#include + +SpiBusConfig::SpiBusConfig(gpio_num_t _pin_mosi, gpio_num_t _pin_miso, gpio_num_t _pin_sclk) : + pin_mosi(_pin_mosi), + pin_miso(_pin_miso), + pin_sclk(_pin_sclk) +{ + if (pin_mosi != GPIO_NUM_NC) { + ESP_ERROR_CHECK(gpio_reset_pin(pin_mosi)); + ESP_ERROR_CHECK(gpio_set_direction(pin_mosi, GPIO_MODE_INPUT_OUTPUT)); + } + + if (pin_miso != GPIO_NUM_NC) { + ESP_ERROR_CHECK(gpio_reset_pin(pin_miso)); + ESP_ERROR_CHECK(gpio_set_direction(pin_miso, GPIO_MODE_INPUT)); + } + + if (pin_sclk != GPIO_NUM_NC) { + ESP_ERROR_CHECK(gpio_reset_pin(pin_sclk)); + ESP_ERROR_CHECK(gpio_set_direction(pin_sclk, GPIO_MODE_INPUT_OUTPUT)); + } +} + +SpiBusConfig::~SpiBusConfig() { + if (pin_mosi != GPIO_NUM_NC) + ESP_ERROR_CHECK(gpio_reset_pin(pin_mosi)); + + if (pin_miso != GPIO_NUM_NC) + ESP_ERROR_CHECK(gpio_reset_pin(pin_miso)); + + if (pin_sclk != GPIO_NUM_NC) + ESP_ERROR_CHECK(gpio_reset_pin(pin_sclk)); +} + +void SpiBusConfig::patch(spi_host_device_t host_device) { + if (pin_mosi != GPIO_NUM_NC) { + esp_rom_gpio_connect_out_signal(pin_mosi, spi_periph_signal[host_device].spid_out, false, false); + esp_rom_gpio_connect_in_signal(pin_mosi, spi_periph_signal[host_device].spid_in, false); + } + + if (pin_miso != GPIO_NUM_NC) + esp_rom_gpio_connect_in_signal(pin_miso, spi_periph_signal[host_device].spiq_in, false); + + if (pin_sclk != GPIO_NUM_NC) { + esp_rom_gpio_connect_out_signal(pin_sclk, spi_periph_signal[host_device].spiclk_out, false, false); + esp_rom_gpio_connect_in_signal(pin_sclk, spi_periph_signal[host_device].spiclk_in, false); + } +} + +void SpiBusConfig::unpatch(spi_host_device_t host_device) { + if (pin_mosi != GPIO_NUM_NC) { + esp_rom_gpio_connect_out_signal(pin_mosi, SIG_GPIO_OUT_IDX, false, false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spid_in, false); + } + + if (pin_miso != GPIO_NUM_NC) + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spiq_in, false); + + if (pin_sclk != GPIO_NUM_NC) { + esp_rom_gpio_connect_out_signal(pin_sclk, SIG_GPIO_OUT_IDX, false, false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spiclk_in, false); + } +} diff --git a/lib/SpiManager/src/SpiBusConfig.h b/lib/SpiManager/src/SpiBusConfig.h new file mode 100644 index 00000000..e4549ef1 --- /dev/null +++ b/lib/SpiManager/src/SpiBusConfig.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +class SpiBusConfig { +public: + explicit SpiBusConfig(gpio_num_t pin_mosi, gpio_num_t pin_miso, gpio_num_t pin_sclk); + SpiBusConfig(const SpiBusConfig&) = delete; + SpiBusConfig &operator=(const SpiBusConfig&) = delete; + ~SpiBusConfig(); + + void patch(spi_host_device_t host_device); + void unpatch(spi_host_device_t host_device); + +private: + gpio_num_t pin_mosi; + gpio_num_t pin_miso; + gpio_num_t pin_sclk; +}; diff --git a/lib/SpiManager/src/SpiCallback.cpp b/lib/SpiManager/src/SpiCallback.cpp new file mode 100644 index 00000000..e281db98 --- /dev/null +++ b/lib/SpiManager/src/SpiCallback.cpp @@ -0,0 +1,65 @@ +#include "SpiCallback.h" + +#include "SpiBus.h" + +#include +#include + +namespace SpiCallback { + namespace { + struct CallbackData { + std::shared_ptr bus; + std::shared_ptr config; + transaction_cb_t inner_pre_cb; + transaction_cb_t inner_post_cb; + }; + + std::array, SPI_MANAGER_CALLBACK_COUNT> instances; + + template + void IRAM_ATTR fn_pre_cb(spi_transaction_t *trans) { + instances[N]->bus->require_config(instances[N]->config.get()); + if (instances[N]->inner_pre_cb) + instances[N]->inner_pre_cb(trans); + } + + template + void IRAM_ATTR fn_post_cb(spi_transaction_t *trans) { + if (instances[N]->inner_post_cb) + instances[N]->inner_post_cb(trans); + } + + template + inline __attribute__((always_inline)) bool alloc(CallbackData *&instance, transaction_cb_t &pre_cb, transaction_cb_t &post_cb) { + if constexpr (N > 0) { + if (alloc(instance, pre_cb, post_cb)) + return true; + if (!instances[N - 1]) { + instances[N - 1].emplace(); + instance = &*instances[N - 1]; + pre_cb = fn_pre_cb; + post_cb = fn_post_cb; + return true; + } + } + return false; + } + } + + bool patch(const std::shared_ptr &bus, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { + CallbackData *instance; + transaction_cb_t pre_cb; + transaction_cb_t post_cb; + if (!alloc(instance, pre_cb, post_cb)) + return false; + + instance->bus = bus; + instance->config = bus_config; + instance->inner_pre_cb = device_config.pre_cb; + instance->inner_post_cb = device_config.post_cb; + device_config.pre_cb = pre_cb; + device_config.post_cb = post_cb; + + return true; + } +} diff --git a/lib/SpiManager/src/SpiCallback.h b/lib/SpiManager/src/SpiCallback.h new file mode 100644 index 00000000..f8d52d0d --- /dev/null +++ b/lib/SpiManager/src/SpiCallback.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include + +// Pre and post callbacks for 2 buses with 3 devices each +#define SPI_MANAGER_CALLBACK_COUNT 6 + +class SpiBus; +class SpiBusConfig; + +namespace SpiCallback { + bool patch(const std::shared_ptr &bus, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); +} diff --git a/lib/SpiManager/src/SpiManager.cpp b/lib/SpiManager/src/SpiManager.cpp new file mode 100644 index 00000000..f4dc967a --- /dev/null +++ b/lib/SpiManager/src/SpiManager.cpp @@ -0,0 +1,64 @@ +#include "SpiManager.h" + +SpiManager::SpiManager() { +} + +bool SpiManager::register_bus(spi_host_device_t host_device) { + for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { + if (available_buses[i]) + continue; + + available_buses[i] = host_device; + return true; + } + + return false; +} + +bool SpiManager::claim_bus(spi_host_device_t &host_device) { + for (int i = SPI_MANAGER_NUM_BUSES - 1; i >= 0; --i) { + if (!available_buses[i]) + continue; + + host_device = *available_buses[i]; + available_buses[i].reset(); + return true; + } + + return false; +} + +spi_device_handle_t SpiManager::alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { + std::shared_ptr shared_bus = get_shared_bus(bus_id); + if (!shared_bus) + return nullptr; + + return shared_bus->add_device(bus_config, device_config); +} + +std::shared_ptr SpiManager::get_shared_bus(const std::string &bus_id) { + // look for existing shared bus + for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { + if (!shared_buses[i]) + continue; + if (shared_buses[i]->get_id() == bus_id) + return shared_buses[i]; + } + + // create new shared bus + for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { + if (shared_buses[i]) + continue; + + spi_host_device_t host_device; + if (!claim_bus(host_device)) + return nullptr; + + shared_buses[i] = std::make_shared(bus_id, host_device); + return shared_buses[i]; + } + + return nullptr; +} + +SpiManager SpiManagerInst; diff --git a/lib/SpiManager/src/SpiManager.h b/lib/SpiManager/src/SpiManager.h new file mode 100644 index 00000000..92923d5b --- /dev/null +++ b/lib/SpiManager/src/SpiManager.h @@ -0,0 +1,33 @@ +#pragma once + +#include "SpiBus.h" +#include "SpiBusConfig.h" + +#include + +#include +#include +#include +#include + +#define SPI_MANAGER_NUM_BUSES SOC_SPI_PERIPH_NUM + +class SpiManager { +public: + explicit SpiManager(); + SpiManager(const SpiManager&) = delete; + SpiManager &operator=(const SpiManager&) = delete; + + bool register_bus(spi_host_device_t host_device); + bool claim_bus(spi_host_device_t &host_device); + + spi_device_handle_t alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); + +private: + std::shared_ptr get_shared_bus(const std::string &bus_id); + + std::array, SPI_MANAGER_NUM_BUSES> available_buses; + std::array, SPI_MANAGER_NUM_BUSES> shared_buses; +}; + +extern SpiManager SpiManagerInst; From 4364daf54c1e57f639f78dc32d4e100b94db487e Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:46:29 +0200 Subject: [PATCH 06/11] Optimize CMT FIFO access --- lib/CMT2300a/cmt_spi3.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index 80d31035..aeae3d46 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -101,10 +101,12 @@ void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) }; SPI_PARAM_LOCK(); + spi_device_acquire_bus(spi_fifo, portMAX_DELAY); for (uint8_t i = 0; i < len; i++) { t.tx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); } + spi_device_release_bus(spi_fifo); SPI_PARAM_UNLOCK(); } @@ -116,9 +118,11 @@ void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) }; SPI_PARAM_LOCK(); + spi_device_acquire_bus(spi_fifo, portMAX_DELAY); for (uint8_t i = 0; i < len; i++) { t.rx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); } + spi_device_release_bus(spi_fifo); SPI_PARAM_UNLOCK(); } From 1a583e765d7a4a78f1df5187e80ef30f884063e6 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:42:52 +0200 Subject: [PATCH 07/11] Change cmt_spi3 implementation from C to C++ --- lib/CMT2300a/{cmt_spi3.c => cmt_spi3.cpp} | 54 +++++++++++++++++------ lib/CMT2300a/cmt_spi3.h | 10 ++++- 2 files changed, 50 insertions(+), 14 deletions(-) rename lib/CMT2300a/{cmt_spi3.c => cmt_spi3.cpp} (74%) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.cpp similarity index 74% rename from lib/CMT2300a/cmt_spi3.c rename to lib/CMT2300a/cmt_spi3.cpp index aeae3d46..aaf00fc9 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.cpp @@ -15,7 +15,7 @@ SemaphoreHandle_t paramLock = NULL; spi_device_handle_t spi_reg, spi_fifo; -void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed) +void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed) { paramLock = xSemaphoreCreateMutex(); @@ -25,24 +25,32 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .sclk_io_num = pin_clk, .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 = 32, + .flags = 0, + .intr_flags = 0, }; + ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED)); + spi_device_interface_config_t devcfg = { .command_bits = 1, .address_bits = 7, .dummy_bits = 0, .mode = 0, // SPI mode 0 + .duty_cycle_pos = 0, .cs_ena_pretrans = 1, .cs_ena_posttrans = 1, .clock_speed_hz = spi_speed, + .input_delay_ns = 0, .spics_io_num = pin_cs, .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, .queue_size = 1, - .pre_cb = NULL, - .post_cb = NULL, + .pre_cb = nullptr, + .post_cb = nullptr, }; - - ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED)); ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg)); // FiFo @@ -51,14 +59,16 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .address_bits = 0, .dummy_bits = 0, .mode = 0, // SPI mode 0 + .duty_cycle_pos = 0, .cs_ena_pretrans = 2, - .cs_ena_posttrans = (uint8_t)(2 * spi_speed / 1000000), // >2 us + .cs_ena_posttrans = static_cast(2 * spi_speed / 1000000), // >2 us .clock_speed_hz = spi_speed, + .input_delay_ns = 0, .spics_io_num = pin_fcs, .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, .queue_size = 1, - .pre_cb = NULL, - .post_cb = NULL, + .pre_cb = nullptr, + .post_cb = nullptr, }; ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); } @@ -66,11 +76,14 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin void cmt_spi3_write(const uint8_t addr, const uint8_t data) { spi_transaction_t t = { + .flags = 0, .cmd = 0, .addr = addr, .length = 8, + .rxlength = 0, + .user = nullptr, .tx_buffer = &data, - .rx_buffer = NULL + .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); @@ -81,11 +94,14 @@ uint8_t cmt_spi3_read(const uint8_t addr) { uint8_t data; spi_transaction_t t = { + .flags = 0, .cmd = 1, .addr = addr, + .length = 0, .rxlength = 8, - .tx_buffer = NULL, - .rx_buffer = &data + .user = nullptr, + .tx_buffer = nullptr, + .rx_buffer = &data, }; SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); @@ -96,8 +112,14 @@ uint8_t cmt_spi3_read(const uint8_t addr) void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) { spi_transaction_t t = { + .flags = 0, + .cmd = 0, + .addr = 0, .length = 8, - .rx_buffer = NULL + .rxlength = 0, + .user = nullptr, + .tx_buffer = nullptr, + .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); @@ -113,8 +135,14 @@ void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) { spi_transaction_t t = { + .flags = 0, + .cmd = 0, + .addr = 0, + .length = 0, .rxlength = 8, - .tx_buffer = NULL + .user = nullptr, + .tx_buffer = nullptr, + .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); diff --git a/lib/CMT2300a/cmt_spi3.h b/lib/CMT2300a/cmt_spi3.h index 6d3a67b6..16655dba 100644 --- a/lib/CMT2300a/cmt_spi3.h +++ b/lib/CMT2300a/cmt_spi3.h @@ -3,7 +3,11 @@ #include -void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed); +#ifdef __cplusplus +extern "C" { +#endif + +void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed); void cmt_spi3_write(const uint8_t addr, const uint8_t dat); uint8_t cmt_spi3_read(const uint8_t addr); @@ -11,4 +15,8 @@ uint8_t cmt_spi3_read(const uint8_t addr); void cmt_spi3_write_fifo(const uint8_t* p_buf, const uint16_t len); void cmt_spi3_read_fifo(uint8_t* p_buf, const uint16_t len); +#ifdef __cplusplus +} +#endif + #endif From ece452068754f72dbb857ef69cb4309439dccbcc Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 21:19:06 +0200 Subject: [PATCH 08/11] Add Arduino SPI translation --- lib/SpiManager/src/SpiManager.cpp | 42 +++++++++++++++++++++++++++++++ lib/SpiManager/src/SpiManager.h | 7 ++++++ 2 files changed, 49 insertions(+) diff --git a/lib/SpiManager/src/SpiManager.cpp b/lib/SpiManager/src/SpiManager.cpp index f4dc967a..efbd5e0a 100644 --- a/lib/SpiManager/src/SpiManager.cpp +++ b/lib/SpiManager/src/SpiManager.cpp @@ -1,8 +1,39 @@ #include "SpiManager.h" +#ifdef ARDUINO +#include +#endif + SpiManager::SpiManager() { } +#ifdef ARDUINO + +std::optional SpiManager::to_arduino(spi_host_device_t host_device) { + switch (host_device) { +#if CONFIG_IDF_TARGET_ESP32 + case SPI1_HOST: + return FSPI; + case SPI2_HOST: + return HSPI; + case SPI3_HOST: + return VSPI; +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + case SPI2_HOST: + return FSPI; + case SPI3_HOST: + return HSPI; +#elif CONFIG_IDF_TARGET_ESP32C3 + case SPI2_HOST: + return FSPI; +#endif + default: + return std::nullopt; + } +} + +#endif + bool SpiManager::register_bus(spi_host_device_t host_device) { for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { if (available_buses[i]) @@ -28,6 +59,17 @@ bool SpiManager::claim_bus(spi_host_device_t &host_device) { return false; } +#ifdef ARDUINO + +std::optional SpiManager::claim_bus_arduino() { + spi_host_device_t host_device; + if (!claim_bus(host_device)) + return std::nullopt; + return to_arduino(host_device); +} + +#endif + spi_device_handle_t SpiManager::alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { std::shared_ptr shared_bus = get_shared_bus(bus_id); if (!shared_bus) diff --git a/lib/SpiManager/src/SpiManager.h b/lib/SpiManager/src/SpiManager.h index 92923d5b..ee3b56a5 100644 --- a/lib/SpiManager/src/SpiManager.h +++ b/lib/SpiManager/src/SpiManager.h @@ -18,8 +18,15 @@ public: SpiManager(const SpiManager&) = delete; SpiManager &operator=(const SpiManager&) = delete; +#ifdef ARDUINO + static std::optional to_arduino(spi_host_device_t host_device); +#endif + bool register_bus(spi_host_device_t host_device); bool claim_bus(spi_host_device_t &host_device); +#ifdef ARDUINO + std::optional claim_bus_arduino(); +#endif spi_device_handle_t alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); From 5457db269cf17238c37e4882b69fb1dbc1d41f68 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 21:45:50 +0200 Subject: [PATCH 09/11] Use SpiManager for nRF, CMT and W5500 --- lib/CMT2300a/cmt_spi3.cpp | 16 ++++++++-------- src/InverterSettings.cpp | 20 +++++--------------- src/W5500.cpp | 10 ++++++++-- src/main.cpp | 9 +++++++++ 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.cpp b/lib/CMT2300a/cmt_spi3.cpp index aaf00fc9..181bbb56 100644 --- a/lib/CMT2300a/cmt_spi3.cpp +++ b/lib/CMT2300a/cmt_spi3.cpp @@ -1,6 +1,7 @@ #include "cmt_spi3.h" #include #include +#include SemaphoreHandle_t paramLock = NULL; #define SPI_PARAM_LOCK() \ @@ -8,17 +9,16 @@ SemaphoreHandle_t paramLock = NULL; } while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS) #define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock) -// for ESP32 this is the so-called HSPI -// for ESP32-S2/S3/C3 this nomenclature does not really exist anymore, -// it is simply the first externally usable hardware SPI master controller -#define SPI_CMT SPI2_HOST - spi_device_handle_t spi_reg, spi_fifo; void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed) { paramLock = xSemaphoreCreateMutex(); + spi_host_device_t host_device; + if (!SpiManagerInst.claim_bus(host_device)) + ESP_ERROR_CHECK(ESP_FAIL); + spi_bus_config_t buscfg = { .mosi_io_num = pin_sdio, .miso_io_num = -1, // single wire MOSI/MISO @@ -33,7 +33,7 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .flags = 0, .intr_flags = 0, }; - ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED)); + ESP_ERROR_CHECK(spi_bus_initialize(host_device, &buscfg, SPI_DMA_DISABLED)); spi_device_interface_config_t devcfg = { .command_bits = 1, @@ -51,7 +51,7 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .pre_cb = nullptr, .post_cb = nullptr, }; - ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg)); + ESP_ERROR_CHECK(spi_bus_add_device(host_device, &devcfg, &spi_reg)); // FiFo spi_device_interface_config_t devcfg2 = { @@ -70,7 +70,7 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .pre_cb = nullptr, .post_cb = nullptr, }; - ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); + ESP_ERROR_CHECK(spi_bus_add_device(host_device, &devcfg2, &spi_fifo)); } void cmt_spi3_write(const uint8_t addr, const uint8_t data) diff --git a/src/InverterSettings.cpp b/src/InverterSettings.cpp index 0e903187..7e7a5f86 100644 --- a/src/InverterSettings.cpp +++ b/src/InverterSettings.cpp @@ -8,20 +8,7 @@ #include "PinMapping.h" #include "SunPosition.h" #include - -// the NRF shall use the second externally usable HW SPI controller -// for ESP32 that is the so-called VSPI, for ESP32-S2/S3 it is now called implicitly -// HSPI, as it has shifted places for these chip generations -// for all generations, this is equivalent to SPI3_HOST in the lower level driver -// For ESP32-C2, the only externally usable HW SPI controller is SPI2, its signal names -// being prefixed with FSPI. -#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 -#define SPI_NRF HSPI -#elif CONFIG_IDF_TARGET_ESP32C3 -#define SPI_NRF FSPI -#else -#define SPI_NRF VSPI -#endif +#include InverterSettingsClass InverterSettings; @@ -44,7 +31,10 @@ void InverterSettingsClass::init(Scheduler& scheduler) if (PinMapping.isValidNrf24Config() || PinMapping.isValidCmt2300Config()) { if (PinMapping.isValidNrf24Config()) { - SPIClass* spiClass = new SPIClass(SPI_NRF); + auto spi_bus = SpiManagerInst.claim_bus_arduino(); + ESP_ERROR_CHECK(spi_bus ? ESP_OK : ESP_FAIL); + + SPIClass* spiClass = new SPIClass(*spi_bus); spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs); Hoymiles.initNRF(spiClass, pin.nrf24_en, pin.nrf24_irq); } diff --git a/src/W5500.cpp b/src/W5500.cpp index 3ce60b8d..93a9a316 100644 --- a/src/W5500.cpp +++ b/src/W5500.cpp @@ -1,5 +1,7 @@ #include "W5500.h" +#include + #include // Internal Arduino functions from WiFiGeneric @@ -10,6 +12,10 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i eth_handle(nullptr), eth_netif(nullptr) { + spi_host_device_t host_device; + if (!SpiManagerInst.claim_bus(host_device)) + ESP_ERROR_CHECK(ESP_FAIL); + gpio_reset_pin(static_cast(pin_rst)); gpio_set_level(static_cast(pin_rst), 0); gpio_set_direction(static_cast(pin_rst), GPIO_MODE_OUTPUT); @@ -40,7 +46,7 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i .intr_flags = 0, }; - ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &bus_config, SPI_DMA_CH_AUTO)); + ESP_ERROR_CHECK(spi_bus_initialize(host_device, &bus_config, SPI_DMA_CH_AUTO)); spi_device_interface_config_t device_config { .command_bits = 16, // actually address phase @@ -60,7 +66,7 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i }; spi_device_handle_t spi; - ESP_ERROR_CHECK(spi_bus_add_device(SPI3_HOST, &device_config, &spi)); + ESP_ERROR_CHECK(spi_bus_add_device(host_device, &device_config, &spi)); // Reset sequence delayMicroseconds(500); diff --git a/src/main.cpp b/src/main.cpp index 00ab3f3a..0377c8b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,12 +26,21 @@ #include #include #include +#include + +#include void setup() { // Move all dynamic allocations >512byte to psram (if available) heap_caps_malloc_extmem_enable(512); + // Initialize SpiManager + SpiManagerInst.register_bus(SPI2_HOST); +#if SOC_SPI_PERIPH_NUM > 2 + SpiManagerInst.register_bus(SPI3_HOST); +#endif + // Initialize serial output Serial.begin(SERIAL_BAUDRATE); #if ARDUINO_USB_CDC_ON_BOOT From 36da830f96e49b3b7e2a2843e43c4e2116248ef8 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 22:06:10 +0200 Subject: [PATCH 10/11] Use shared SPI bus for CMT and W5500 --- lib/CMT2300a/cmt_spi3.cpp | 34 +++++++++++++--------------------- src/W5500.cpp | 34 ++++++++-------------------------- 2 files changed, 21 insertions(+), 47 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.cpp b/lib/CMT2300a/cmt_spi3.cpp index 181bbb56..f6f912fb 100644 --- a/lib/CMT2300a/cmt_spi3.cpp +++ b/lib/CMT2300a/cmt_spi3.cpp @@ -15,25 +15,11 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin { paramLock = xSemaphoreCreateMutex(); - spi_host_device_t host_device; - if (!SpiManagerInst.claim_bus(host_device)) - ESP_ERROR_CHECK(ESP_FAIL); - - spi_bus_config_t buscfg = { - .mosi_io_num = pin_sdio, - .miso_io_num = -1, // single wire MOSI/MISO - .sclk_io_num = pin_clk, - .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 = 32, - .flags = 0, - .intr_flags = 0, - }; - ESP_ERROR_CHECK(spi_bus_initialize(host_device, &buscfg, SPI_DMA_DISABLED)); + auto bus_config = std::make_shared( + static_cast(pin_sdio), + GPIO_NUM_NC, + static_cast(pin_clk) + ); spi_device_interface_config_t devcfg = { .command_bits = 1, @@ -51,7 +37,10 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .pre_cb = nullptr, .post_cb = nullptr, }; - ESP_ERROR_CHECK(spi_bus_add_device(host_device, &devcfg, &spi_reg)); + + spi_reg = SpiManagerInst.alloc_device("", bus_config, devcfg); + if (!spi_reg) + ESP_ERROR_CHECK(ESP_FAIL); // FiFo spi_device_interface_config_t devcfg2 = { @@ -70,7 +59,10 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .pre_cb = nullptr, .post_cb = nullptr, }; - ESP_ERROR_CHECK(spi_bus_add_device(host_device, &devcfg2, &spi_fifo)); + + spi_fifo = SpiManagerInst.alloc_device("", bus_config, devcfg2); + if (!spi_fifo) + ESP_ERROR_CHECK(ESP_ERR_NOT_SUPPORTED); } void cmt_spi3_write(const uint8_t addr, const uint8_t data) diff --git a/src/W5500.cpp b/src/W5500.cpp index 93a9a316..3f274204 100644 --- a/src/W5500.cpp +++ b/src/W5500.cpp @@ -12,41 +12,22 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i eth_handle(nullptr), eth_netif(nullptr) { - spi_host_device_t host_device; - if (!SpiManagerInst.claim_bus(host_device)) - ESP_ERROR_CHECK(ESP_FAIL); - gpio_reset_pin(static_cast(pin_rst)); gpio_set_level(static_cast(pin_rst), 0); gpio_set_direction(static_cast(pin_rst), GPIO_MODE_OUTPUT); - gpio_reset_pin(static_cast(pin_mosi)); - gpio_reset_pin(static_cast(pin_miso)); - gpio_reset_pin(static_cast(pin_sclk)); gpio_reset_pin(static_cast(pin_cs)); - gpio_reset_pin(static_cast(pin_int)); esp_err_t err = gpio_install_isr_service(ARDUINO_ISR_FLAG); if (err != ESP_ERR_INVALID_STATE) // don't raise an error when ISR service is already installed ESP_ERROR_CHECK(err); - spi_bus_config_t bus_config { - .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(host_device, &bus_config, SPI_DMA_CH_AUTO)); + auto bus_config = std::make_shared( + static_cast(pin_mosi), + static_cast(pin_miso), + static_cast(pin_sclk) + ); spi_device_interface_config_t device_config { .command_bits = 16, // actually address phase @@ -65,8 +46,9 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i .post_cb = nullptr, }; - spi_device_handle_t spi; - ESP_ERROR_CHECK(spi_bus_add_device(host_device, &device_config, &spi)); + spi_device_handle_t spi = SpiManagerInst.alloc_device("", bus_config, device_config); + if (!spi) + ESP_ERROR_CHECK(ESP_FAIL); // Reset sequence delayMicroseconds(500); From 31cf756a7ecbeed133218ea56263ac9a0a302fae Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 23:00:31 +0200 Subject: [PATCH 11/11] Only use a single SPI device for CMT --- lib/CMT2300a/cmt_spi3.cpp | 137 ++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 65 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.cpp b/lib/CMT2300a/cmt_spi3.cpp index f6f912fb..28fdc8ae 100644 --- a/lib/CMT2300a/cmt_spi3.cpp +++ b/lib/CMT2300a/cmt_spi3.cpp @@ -9,7 +9,16 @@ SemaphoreHandle_t paramLock = NULL; } while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS) #define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock) -spi_device_handle_t spi_reg, spi_fifo; +static void IRAM_ATTR pre_cb(spi_transaction_t *trans) { + gpio_set_level(*reinterpret_cast(trans->user), 0); +} + +static void IRAM_ATTR post_cb(spi_transaction_t *trans) { + gpio_set_level(*reinterpret_cast(trans->user), 1); +} + +spi_device_handle_t spi; +gpio_num_t cs_reg, cs_fifo; void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed) { @@ -21,128 +30,126 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin static_cast(pin_clk) ); - spi_device_interface_config_t devcfg = { - .command_bits = 1, - .address_bits = 7, + spi_device_interface_config_t device_config { + .command_bits = 0, // set by transactions individually + .address_bits = 0, // set by transactions individually .dummy_bits = 0, .mode = 0, // SPI mode 0 .duty_cycle_pos = 0, - .cs_ena_pretrans = 1, - .cs_ena_posttrans = 1, - .clock_speed_hz = spi_speed, - .input_delay_ns = 0, - .spics_io_num = pin_cs, - .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, - .queue_size = 1, - .pre_cb = nullptr, - .post_cb = nullptr, - }; - - spi_reg = SpiManagerInst.alloc_device("", bus_config, devcfg); - if (!spi_reg) - ESP_ERROR_CHECK(ESP_FAIL); - - // FiFo - spi_device_interface_config_t devcfg2 = { - .command_bits = 0, - .address_bits = 0, - .dummy_bits = 0, - .mode = 0, // SPI mode 0 - .duty_cycle_pos = 0, - .cs_ena_pretrans = 2, + .cs_ena_pretrans = 2, // only 1 pre and post cycle would be required for register access .cs_ena_posttrans = static_cast(2 * spi_speed / 1000000), // >2 us .clock_speed_hz = spi_speed, .input_delay_ns = 0, - .spics_io_num = pin_fcs, + .spics_io_num = -1, // CS handled by callbacks .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, .queue_size = 1, - .pre_cb = nullptr, - .post_cb = nullptr, + .pre_cb = pre_cb, + .post_cb = post_cb, }; - spi_fifo = SpiManagerInst.alloc_device("", bus_config, devcfg2); - if (!spi_fifo) - ESP_ERROR_CHECK(ESP_ERR_NOT_SUPPORTED); + spi = SpiManagerInst.alloc_device("", bus_config, device_config); + if (!spi) + ESP_ERROR_CHECK(ESP_FAIL); + + cs_reg = static_cast(pin_cs); + ESP_ERROR_CHECK(gpio_reset_pin(cs_reg)); + ESP_ERROR_CHECK(gpio_set_level(cs_reg, 1)); + ESP_ERROR_CHECK(gpio_set_direction(cs_reg, GPIO_MODE_OUTPUT)); + + cs_fifo = static_cast(pin_fcs); + ESP_ERROR_CHECK(gpio_reset_pin(cs_fifo)); + ESP_ERROR_CHECK(gpio_set_level(cs_fifo, 1)); + ESP_ERROR_CHECK(gpio_set_direction(cs_fifo, GPIO_MODE_OUTPUT)); } void cmt_spi3_write(const uint8_t addr, const uint8_t data) { - spi_transaction_t t = { - .flags = 0, - .cmd = 0, - .addr = addr, - .length = 8, - .rxlength = 0, - .user = nullptr, - .tx_buffer = &data, - .rx_buffer = nullptr, + spi_transaction_ext_t trans { + .base { + .flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR, + .cmd = 0, + .addr = addr, + .length = 8, + .rxlength = 0, + .user = &cs_reg, // CS for register access + .tx_buffer = &data, + .rx_buffer = nullptr, + }, + .command_bits = 1, + .address_bits = 7, + .dummy_bits = 0, }; SPI_PARAM_LOCK(); - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast(&trans))); SPI_PARAM_UNLOCK(); } uint8_t cmt_spi3_read(const uint8_t addr) { uint8_t data; - spi_transaction_t t = { - .flags = 0, - .cmd = 1, - .addr = addr, - .length = 0, - .rxlength = 8, - .user = nullptr, - .tx_buffer = nullptr, - .rx_buffer = &data, + spi_transaction_ext_t trans { + .base { + .flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR, + .cmd = 1, + .addr = addr, + .length = 0, + .rxlength = 8, + .user = &cs_reg, // CS for register access + .tx_buffer = nullptr, + .rx_buffer = &data, + }, + .command_bits = 1, + .address_bits = 7, + .dummy_bits = 0, }; SPI_PARAM_LOCK(); - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast(&trans))); SPI_PARAM_UNLOCK(); return data; } void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) { - spi_transaction_t t = { + spi_transaction_t trans { .flags = 0, .cmd = 0, .addr = 0, .length = 8, .rxlength = 0, - .user = nullptr, + .user = &cs_fifo, // CS for FIFO access .tx_buffer = nullptr, .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); - spi_device_acquire_bus(spi_fifo, portMAX_DELAY); + spi_device_acquire_bus(spi, portMAX_DELAY); for (uint8_t i = 0; i < len; i++) { - t.tx_buffer = buf + i; - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); + trans.tx_buffer = buf + i; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans)); } - spi_device_release_bus(spi_fifo); + spi_device_release_bus(spi); SPI_PARAM_UNLOCK(); } void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) { - spi_transaction_t t = { + spi_transaction_t trans { .flags = 0, .cmd = 0, .addr = 0, .length = 0, .rxlength = 8, - .user = nullptr, + .user = &cs_fifo, // CS for FIFO access .tx_buffer = nullptr, .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); - spi_device_acquire_bus(spi_fifo, portMAX_DELAY); + spi_device_acquire_bus(spi, portMAX_DELAY); for (uint8_t i = 0; i < len; i++) { - t.rx_buffer = buf + i; - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); + trans.rx_buffer = buf + i; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans)); } - spi_device_release_bus(spi_fifo); + spi_device_release_bus(spi); SPI_PARAM_UNLOCK(); }