Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: SPIPortManager to allow use of CMT radio and Huawei charger at the same time #1144

Merged
55 changes: 55 additions & 0 deletions include/SPIPortManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include <array>
#include <optional>
#include <string>
#include <driver/spi_master.h>

/**
* SPI# to SPI ID and SPI_HOST mapping
*
* ESP32
* | SPI # | SPI ID | SPI_HOST |
* | 2 | 2 | 1 |
* | 3 | 3 | 2 |
*
* ESP32-S3
* | SPI # | SPI ID | SPI_HOST |
* | 2 | 0 | 1 |
* | 3 | 1 | 2 |
*
* ESP32-C3
* | SPI # | SPI ID | SPI_HOST |
* | 2 | 0 | 1 |
*
*/

class SPIPortManagerClass {
public:
void init();

std::optional<uint8_t> allocatePort(std::string const& owner);
void freePort(std::string const& owner);
spi_host_device_t SPIhostNum(uint8_t spi_num);

private:
// the amount of SPIs available on supported ESP32 chips
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3
static size_t constexpr _num_controllers = 4;
#else
static size_t constexpr _num_controllers = 3;
#endif

#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
static int8_t constexpr _offset_spi_num = -2; // FSPI=0, HSPI=1
static int8_t constexpr _offset_spi_host = 1; // SPI1_HOST=0 but not usable, SPI2_HOST=1 and SPI3_HOST=2, first usable is SPI2_HOST
#else
static int8_t constexpr _offset_spi_num = 0; // HSPI=2, VSPI=3
static int8_t constexpr _offset_spi_host = -1; // SPI1_HOST=0 but not usable, SPI2_HOST=1 and SPI3_HOST=2, first usable is SPI2_HOST
#endif

std::array<std::string, _num_controllers> _ports = { "" };
};

