15#include "tle92466ed_spi_interface.hpp"
17#include "driver/spi_master.h"
18#include "driver/gpio.h"
24#include "freertos/FreeRTOS.h"
25#include "freertos/task.h"
26#include "tle92466ed_registers.hpp"
28using namespace tle92466ed;
31 [[nodiscard]]
constexpr inline uint32_t
byte_swap_32(uint32_t value)
noexcept {
32 return ((value & 0xFF000000U) >> 24) |
33 ((value & 0x00FF0000U) >> 8) |
34 ((value & 0x0000FF00U) << 8) |
35 ((value & 0x000000FFU) << 24);
54 spi_host_device_t
host = SPI2_HOST;
81 ESP_LOGI(
TAG,
"Esp32Tle92466edSpiBus created with SPI config:");
88 const char* mode_desc;
90 case 0: mode_desc =
"CPOL=0, CPHA=0";
break;
91 case 1: mode_desc =
"CPOL=0, CPHA=1";
break;
92 case 2: mode_desc =
"CPOL=1, CPHA=0";
break;
93 case 3: mode_desc =
"CPOL=1, CPHA=1";
break;
94 default: mode_desc =
"Invalid";
break;
125 auto Init() noexcept -> CommResult<
void> {
127 ESP_LOGW(
TAG,
"CommInterface already initialized");
130 ESP_LOGI(
TAG,
"Initializing Esp32Tle92466edSpiBus...");
132 ESP_LOGE(
TAG,
"Failed to initialize GPIO pins");
133 return tle::unexpected(result.error());
136 ESP_LOGE(
TAG,
"Failed to initialize SPI bus");
137 return tle::unexpected(result.error());
140 ESP_LOGE(
TAG,
"Failed to add SPI device");
142 return tle::unexpected(result.error());
145 ESP_LOGI(
TAG,
"Esp32Tle92466edSpiBus initialized successfully");
153 auto Deinit() noexcept -> CommResult<
void> {
155 ESP_LOGW(
TAG,
"CommInterface not initialized");
158 ESP_LOGI(
TAG,
"Deinitializing Esp32Tle92466edSpiBus...");
165 ESP_LOGI(
TAG,
"Esp32Tle92466edSpiBus deinitialized successfully");
174 auto Transfer32(uint32_t tx_data)
noexcept -> CommResult<uint32_t> {
176 ESP_LOGE(
TAG,
"CommInterface not initialized");
177 return tle::unexpected(CommError::HardwareNotReady);
180 uint32_t rx_data_raw = 0;
181 spi_transaction_t trans = {};
183 trans.tx_buffer = &tx_data_swapped;
184 trans.rx_buffer = &rx_data_raw;
186#if ESP32_TLE_COMM_ENABLE_DETAILED_SPI_LOGGING
187 uint8_t* tx_bytes_orig =
reinterpret_cast<uint8_t*
>(&tx_data);
188 uint8_t* tx_bytes_swapped =
reinterpret_cast<uint8_t*
>(&tx_data_swapped);
189 uint8_t crc_tx = tx_bytes_orig[3];
190 uint8_t addr_byte = tx_bytes_orig[2];
191 uint8_t data_high = tx_bytes_orig[1];
192 uint8_t data_low = tx_bytes_orig[0];
193 uint8_t address_upper = (addr_byte >> 1) & 0x7F;
194 bool is_write = (addr_byte & 0x01) != 0;
195 uint16_t data_16bit = (
static_cast<uint16_t
>(data_high) << 8) | data_low;
196 ESP_LOGI(
TAG,
"SPI TX Frame: 0x%08X | Swapped: 0x%08X", tx_data, tx_data_swapped);
197 SPIFrame tx_frame_for_crc;
198 tx_frame_for_crc.word = tx_data;
199 uint8_t tx_crc_received = tx_frame_for_crc.tx_fields.crc;
200 tx_frame_for_crc.tx_fields.crc = 0;
201 uint8_t tx_crc_calculated = CalculateFrameCrc(tx_frame_for_crc);
202 bool tx_crc_match = (tx_crc_received == tx_crc_calculated);
203 ESP_LOGI(
TAG,
" CRC=0x%02X calc=0x%02X %s | Addr=0x%02X %s | Data=0x%04X",
204 tx_crc_received, tx_crc_calculated, tx_crc_match ?
"OK" :
"MISMATCH",
205 address_upper, is_write ?
"W" :
"R", data_16bit);
207 esp_err_t ret = spi_device_transmit(
spi_device_, &trans);
209 ESP_LOGE(
TAG,
"SPI transfer failed: %s", esp_err_to_name(ret));
210 return tle::unexpected(CommError::TransferError);
213#if ESP32_TLE_COMM_ENABLE_DETAILED_SPI_LOGGING
215 rx_frame.word = rx_data;
216 uint8_t reply_mode = rx_frame.rx_common.reply_mode;
217 const char* reply_mode_str;
218 switch (reply_mode) {
219 case 0x00: reply_mode_str =
"16-bit Reply";
break;
220 case 0x01: reply_mode_str =
"22-bit Reply";
break;
221 case 0x02: reply_mode_str =
"Critical Fault";
break;
222 default: reply_mode_str =
"Unknown";
break;
224 ESP_LOGI(
TAG,
"SPI RX Frame: 0x%08X (raw: 0x%08X) | %s", rx_data, rx_data_raw, reply_mode_str);
225 if (reply_mode != 0x02) {
226 bool rx_crc_valid = VerifyFrameCrc(rx_frame);
227 ESP_LOGI(
TAG,
" CRC %s", rx_crc_valid ?
"OK" :
"MISMATCH");
229 if (reply_mode == 0x00) {
230 ESP_LOGI(
TAG,
" Status=0x%02X RW=%d Data=0x%04X",
231 rx_frame.rx_16bit.status, rx_frame.rx_16bit.rw_echo, rx_frame.rx_16bit.data);
232 }
else if (reply_mode == 0x01) {
233 ESP_LOGI(
TAG,
" 22-bit Data=0x%06X", rx_frame.rx_22bit.data);
234 }
else if (reply_mode == 0x02) {
235 auto fault_flags = CriticalFaultFlags::Extract(rx_frame);
236 ESP_LOGI(
TAG,
" Faults: 1V5=%d 2V5=%d BG=%d CLK_SLOW=%d CLK_FAST=%d",
237 fault_flags.supply_1v5_ok, fault_flags.supply_2v5_ok,
238 fault_flags.adc_bandgap_ok, fault_flags.clk_too_slow, fault_flags.clk_too_fast);
251 std::span<uint32_t> rx_data)
noexcept -> CommResult<void> {
253 ESP_LOGE(
TAG,
"CommInterface not initialized");
254 return tle::unexpected(CommError::HardwareNotReady);
256 if (tx_data.size() != rx_data.size()) {
257 ESP_LOGE(
TAG,
"Buffer size mismatch: tx=%zu, rx=%zu", tx_data.size(), rx_data.size());
258 return tle::unexpected(CommError::InvalidParameter);
260 for (
size_t i = 0; i < tx_data.size(); ++i) {
261 if (
auto result =
Transfer32(tx_data[i]); !result) {
262 return tle::unexpected(result.error());
264 rx_data[i] = *result;
275 auto Delay(uint32_t microseconds)
noexcept -> CommResult<void> {
276 if (microseconds == 0)
return {};
277 if (microseconds < 1000) {
278 int64_t start_time = esp_timer_get_time();
279 while ((esp_timer_get_time() - start_time) < microseconds) {}
281 vTaskDelay(pdMS_TO_TICKS(microseconds / 1000));
282 uint32_t remaining_us = microseconds % 1000;
283 if (remaining_us > 0) {
284 int64_t start_time = esp_timer_get_time();
285 while ((esp_timer_get_time() - start_time) < remaining_us) {}
296 auto Configure(
const tle92466ed::SPIConfig& config)
noexcept -> CommResult<void> {
297 ESP_LOGW(
TAG,
"SPI configuration update requested - not fully implemented");
342 auto GpioSet(CtrlPin pin, GpioSignal signal)
noexcept -> CommResult<void> {
345 return tle::unexpected(CommError::HardwareNotReady);
351 case CtrlPin::FAULTN:
353 return tle::unexpected(CommError::InvalidParameter);
356 ESP_LOGE(
TAG,
"GPIO pin not configured for %s", pin == CtrlPin::RESN ?
"RESN" :
"EN");
358 return tle::unexpected(CommError::InvalidParameter);
366 gpio_level = (signal == GpioSignal::ACTIVE) ? 0 : 1;
369 gpio_level = (signal == GpioSignal::ACTIVE) ? 1 : 0;
372 return tle::unexpected(CommError::InvalidParameter);
374 esp_err_t ret = gpio_set_level(
static_cast<gpio_num_t
>(gpio_pin), gpio_level);
376 ESP_LOGE(
TAG,
"Failed to set GPIO%d level: %s", gpio_pin, esp_err_to_name(ret));
378 return tle::unexpected(CommError::BusError);
380 ESP_LOGD(
TAG,
"Set %s pin (GPIO%d) to %s",
381 pin == CtrlPin::RESN ?
"RESN" :
"EN", gpio_pin,
382 signal == GpioSignal::ACTIVE ?
"ACTIVE" :
"INACTIVE");
394 auto GpioRead(CtrlPin pin)
noexcept -> CommResult<GpioSignal> {
397 return tle::unexpected(CommError::HardwareNotReady);
399 if (pin != CtrlPin::FAULTN) {
401 return tle::unexpected(CommError::InvalidParameter);
405 ESP_LOGE(
TAG,
"FAULTN GPIO pin not configured");
407 return tle::unexpected(CommError::InvalidParameter);
409 int gpio_level = gpio_get_level(
static_cast<gpio_num_t
>(gpio_pin));
411 GpioSignal signal = (gpio_level == 0) ? GpioSignal::ACTIVE : GpioSignal::INACTIVE;
412 ESP_LOGD(
TAG,
"Read FAULTN pin (GPIO%d): %s", gpio_pin,
413 signal == GpioSignal::ACTIVE ?
"FAULT" :
"NO FAULT");
424 void Log(LogLevel level,
const char* tag,
const char* format, va_list args)
noexcept {
425 esp_log_level_t esp_level;
427 case LogLevel::Error: esp_level = ESP_LOG_ERROR;
break;
428 case LogLevel::Warn: esp_level = ESP_LOG_WARN;
break;
429 case LogLevel::Info: esp_level = ESP_LOG_INFO;
break;
430 case LogLevel::Debug: esp_level = ESP_LOG_DEBUG;
break;
431 case LogLevel::Verbose: esp_level = ESP_LOG_VERBOSE;
break;
432 default: esp_level = ESP_LOG_INFO;
break;
434 esp_log_writev(esp_level, tag, format, args);
449 static constexpr const char*
TAG =
"Esp32TleComm";
456 auto configure_output_pin = [
this](int16_t pin,
const char* name,
int initial_level) -> CommResult<void> {
457 if (pin < 0)
return {};
458 gpio_config_t cfg = {
459 .pin_bit_mask = (1ULL << pin),
460 .mode = GPIO_MODE_OUTPUT,
461 .pull_up_en = GPIO_PULLUP_DISABLE,
462 .pull_down_en = GPIO_PULLDOWN_DISABLE,
463 .intr_type = GPIO_INTR_DISABLE
465 if (gpio_config(&cfg) != ESP_OK) {
466 ESP_LOGE(
TAG,
"Failed to configure %s pin (GPIO%d)", name, pin);
467 return tle::unexpected(CommError::HardwareNotReady);
469 gpio_set_level(
static_cast<gpio_num_t
>(pin), initial_level);
470 ESP_LOGI(
TAG,
"%s pin (GPIO%d) initialized, level=%d", name, pin, initial_level);
473 if (
auto r = configure_output_pin(
config_.
resn_pin,
"RESN", 0); !r)
return r;
474 if (
auto r = configure_output_pin(
config_.
en_pin,
"EN", 0); !r)
return r;
475 if (
auto r = configure_output_pin(
config_.
drv0_pin,
"DRV0", 0); !r)
return r;
476 if (
auto r = configure_output_pin(
config_.
drv1_pin,
"DRV1", 0); !r)
return r;
478 gpio_config_t cfg = {
480 .mode = GPIO_MODE_INPUT,
481 .pull_up_en = GPIO_PULLUP_ENABLE,
482 .pull_down_en = GPIO_PULLDOWN_DISABLE,
483 .intr_type = GPIO_INTR_DISABLE
485 if (gpio_config(&cfg) != ESP_OK) {
487 return tle::unexpected(CommError::HardwareNotReady);
499 spi_bus_config_t bus_config = {
509 .max_transfer_sz = 32,
510 .flags = SPICOMMON_BUSFLAG_MASTER
512 esp_err_t ret = spi_bus_initialize(
config_.
host, &bus_config, SPI_DMA_DISABLED);
514 ESP_LOGE(
TAG,
"SPI bus initialization failed: %s", esp_err_to_name(ret));
515 return tle::unexpected(CommError::HardwareNotReady);
517 ESP_LOGI(
TAG,
"SPI bus initialized successfully");
526 uint8_t spi_mode =
static_cast<uint8_t
>(
config_.
mode);
528 ESP_LOGE(
TAG,
"Invalid SPI mode %d, using Mode 1", spi_mode);
531 spi_device_interface_config_t dev_config = {};
532 dev_config.command_bits = 0;
533 dev_config.address_bits = 0;
534 dev_config.dummy_bits = 0;
535 dev_config.mode = spi_mode;
536 dev_config.clock_source = SPI_CLK_SRC_DEFAULT;
537 dev_config.duty_cycle_pos = 128;
541 dev_config.input_delay_ns = 0;
543 dev_config.flags = 0;
545 dev_config.pre_cb =
nullptr;
546 dev_config.post_cb =
nullptr;
549 ESP_LOGE(
TAG,
"Failed to add SPI device: %s", esp_err_to_name(ret));
550 return tle::unexpected(CommError::HardwareNotReady);
552 ESP_LOGI(
TAG,
"SPI device added successfully with Mode %d", spi_mode);
568 config.
host = SPI2_HOST;
572 config.
cs_pin = SPIPins::CS;
573 config.
resn_pin = ControlPins::RESN;
574 config.
en_pin = ControlPins::EN;
576 config.
drv0_pin = ControlPins::DRV0;
577 config.
drv1_pin = ControlPins::DRV1;
579 config.
mode = SPIParams::MODE;
584 if (
auto result = interface->Init(); !result) {
585 ESP_LOGE(
"TLE_Factory",
"Failed to initialize CommInterface");
ESP32 implementation of the TLE92466ED CommInterface.
Definition esp32_tle92466ed_bus.hpp:46
bool initialized_
Initialization state.
Definition esp32_tle92466ed_bus.hpp:446
CommError last_error_
Last error that occurred.
Definition esp32_tle92466ed_bus.hpp:447
Esp32Tle92466edSpiBus(const SPIConfig &config) noexcept
Constructor with custom SPI configuration.
Definition esp32_tle92466ed_bus.hpp:80
CommError GetLastError() const noexcept
Get the last error that occurred.
Definition esp32_tle92466ed_bus.hpp:313
auto initializeSPI() noexcept -> CommResult< void >
Initialize SPI bus.
Definition esp32_tle92466ed_bus.hpp:498
auto Init() noexcept -> CommResult< void >
Initialize the CommInterface (must be called before use)
Definition esp32_tle92466ed_bus.hpp:125
auto GpioRead(CtrlPin pin) noexcept -> CommResult< GpioSignal >
Read GPIO control pin signal.
Definition esp32_tle92466ed_bus.hpp:394
void Log(LogLevel level, const char *tag, const char *format, va_list args) noexcept
Log a message with specified severity level and tag (ESP_LOG implementation)
Definition esp32_tle92466ed_bus.hpp:424
auto Configure(const tle92466ed::SPIConfig &config) noexcept -> CommResult< void >
Configure SPI parameters.
Definition esp32_tle92466ed_bus.hpp:296
auto Deinit() noexcept -> CommResult< void >
Deinitialize the CommInterface.
Definition esp32_tle92466ed_bus.hpp:153
static constexpr const char * TAG
Logging tag.
Definition esp32_tle92466ed_bus.hpp:449
Esp32Tle92466edSpiBus()
Constructor with default SPI configuration.
Definition esp32_tle92466ed_bus.hpp:74
bool IsReady() const noexcept
Check if hardware is ready for communication.
Definition esp32_tle92466ed_bus.hpp:305
auto getConfig() const noexcept -> const SPIConfig &
Get the current SPI configuration.
Definition esp32_tle92466ed_bus.hpp:330
auto GpioSet(CtrlPin pin, GpioSignal signal) noexcept -> CommResult< void >
Set GPIO control pin signal.
Definition esp32_tle92466ed_bus.hpp:342
~Esp32Tle92466edSpiBus() noexcept
Destructor - cleans up SPI resources.
Definition esp32_tle92466ed_bus.hpp:109
SPIConfig config_
SPI configuration.
Definition esp32_tle92466ed_bus.hpp:444
auto Transfer32(uint32_t tx_data) noexcept -> CommResult< uint32_t >
Transfer 32-bit data via SPI (full-duplex)
Definition esp32_tle92466ed_bus.hpp:174
spi_device_handle_t spi_device_
SPI device handle.
Definition esp32_tle92466ed_bus.hpp:445
auto TransferMulti(std::span< const uint32_t > tx_data, std::span< uint32_t > rx_data) noexcept -> CommResult< void >
Transfer multiple 32-bit words via SPI.
Definition esp32_tle92466ed_bus.hpp:250
auto isInitialized() const noexcept -> bool
Check if CommInterface is initialized.
Definition esp32_tle92466ed_bus.hpp:441
auto initializeGPIO() noexcept -> CommResult< void >
Initialize GPIO pins (RESN, EN, FAULTN)
Definition esp32_tle92466ed_bus.hpp:455
auto Delay(uint32_t microseconds) noexcept -> CommResult< void >
Delay for specified duration.
Definition esp32_tle92466ed_bus.hpp:275
auto addSPIDevice() noexcept -> CommResult< void >
Add SPI device to the bus.
Definition esp32_tle92466ed_bus.hpp:525
auto ClearErrors() noexcept -> CommResult< void >
Clear any pending errors.
Definition esp32_tle92466ed_bus.hpp:321
auto CreateEsp32Tle92466edSpiBus() noexcept -> std::unique_ptr< Esp32Tle92466edSpiBus >
Create a configured Esp32Tle92466edSpiBus instance for TLE92466ED.
Definition esp32_tle92466ed_bus.hpp:565
Definition esp32_tle92466ed_test_config.hpp:39
constexpr uint32_t byte_swap_32(uint32_t value) noexcept
Definition esp32_tle92466ed_bus.hpp:31
SPI configuration structure for ESP32.
Definition esp32_tle92466ed_bus.hpp:53
int16_t faultn_pin
FAULTN pin (active low, -1 = not configured)
Definition esp32_tle92466ed_bus.hpp:61
int16_t mosi_pin
MOSI pin (GPIO7, -1 = not configured)
Definition esp32_tle92466ed_bus.hpp:56
int16_t resn_pin
RESN pin (active low, -1 = not configured)
Definition esp32_tle92466ed_bus.hpp:59
uint8_t cs_ena_posttrans
CS held N clock cycles after transaction.
Definition esp32_tle92466ed_bus.hpp:68
int16_t drv1_pin
DRV1 pin (external drive control, -1 = not configured)
Definition esp32_tle92466ed_bus.hpp:63
int16_t cs_pin
CS pin (GPIO10, -1 = not configured)
Definition esp32_tle92466ed_bus.hpp:58
int16_t en_pin
EN pin (active high, -1 = not configured)
Definition esp32_tle92466ed_bus.hpp:60
int16_t drv0_pin
DRV0 pin (external drive control, -1 = not configured)
Definition esp32_tle92466ed_bus.hpp:62
uint8_t queue_size
Transaction queue size.
Definition esp32_tle92466ed_bus.hpp:66
uint8_t mode
SPI mode (1 = CPOL=0, CPHA=1)
Definition esp32_tle92466ed_bus.hpp:65
uint32_t frequency
SPI frequency (1MHz)
Definition esp32_tle92466ed_bus.hpp:64
spi_host_device_t host
SPI host (SPI2_HOST for ESP32-C6)
Definition esp32_tle92466ed_bus.hpp:54
uint8_t cs_ena_pretrans
CS asserted N clock cycles before transaction.
Definition esp32_tle92466ed_bus.hpp:67
int16_t sclk_pin
SCLK pin (GPIO6, -1 = not configured)
Definition esp32_tle92466ed_bus.hpp:57
int16_t miso_pin
MISO pin (GPIO2, -1 = not configured)
Definition esp32_tle92466ed_bus.hpp:55