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 CACHE_SYNC_FLAG_UNALIGNED = (1 << 1);
85constexpr int CACHE_SYNC_FLAG_DIR_C2M = (1 << 2);
86constexpr int CACHE_SYNC_FLAG_DIR_M2C = (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 ? CACHE_SYNC_FLAG_DIR_C2M : CACHE_SYNC_FLAG_DIR_M2C;
103 flags |= CACHE_SYNC_FLAG_UNALIGNED;
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() { return !busy_.TestAndSet(); }
172
173void ESP32SPI::Release() { busy_.Clear(); }
174
175ErrorCode ESP32SPI::InitializeHardware()
176{
177 if (initialized_)
178 {
179 return ErrorCode::OK;
180 }
181
182 if ((host_ <= SPI1_HOST) || (host_ >= SPI_HOST_MAX) ||
183 (static_cast<int>(host_) >= SOC_SPI_PERIPH_NUM))
184 {
185 return ErrorCode::ARG_ERR;
186 }
187
188 const auto& signal = spi_periph_signal[host_];
189 hw_ = signal.hw;
190 if (hw_ == nullptr)
191 {
192 return ErrorCode::NOT_SUPPORT;
193 }
194
195 PERIPH_RCC_ATOMIC()
196 {
197 (void)__DECLARE_RCC_ATOMIC_ENV;
198 spi_ll_enable_bus_clock(host_, true);
199 spi_ll_reset_register(host_);
200 }
201 spi_ll_enable_clock(host_, true);
202 spi_ll_master_init(hw_);
203
204 const spi_line_mode_t line_mode = {
205 .cmd_lines = 1,
206 .addr_lines = 1,
207 .data_lines = 1,
208 };
209 spi_ll_master_set_line_mode(hw_, line_mode);
210 spi_ll_set_half_duplex(hw_, false);
211 spi_ll_set_tx_lsbfirst(hw_, false);
212 spi_ll_set_rx_lsbfirst(hw_, false);
213 spi_ll_set_mosi_delay(hw_, 0, 0);
214 spi_ll_enable_mosi(hw_, 1);
215 spi_ll_enable_miso(hw_, 1);
216 spi_ll_set_dummy(hw_, 0);
217 spi_ll_set_command_bitlen(hw_, 0);
218 spi_ll_set_addr_bitlen(hw_, 0);
219 spi_ll_set_command(hw_, 0, 0, false);
220 spi_ll_set_address(hw_, 0, 0, false);
221 hw_->user.usr_command = 0;
222 hw_->user.usr_addr = 0;
223
224 spi_ll_set_clk_source(hw_, SPI_CLK_SRC_DEFAULT);
225 if (ResolveClockSource(source_clock_hz_) != ErrorCode::OK)
226 {
227 return ErrorCode::INIT_ERR;
228 }
229
230 spi_ll_disable_int(hw_);
231 spi_ll_clear_int_stat(hw_);
232 initialized_ = true;
233 return ErrorCode::OK;
234}
235
236ErrorCode ESP32SPI::ConfigurePins()
237{
238 if (!initialized_ || (hw_ == nullptr))
239 {
240 return ErrorCode::STATE_ERR;
241 }
242
243 const auto& signal = spi_periph_signal[host_];
244
245 if (sclk_pin_ >= 0)
246 {
247 if (!GPIO_IS_VALID_OUTPUT_GPIO(sclk_pin_))
248 {
249 return ErrorCode::ARG_ERR;
250 }
251 esp_rom_gpio_pad_select_gpio(static_cast<uint32_t>(sclk_pin_));
252 esp_rom_gpio_connect_out_signal(sclk_pin_, signal.spiclk_out, false, false);
253 gpio_input_enable(static_cast<gpio_num_t>(sclk_pin_));
254 esp_rom_gpio_connect_in_signal(sclk_pin_, signal.spiclk_in, false);
255 }
256
257 if (mosi_pin_ >= 0)
258 {
259 if (!GPIO_IS_VALID_OUTPUT_GPIO(mosi_pin_))
260 {
261 return ErrorCode::ARG_ERR;
262 }
263 esp_rom_gpio_pad_select_gpio(static_cast<uint32_t>(mosi_pin_));
264 esp_rom_gpio_connect_out_signal(mosi_pin_, signal.spid_out, false, false);
265 gpio_input_enable(static_cast<gpio_num_t>(mosi_pin_));
266 esp_rom_gpio_connect_in_signal(mosi_pin_, signal.spid_in, false);
267 }
268
269 if (miso_pin_ >= 0)
270 {
271 if (!GPIO_IS_VALID_GPIO(miso_pin_))
272 {
273 return ErrorCode::ARG_ERR;
274 }
275 gpio_input_enable(static_cast<gpio_num_t>(miso_pin_));
276 esp_rom_gpio_connect_in_signal(miso_pin_, signal.spiq_in, false);
277 }
278
279 return ErrorCode::OK;
280}
281
282ErrorCode ESP32SPI::ResolveClockSource(uint32_t& source_hz)
283{
284 source_hz = 0;
285 const esp_err_t err =
286 esp_clk_tree_src_get_freq_hz(static_cast<soc_module_clk_t>(SPI_CLK_SRC_DEFAULT),
287 ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &source_hz);
288 if ((err != ESP_OK) || (source_hz == 0))
289 {
290 return ErrorCode::INIT_ERR;
291 }
292 return ErrorCode::OK;
293}
294
295ErrorCode ESP32SPI::InstallInterrupt()
296{
297 if (intr_installed_)
298 {
299 return ErrorCode::OK;
300 }
301
302 const int irq_source = static_cast<int>(spi_periph_signal[host_].irq);
303 if (esp_intr_alloc(irq_source, ESP_INTR_FLAG_IRAM, &ESP32SPI::SpiIsrEntry, this,
304 &intr_handle_) != ESP_OK)
305 {
306 intr_handle_ = nullptr;
307 return ErrorCode::INIT_ERR;
308 }
309
310 intr_installed_ = true;
311 return ErrorCode::OK;
312}
313
314ErrorCode ESP32SPI::InitDmaBackend()
315{
316 if (dma_enabled_)
317 {
318 return ErrorCode::OK;
319 }
320
321 spi_dma_ctx_t* ctx = nullptr;
322 if (spicommon_dma_chan_alloc(host_, SPI_DMA_CH_AUTO, &ctx) != ESP_OK)
323 {
324 return ErrorCode::INIT_ERR;
325 }
326
327 const size_t cfg_max_size = std::max(dma_rx_raw_.size_, dma_tx_raw_.size_);
328 int actual_max_size = 0;
329 if (spicommon_dma_desc_alloc(ctx, static_cast<int>(cfg_max_size), &actual_max_size) !=
330 ESP_OK)
331 {
332 (void)spicommon_dma_chan_free(ctx);
333 return ErrorCode::INIT_ERR;
334 }
335
336 dma_ctx_ = ctx;
337 dma_enabled_ = true;
338 dma_max_transfer_bytes_ =
339 std::min<size_t>({static_cast<size_t>(actual_max_size), dma_rx_raw_.size_,
340 dma_tx_raw_.size_, MAX_DMA_TRANSFER_BYTES});
341
342 if (dma_max_transfer_bytes_ == 0U)
343 {
344 return ErrorCode::INIT_ERR;
345 }
346
347 return ErrorCode::OK;
348}
349
350void IRAM_ATTR ESP32SPI::SpiIsrEntry(void* arg)
351{
352 auto* spi = static_cast<ESP32SPI*>(arg);
353 if (spi != nullptr)
354 {
355 spi->HandleInterrupt();
356 }
357}
358
359void IRAM_ATTR ESP32SPI::HandleInterrupt()
360{
361 if ((hw_ == nullptr) || !initialized_)
362 {
363 return;
364 }
365
366 if (!async_running_)
367 {
368 spi_ll_clear_int_stat(hw_);
369 return;
370 }
371
372 if (!spi_ll_usr_is_done(hw_))
373 {
374 return;
375 }
376
377 FinishAsync(true, ErrorCode::OK);
378}
379
380void IRAM_ATTR ESP32SPI::FinishAsync(bool in_isr, ErrorCode ec)
381{
382 if (!async_running_)
383 {
384 return;
385 }
386
387#if CONFIG_IDF_TARGET_ESP32
388 // Keep ESP32 SPI DMA workaround state in sync with transfer lifecycle.
389 if (dma_enabled_)
390 {
391 spi_dma_ctx_t* ctx = ToDmaCtx(dma_ctx_);
392 if (ctx != nullptr)
393 {
394 spicommon_dmaworkaround_idle(ctx->tx_dma_chan.chan_id);
395 }
396 }
397#endif
398
399 spi_ll_disable_int(hw_);
400 spi_ll_clear_int_stat(hw_);
401
402 RawData rx = {nullptr, 0U};
403 const RawData read_back = read_back_;
404 const bool mem_read = mem_read_;
405 if ((ec == ErrorCode::OK) && async_dma_rx_enabled_)
406 {
407 rx = GetRxBuffer();
408#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
409 if (!CacheSyncDmaBuffer(rx.addr_, async_dma_size_, false))
410 {
411 ec = ErrorCode::FAILED;
412 }
413#endif
414 }
415
416 if ((ec == ErrorCode::OK) && (read_back.size_ > 0U))
417 {
418 if (rx.addr_ == nullptr)
419 {
420 rx = GetRxBuffer();
421 }
422 uint8_t* src = static_cast<uint8_t*>(rx.addr_);
423 if (mem_read)
424 {
425 ASSERT(rx.size_ >= (read_back.size_ + 1U));
426 src += 1;
427 }
428 else
429 {
430 ASSERT(rx.size_ >= read_back.size_);
431 }
432 Memory::FastCopy(read_back.addr_, src, read_back.size_);
433 }
434
435 if (ec == ErrorCode::OK)
436 {
437 SwitchBufferLocal();
438 }
439
440 async_running_ = false;
441 async_dma_size_ = 0U;
442 async_dma_rx_enabled_ = false;
443 mem_read_ = false;
444 read_back_ = {nullptr, 0};
445 Release();
446 if (rw_op_.type == OperationRW::OperationType::BLOCK)
447 {
448 (void)block_wait_.TryPost(in_isr, ec);
449 }
450 else
451 {
452 rw_op_.UpdateStatus(in_isr, ec);
453 }
454}
455
456ErrorCode ESP32SPI::SetConfig(SPI::Configuration config)
457{
458 if (!initialized_ || (hw_ == nullptr))
459 {
460 return ErrorCode::STATE_ERR;
461 }
462 if (busy_.IsSet())
463 {
464 return ErrorCode::BUSY;
465 }
466
467 if (config.prescaler == Prescaler::UNKNOWN)
468 {
469 return ErrorCode::ARG_ERR;
470 }
471 if (config.double_buffer &&
472 ((dbuf_rx_block_size_ == 0U) || (dbuf_tx_block_size_ == 0U)))
473 {
474 return ErrorCode::ARG_ERR;
475 }
476
477 const uint32_t div = PrescalerToDiv(config.prescaler);
478 if ((div == 0) || (source_clock_hz_ == 0))
479 {
480 return ErrorCode::ARG_ERR;
481 }
482
483 const uint32_t target_hz = source_clock_hz_ / div;
484 if (target_hz == 0)
485 {
486 return ErrorCode::ARG_ERR;
487 }
488
489 const uint8_t mode = ResolveSpiMode(config.clock_polarity, config.clock_phase);
490 spi_ll_master_set_mode(hw_, mode);
491
492 const int applied_hz = spi_ll_master_set_clock(hw_, static_cast<int>(source_clock_hz_),
493 static_cast<int>(target_hz), 128);
494 if (applied_hz <= 0)
495 {
496 return ErrorCode::INIT_ERR;
497 }
498
499 spi_ll_apply_config(hw_);
500 GetConfig() = config;
501 dbuf_enabled_ = config.double_buffer;
502 dbuf_active_block_ = 0U;
503
504 return ErrorCode::OK;
505}
506
507bool ESP32SPI::UseLocalDoubleBuffer() const
508{
509 return dbuf_enabled_ && (dbuf_rx_block_size_ > 0U) && (dbuf_tx_block_size_ > 0U);
510}
511
512RawData ESP32SPI::GetRxBuffer()
513{
514 if (UseLocalDoubleBuffer())
515 {
516 auto* base = static_cast<uint8_t*>(dma_rx_raw_.addr_);
517 return {base + static_cast<size_t>(dbuf_active_block_) * dbuf_rx_block_size_,
518 dbuf_rx_block_size_};
519 }
520 return dma_rx_raw_;
521}
522
523RawData ESP32SPI::GetTxBuffer()
524{
525 if (UseLocalDoubleBuffer())
526 {
527 auto* base = static_cast<uint8_t*>(dma_tx_raw_.addr_);
528 return {base + static_cast<size_t>(dbuf_active_block_) * dbuf_tx_block_size_,
529 dbuf_tx_block_size_};
530 }
531 return dma_tx_raw_;
532}
533
534void ESP32SPI::SwitchBufferLocal()
535{
536 if (UseLocalDoubleBuffer())
537 {
538 dbuf_active_block_ ^= 1U;
539 }
540}
541
542bool ESP32SPI::CanUseDma(size_t size) const
543{
544 return dma_requested_ && dma_enabled_ && (dma_ctx_ != nullptr) &&
545 (size > dma_enable_min_size_) && (size <= dma_max_transfer_bytes_);
546}
547
548ErrorCode ESP32SPI::EnsureReadyAndAcquire()
549{
550 if (!initialized_)
551 {
552 return ErrorCode::INIT_ERR;
553 }
554 if (!Acquire())
555 {
556 return ErrorCode::BUSY;
557 }
558 return ErrorCode::OK;
559}
560
561ErrorCode ESP32SPI::FinalizeSyncResult(OperationRW& op, bool in_isr, ErrorCode ec)
562{
563 if (op.type != OperationRW::OperationType::BLOCK)
564 {
565 op.UpdateStatus(in_isr, ec);
566 }
567 return ec;
568}
569
570ErrorCode ESP32SPI::CompleteZeroSize(OperationRW& op, bool in_isr)
571{
572 return FinalizeSyncResult(op, in_isr, ErrorCode::OK);
573}
574
575ErrorCode ESP32SPI::ReturnAsyncStartResult(ErrorCode ec, bool started)
576{
577 if (!started)
578 {
579 Release();
580 }
581 return ec;
582}
583
584void ESP32SPI::ConfigureTransferRegisters(size_t size)
585{
586 static constexpr spi_line_mode_t LINE_MODE = {
587 .cmd_lines = 1,
588 .addr_lines = 1,
589 .data_lines = 1,
590 };
591 const size_t bitlen = size * 8U;
592
593 spi_ll_master_set_line_mode(hw_, LINE_MODE);
594 spi_ll_set_mosi_bitlen(hw_, bitlen);
595 spi_ll_set_miso_bitlen(hw_, bitlen);
596 spi_ll_set_command_bitlen(hw_, 0);
597 spi_ll_set_addr_bitlen(hw_, 0);
598 spi_ll_set_command(hw_, 0, 0, false);
599 spi_ll_set_address(hw_, 0, 0, false);
600 hw_->user.usr_command = 0;
601 hw_->user.usr_addr = 0;
602}
603
604ErrorCode ESP32SPI::StartAsyncTransfer(const uint8_t* tx, uint8_t* rx, size_t size,
605 bool enable_rx, RawData read_back, bool mem_read,
606 OperationRW& op, bool& started)
607{
608 started = false;
609
610 if (!CanUseDma(size))
611 {
612 return ErrorCode::NOT_SUPPORT;
613 }
614 if ((tx == nullptr) || (size == 0U))
615 {
616 return ErrorCode::ARG_ERR;
617 }
618
619 spi_dma_ctx_t* ctx = ToDmaCtx(dma_ctx_);
620 if (ctx == nullptr)
621 {
622 return ErrorCode::INIT_ERR;
623 }
624
625 const bool rx_enabled = enable_rx && (rx != nullptr);
626 ConfigureTransferRegisters(size);
627
628#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
629 if (!CacheSyncDmaBuffer(tx, size, true))
630 {
631 return ErrorCode::FAILED;
632 }
633#endif
634
635 if (enable_rx && (rx != nullptr))
636 {
637 spicommon_dma_desc_setup_link(ctx->dmadesc_rx, rx, static_cast<int>(size), true);
638 if (DmaReset(ctx->rx_dma_chan) != ESP_OK)
639 {
640 return ErrorCode::INIT_ERR;
641 }
642 spi_hal_hw_prepare_rx(hw_);
643 if (DmaStart(ctx->rx_dma_chan, ctx->dmadesc_rx) != ESP_OK)
644 {
645 return ErrorCode::INIT_ERR;
646 }
647 }
648#if CONFIG_IDF_TARGET_ESP32
649 else
650 {
651 // Keep ESP32 full-duplex TX-only DMA behavior aligned with IDF workaround.
652 spi_ll_dma_rx_enable(hw_, true);
653 (void)DmaStart(ctx->rx_dma_chan, nullptr);
654 }
655#endif
656
657 spicommon_dma_desc_setup_link(ctx->dmadesc_tx, tx, static_cast<int>(size), false);
658 if (DmaReset(ctx->tx_dma_chan) != ESP_OK)
659 {
660 return ErrorCode::INIT_ERR;
661 }
662 spi_hal_hw_prepare_tx(hw_);
663 if (DmaStart(ctx->tx_dma_chan, ctx->dmadesc_tx) != ESP_OK)
664 {
665 return ErrorCode::INIT_ERR;
666 }
667
668#if CONFIG_IDF_TARGET_ESP32
669 spicommon_dmaworkaround_transfer_active(ctx->tx_dma_chan.chan_id);
670#endif
671
672 rw_op_ = op;
673 read_back_ = read_back;
674 mem_read_ = mem_read;
675 async_dma_size_ = size;
676 async_dma_rx_enabled_ = rx_enabled;
677 if (op.type == OperationRW::OperationType::BLOCK)
678 {
679 block_wait_.Start(*op.data.sem_info.sem);
680 }
681 started = true;
682
683 // On ESP32 classic, enable data lines only after DMA descriptors/channels are ready.
684 spi_ll_enable_mosi(hw_, 1);
685 spi_ll_enable_miso(hw_, rx_enabled ? 1 : 0);
686 spi_ll_clear_int_stat(hw_);
687 spi_ll_enable_int(hw_);
688 async_running_ = true;
689 spi_ll_apply_config(hw_);
690 spi_ll_user_start(hw_);
691
692 op.MarkAsRunning();
693 if (op.type == OperationRW::OperationType::BLOCK)
694 {
695 return block_wait_.Wait(op.data.sem_info.timeout);
696 }
697 return ErrorCode::OK;
698}
699
700ErrorCode ESP32SPI::ExecuteChunk(const uint8_t* tx, uint8_t* rx, size_t size,
701 bool enable_rx)
702{
703 if ((size == 0U) || (size > MAX_POLLING_TRANSFER_BYTES))
704 {
705 return ErrorCode::SIZE_ERR;
706 }
707
708 static constexpr std::array<uint8_t, MAX_POLLING_TRANSFER_BYTES> ZERO = {};
709 const uint8_t* tx_data = (tx != nullptr) ? tx : ZERO.data();
710
711 ConfigureTransferRegisters(size);
712 spi_ll_enable_mosi(hw_, 1);
713 spi_ll_enable_miso(hw_, enable_rx ? 1 : 0);
714 spi_ll_write_buffer(hw_, tx_data, size * 8U);
715 spi_ll_clear_int_stat(hw_);
716 spi_ll_apply_config(hw_);
717 spi_ll_user_start(hw_);
718
719 const uint64_t timeout_us = CalcPollingTimeoutUs(size, GetBusSpeed());
720 const uint64_t start_us = GetNowUs();
721 while (!spi_ll_usr_is_done(hw_))
722 {
723 if ((GetNowUs() - start_us) > timeout_us)
724 {
725 return ErrorCode::TIMEOUT;
726 }
727 }
728
729 if (enable_rx && (rx != nullptr))
730 {
731 spi_ll_read_buffer(hw_, rx, size * 8U);
732 }
733 spi_ll_clear_int_stat(hw_);
734 return ErrorCode::OK;
735}
736
737ErrorCode ESP32SPI::ExecuteTransfer(const uint8_t* tx, uint8_t* rx, size_t size,
738 bool enable_rx)
739{
740 size_t offset = 0U;
741 while (offset < size)
742 {
743 const size_t remain = size - offset;
744 const size_t chunk = std::min(remain, MAX_POLLING_TRANSFER_BYTES);
745 const uint8_t* tx_chunk = (tx != nullptr) ? (tx + offset) : nullptr;
746 uint8_t* rx_chunk = (enable_rx && (rx != nullptr)) ? (rx + offset) : nullptr;
747
748 const ErrorCode ec = ExecuteChunk(tx_chunk, rx_chunk, chunk, enable_rx);
749 if (ec != ErrorCode::OK)
750 {
751 return ec;
752 }
753 offset += chunk;
754 }
755
756 return ErrorCode::OK;
757}
758
759ErrorCode ESP32SPI::ReadAndWrite(RawData read_data, ConstRawData write_data,
760 OperationRW& op, bool in_isr)
761{
762 const size_t need = std::max(read_data.size_, write_data.size_);
763 if (need == 0U)
764 {
765 return CompleteZeroSize(op, in_isr);
766 }
767
768 const ErrorCode lock_ec = EnsureReadyAndAcquire();
769 if (lock_ec != ErrorCode::OK)
770 {
771 return lock_ec;
772 }
773
774 RawData rx = GetRxBuffer();
775 RawData tx = GetTxBuffer();
776 ASSERT(rx.size_ >= need);
777 ASSERT(tx.size_ >= need);
778
779 auto* tx_ptr = static_cast<uint8_t*>(tx.addr_);
780 if (write_data.size_ > 0U)
781 {
782 Memory::FastCopy(tx_ptr, write_data.addr_, write_data.size_);
783 }
784 if (need > write_data.size_)
785 {
786 Memory::FastSet(tx_ptr + write_data.size_, 0x00, need - write_data.size_);
787 }
788
789 if (CanUseDma(need))
790 {
791 bool started = false;
792 const ErrorCode ec = StartAsyncTransfer(tx_ptr, static_cast<uint8_t*>(rx.addr_), need,
793 true, read_data, false, op, started);
794 return ReturnAsyncStartResult(ec, started);
795 }
796
797 const ErrorCode ec =
798 ExecuteTransfer(tx_ptr, static_cast<uint8_t*>(rx.addr_), need, true);
799 if (ec == ErrorCode::OK)
800 {
801 if (read_data.size_ > 0U)
802 {
803 Memory::FastCopy(read_data.addr_, rx.addr_, read_data.size_);
804 }
805 SwitchBufferLocal();
806 }
807
808 Release();
809 return FinalizeSyncResult(op, in_isr, ec);
810}
811
812ErrorCode ESP32SPI::MemRead(uint16_t reg, RawData read_data, OperationRW& op, bool in_isr)
813{
814 if (read_data.size_ == 0U)
815 {
816 return CompleteZeroSize(op, in_isr);
817 }
818
819 const ErrorCode lock_ec = EnsureReadyAndAcquire();
820 if (lock_ec != ErrorCode::OK)
821 {
822 return lock_ec;
823 }
824
825 RawData rx = GetRxBuffer();
826 RawData tx = GetTxBuffer();
827 const size_t total = read_data.size_ + 1U;
828
829 ASSERT(rx.size_ >= total);
830 ASSERT(tx.size_ >= total);
831
832 auto* tx_ptr = static_cast<uint8_t*>(tx.addr_);
833 tx_ptr[0] = static_cast<uint8_t>(reg | 0x80U);
834 Memory::FastSet(tx_ptr + 1, 0x00, read_data.size_);
835
836 if (CanUseDma(total))
837 {
838 bool started = false;
839 const ErrorCode ec = StartAsyncTransfer(tx_ptr, static_cast<uint8_t*>(rx.addr_),
840 total, true, read_data, true, op, started);
841 return ReturnAsyncStartResult(ec, started);
842 }
843
844 const ErrorCode ec =
845 ExecuteTransfer(tx_ptr, static_cast<uint8_t*>(rx.addr_), total, true);
846 if (ec == ErrorCode::OK)
847 {
848 auto* rx_ptr = static_cast<uint8_t*>(rx.addr_);
849 Memory::FastCopy(read_data.addr_, rx_ptr + 1, read_data.size_);
850 SwitchBufferLocal();
851 }
852
853 Release();
854 return FinalizeSyncResult(op, in_isr, ec);
855}
856
857ErrorCode ESP32SPI::MemWrite(uint16_t reg, ConstRawData write_data, OperationRW& op,
858 bool in_isr)
859{
860 if (write_data.size_ == 0U)
861 {
862 return CompleteZeroSize(op, in_isr);
863 }
864
865 const ErrorCode lock_ec = EnsureReadyAndAcquire();
866 if (lock_ec != ErrorCode::OK)
867 {
868 return lock_ec;
869 }
870
871 RawData tx = GetTxBuffer();
872 const size_t total = write_data.size_ + 1U;
873 ASSERT(tx.size_ >= total);
874
875 auto* tx_ptr = static_cast<uint8_t*>(tx.addr_);
876 tx_ptr[0] = static_cast<uint8_t>(reg & 0x7FU);
877 Memory::FastCopy(tx_ptr + 1, write_data.addr_, write_data.size_);
878
879 if (CanUseDma(total))
880 {
881 bool started = false;
882 const ErrorCode ec = StartAsyncTransfer(tx_ptr, nullptr, total, false, {nullptr, 0},
883 false, op, started);
884 return ReturnAsyncStartResult(ec, started);
885 }
886
887 const ErrorCode ec = ExecuteTransfer(tx_ptr, nullptr, total, false);
888 if (ec == ErrorCode::OK)
889 {
890 SwitchBufferLocal();
891 }
892
893 Release();
894 return FinalizeSyncResult(op, in_isr, ec);
895}
896
897ErrorCode ESP32SPI::Transfer(size_t size, OperationRW& op, bool in_isr)
898{
899 if (size == 0U)
900 {
901 return CompleteZeroSize(op, in_isr);
902 }
903
904 const ErrorCode lock_ec = EnsureReadyAndAcquire();
905 if (lock_ec != ErrorCode::OK)
906 {
907 return lock_ec;
908 }
909
910 RawData rx = GetRxBuffer();
911 RawData tx = GetTxBuffer();
912 ASSERT(rx.size_ >= size);
913 ASSERT(tx.size_ >= size);
914
915 if (CanUseDma(size))
916 {
917 bool started = false;
918 const ErrorCode ec = StartAsyncTransfer(static_cast<const uint8_t*>(tx.addr_),
919 static_cast<uint8_t*>(rx.addr_), size, true,
920 {nullptr, 0}, false, op, started);
921 return ReturnAsyncStartResult(ec, started);
922 }
923
924 const ErrorCode ec = ExecuteTransfer(static_cast<const uint8_t*>(tx.addr_),
925 static_cast<uint8_t*>(rx.addr_), size, true);
926 if (ec == ErrorCode::OK)
927 {
928 SwitchBufferLocal();
929 }
930
931 Release();
932 return FinalizeSyncResult(op, in_isr, ec);
933}
934
935uint32_t ESP32SPI::GetMaxBusSpeed() const { return source_clock_hz_; }
936
937SPI::Prescaler ESP32SPI::GetMaxPrescaler() const { return Prescaler::DIV_65536; }
938
939} // namespace LibXR
只读原始数据视图 / Immutable raw data view
size_t size_
数据字节数 / Data size in bytes
const void * addr_
数据起始地址 / Data start address
可写原始数据视图 / Mutable raw data view
size_t size_
数据字节数 / Data size in bytes
void * addr_
数据起始地址 / Data start address
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
ErrorCode
定义错误码枚举
存储 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