extern SPIPortManagerClass SPIPortManager;
4 changes: 2 additions & 2 deletions lib/CMT2300a/cmt2300a_hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
* @name CMT2300A_InitSpi
* @desc Initializes the CMT2300A SPI interface.
* *********************************************************/
void CMT2300A_InitSpi(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 CMT2300A_InitSpi(const spi_host_device_t spi_host, 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)
{
cmt_spi3_init(pin_sdio, pin_clk, pin_cs, pin_fcs, spi_speed);
cmt_spi3_init(spi_host, pin_sdio, pin_clk, pin_cs, pin_fcs, spi_speed);
}

/*! ********************************************************
Expand Down
3 changes: 2 additions & 1 deletion lib/CMT2300a/cmt2300a_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include <stdint.h>
#include <Arduino.h>
#include <driver/spi_master.h>

#ifdef __cplusplus
extern "C" {
Expand All @@ -36,7 +37,7 @@ extern "C" {
#define CMT2300A_GetTickCount() millis()
/* ************************************************************************ */

void CMT2300A_InitSpi(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 CMT2300A_InitSpi(const spi_host_device_t spi_host, 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);

uint8_t CMT2300A_ReadReg(const uint8_t addr);
void CMT2300A_WriteReg(const uint8_t addr, const uint8_t dat);
Expand Down
5 changes: 3 additions & 2 deletions lib/CMT2300a/cmt2300wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
#include "cmt2300a_params_860.h"
#include "cmt2300a_params_900.h"

CMT2300A::CMT2300A(const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t spi_speed)
CMT2300A::CMT2300A(const spi_host_device_t spi_host, const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t spi_speed)
{
_spi_host = spi_host;
_pin_sdio = pin_sdio;
_pin_clk = pin_clk;
_pin_cs = pin_cs;
Expand Down Expand Up @@ -266,7 +267,7 @@ void CMT2300A::flush_rx(void)

bool CMT2300A::_init_pins()
{
CMT2300A_InitSpi(_pin_sdio, _pin_clk, _pin_cs, _pin_fcs, _spi_speed);
CMT2300A_InitSpi(_spi_host, _pin_sdio, _pin_clk, _pin_cs, _pin_fcs, _spi_speed);

return true; // assuming pins are connected properly
}
Expand Down
4 changes: 3 additions & 1 deletion lib/CMT2300a/cmt2300wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#pragma once

#include <stdint.h>
#include <driver/spi_master.h>

#define CMT2300A_ONE_STEP_SIZE 2500 // frequency channel step size for fast frequency hopping operation: One step size is 2.5 kHz.
#define FH_OFFSET 100 // value * CMT2300A_ONE_STEP_SIZE = channel frequency offset
Expand All @@ -18,7 +19,7 @@ enum FrequencyBand_t {

class CMT2300A {
public:
CMT2300A(const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t _spi_speed = CMT_SPI_SPEED);
CMT2300A(const spi_host_device_t spi_host, const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t _spi_speed = CMT_SPI_SPEED);

bool begin(void);

Expand Down Expand Up @@ -128,6 +129,7 @@ class CMT2300A {
*/
bool _init_radio();

spi_host_device_t _spi_host;
int8_t _pin_sdio;
int8_t _pin_clk;
int8_t _pin_cs;
Expand Down
16 changes: 5 additions & 11 deletions lib/CMT2300a/cmt_spi3.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "cmt_spi3.h"
#include <Arduino.h>
#include <driver/spi_master.h>
#include <esp_rom_gpio.h> // for esp_rom_gpio_connect_out_signal

SemaphoreHandle_t paramLock = NULL;
Expand All @@ -9,14 +8,9 @@ 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 uint32_t spi_speed)
void cmt_spi3_init(const spi_host_device_t spi_host, 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)
{
paramLock = xSemaphoreCreateMutex();

Expand All @@ -43,8 +37,8 @@ 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_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED));
ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg));
ESP_ERROR_CHECK(spi_bus_initialize(spi_host, &buscfg, SPI_DMA_DISABLED));
ESP_ERROR_CHECK(spi_bus_add_device(spi_host, &devcfg, &spi_reg));

// FiFo
spi_device_interface_config_t devcfg2 = {
Expand All @@ -61,9 +55,9 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin
.pre_cb = NULL,
.post_cb = NULL,
};
ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo));
ESP_ERROR_CHECK(spi_bus_add_device(spi_host, &devcfg2, &spi_fifo));

esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[SPI_CMT].spid_out, true, false);
esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[spi_host].spid_out, true, false);
delay(100);
}

Expand Down
3 changes: 2 additions & 1 deletion lib/CMT2300a/cmt_spi3.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
#define __CMT_SPI3_H

#include <stdint.h>
#include <driver/spi_master.h>

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 spi_host_device_t spi_host, 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_write(const uint8_t addr, const uint8_t dat);
uint8_t cmt_spi3_read(const uint8_t addr);
Expand Down
4 changes: 2 additions & 2 deletions lib/Hoymiles/src/Hoymiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ void HoymilesClass::initNRF(SPIClass* initialisedSpiBus, const uint8_t pinCE, co
_radioNrf->init(initialisedSpiBus, pinCE, pinIRQ);
}

void HoymilesClass::initCMT(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3)
void HoymilesClass::initCMT(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3)
{
_radioCmt->init(pin_sdio, pin_clk, pin_cs, pin_fcs, pin_gpio2, pin_gpio3);
_radioCmt->init(spi_host, pin_sdio, pin_clk, pin_cs, pin_fcs, pin_gpio2, pin_gpio3);
}

void HoymilesClass::loop()
Expand Down
5 changes: 3 additions & 2 deletions lib/Hoymiles/src/Hoymiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <SPI.h>
#include <memory>
#include <vector>
#include <driver/spi_master.h>

