HF-MAX22200 Driver 0.1.0-dev
HF-MAX22200 C++ Driver
Loading...
Searching...
No Matches
esp32_max22200_bus.hpp
Go to the documentation of this file.
1
16#pragma once
17
20#include "driver/gpio.h"
21#include "driver/spi_master.h"
22#include "esp_log.h"
23#include "esp_rom_sys.h"
24#include <cstddef>
25#include <cstdint>
26#include <cstdio>
27#include <cstring>
28#include <memory>
29
38class Esp32Max22200SpiBus : public max22200::SpiInterface<Esp32Max22200SpiBus> {
39public:
46 struct SPIConfig {
47 spi_host_device_t host = SPI2_HOST;
48 gpio_num_t miso_pin;
49 gpio_num_t mosi_pin;
50 gpio_num_t sclk_pin;
51 gpio_num_t cs_pin;
52 int16_t enable_pin = -1;
53 int16_t fault_pin = -1;
54 int16_t cmd_pin = -1;
55 int16_t triga_pin = -1;
56 int16_t trigb_pin = -1;
57 uint32_t frequency = 10000000;
58 uint8_t mode = 0;
59 uint8_t queue_size = 1;
60 uint8_t cs_ena_pretrans = 1;
61 uint8_t cs_ena_posttrans = 1;
62 };
63
68 explicit Esp32Max22200SpiBus(const SPIConfig &config)
69 : config_(config), spi_device_(nullptr), initialized_(false) {}
70
75 if (spi_device_ != nullptr) {
76 spi_bus_remove_device(spi_device_);
77 spi_bus_free(config_.host);
78 spi_device_ = nullptr;
79 }
80 }
81
86 bool Initialize() {
87 if (initialized_) {
88 return true;
89 }
90
91 if (!initializeGPIO()) {
92 ESP_LOGE(TAG, "Failed to initialize GPIO pins");
93 return false;
94 }
95
96 if (!initializeSPI()) {
97 ESP_LOGE(TAG, "Failed to initialize SPI bus");
98 return false;
99 }
100
101 if (!addSPIDevice()) {
102 ESP_LOGE(TAG, "Failed to add SPI device");
103 spi_bus_free(config_.host);
104 return false;
105 }
106
107 initialized_ = true;
108#if (ESP32_MAX22200_ENABLE_VERBOSE_BUS_LOGGING != 0)
109 ESP_LOGI(TAG, "SPI interface initialized successfully");
110#endif
111 return true;
112 }
113
123
124 bool Transfer(const uint8_t *tx_data, uint8_t *rx_data, size_t length) {
125 if (!initialized_ || spi_device_ == nullptr) {
126 ESP_LOGE(TAG, "SPI not initialized");
127 return false;
128 }
129
130 spi_transaction_t trans = {};
131 trans.length = length * 8; // Length in bits
132 trans.tx_buffer = tx_data;
133 trans.rx_buffer = rx_data;
134
135 esp_err_t ret = spi_device_transmit(spi_device_, &trans);
136 if (ret != ESP_OK) {
137 ESP_LOGE(TAG, "SPI transfer failed: %s", esp_err_to_name(ret));
138 return false;
139 }
140
141 if (LOG_SPI_HEX && tx_data != nullptr && rx_data != nullptr && length > 0) {
142 char buf[128];
143 int n = 0;
144 n += snprintf(buf + n, sizeof(buf) - n, "SPI(%zu) TX:", length);
145 for (size_t i = 0; i < length && n < (int)sizeof(buf) - 6; i++)
146 n += snprintf(buf + n, sizeof(buf) - n, " %02X", tx_data[i]);
147 n += snprintf(buf + n, sizeof(buf) - n, " RX:");
148 for (size_t i = 0; i < length && n < (int)sizeof(buf) - 6; i++)
149 n += snprintf(buf + n, sizeof(buf) - n, " %02X", rx_data[i]);
150 ESP_LOGI(TAG, "%s", buf);
151 }
152
153 return true;
154 }
155
160 void SetChipSelect(bool state) {
161 // CS is handled automatically by ESP-IDF SPI driver
162 // This function is kept for interface compatibility
163 (void)state;
164 }
165
173 bool Configure(uint32_t speed_hz, uint8_t mode, bool msb_first = true) {
174 if (!initialized_) {
175 ESP_LOGE(TAG, "SPI not initialized");
176 return false;
177 }
178
179 // ESP-IDF doesn't support runtime reconfiguration easily
180 // Configuration is set during device addition
181 (void)speed_hz;
182 (void)mode;
183 (void)msb_first;
184 return true;
185 }
186
191 bool IsReady() const {
192 return initialized_ && (spi_device_ != nullptr);
193 }
194
199 void DelayUs(uint32_t us) {
200 esp_rom_delay_us(us);
201 }
202
203 // ── GPIO Pin Control ─────────────────────────────────────────────────
204
217 int gpio_pin = -1;
218 int level = 0;
219 switch (pin) {
221 gpio_pin = config_.enable_pin;
222 level = (signal == max22200::GpioSignal::ACTIVE) ? 1 : 0; // Active-high
223 break;
225 gpio_pin = config_.cmd_pin;
226 level = (signal == max22200::GpioSignal::ACTIVE) ? 1 : 0; // Active-high
227 break;
229 return; // FAULT is read-only
230 }
231 if (gpio_pin >= 0) {
232 gpio_set_level(static_cast<gpio_num_t>(gpio_pin), level);
233 }
234 }
235
248 if (pin != max22200::CtrlPin::FAULT || config_.fault_pin < 0) {
249 return false;
250 }
251 int level = gpio_get_level(static_cast<gpio_num_t>(config_.fault_pin));
252 // Active-low: low = fault (ACTIVE), high = no fault (INACTIVE)
253 signal = (level == 0) ? max22200::GpioSignal::ACTIVE
255 return true;
256 }
257
262 void SetTrigA(bool active) {
263 if (config_.triga_pin >= 0) {
264 gpio_set_level(static_cast<gpio_num_t>(config_.triga_pin), active ? 1 : 0);
265 }
266 }
267
272 void SetTrigB(bool active) {
273 if (config_.trigb_pin >= 0) {
274 gpio_set_level(static_cast<gpio_num_t>(config_.trigb_pin), active ? 1 : 0);
275 }
276 }
277
279 bool HasTrigA() const { return config_.triga_pin >= 0; }
281 bool HasTrigB() const { return config_.trigb_pin >= 0; }
282
283private:
284 SPIConfig config_;
285 spi_device_handle_t spi_device_;
286 bool initialized_;
287 static constexpr const char *TAG = "Esp32Max22200SpiBus";
288
293 bool initializeGPIO() {
294 // Configure output pins (ENABLE, CMD)
295 auto configure_output = [this](int16_t pin, const char *name, int initial) -> bool {
296 if (pin < 0) return true; // Not configured, skip
297 gpio_config_t cfg = {
298 .pin_bit_mask = (1ULL << pin),
299 .mode = GPIO_MODE_OUTPUT,
300 .pull_up_en = GPIO_PULLUP_DISABLE,
301 .pull_down_en = GPIO_PULLDOWN_DISABLE,
302 .intr_type = GPIO_INTR_DISABLE};
303 if (gpio_config(&cfg) != ESP_OK) {
304 ESP_LOGE(TAG, "Failed to configure %s pin (GPIO%d)", name, pin);
305 return false;
306 }
307 gpio_set_level(static_cast<gpio_num_t>(pin), initial);
308#if (ESP32_MAX22200_ENABLE_VERBOSE_BUS_LOGGING != 0)
309 ESP_LOGI(TAG, "%s pin (GPIO%d) initialized, level=%d", name, pin, initial);
310#endif
311 return true;
312 };
313 if (!configure_output(config_.enable_pin, "ENABLE", 0)) return false;
314 if (!configure_output(config_.cmd_pin, "CMD", 1)) return false; // CMD HIGH = SPI mode
315 if (!configure_output(config_.triga_pin, "TRIGA", 1)) return false; // TRIGA high = inactive
316 if (!configure_output(config_.trigb_pin, "TRIGB", 1)) return false; // TRIGB high = inactive
317
318 // FAULT: input, active-low, inactive-high. Pull-up so inactive = high when open-drain released.
319 if (config_.fault_pin >= 0) {
320 gpio_config_t cfg = {
321 .pin_bit_mask = (1ULL << config_.fault_pin),
322 .mode = GPIO_MODE_INPUT,
323 .pull_up_en = GPIO_PULLUP_ENABLE,
324 .pull_down_en = GPIO_PULLDOWN_DISABLE,
325 .intr_type = GPIO_INTR_DISABLE};
326 if (gpio_config(&cfg) != ESP_OK) {
327 ESP_LOGE(TAG, "Failed to configure FAULT pin (GPIO%d)", config_.fault_pin);
328 return false;
329 }
330#if (ESP32_MAX22200_ENABLE_VERBOSE_BUS_LOGGING != 0)
331 ESP_LOGI(TAG, "FAULT pin (GPIO%d) initialized as input (active-low, inactive-high)", config_.fault_pin);
332#endif
333 }
334 return true;
335 }
336
346 bool initializeSPI() {
347 // Set MISO pull-up before SPI claims the pin (SDO is open-drain; pull high when released)
348 esp_err_t ret = ESP_OK;
349 ret = gpio_set_pull_mode(static_cast<gpio_num_t>(config_.miso_pin), GPIO_PULLUP_ONLY);
350 if (ret != ESP_OK) {
351 ESP_LOGW(TAG, "MISO pullup set failed: %s (external pullup on SDO recommended)", esp_err_to_name(ret));
352 }
353
354 spi_bus_config_t bus_cfg = {};
355 bus_cfg.mosi_io_num = config_.mosi_pin;
356 bus_cfg.miso_io_num = config_.miso_pin;
357 bus_cfg.sclk_io_num = config_.sclk_pin;
358 bus_cfg.quadwp_io_num = -1;
359 bus_cfg.quadhd_io_num = -1;
360 bus_cfg.max_transfer_sz = 64; // MAX22200 uses 3-byte transfers
361 bus_cfg.flags = SPICOMMON_BUSFLAG_MASTER;
362
363 ret = spi_bus_initialize(config_.host, &bus_cfg, SPI_DMA_CH_AUTO);
364 if (ret != ESP_OK) {
365 ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret));
366 return false;
367 }
368
369 return true;
370 }
371
376 bool addSPIDevice() {
377 spi_device_interface_config_t dev_cfg = {};
378 dev_cfg.command_bits = 0;
379 dev_cfg.address_bits = 0;
380 dev_cfg.dummy_bits = 0;
381 dev_cfg.clock_speed_hz = config_.frequency;
382 dev_cfg.mode = config_.mode;
383 dev_cfg.duty_cycle_pos = 128;
384 dev_cfg.spics_io_num = config_.cs_pin;
385 dev_cfg.queue_size = config_.queue_size;
386 dev_cfg.cs_ena_pretrans = config_.cs_ena_pretrans;
387 dev_cfg.cs_ena_posttrans = config_.cs_ena_posttrans;
388 dev_cfg.flags = 0;
389 dev_cfg.input_delay_ns = 0;
390 dev_cfg.pre_cb = nullptr;
391 dev_cfg.post_cb = nullptr;
392
393 esp_err_t ret = spi_bus_add_device(config_.host, &dev_cfg, &spi_device_);
394 if (ret != ESP_OK) {
395 ESP_LOGE(TAG, "Failed to add SPI device: %s", esp_err_to_name(ret));
396 return false;
397 }
398
399 return true;
400 }
401};
402
411inline auto CreateEsp32Max22200SpiBus() noexcept -> std::unique_ptr<Esp32Max22200SpiBus> {
412 using namespace MAX22200_TestConfig;
413
415
416 // SPI pins from esp32_max22200_test_config.hpp
417 config.host = SPI2_HOST;
418 config.miso_pin = static_cast<gpio_num_t>(SPIPins::MISO);
419 config.mosi_pin = static_cast<gpio_num_t>(SPIPins::MOSI);
420 config.sclk_pin = static_cast<gpio_num_t>(SPIPins::SCLK);
421 config.cs_pin = static_cast<gpio_num_t>(SPIPins::CS);
422
423 // SPI parameters from esp32_max22200_test_config.hpp
424 config.frequency = SPIParams::FREQUENCY;
425 config.mode = SPIParams::MODE;
426 config.queue_size = SPIParams::QUEUE_SIZE;
427 config.cs_ena_pretrans = SPIParams::CS_ENA_PRETRANS;
428 config.cs_ena_posttrans = SPIParams::CS_ENA_POSTTRANS;
429
430 // Control pins from esp32_max22200_test_config.hpp
431 config.enable_pin = ControlPins::ENABLE;
432 config.fault_pin = ControlPins::FAULT;
433 config.cmd_pin = ControlPins::CMD;
434 config.triga_pin = ControlPins::TRIGA;
435 config.trigb_pin = ControlPins::TRIGB;
436
437 return std::make_unique<Esp32Max22200SpiBus>(config);
438}
#define ESP32_MAX22200_ENABLE_DETAILED_SPI_LOGGING
Definition c21_cycle_test.cpp:61
ESP32 SPI transport implementation for MAX22200 driver.
Definition esp32_max22200_bus.hpp:38
~Esp32Max22200SpiBus()
Destructor - cleans up SPI resources.
Definition esp32_max22200_bus.hpp:74
bool Transfer(const uint8_t *tx_data, uint8_t *rx_data, size_t length)
Definition esp32_max22200_bus.hpp:124
bool HasTrigB() const
Definition esp32_max22200_bus.hpp:281
void DelayUs(uint32_t us)
Blocking delay in microseconds (for MAX22200 power-up, etc.). Uses ESP-ROM delay so it is safe before...
Definition esp32_max22200_bus.hpp:199
bool HasTrigA() const
Definition esp32_max22200_bus.hpp:279
bool IsReady() const
Check if SPI interface is ready.
Definition esp32_max22200_bus.hpp:191
void SetTrigB(bool active)
Set TRIGB pin level (direct-drive trigger B)
Definition esp32_max22200_bus.hpp:272
void SetChipSelect(bool state)
Set chip select state.
Definition esp32_max22200_bus.hpp:160
void GpioSet(max22200::CtrlPin pin, max22200::GpioSignal signal)
Set a control pin to the specified signal state.
Definition esp32_max22200_bus.hpp:216
static constexpr bool LOG_SPI_HEX
Perform a full-duplex SPI data transfer.
Definition esp32_max22200_bus.hpp:122
bool Initialize()
Initialize the SPI bus.
Definition esp32_max22200_bus.hpp:86
Esp32Max22200SpiBus(const SPIConfig &config)
Constructor with SPI configuration (pins must be set by caller)
Definition esp32_max22200_bus.hpp:68
bool Configure(uint32_t speed_hz, uint8_t mode, bool msb_first=true)
Configure SPI parameters.
Definition esp32_max22200_bus.hpp:173
void SetTrigA(bool active)
Set TRIGA pin level (direct-drive trigger A)
Definition esp32_max22200_bus.hpp:262
bool GpioRead(max22200::CtrlPin pin, max22200::GpioSignal &signal)
Read the current state of a control pin.
Definition esp32_max22200_bus.hpp:247
CRTP-based template interface for SPI communication.
Definition max22200_spi_interface.hpp:114
auto CreateEsp32Max22200SpiBus() noexcept -> std::unique_ptr< Esp32Max22200SpiBus >
Factory function to create a configured Esp32Max22200SpiBus instance.
Definition esp32_max22200_bus.hpp:411
Hardware configuration for MAX22200 driver on ESP32-C6.
CRTP-based template interface for SPI communication.
Definition esp32_max22200_test_config.hpp:55
Definition c21_cycle_test.cpp:82
GpioSignal
Abstract signal level for control pins.
Definition max22200_spi_interface.hpp:85
@ ACTIVE
Pin function is asserted.
@ INACTIVE
Pin function is deasserted.
CtrlPin
Identifies the hardware control pins of the MAX22200.
Definition max22200_spi_interface.hpp:71
@ FAULT
Fault status output (active-low, open-drain)
@ CMD
Command mode select (HIGH = SPI register, LOW = direct drive)
@ ENABLE
Output enable (active-high on the physical pin)
SPI configuration structure.
Definition esp32_max22200_bus.hpp:46
int16_t triga_pin
TRIGA trigger pin (direct drive, -1 = not configured)
Definition esp32_max22200_bus.hpp:55
uint32_t frequency
SPI frequency in Hz (default 10MHz)
Definition esp32_max22200_bus.hpp:57
gpio_num_t sclk_pin
SCLK pin (set from board config)
Definition esp32_max22200_bus.hpp:50
uint8_t cs_ena_pretrans
CS asserted N clock cycles before transaction.
Definition esp32_max22200_bus.hpp:60
int16_t trigb_pin
TRIGB trigger pin (direct drive, -1 = not configured)
Definition esp32_max22200_bus.hpp:56
int16_t enable_pin
ENABLE pin (active-high, -1 = not configured)
Definition esp32_max22200_bus.hpp:52
int16_t fault_pin
FAULT pin (active-low, inactive-high; open-drain input; -1 = not configured)
Definition esp32_max22200_bus.hpp:53
gpio_num_t miso_pin
MISO pin (set from board config)
Definition esp32_max22200_bus.hpp:48
uint8_t cs_ena_posttrans
CS held N clock cycles after transaction.
Definition esp32_max22200_bus.hpp:61
uint8_t queue_size
Transaction queue size.
Definition esp32_max22200_bus.hpp:59
spi_host_device_t host
SPI host (e.g. SPI2_HOST for ESP32-C6)
Definition esp32_max22200_bus.hpp:47
int16_t cmd_pin
CMD pin (active-high = SPI mode, -1 = not configured)
Definition esp32_max22200_bus.hpp:54
uint8_t mode
SPI mode (default 0: CPOL=0, CPHA=0)
Definition esp32_max22200_bus.hpp:58
gpio_num_t cs_pin
CS pin (set from board config)
Definition esp32_max22200_bus.hpp:51
gpio_num_t mosi_pin
MOSI pin (set from board config)
Definition esp32_max22200_bus.hpp:49