libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
esp_spi.cpp
1#include "esp_spi.hpp"
2
3#include <algorithm>
4#include <array>
5#include <cstdlib>
6#include <cstring>
7
8#include "driver/gpio.h"
9#include "driver/spi_common.h"
10#include "esp_attr.h"
11#include "esp_clk_tree.h"
12#include "esp_err.h"
13#include "esp_memory_utils.h"
14#include "esp_private/periph_ctrl.h"
15#include "esp_private/spi_common_internal.h"
16#include "esp_timer.h"
17#if SOC_GDMA_SUPPORTED
18#include "esp_private/gdma.h"
19#endif
20#include "esp_rom_gpio.h"
21#include "hal/spi_hal.h"
22#include "libxr_def.hpp"
23#include "soc/spi_periph.h"
24#include "timebase.hpp"
25
26namespace LibXR
27{
28namespace
29{
30
31uint8_t ResolveSpiMode(SPI::ClockPolarity polarity, SPI::ClockPhase phase)
32{
33 if (polarity == SPI::ClockPolarity::LOW)
34 {
35 return (phase == SPI::ClockPhase::EDGE_1) ? 0U : 1U;
36 }
37 return (phase == SPI::ClockPhase::EDGE_1) ? 2U : 3U;
38}
39
40spi_dma_ctx_t* ToDmaCtx(void* ctx) { return reinterpret_cast<spi_dma_ctx_t*>(ctx); }
41
42uint64_t GetNowUs()
43{
44 if (Timebase::timebase != nullptr)
45 {
46 return static_cast<uint64_t>(Timebase::GetMicroseconds());
47 }
48 return static_cast<uint64_t>(esp_timer_get_time());
49}
50
51uint64_t CalcPollingTimeoutUs(size_t size, uint32_t bus_hz)
52{
53 const uint32_t safe_hz = (bus_hz == 0U) ? 1U : bus_hz;
54 const uint64_t bits = static_cast<uint64_t>(size) * 8ULL;
55 const uint64_t wire_time_us = (bits * 1000000ULL + safe_hz - 1ULL) / safe_hz;
56 // Keep a generous margin for bus contention / APB jitter while staying time-based.
57 return std::max<uint64_t>(100ULL, wire_time_us * 8ULL + 50ULL);
58}
59
60#if SOC_GDMA_SUPPORTED
61esp_err_t DmaReset(gdma_channel_handle_t chan) { return gdma_reset(chan); }
62
63esp_err_t DmaStart(gdma_channel_handle_t chan, const void* desc)
64{
65 return gdma_start(chan, reinterpret_cast<intptr_t>(desc));
66}
67#else
68esp_err_t DmaReset(spi_dma_chan_handle_t chan)
69{
70 spi_dma_reset(chan);
71 return ESP_OK;
72}
73
74esp_err_t DmaStart(spi_dma_chan_handle_t chan, const void* desc)
75{
76 spi_dma_start(chan, const_cast<void*>(desc));
77 return ESP_OK;
78}
79#endif
80
81#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
82extern "C" esp_err_t esp_cache_msync(void* addr, size_t size, int flags);
83
84constexpr int kCacheSyncFlagUnaligned = (1 << 1);
85constexpr int kCacheSyncFlagDirC2M = (1 << 2);
86constexpr int kCacheSyncFlagDirM2C = (1 << 3);
87
88bool CacheSyncDmaBuffer(const void* addr, size_t size, bool cache_to_mem)
89{
90 if ((addr == nullptr) || (size == 0U))
91 {
92 return true;
93 }
94
95#if SOC_PSRAM_DMA_CAPABLE && !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
96 if (!esp_ptr_external_ram(addr))
97 {
98 return true;
99 }
100#endif
101
102 int flags = cache_to_mem ? kCacheSyncFlagDirC2M : kCacheSyncFlagDirM2C;
103 flags |= kCacheSyncFlagUnaligned;
104
105 const esp_err_t ret = esp_cache_msync(const_cast<void*>(addr), size, flags);
106 // Non-cacheable regions can return ESP_ERR_INVALID_ARG; treat as no-op success.
107 return (ret == ESP_OK) || (ret == ESP_ERR_INVALID_ARG);
108}
109#endif
110
111} // namespace
112
113ESP32SPI::ESP32SPI(spi_host_device_t host, int sclk_pin, int miso_pin, int mosi_pin,
114 RawData dma_rx, RawData dma_tx, SPI::Configuration config,
115 uint32_t dma_enable_min_size, bool enable_dma)
116 : SPI(dma_rx, dma_tx),
117 host_(host),
118 sclk_pin_(sclk_pin),
119 miso_pin_(miso_pin),
120 mosi_pin_(mosi_pin),
121 dma_enable_min_size_(dma_enable_min_size),
122 dma_requested_(enable_dma),
123 dma_rx_raw_(dma_rx),
124 dma_tx_raw_(dma_tx),
125 dbuf_rx_block_size_(dma_rx.size_ / 2U),
126 dbuf_tx_block_size_(dma_tx.size_ / 2U)
127{
128 ASSERT(host_ != SPI1_HOST);
129 ASSERT(host_ < SPI_HOST_MAX);
130 ASSERT(static_cast<int>(host_) < SOC_SPI_PERIPH_NUM);
131 ASSERT(dma_rx_raw_.addr_ != nullptr);
132 ASSERT(dma_tx_raw_.addr_ != nullptr);
133 ASSERT(dma_rx_raw_.size_ > 0U);
134 ASSERT(dma_tx_raw_.size_ > 0U);
135
136 if (InitializeHardware() != ErrorCode::OK)
137 {
138 ASSERT(false);
139 return;
140 }
141
142 if (ConfigurePins() != ErrorCode::OK)
143 {
144 ASSERT(false);
145 return;
146 }
147
148 if (InstallInterrupt() != ErrorCode::OK)
149 {
150 ASSERT(false);
151 return;
152 }
153
154 if (dma_requested_)
155 {
156 const ErrorCode dma_ans = InitDmaBackend();
157 ASSERT(dma_ans == ErrorCode::OK);
158 if (dma_ans != ErrorCode::OK)
159 {
160 return;
161 }
162 }
163
164 if (SetConfig(config) != ErrorCode::OK)
165 {
166 ASSERT(false);
167 return;
168 }
169}
170
171bool ESP32SPI::Acquire()
172{
173 bool expected = false;
174 return busy_.compare_exchange_strong(expected, true, std::memory_order_acq_rel,
175 std::memory_order_acquire);
176}
177
178void ESP32SPI::Release() { busy_.store(false, std::memory_order_release); }
179
180ErrorCode ESP32SPI::InitializeHardware()
181{
182 if (initialized_)
183 {
184 return ErrorCode::OK;
185 }
186
187 if ((host_ <= SPI1_HOST) || (host_ >= SPI_HOST_MAX) ||
188 (static_cast<int>(host_) >= SOC_SPI_PERIPH_NUM))
189 {
190 return ErrorCode::ARG_ERR;
191 }
192
193 const auto& signal = spi_periph_signal[host_];
194 hw_ = signal.hw;
195 if (hw_ == nullptr)
196 {
197 return ErrorCode::NOT_SUPPORT;
198 }
199
200 PERIPH_RCC_ATOMIC()
201 {
202 (void)__DECLARE_RCC_ATOMIC_ENV;
203 spi_ll_enable_bus_clock(host_, true);
204 spi_ll_reset_register(host_);
205 }
206 spi_ll_enable_clock(host_, true);
207 spi_ll_master_init(hw_);
208
209 const spi_line_mode_t line_mode = {
210 .cmd_lines = 1,
211 .addr_lines = 1,
212 .data_lines = 1,
213 };
214 spi_ll_master_set_line_mode(hw_, line_mode);
215 spi_ll_set_half_duplex(hw_, false);
216 spi_ll_set_tx_lsbfirst(hw_, false);
217 spi_ll_set_rx_lsbfirst(hw_, false);
218 spi_ll_set_mosi_delay(hw_, 0, 0);
219 spi_ll_enable_mosi(hw_, 1);
220 spi_ll_enable_miso(hw_, 1);
221 spi_ll_set_dummy(hw_, 0);
222 spi_ll_set_command_bitlen(hw_, 0);
223 spi_ll_set_addr_bitlen(hw_, 0);
224 spi_ll_set_command(hw_, 0, 0, false);
225 spi_ll_set_address(hw_, 0, 0, false);
226 hw_->user.usr_command = 0;
227 hw_->user.usr_addr = 0;
228
229 spi_ll_set_clk_source(hw_, SPI_CLK_SRC_DEFAULT);
230 if (ResolveClockSource(source_clock_hz_) != ErrorCode::OK)
231 {
232 return ErrorCode::INIT_ERR;
233 }
234
235 spi_ll_disable_int(hw_);
236 spi_ll_clear_int_stat(hw_);
237 initialized_ = true;
238 return ErrorCode::OK;
239}
240
241ErrorCode ESP32SPI::ConfigurePins()
242{
243 if (!initialized_ || (hw_ == nullptr))
244 {
245 return ErrorCode::STATE_ERR;
246 }
247
248 const auto& signal = spi_periph_signal[host_];
249
250 if (sclk_pin_ >= 0)
251 {
252 if (!GPIO_IS_VALID_OUTPUT_GPIO(sclk_pin_))
253 {
254 return ErrorCode::ARG_ERR;
255 }
256 esp_rom_gpio_pad_select_gpio(static_cast<uint32_t>(sclk_pin_));
257 esp_rom_gpio_connect_out_signal(sclk_pin_, signal.spiclk_out, false, false);
258 gpio_input_enable(static_cast<gpio_num_t>(sclk_pin_));
259 esp_rom_gpio_connect_in_signal(sclk_pin_, signal.spiclk_in, false);
260 }
261
262 if (mosi_pin_ >= 0)
263 {
264 if (!GPIO_IS_VALID_OUTPUT_GPIO(mosi_pin_))
265 {
266 return ErrorCode::ARG_ERR;
267 }
268 esp_rom_gpio_pad_select_gpio(static_cast<uint32_t>(mosi_pin_));
269 esp_rom_gpio_connect_out_signal(mosi_pin_, signal.spid_out, false, false);
270 gpio_input_enable(static_cast<gpio_num_t>(mosi_pin_));
271 esp_rom_gpio_connect_in_signal(mosi_pin_, signal.spid_in, false);
272 }
273
274 if (miso_pin_ >= 0)
275 {
276 if (!GPIO_IS_VALID_GPIO(miso_pin_))
277 {
278 return ErrorCode::ARG_ERR;
279 }
280 gpio_input_enable(static_cast<gpio_num_t>(miso_pin_));
281 esp_rom_gpio_connect_in_signal(miso_pin_, signal.spiq_in, false);
282 }
283
284 return ErrorCode::OK;
285}
286
287ErrorCode ESP32SPI::ResolveClockSource(uint32_t& source_hz)
288{
289 source_hz = 0;
290 const esp_err_t err =
291 esp_clk_tree_src_get_freq_hz(static_cast<soc_module_clk_t>(SPI_CLK_SRC_DEFAULT),
292 ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &source_hz);
293 if ((err != ESP_OK) || (source_hz == 0))
294 {
295 return ErrorCode::INIT_ERR;
296 }
297 return ErrorCode::OK;
298}
299
300ErrorCode ESP32SPI::InstallInterrupt()
301{
302 if (intr_installed_)
303 {
304 return ErrorCode::OK;
305 }
306
307 const int irq_source = static_cast<int>(spi_periph_signal[host_].irq);
308 if (esp_intr_alloc(irq_source, ESP_INTR_FLAG_IRAM, &ESP32SPI::SpiIsrEntry, this,
309 &intr_handle_) != ESP_OK)
310 {
311 intr_handle_ = nullptr;
312 return ErrorCode::INIT_ERR;
313 }
314
315 intr_installed_ = true;
316 return ErrorCode::OK;
317}
318
319ErrorCode ESP32SPI::InitDmaBackend()
320{
321 if (dma_enabled_)
322 {
323 return ErrorCode::OK;
324 }
325
326 spi_dma_ctx_t* ctx = nullptr;
327 if (spicommon_dma_chan_alloc(host_, SPI_DMA_CH_AUTO, &ctx) != ESP_OK)
328 {
329 return ErrorCode::INIT_ERR;
330 }
331
332 const size_t cfg_max_size = std::max(dma_rx_raw_.size_, dma_tx_raw_.size_);
333 int actual_max_size = 0;
334 if (spicommon_dma_desc_alloc(ctx, static_cast<int>(cfg_max_size), &actual_max_size) !=
335 ESP_OK)
336 {
337 (void)spicommon_dma_chan_free(ctx);
338 return ErrorCode::INIT_ERR;
339 }
340
341 dma_ctx_ = ctx;
342 dma_enabled_ = true;
343 dma_max_transfer_bytes_ =
344 std::min<size_t>({static_cast<size_t>(actual_max_size), dma_rx_raw_.size_,
345 dma_tx_raw_.size_, kMaxDmaTransferBytes});
346
347 if (dma_max_transfer_bytes_ == 0U)
348 {
349 return ErrorCode::INIT_ERR;
350 }
351
352 return ErrorCode::OK;
353}
354
355void IRAM_ATTR ESP32SPI::SpiIsrEntry(void* arg)
356{
357 auto* spi = static_cast<ESP32SPI*>(arg);
358 if (spi != nullptr)
359 {
360 spi->HandleInterrupt();
361 }
362}
363
364void IRAM_ATTR ESP32SPI::HandleInterrupt()
365{
366 if ((hw_ == nullptr) || !initialized_)
367 {
368 return;
369 }
370
371 if (!async_running_)
372 {
373 spi_ll_clear_int_stat(hw_);
374 return;
375 }
376
377 if (!spi_ll_usr_is_done(hw_))
378 {
379 return;
380 }
381
382 FinishAsync(true, ErrorCode::OK);
383}
384
385void IRAM_ATTR ESP32SPI::FinishAsync(bool in_isr, ErrorCode ec)
386{
387 if (!async_running_)
388 {
389 return;
390 }
391
392#if CONFIG_IDF_TARGET_ESP32
393 // Keep ESP32 SPI DMA workaround state in sync with transfer lifecycle.
394 if (dma_enabled_)
395 {
396 spi_dma_ctx_t* ctx = ToDmaCtx(dma_ctx_);
397 if (ctx != nullptr)
398 {
399 spicommon_dmaworkaround_idle(ctx->tx_dma_chan.chan_id);
400 }
401 }
402#endif
403
404 spi_ll_disable_int(hw_);
405 spi_ll_clear_int_stat(hw_);
406
407 RawData rx = {nullptr, 0U};
408 if ((ec == ErrorCode::OK) && async_dma_rx_enabled_)
409 {
410 rx = GetRxBuffer();
411#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
412 if (!CacheSyncDmaBuffer(rx.addr_, async_dma_size_, false))
413 {
414 ec = ErrorCode::FAILED;
415 }
416#endif
417 }
418
419 if ((ec == ErrorCode::OK) && (read_back_.size_ > 0U))
420 {
421 if (rx.addr_ == nullptr)
422 {
423 rx = GetRxBuffer();
424 }
425 uint8_t* src = static_cast<uint8_t*>(rx.addr_);
426 if (mem_read_)
427 {
428 ASSERT(rx.size_ >= (read_back_.size_ + 1U));
429 src += 1;
430 }
431 else
432 {
433 ASSERT(rx.size_ >= read_back_.size_);
434 }
435 Memory::FastCopy(read_back_.addr_, src, read_back_.size_);
436 }
437
438 if (ec == ErrorCode::OK)
439 {
440 SwitchBufferLocal();
441 }
442
443 async_running_ = false;
444 async_dma_size_ = 0U;
445 async_dma_rx_enabled_ = false;
446 mem_read_ = false;
447 read_back_ = {nullptr, 0};
448 Release();
449 rw_op_.UpdateStatus(in_isr, ec);
450}
451
452ErrorCode ESP32SPI::SetConfig(SPI::Configuration config)
453{
454 if (!initialized_ || (hw_ == nullptr))
455 {
456 return ErrorCode::STATE_ERR;
457 }
458 if (busy_.load(std::memory_order_acquire))
459 {
460 return ErrorCode::BUSY;
461 }
462
463 if (config.prescaler == Prescaler::UNKNOWN)
464 {
465 return ErrorCode::ARG_ERR;
466 }
467 if (config.double_buffer &&
468 ((dbuf_rx_block_size_ == 0U) || (dbuf_tx_block_size_ == 0U)))
469 {
470 return ErrorCode::ARG_ERR;
471 }
472
473 const uint32_t div = PrescalerToDiv(config.prescaler);
474 if ((div == 0) || (source_clock_hz_ == 0))
475 {
476 return ErrorCode::ARG_ERR;
477 }
478
479 const uint32_t target_hz = source_clock_hz_ / div;
480 if (target_hz == 0)
481 {
482 return ErrorCode::ARG_ERR;
483 }
484
485 const uint8_t mode = ResolveSpiMode(config.clock_polarity, config.clock_phase);
486 spi_ll_master_set_mode(hw_, mode);
487
488 const int applied_hz = spi_ll_master_set_clock(hw_, static_cast<int>(source_clock_hz_),
489 static_cast<int>(target_hz), 128);
490 if (applied_hz <= 0)
491 {
492 return ErrorCode::INIT_ERR;
493 }
494
495 spi_ll_apply_config(hw_);
496 GetConfig() = config;
497 dbuf_enabled_ = config.double_buffer;
498 dbuf_active_block_ = 0U;
499
500 return ErrorCode::OK;
501}
502
503bool ESP32SPI::UseLocalDoubleBuffer() const
504{
505 return dbuf_enabled_ && (dbuf_rx_block_size_ > 0U) && (dbuf_tx_block_size_ > 0U);
506}
507
508RawData ESP32SPI::GetRxBuffer()
509{
510 if (UseLocalDoubleBuffer())
511 {
512 auto* base = static_cast<uint8_t*>(dma_rx_raw_.addr_);
513 return {base + static_cast<size_t>(dbuf_active_block_) * dbuf_rx_block_size_,
514 dbuf_rx_block_size_};
515 }
516 return dma_rx_raw_;
517}
518
519RawData ESP32SPI::GetTxBuffer()
520{
521 if (UseLocalDoubleBuffer())
522 {
523 auto* base = static_cast<uint8_t*>(dma_tx_raw_.addr_);
524 return {base + static_cast<size_t>(dbuf_active_block_) * dbuf_tx_block_size_,
525 dbuf_tx_block_size_};
526 }
527 return dma_tx_raw_;
528}
529
530void ESP32SPI::SwitchBufferLocal()
531{
532 if (UseLocalDoubleBuffer())
533 {
534 dbuf_active_block_ ^= 1U;
535 }
536}
537
538bool ESP32SPI::CanUseDma(size_t size) const
539{
540 return dma_requested_ && dma_enabled_ && (dma_ctx_ != nullptr) &&
541 (size > dma_enable_min_size_) && (size <= dma_max_transfer_bytes_);
542}
543
544ErrorCode ESP32SPI::EnsureReadyAndAcquire()
545{
546 if (!initialized_)
547 {
548 return ErrorCode::INIT_ERR;
549 }
550 if (!Acquire())
551 {
552 return ErrorCode::BUSY;
553 }
554 return ErrorCode::OK;
555}
556
557ErrorCode ESP32SPI::FinalizeSyncResult(OperationRW& op, bool in_isr, ErrorCode ec)
558{
559 if (op.type != OperationRW::OperationType::BLOCK)
560 {
561 op.UpdateStatus(in_isr, ec);
562 }
563 return ec;
564}
565
566ErrorCode ESP32SPI::CompleteZeroSize(OperationRW& op, bool in_isr)
567{
568 return FinalizeSyncResult(op, in_isr, ErrorCode::OK);
569}
570
571ErrorCode ESP32SPI::ReturnAsyncStartResult(ErrorCode ec, bool started)
572{
573 if (!started)
574 {
575 Release();
576 }
577 return ec;
578}
579
580void ESP32SPI::ConfigureTransferRegisters(size_t size)
581{
582 static constexpr spi_line_mode_t kLineMode = {
583 .cmd_lines = 1,
584 .addr_lines = 1,
585 .data_lines = 1,
586 };
587 const size_t bitlen = size * 8U;
588
589 spi_ll_master_set_line_mode(hw_, kLineMode);
590 spi_ll_set_mosi_bitlen(hw_, bitlen);
591 spi_ll_set_miso_bitlen(hw_, bitlen);
592 spi_ll_set_command_bitlen(hw_, 0);
593 spi_ll_set_addr_bitlen(hw_, 0);
594 spi_ll_set_command(hw_, 0, 0, false);
595 spi_ll_set_address(hw_, 0, 0, false);
596 hw_->user.usr_command = 0;
597 hw_->user.usr_addr = 0;
598}
599
600ErrorCode ESP32SPI::StartAsyncTransfer(const uint8_t* tx, uint8_t* rx, size_t size,
601 bool enable_rx, RawData read_back, bool mem_read,
602 OperationRW& op, bool& started)
603{
604 started = false;
605
606 if (!CanUseDma(size))
607 {
608 return ErrorCode::NOT_SUPPORT;
609 }
610 if ((tx == nullptr) || (size == 0U))
611 {
612 return ErrorCode::ARG_ERR;
613 }
614
615 spi_dma_ctx_t* ctx = ToDmaCtx(dma_ctx_);
616 if (ctx == nullptr)
617 {
618 return ErrorCode::INIT_ERR;
619 }
620
621 const bool rx_enabled = enable_rx && (rx != nullptr);
622 ConfigureTransferRegisters(size);
623
624#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
625 if (!CacheSyncDmaBuffer(tx, size, true))
626 {
627 return ErrorCode::FAILED;
628 }
629#endif
630
631 if (enable_rx && (rx != nullptr))
632 {
633 spicommon_dma_desc_setup_link(ctx->dmadesc_rx, rx, static_cast<int>(size), true);
634 if (DmaReset(ctx->rx_dma_chan) != ESP_OK)
635 {
636 return ErrorCode::INIT_ERR;
637 }
638 spi_hal_hw_prepare_rx(hw_);
639 if (DmaStart(ctx->rx_dma_chan, ctx->dmadesc_rx) != ESP_OK)
640 {
641 return ErrorCode::INIT_ERR;
642 }
643 }
644#if CONFIG_IDF_TARGET_ESP32
645 else
646 {
647 // Keep ESP32 full-duplex TX-only DMA behavior aligned with IDF workaround.
648 spi_ll_dma_rx_enable(hw_, true);
649 (void)DmaStart(ctx->rx_dma_chan, nullptr);
650 }
651#endif
652
653 spicommon_dma_desc_setup_link(ctx->dmadesc_tx, tx, static_cast<int>(size), false);
654 if (DmaReset(ctx->tx_dma_chan) != ESP_OK)
655 {
656 return ErrorCode::INIT_ERR;
657 }
658 spi_hal_hw_prepare_tx(hw_);
659 if (DmaStart(ctx->tx_dma_chan, ctx->dmadesc_tx) != ESP_OK)
660 {
661 return ErrorCode::INIT_ERR;
662 }
663
664#if CONFIG_IDF_TARGET_ESP32
665 spicommon_dmaworkaround_transfer_active(ctx->tx_dma_chan.chan_id);
666#endif
667
668 rw_op_ = op;
669 read_back_ = read_back;
670 mem_read_ = mem_read;
671 async_dma_size_ = size;
672 async_dma_rx_enabled_ = rx_enabled;
673 started = true;
674
675 // On ESP32 classic, enable data lines only after DMA descriptors/channels are ready.
676 spi_ll_enable_mosi(hw_, 1);
677 spi_ll_enable_miso(hw_, rx_enabled ? 1 : 0);
678 spi_ll_clear_int_stat(hw_);
679 spi_ll_enable_int(hw_);
680 async_running_ = true;
681 spi_ll_apply_config(hw_);
682 spi_ll_user_start(hw_);
683
684 op.MarkAsRunning();
685 if (op.type == OperationRW::OperationType::BLOCK)
686 {
687 return op.data.sem_info.sem->Wait(op.data.sem_info.timeout);
688 }
689 return ErrorCode::OK;
690}
691
692ErrorCode ESP32SPI::ExecuteChunk(const uint8_t* tx, uint8_t* rx, size_t size,
693 bool enable_rx)
694{
695 if ((size == 0U) || (size > kMaxPollingTransferBytes))
696 {
697 return ErrorCode::SIZE_ERR;
698 }
699
700 static constexpr std::array<uint8_t, kMaxPollingTransferBytes> kZero = {};
701 const uint8_t* tx_data = (tx != nullptr) ? tx : kZero.data();
702
703 ConfigureTransferRegisters(size);
704 spi_ll_enable_mosi(hw_, 1);
705 spi_ll_enable_miso(hw_, enable_rx ? 1 : 0);
706 spi_ll_write_buffer(hw_, tx_data, size * 8U);
707 spi_ll_clear_int_stat(hw_);
708 spi_ll_apply_config(hw_);
709 spi_ll_user_start(hw_);
710
711 const uint64_t timeout_us = CalcPollingTimeoutUs(size, GetBusSpeed());
712 const uint64_t start_us = GetNowUs();
713 while (!spi_ll_usr_is_done(hw_))
714 {
715 if ((GetNowUs() - start_us) > timeout_us)
716 {
717 return ErrorCode::TIMEOUT;
718 }
719 }
720
721 if (enable_rx && (rx != nullptr))
722 {
723 spi_ll_read_buffer(hw_, rx, size * 8U);
724 }
725 spi_ll_clear_int_stat(hw_);
726 return ErrorCode::OK;
727}
728
729ErrorCode ESP32SPI::ExecuteTransfer(const uint8_t* tx, uint8_t* rx, size_t size,
730 bool enable_rx)
731{
732 size_t offset = 0U;
733 while (offset < size)
734 {
735 const size_t remain = size - offset;
736 const size_t chunk = std::min(remain, kMaxPollingTransferBytes);
737 const uint8_t* tx_chunk = (tx != nullptr) ? (tx + offset) : nullptr;
738 uint8_t* rx_chunk = (enable_rx && (rx != nullptr)) ? (rx + offset) : nullptr;
739
740 const ErrorCode ec = ExecuteChunk(tx_chunk, rx_chunk, chunk, enable_rx);
741 if (ec != ErrorCode::OK)
742 {
743 return ec;
744 }
745 offset += chunk;
746 }
747
748 return ErrorCode::OK;
749}
750
751ErrorCode ESP32SPI::ReadAndWrite(RawData read_data, ConstRawData write_data,
752 OperationRW& op, bool in_isr)
753{
754 const size_t need = std::max(read_data.size_, write_data.size_);
755 if (need == 0U)
756 {
757 return CompleteZeroSize(op, in_isr);
758 }
759
760 const ErrorCode lock_ec = EnsureReadyAndAcquire();
761 if (lock_ec != ErrorCode::OK)
762 {
763 return lock_ec;
764 }
765
766 RawData rx = GetRxBuffer();
767 RawData tx = GetTxBuffer();
768 ASSERT(rx.size_ >= need);
769 ASSERT(tx.size_ >= need);
770
771 auto* tx_ptr = static_cast<uint8_t*>(tx.addr_);
772 if (write_data.size_ > 0U)
773 {
774 Memory::FastCopy(tx_ptr, write_data.addr_, write_data.size_);
775 }
776 if (need > write_data.size_)
777 {
778 Memory::FastSet(tx_ptr + write_data.size_, 0x00, need - write_data.size_);
779 }
780
781 if (CanUseDma(need))
782 {
783 bool started = false;
784 const ErrorCode ec = StartAsyncTransfer(tx_ptr, static_cast<uint8_t*>(rx.addr_), need,
785 true, read_data, false, op, started);
786 return ReturnAsyncStartResult(ec, started);
787 }
788
789 const ErrorCode ec =
790 ExecuteTransfer(tx_ptr, static_cast<uint8_t*>(rx.addr_), need, true);
791 if (ec == ErrorCode::OK)
792 {
793 if (read_data.size_ > 0U)
794 {
795 Memory::FastCopy(read_data.addr_, rx.addr_, read_data.size_);
796 }
797 SwitchBufferLocal();
798 }
799
800 Release();
801 return FinalizeSyncResult(op, in_isr, ec);
802}
803
804ErrorCode ESP32SPI::MemRead(uint16_t reg, RawData read_data, OperationRW& op, bool in_isr)
805{
806 if (read_data.size_ == 0U)
807 {
808 return CompleteZeroSize(op, in_isr);
809 }
810
811 const ErrorCode lock_ec = EnsureReadyAndAcquire();
812 if (lock_ec != ErrorCode::OK)
813 {
814 return lock_ec;
815 }
816
817 RawData rx = GetRxBuffer();
818 RawData tx = GetTxBuffer();
819 const size_t total = read_data.size_ + 1U;
820
821 ASSERT(rx.size_ >= total);
822 ASSERT(tx.size_ >= total);
823
824 auto* tx_ptr = static_cast<uint8_t*>(tx.addr_);
825 tx_ptr[0] = static_cast<uint8_t>(reg | 0x80U);
826 Memory::FastSet(tx_ptr + 1, 0x00, read_data.size_);
827
828 if (CanUseDma(total))
829 {
830 bool started = false;
831 const ErrorCode ec = StartAsyncTransfer(tx_ptr, static_cast<uint8_t*>(rx.addr_),
832 total, true, read_data, true, op, started);
833 return ReturnAsyncStartResult(ec, started);
834 }
835
836 const ErrorCode ec =
837 ExecuteTransfer(tx_ptr, static_cast<uint8_t*>(rx.addr_), total, true);
838 if (ec == ErrorCode::OK)
839 {
840 auto* rx_ptr = static_cast<uint8_t*>(rx.addr_);
841 Memory::FastCopy(read_data.addr_, rx_ptr + 1, read_data.size_);
842 SwitchBufferLocal();
843 }
844
845 Release();
846 return FinalizeSyncResult(op, in_isr, ec);
847}
848
849ErrorCode ESP32SPI::MemWrite(uint16_t reg, ConstRawData write_data, OperationRW& op,
850 bool in_isr)
851{
852 if (write_data.size_ == 0U)
853 {
854 return CompleteZeroSize(op, in_isr);
855 }
856
857 const ErrorCode lock_ec = EnsureReadyAndAcquire();
858 if (lock_ec != ErrorCode::OK)
859 {
860 return lock_ec;
861 }
862
863 RawData tx = GetTxBuffer();
864 const size_t total = write_data.size_ + 1U;
865 ASSERT(tx.size_ >= total);
866
867 auto* tx_ptr = static_cast<uint8_t*>(tx.addr_);
868 tx_ptr[0] = static_cast<uint8_t>(reg & 0x7FU);
869 Memory::FastCopy(tx_ptr + 1, write_data.addr_, write_data.size_);
870
871 if (CanUseDma(total))
872 {
873 bool started = false;
874 const ErrorCode ec = StartAsyncTransfer(tx_ptr, nullptr, total, false, {nullptr, 0},
875 false, op, started);
876 return ReturnAsyncStartResult(ec, started);
877 }
878
879 const ErrorCode ec = ExecuteTransfer(tx_ptr, nullptr, total, false);
880 if (ec == ErrorCode::OK)
881 {
882 SwitchBufferLocal();
883 }
884
885 Release();
886 return FinalizeSyncResult(op, in_isr, ec);
887}
888
889ErrorCode ESP32SPI::Transfer(size_t size, OperationRW& op, bool in_isr)
890{
891 if (size == 0U)
892 {
893 return CompleteZeroSize(op, in_isr);
894 }
895
896 const ErrorCode lock_ec = EnsureReadyAndAcquire();
897 if (lock_ec != ErrorCode::OK)
898 {
899 return lock_ec;
900 }
901
902 RawData rx = GetRxBuffer();
903 RawData tx = GetTxBuffer();
904 ASSERT(rx.size_ >= size);
905 ASSERT(tx.size_ >= size);
906
907 if (CanUseDma(size))
908 {
909 bool started = false;
910 const ErrorCode ec = StartAsyncTransfer(static_cast<const uint8_t*>(tx.addr_),
911 static_cast<uint8_t*>(rx.addr_), size, true,
912 {nullptr, 0}, false, op, started);
913 return ReturnAsyncStartResult(ec, started);
914 }
915
916 const ErrorCode ec = ExecuteTransfer(static_cast<const uint8_t*>(tx.addr_),
917 static_cast<uint8_t*>(rx.addr_), size, true);
918 if (ec == ErrorCode::OK)
919 {
920 SwitchBufferLocal();
921 }
922
923 Release();
924 return FinalizeSyncResult(op, in_isr, ec);
925}
926
927uint32_t ESP32SPI::GetMaxBusSpeed() const { return source_clock_hz_; }
928
929SPI::Prescaler ESP32SPI::GetMaxPrescaler() const { return Prescaler::DIV_65536; }
930
931} // namespace LibXR
常量原始数据封装类。 A class for encapsulating constant raw data.
size_t size_
数据大小(字节)。 The size of the data (in bytes).
const void * addr_
数据存储地址(常量)。 The storage address of the data (constant).
原始数据封装类。 A class for encapsulating raw data.
size_t size_
数据大小(字节)。 The size of the data (in bytes).
void * addr_
数据存储地址。 The storage address of the data.
ClockPhase
定义 SPI 时钟相位。Defines the SPI clock phase.
Definition spi.hpp:31
@ EDGE_1
在第一个时钟边沿采样数据。Data sampled on the first clock edge.
ClockPolarity
定义 SPI 时钟极性。Defines the SPI clock polarity.
Definition spi.hpp:21
@ LOW
时钟空闲时为低电平。Clock idle low.
static Timebase * timebase
静态指针,用于存储全局时间基对象。 Static pointer storing the global timebase instance.
Definition timebase.hpp:119
static MicrosecondTimestamp GetMicroseconds()
获取当前时间的微秒级时间戳。 Gets the current timestamp in microseconds.
Definition timebase.hpp:49
LibXR 命名空间
Definition ch32_can.hpp:14
存储 SPI 配置参数的结构体。Structure for storing SPI configuration parameters.
Definition spi.hpp:85
bool double_buffer
是否使用双缓冲区。Whether to use double buffer.
Definition spi.hpp:90
ClockPhase clock_phase
SPI 时钟相位。SPI clock phase.
Definition spi.hpp:88
Prescaler prescaler
SPI 分频系数。SPI prescaler.
Definition spi.hpp:89
ClockPolarity clock_polarity
SPI 时钟极性。SPI clock polarity.
Definition spi.hpp:86