#define HOY_SYSTEM_CONFIG_PARA_POLL_INTERVAL (2 * 60 * 1000) // 2 minutes
#define HOY_SYSTEM_CONFIG_PARA_POLL_MIN_DURATION (4 * 60 * 1000) // at least 4 minutes between sending limit command and read request. Otherwise eventlog entry
Expand All @@ -17,7 +18,7 @@ class HoymilesClass {
public:
void init();
void initNRF(SPIClass* initialisedSpiBus, const uint8_t pinCE, const uint8_t pinIRQ);
void initCMT(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3);
void initCMT(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3);
void loop();

void setMessageOutput(Print* output);
Expand Down Expand Up @@ -54,4 +55,4 @@ class HoymilesClass {
Print* _messageOutput = &Serial;
};

extern HoymilesClass Hoymiles;
extern HoymilesClass Hoymiles;
4 changes: 2 additions & 2 deletions lib/Hoymiles/src/HoymilesRadio_CMT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ bool HoymilesRadio_CMT::cmtSwitchDtuFreq(const uint32_t to_frequency)
return true;
}

void HoymilesRadio_CMT::init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3)
void HoymilesRadio_CMT::init(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3)
{
_dtuSerial.u64 = 0;

_radio.reset(new CMT2300A(pin_sdio, pin_clk, pin_cs, pin_fcs));
_radio.reset(new CMT2300A(spi_host, pin_sdio, pin_clk, pin_cs, pin_fcs));

_radio->begin();

Expand Down
3 changes: 2 additions & 1 deletion lib/Hoymiles/src/HoymilesRadio_CMT.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <memory>
#include <queue>
#include <vector>
#include <driver/spi_master.h>

// number of fragments hold in buffer
#define FRAGMENT_BUFFER_SIZE 30
Expand Down Expand Up @@ -41,7 +42,7 @@ struct CountryFrequencyList_t {

class HoymilesRadio_CMT : public HoymilesRadio {
public:
void init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3);
void init(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3);
void loop();
void setPALevel(const int8_t paLevel);
void setInverterTargetFrequency(const uint32_t frequency);
Expand Down
9 changes: 7 additions & 2 deletions src/Huawei_can.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "PowerLimiter.h"
#include "Configuration.h"
#include "Battery.h"
#include "SPIPortManager.h"
#include <SPI.h>
#include <mcp_can.h>

Expand All @@ -35,7 +36,11 @@ void HuaweiCanCommunicationTask(void* parameter) {

bool HuaweiCanCommClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk,
uint8_t huawei_irq, uint8_t huawei_cs, uint32_t frequency) {
SPI = new SPIClass(HSPI);

auto oSPInum = SPIPortManager.allocatePort("Huawei CAN");
if (!oSPInum) { return false; }

SPI = new SPIClass(*oSPInum);
SPI->begin(huawei_clk, huawei_miso, huawei_mosi, huawei_cs);
pinMode(huawei_cs, OUTPUT);
digitalWrite(huawei_cs, HIGH);
Expand Down Expand Up @@ -231,7 +236,7 @@ void HuaweiCanClass::updateSettings(uint8_t huawei_miso, uint8_t huawei_mosi, ui
_mode = HUAWEI_MODE_AUTO_INT;
}

xTaskCreate(HuaweiCanCommunicationTask,"HUAWEI_CAN_0",1000,NULL,0,&_HuaweiCanCommunicationTaskHdl);
xTaskCreate(HuaweiCanCommunicationTask,"HUAWEI_CAN_0",2000,NULL,0,&_HuaweiCanCommunicationTaskHdl);

MessageOutput.println("[HuaweiCanClass::init] MCP2515 Initialized Successfully!");
_initialized = true;
Expand Down
41 changes: 18 additions & 23 deletions src/InverterSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,9 @@
#include "MessageOutput.h"
#include "PinMapping.h"
#include "SunPosition.h"
#include "SPIPortManager.h"
#include <Hoymiles.h>

// 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

InverterSettingsClass InverterSettings;

InverterSettingsClass::InverterSettingsClass()
Expand All @@ -37,24 +24,32 @@ void InverterSettingsClass::init(Scheduler& scheduler)
const PinMapping_t& pin = PinMapping.get();

// Initialize inverter communication
MessageOutput.print("Initialize Hoymiles interface... ");
MessageOutput.println("Initialize Hoymiles interface... ");

Hoymiles.setMessageOutput(&MessageOutput);
Hoymiles.init();

if (PinMapping.isValidNrf24Config() || PinMapping.isValidCmt2300Config()) {
if (PinMapping.isValidNrf24Config()) {
SPIClass* spiClass = new SPIClass(SPI_NRF);
spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs);
Hoymiles.initNRF(spiClass, pin.nrf24_en, pin.nrf24_irq);
auto oSPInum = SPIPortManager.allocatePort("NRF24");

if (oSPInum) {
SPIClass* spiClass = new SPIClass(*oSPInum);
spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs);
Hoymiles.initNRF(spiClass, pin.nrf24_en, pin.nrf24_irq);
}
}

if (PinMapping.isValidCmt2300Config()) {
Hoymiles.initCMT(pin.cmt_sdio, pin.cmt_clk, pin.cmt_cs, pin.cmt_fcs, pin.cmt_gpio2, pin.cmt_gpio3);
MessageOutput.println(" Setting country mode... ");
Hoymiles.getRadioCmt()->setCountryMode(static_cast<CountryModeId_t>(config.Dtu.Cmt.CountryMode));
MessageOutput.println(" Setting CMT target frequency... ");
Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency);
auto oSPInum = SPIPortManager.allocatePort("CMT2300A");

if (oSPInum) {
Hoymiles.initCMT(SPIPortManager.SPIhostNum(*oSPInum), pin.cmt_sdio, pin.cmt_clk, pin.cmt_cs, pin.cmt_fcs, pin.cmt_gpio2, pin.cmt_gpio3);
MessageOutput.println(" Setting country mode... ");
Hoymiles.getRadioCmt()->setCountryMode(static_cast<CountryModeId_t>(config.Dtu.Cmt.CountryMode));
MessageOutput.println(" Setting CMT target frequency... ");
Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency);
}
}

