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