HF-AS5047U Driver  0.1.0-dev
HF-AS5047U C++ Driver
Loading...
Searching...
No Matches
esp32_as5047u_bus.hpp
Go to the documentation of this file.
1
15#pragma once
16
19#include "driver/gpio.h"
20#include "driver/spi_master.h"
21#include "esp_log.h"
22#include <cstdint>
23#include <cstdio>
24#include <cstring>
25#include <memory>
26
35class Esp32As5047uSpiBus : public as5047u::SpiInterface<Esp32As5047uSpiBus> {
36public:
47 struct SPIConfig {
48 spi_host_device_t host;
49 gpio_num_t miso_pin;
50 gpio_num_t mosi_pin;
51 gpio_num_t sclk_pin;
52 gpio_num_t cs_pin;
53 uint32_t frequency;
54 uint8_t mode;
55 uint8_t queue_size;
58 };
59
68 explicit Esp32As5047uSpiBus(const SPIConfig &config) : config_(config) {}
69
74
83 void transfer(const uint8_t *tx, uint8_t *rx, std::size_t len) {
84 if (!initialized_ || spi_device_ == nullptr) {
85 ESP_LOGE(TAG, "SPI bus not initialized");
86 return;
87 }
88
89 spi_transaction_t trans = {};
90 trans.length = len * 8; // Length in bits
91 trans.tx_buffer = tx;
92 trans.rx_buffer = rx;
93
94 esp_err_t ret = spi_device_transmit(spi_device_, &trans);
95 if (ret != ESP_OK) {
96 ESP_LOGE(TAG, "SPI transfer failed: %s", esp_err_to_name(ret));
97 }
98
99#if ESP32_AS5047U_ENABLE_DETAILED_SPI_LOGGING
100 // Log TX/RX frame per transfer for debugging (direction, zero position, etc.)
101 if (len > 0 && len <= 8) {
102 char buf[80];
103 int n = 0;
104 n += snprintf(buf + n, sizeof(buf) - n, "SPI[%zu] TX:", len);
105 for (std::size_t i = 0; i < len && n < (int)(sizeof(buf) - 4); ++i) {
106 n += snprintf(buf + n, sizeof(buf) - n, " %02X", tx ? tx[i] : 0u);
107 }
108 if (rx) {
109 n += snprintf(buf + n, sizeof(buf) - n, " RX:");
110 for (std::size_t i = 0; i < len && n < (int)(sizeof(buf) - 4); ++i) {
111 n += snprintf(buf + n, sizeof(buf) - n, " %02X", rx[i]);
112 }
113 } else {
114 n += snprintf(buf + n, sizeof(buf) - n, " RX: (none)");
115 }
116 ESP_LOGI(TAG, "%s", buf);
117 }
118#endif
119 }
120
125 bool initialize() {
126 if (initialized_) {
127 ESP_LOGW(TAG, "SPI bus already initialized");
128 return true;
129 }
130
131 if (!initializeSPI()) {
132 ESP_LOGE(TAG, "Failed to initialize SPI bus");
133 return false;
134 }
135
136 if (!addSPIDevice()) {
137 ESP_LOGE(TAG, "Failed to add SPI device");
138 deinitialize();
139 return false;
140 }
141
142 initialized_ = true;
143 ESP_LOGI(TAG, "SPI bus initialized successfully");
144 return true;
145 }
146
151 if (!initialized_) {
152 return;
153 }
154
155 if (spi_device_ != nullptr) {
156 spi_bus_remove_device(spi_device_);
157 spi_device_ = nullptr;
158 }
159
160 spi_bus_free(config_.host);
161
162 initialized_ = false;
163 ESP_LOGI(TAG, "SPI bus deinitialized");
164 }
165
170 const SPIConfig &getConfig() const noexcept { return config_; }
171
176 bool isInitialized() const noexcept { return initialized_; }
177
178private:
179 SPIConfig config_;
180 spi_device_handle_t spi_device_ = nullptr;
181 bool initialized_ = false;
182 static constexpr const char *TAG = "Esp32As5047uSpiBus";
183
188 bool initializeSPI() {
189 // Verify pin numbers are valid before configuring
190 if (config_.sclk_pin < 0 || config_.sclk_pin > 48) {
191 ESP_LOGE(TAG, "Invalid SCLK pin: %d", config_.sclk_pin);
192 return false;
193 }
194 if (config_.mosi_pin < 0 || config_.mosi_pin > 48) {
195 ESP_LOGE(TAG, "Invalid MOSI pin: %d", config_.mosi_pin);
196 return false;
197 }
198 if (config_.miso_pin < 0 || config_.miso_pin > 48) {
199 ESP_LOGE(TAG, "Invalid MISO pin: %d", config_.miso_pin);
200 return false;
201 }
202
203 spi_bus_config_t buscfg = {};
204 buscfg.mosi_io_num = config_.mosi_pin;
205 buscfg.miso_io_num = config_.miso_pin;
206 buscfg.sclk_io_num = config_.sclk_pin;
207 buscfg.quadwp_io_num = -1;
208 buscfg.quadhd_io_num = -1;
209 buscfg.max_transfer_sz = 64;
210 buscfg.flags = SPICOMMON_BUSFLAG_MASTER;
211
212 // ESP-IDF: SPI1_HOST=0, SPI2_HOST=1, SPI3_HOST=2. Log as SPI1/SPI2/SPI3 for clarity.
213 ESP_LOGI(TAG, "Initializing SPI bus: MISO=GPIO%d, MOSI=GPIO%d, SCLK=GPIO%d, Host=SPI%d",
214 config_.miso_pin, config_.mosi_pin, config_.sclk_pin, config_.host + 1);
215
216 esp_err_t ret = spi_bus_initialize(config_.host, &buscfg, SPI_DMA_CH_AUTO);
217 if (ret != ESP_OK) {
218 ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret));
219 ESP_LOGE(TAG, "Check: SCLK pin GPIO%d is not used by another peripheral", config_.sclk_pin);
220 return false;
221 }
222
223 ESP_LOGI(TAG, "SPI bus initialized successfully: MISO=GPIO%d, MOSI=GPIO%d, SCLK=GPIO%d",
224 config_.miso_pin, config_.mosi_pin, config_.sclk_pin);
225
226 // If you see CS/MOSI/MISO but no SCLK on GPIO%d:
227 // - Ensure SPI frequency > 0 (addSPIDevice checks this).
228 // - On ESP32-C6, GPIO12 is USB-JTAG; use another pin or disable USB-JTAG.
229 // - If SPI2 is used elsewhere, try SPI3_HOST in CreateEsp32As5047uSpiBus().
230 // - Confirm no other driver (e.g. PSRAM, display) has claimed this GPIO.
231 ESP_LOGI(TAG, "SCLK pin GPIO%d is driven by SPI%d; freq=%lu Hz",
232 config_.sclk_pin, config_.host + 1, (unsigned long)config_.frequency);
233
234 return true;
235 }
236
241 bool addSPIDevice() {
242 if (config_.frequency == 0) {
243 ESP_LOGE(TAG, "SPI frequency is 0 - SCLK will not run. Set SPIParams::FREQUENCY > 0 (e.g. 4000000)");
244 return false;
245 }
246 spi_device_interface_config_t devcfg = {};
247 devcfg.command_bits = 0;
248 devcfg.address_bits = 0;
249 devcfg.dummy_bits = 0;
250 devcfg.clock_speed_hz = config_.frequency;
251 devcfg.mode = config_.mode;
252 devcfg.duty_cycle_pos = 128; // 50% duty cycle
253 devcfg.spics_io_num = config_.cs_pin;
254 devcfg.queue_size = config_.queue_size;
255 devcfg.cs_ena_pretrans = config_.cs_ena_pretrans;
256 devcfg.cs_ena_posttrans = config_.cs_ena_posttrans;
257 devcfg.flags = 0; // No special flags
258 devcfg.input_delay_ns = 0;
259 devcfg.pre_cb = nullptr;
260 devcfg.post_cb = nullptr;
261
262 ESP_LOGI(TAG, "Adding SPI device: CS=GPIO%d, Freq=%lu Hz, Mode=%d, SCLK=GPIO%d",
263 config_.cs_pin, config_.frequency, config_.mode, config_.sclk_pin);
264
265 esp_err_t ret = spi_bus_add_device(config_.host, &devcfg, &spi_device_);
266 if (ret != ESP_OK) {
267 ESP_LOGE(TAG, "Failed to add SPI device: %s", esp_err_to_name(ret));
268 ESP_LOGE(TAG, "Check: CS pin GPIO%d and SCLK pin GPIO%d are not used elsewhere",
269 config_.cs_pin, config_.sclk_pin);
270 return false;
271 }
272
273 ESP_LOGI(TAG, "SPI device added successfully: CS=GPIO%d, Freq=%lu Hz, Mode=%d",
274 config_.cs_pin, config_.frequency, config_.mode);
275
276 return true;
277 }
278};
279
288inline auto CreateEsp32As5047uSpiBus() noexcept -> std::unique_ptr<Esp32As5047uSpiBus> {
289 using namespace AS5047U_TestConfig;
290
292
293 // SPI pins and host from esp32_as5047u_test_config.hpp
294 config.host = (SPIParams::SPI_HOST_ID == 3) ? SPI3_HOST : SPI2_HOST;
295 config.miso_pin = static_cast<gpio_num_t>(SPIPins::MISO);
296 config.mosi_pin = static_cast<gpio_num_t>(SPIPins::MOSI);
297 config.sclk_pin = static_cast<gpio_num_t>(SPIPins::SCLK);
298 config.cs_pin = static_cast<gpio_num_t>(SPIPins::CS);
299
300 // SPI parameters from esp32_as5047u_test_config.hpp
301 config.frequency = SPIParams::FREQUENCY;
302 config.mode = SPIParams::MODE;
303 config.queue_size = SPIParams::QUEUE_SIZE;
304 config.cs_ena_pretrans = SPIParams::CS_ENA_PRETRANS;
305 config.cs_ena_posttrans = SPIParams::CS_ENA_POSTTRANS;
306
307 return std::make_unique<Esp32As5047uSpiBus>(config);
308}
CRTP-based SPI bus interface for AS5047U driver.
ESP32 SPI transport implementation for AS5047U driver.
Definition esp32_as5047u_bus.hpp:35
Esp32As5047uSpiBus(const SPIConfig &config)
Constructor with SPI configuration.
Definition esp32_as5047u_bus.hpp:68
const SPIConfig & getConfig() const noexcept
Get the current SPI configuration.
Definition esp32_as5047u_bus.hpp:170
~Esp32As5047uSpiBus()
Destructor - cleans up SPI resources.
Definition esp32_as5047u_bus.hpp:73
bool isInitialized() const noexcept
Check if SPI bus is initialized.
Definition esp32_as5047u_bus.hpp:176
bool initialize()
Initialize the SPI bus (must be called before use)
Definition esp32_as5047u_bus.hpp:125
void deinitialize()
Deinitialize the SPI bus.
Definition esp32_as5047u_bus.hpp:150
void transfer(const uint8_t *tx, uint8_t *rx, std::size_t len)
Perform a full-duplex SPI data transfer.
Definition esp32_as5047u_bus.hpp:83
CRTP-based template interface for SPI bus operations.
Definition as5047u_spi_interface.hpp:36
auto CreateEsp32As5047uSpiBus() noexcept -> std::unique_ptr< Esp32As5047uSpiBus >
Factory function to create a configured Esp32As5047uSpiBus instance.
Definition esp32_as5047u_bus.hpp:288
Hardware configuration for AS5047U driver on ESP32-C6.
Definition esp32_as5047u_test_config.hpp:39
SPI configuration structure.
Definition esp32_as5047u_bus.hpp:47
uint8_t cs_ena_posttrans
CS held N clock cycles after transaction.
Definition esp32_as5047u_bus.hpp:57
gpio_num_t sclk_pin
SCLK pin (SPI Clock)
Definition esp32_as5047u_bus.hpp:51
uint8_t cs_ena_pretrans
CS asserted N clock cycles before transaction.
Definition esp32_as5047u_bus.hpp:56
uint8_t queue_size
Transaction queue size.
Definition esp32_as5047u_bus.hpp:55
uint32_t frequency
SPI frequency in Hz (max 10MHz for AS5047U)
Definition esp32_as5047u_bus.hpp:53
gpio_num_t cs_pin
CS pin (Chip Select, active low)
Definition esp32_as5047u_bus.hpp:52
gpio_num_t mosi_pin
MOSI pin (Master Out Slave In)
Definition esp32_as5047u_bus.hpp:50
uint8_t mode
SPI mode (must be 1: CPOL=0, CPHA=1 for AS5047U)
Definition esp32_as5047u_bus.hpp:54
spi_host_device_t host
SPI host (e.g., SPI2_HOST for ESP32-S3)
Definition esp32_as5047u_bus.hpp:48
gpio_num_t miso_pin
MISO pin (Master In Slave Out)
Definition esp32_as5047u_bus.hpp:49