MessageOutput.println(" Setting radio PA level... ");
Expand Down
46 changes: 46 additions & 0 deletions src/SPIPortManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "SPIPortManager.h"
#include "MessageOutput.h"

SPIPortManagerClass SPIPortManager;
static constexpr char TAG[] = "[SPIPortManager]";

void SPIPortManagerClass::init() {
MessageOutput.printf("%s SPI0 and SPI1 reserved by 'Flash and PSRAM'\r\n", TAG);
_ports[0] = "Flash";
_ports[1] = "PSRAM";
}

std::optional<uint8_t> SPIPortManagerClass::allocatePort(std::string const& owner)
{
for (size_t i = 0; i < _ports.size(); ++i) {
if (_ports[i] != "") {
MessageOutput.printf("%s SPI%d already in use by '%s'\r\n", TAG, i, _ports[i].c_str());
continue;
}

_ports[i] = owner;

MessageOutput.printf("%s SPI%d now in use by '%s'\r\n", TAG, i, owner.c_str());

return i + _offset_spi_num;
}

MessageOutput.printf("%s Cannot assign another SPI port to '%s'\r\n", TAG, owner.c_str());
return std::nullopt;
}

void SPIPortManagerClass::freePort(std::string const& owner)
{
for (size_t i = 0; i < _ports.size(); ++i) {
if (_ports[i] != owner) { continue; }

MessageOutput.printf("%s Freeing SPI%d, owner was '%s'\r\n", TAG, i + _offset_spi_num, owner.c_str());
_ports[i] = "";
}
}

spi_host_device_t SPIPortManagerClass::SPIhostNum(uint8_t spi_num)
{
return (spi_host_device_t)(spi_num + _offset_spi_host);
}
Loading
Loading