Skip to content

Commit

Permalink
Add SpiManager library
Browse files Browse the repository at this point in the history
  • Loading branch information
LennartF22 committed Sep 24, 2024
1 parent 851190d commit 9b9c1e2
Show file tree
Hide file tree
Showing 9 changed files with 371 additions and 0 deletions.
13 changes: 13 additions & 0 deletions lib/SpiManager/library.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
49 changes: 49 additions & 0 deletions lib/SpiManager/src/SpiBus.cpp
Original file line number Diff line number Diff line change
@@ -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<SpiBusConfig> &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);
}
45 changes: 45 additions & 0 deletions lib/SpiManager/src/SpiBus.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include <driver/spi_master.h>

#include <memory>
#include <string>

class SpiBusConfig;

class SpiBus : public std::enable_shared_from_this<SpiBus> {
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<SpiBusConfig> &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;
};
67 changes: 67 additions & 0 deletions lib/SpiManager/src/SpiBusConfig.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "SpiBusConfig.h"

#include <driver/gpio.h>
#include <esp_rom_gpio.h>
#include <soc/spi_periph.h>

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);
}
}
20 changes: 20 additions & 0 deletions lib/SpiManager/src/SpiBusConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <hal/gpio_types.h>
#include <hal/spi_types.h>

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;
};
65 changes: 65 additions & 0 deletions lib/SpiManager/src/SpiCallback.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "SpiCallback.h"

#include "SpiBus.h"

#include <array>
#include <optional>

namespace SpiCallback {
namespace {
struct CallbackData {
std::shared_ptr<SpiBus> bus;
std::shared_ptr<SpiBusConfig> config;
transaction_cb_t inner_pre_cb;
transaction_cb_t inner_post_cb;
};

std::array<std::optional<CallbackData>, SPI_MANAGER_CALLBACK_COUNT> instances;

template<int N>
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<int N>
void IRAM_ATTR fn_post_cb(spi_transaction_t *trans) {
if (instances[N]->inner_post_cb)
instances[N]->inner_post_cb(trans);
}

template<int N>
inline __attribute__((always_inline)) bool alloc(CallbackData *&instance, transaction_cb_t &pre_cb, transaction_cb_t &post_cb) {
if constexpr (N > 0) {
if (alloc<N - 1>(instance, pre_cb, post_cb))
return true;
if (!instances[N - 1]) {
instances[N - 1].emplace();
instance = &*instances[N - 1];
pre_cb = fn_pre_cb<N - 1>;
post_cb = fn_post_cb<N - 1>;
return true;
}
}
return false;
}
}

bool patch(const std::shared_ptr<SpiBus> &bus, const std::shared_ptr<SpiBusConfig> &bus_config, spi_device_interface_config_t &device_config) {
CallbackData *instance;
transaction_cb_t pre_cb;
transaction_cb_t post_cb;
if (!alloc<SPI_MANAGER_CALLBACK_COUNT>(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;
}
}
15 changes: 15 additions & 0 deletions lib/SpiManager/src/SpiCallback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <driver/spi_master.h>

#include <memory>

// 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<SpiBus> &bus, const std::shared_ptr<SpiBusConfig> &bus_config, spi_device_interface_config_t &device_config);
}
64 changes: 64 additions & 0 deletions lib/SpiManager/src/SpiManager.cpp
Original file line number Diff line number Diff line change
@@ -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<SpiBusConfig> &bus_config, spi_device_interface_config_t &device_config) {
std::shared_ptr<SpiBus> shared_bus = get_shared_bus(bus_id);
if (!shared_bus)
return nullptr;

return shared_bus->add_device(bus_config, device_config);
}

std::shared_ptr<SpiBus> 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<SpiBus>(bus_id, host_device);
return shared_buses[i];
}

return nullptr;
}

SpiManager SpiManagerInst;
33 changes: 33 additions & 0 deletions lib/SpiManager/src/SpiManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include "SpiBus.h"
#include "SpiBusConfig.h"

#include <driver/spi_master.h>

#include <array>
#include <memory>
#include <optional>
#include <utility>

#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<SpiBusConfig> &bus_config, spi_device_interface_config_t &device_config);

private:
std::shared_ptr<SpiBus> get_shared_bus(const std::string &bus_id);

std::array<std::optional<spi_host_device_t>, SPI_MANAGER_NUM_BUSES> available_buses;
std::array<std::shared_ptr<SpiBus>, SPI_MANAGER_NUM_BUSES> shared_buses;
};

extern SpiManager SpiManagerInst;

0 comments on commit 9b9c1e2

Please sign in to comment.