8#include "driver/gpio.h"
9#include "driver/spi_common.h"
11#include "esp_clk_tree.h"
13#include "esp_memory_utils.h"
14#include "esp_private/periph_ctrl.h"
15#include "esp_private/spi_common_internal.h"
18#include "esp_private/gdma.h"
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"
40spi_dma_ctx_t* ToDmaCtx(
void* ctx) {
return reinterpret_cast<spi_dma_ctx_t*
>(ctx); }
48 return static_cast<uint64_t
>(esp_timer_get_time());
51uint64_t CalcPollingTimeoutUs(
size_t size, uint32_t bus_hz)
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;
57 return std::max<uint64_t>(100ULL, wire_time_us * 8ULL + 50ULL);
61esp_err_t DmaReset(gdma_channel_handle_t chan) {
return gdma_reset(chan); }
63esp_err_t DmaStart(gdma_channel_handle_t chan,
const void* desc)
65 return gdma_start(chan,
reinterpret_cast<intptr_t
>(desc));
68esp_err_t DmaReset(spi_dma_chan_handle_t chan)
74esp_err_t DmaStart(spi_dma_chan_handle_t chan,
const void* desc)
76 spi_dma_start(chan,
const_cast<void*
>(desc));
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);
84constexpr int kCacheSyncFlagUnaligned = (1 << 1);
85constexpr int kCacheSyncFlagDirC2M = (1 << 2);
86constexpr int kCacheSyncFlagDirM2C = (1 << 3);
88bool CacheSyncDmaBuffer(
const void* addr,
size_t size,
bool cache_to_mem)
90 if ((addr ==
nullptr) || (size == 0U))
95#if SOC_PSRAM_DMA_CAPABLE && !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
96 if (!esp_ptr_external_ram(addr))
102 int flags = cache_to_mem ? kCacheSyncFlagDirC2M : kCacheSyncFlagDirM2C;
103 flags |= kCacheSyncFlagUnaligned;
105 const esp_err_t ret = esp_cache_msync(
const_cast<void*
>(addr), size, flags);
107 return (ret == ESP_OK) || (ret == ESP_ERR_INVALID_ARG);
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),
121 dma_enable_min_size_(dma_enable_min_size),
122 dma_requested_(enable_dma),
125 dbuf_rx_block_size_(dma_rx.size_ / 2U),
126 dbuf_tx_block_size_(dma_tx.size_ / 2U)
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);
136 if (InitializeHardware() != ErrorCode::OK)
142 if (ConfigurePins() != ErrorCode::OK)
148 if (InstallInterrupt() != ErrorCode::OK)
156 const ErrorCode dma_ans = InitDmaBackend();
157 ASSERT(dma_ans == ErrorCode::OK);
158 if (dma_ans != ErrorCode::OK)
164 if (SetConfig(config) != ErrorCode::OK)
171bool ESP32SPI::Acquire()
173 bool expected =
false;
174 return busy_.compare_exchange_strong(expected,
true, std::memory_order_acq_rel,
175 std::memory_order_acquire);
178void ESP32SPI::Release() { busy_.store(
false, std::memory_order_release); }
180ErrorCode ESP32SPI::InitializeHardware()
184 return ErrorCode::OK;
187 if ((host_ <= SPI1_HOST) || (host_ >= SPI_HOST_MAX) ||
188 (
static_cast<int>(host_) >= SOC_SPI_PERIPH_NUM))
190 return ErrorCode::ARG_ERR;
193 const auto& signal = spi_periph_signal[host_];
197 return ErrorCode::NOT_SUPPORT;
202 (void)__DECLARE_RCC_ATOMIC_ENV;
203 spi_ll_enable_bus_clock(host_,
true);
204 spi_ll_reset_register(host_);
206 spi_ll_enable_clock(host_,
true);
207 spi_ll_master_init(hw_);
209 const spi_line_mode_t line_mode = {
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;
229 spi_ll_set_clk_source(hw_, SPI_CLK_SRC_DEFAULT);
230 if (ResolveClockSource(source_clock_hz_) != ErrorCode::OK)
232 return ErrorCode::INIT_ERR;
235 spi_ll_disable_int(hw_);
236 spi_ll_clear_int_stat(hw_);
238 return ErrorCode::OK;
241ErrorCode ESP32SPI::ConfigurePins()
243 if (!initialized_ || (hw_ ==
nullptr))
245 return ErrorCode::STATE_ERR;
248 const auto& signal = spi_periph_signal[host_];
252 if (!GPIO_IS_VALID_OUTPUT_GPIO(sclk_pin_))
254 return ErrorCode::ARG_ERR;
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);
264 if (!GPIO_IS_VALID_OUTPUT_GPIO(mosi_pin_))
266 return ErrorCode::ARG_ERR;
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);
276 if (!GPIO_IS_VALID_GPIO(miso_pin_))
278 return ErrorCode::ARG_ERR;
280 gpio_input_enable(
static_cast<gpio_num_t
>(miso_pin_));
281 esp_rom_gpio_connect_in_signal(miso_pin_, signal.spiq_in,
false);
284 return ErrorCode::OK;
287ErrorCode ESP32SPI::ResolveClockSource(uint32_t& source_hz)
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))
295 return ErrorCode::INIT_ERR;
297 return ErrorCode::OK;
300ErrorCode ESP32SPI::InstallInterrupt()
304 return ErrorCode::OK;
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)
311 intr_handle_ =
nullptr;
312 return ErrorCode::INIT_ERR;
315 intr_installed_ =
true;
316 return ErrorCode::OK;
319ErrorCode ESP32SPI::InitDmaBackend()
323 return ErrorCode::OK;
326 spi_dma_ctx_t* ctx =
nullptr;
327 if (spicommon_dma_chan_alloc(host_, SPI_DMA_CH_AUTO, &ctx) != ESP_OK)
329 return ErrorCode::INIT_ERR;
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) !=
337 (void)spicommon_dma_chan_free(ctx);
338 return ErrorCode::INIT_ERR;
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});
347 if (dma_max_transfer_bytes_ == 0U)
349 return ErrorCode::INIT_ERR;
352 return ErrorCode::OK;
355void IRAM_ATTR ESP32SPI::SpiIsrEntry(
void* arg)
357 auto* spi =
static_cast<ESP32SPI*
>(arg);
360 spi->HandleInterrupt();
364void IRAM_ATTR ESP32SPI::HandleInterrupt()
366 if ((hw_ ==
nullptr) || !initialized_)
373 spi_ll_clear_int_stat(hw_);
377 if (!spi_ll_usr_is_done(hw_))
382 FinishAsync(
true, ErrorCode::OK);
385void IRAM_ATTR ESP32SPI::FinishAsync(
bool in_isr, ErrorCode ec)
392#if CONFIG_IDF_TARGET_ESP32
396 spi_dma_ctx_t* ctx = ToDmaCtx(dma_ctx_);
399 spicommon_dmaworkaround_idle(ctx->tx_dma_chan.chan_id);
404 spi_ll_disable_int(hw_);
405 spi_ll_clear_int_stat(hw_);
407 RawData rx = {
nullptr, 0U};
408 if ((ec == ErrorCode::OK) && async_dma_rx_enabled_)
411#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
412 if (!CacheSyncDmaBuffer(rx.addr_, async_dma_size_,
false))
414 ec = ErrorCode::FAILED;
419 if ((ec == ErrorCode::OK) && (read_back_.size_ > 0U))
421 if (rx.addr_ ==
nullptr)
425 uint8_t* src =
static_cast<uint8_t*
>(rx.addr_);
428 ASSERT(rx.size_ >= (read_back_.size_ + 1U));
433 ASSERT(rx.size_ >= read_back_.size_);
435 Memory::FastCopy(read_back_.addr_, src, read_back_.size_);
438 if (ec == ErrorCode::OK)
443 async_running_ =
false;
444 async_dma_size_ = 0U;
445 async_dma_rx_enabled_ =
false;
447 read_back_ = {
nullptr, 0};
449 rw_op_.UpdateStatus(in_isr, ec);
454 if (!initialized_ || (hw_ ==
nullptr))
456 return ErrorCode::STATE_ERR;
458 if (busy_.load(std::memory_order_acquire))
460 return ErrorCode::BUSY;
463 if (config.
prescaler == Prescaler::UNKNOWN)
465 return ErrorCode::ARG_ERR;
468 ((dbuf_rx_block_size_ == 0U) || (dbuf_tx_block_size_ == 0U)))
470 return ErrorCode::ARG_ERR;
473 const uint32_t div = PrescalerToDiv(config.
prescaler);
474 if ((div == 0) || (source_clock_hz_ == 0))
476 return ErrorCode::ARG_ERR;
479 const uint32_t target_hz = source_clock_hz_ / div;
482 return ErrorCode::ARG_ERR;
486 spi_ll_master_set_mode(hw_, mode);
488 const int applied_hz = spi_ll_master_set_clock(hw_,
static_cast<int>(source_clock_hz_),
489 static_cast<int>(target_hz), 128);
492 return ErrorCode::INIT_ERR;
495 spi_ll_apply_config(hw_);
496 GetConfig() = config;
498 dbuf_active_block_ = 0U;
500 return ErrorCode::OK;
503bool ESP32SPI::UseLocalDoubleBuffer()
const
505 return dbuf_enabled_ && (dbuf_rx_block_size_ > 0U) && (dbuf_tx_block_size_ > 0U);
508RawData ESP32SPI::GetRxBuffer()
510 if (UseLocalDoubleBuffer())
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_};
519RawData ESP32SPI::GetTxBuffer()
521 if (UseLocalDoubleBuffer())
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_};
530void ESP32SPI::SwitchBufferLocal()
532 if (UseLocalDoubleBuffer())
534 dbuf_active_block_ ^= 1U;
538bool ESP32SPI::CanUseDma(
size_t size)
const
540 return dma_requested_ && dma_enabled_ && (dma_ctx_ !=
nullptr) &&
541 (size > dma_enable_min_size_) && (size <= dma_max_transfer_bytes_);
544ErrorCode ESP32SPI::EnsureReadyAndAcquire()
548 return ErrorCode::INIT_ERR;
552 return ErrorCode::BUSY;
554 return ErrorCode::OK;
557ErrorCode ESP32SPI::FinalizeSyncResult(OperationRW& op,
bool in_isr, ErrorCode ec)
559 if (op.type != OperationRW::OperationType::BLOCK)
561 op.UpdateStatus(in_isr, ec);
566ErrorCode ESP32SPI::CompleteZeroSize(OperationRW& op,
bool in_isr)
568 return FinalizeSyncResult(op, in_isr, ErrorCode::OK);
571ErrorCode ESP32SPI::ReturnAsyncStartResult(ErrorCode ec,
bool started)
580void ESP32SPI::ConfigureTransferRegisters(
size_t size)
582 static constexpr spi_line_mode_t kLineMode = {
587 const size_t bitlen = size * 8U;
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;
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)
606 if (!CanUseDma(size))
608 return ErrorCode::NOT_SUPPORT;
610 if ((tx ==
nullptr) || (size == 0U))
612 return ErrorCode::ARG_ERR;
615 spi_dma_ctx_t* ctx = ToDmaCtx(dma_ctx_);
618 return ErrorCode::INIT_ERR;
621 const bool rx_enabled = enable_rx && (rx !=
nullptr);
622 ConfigureTransferRegisters(size);
624#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
625 if (!CacheSyncDmaBuffer(tx, size,
true))
627 return ErrorCode::FAILED;
631 if (enable_rx && (rx !=
nullptr))
633 spicommon_dma_desc_setup_link(ctx->dmadesc_rx, rx,
static_cast<int>(size),
true);
634 if (DmaReset(ctx->rx_dma_chan) != ESP_OK)
636 return ErrorCode::INIT_ERR;
638 spi_hal_hw_prepare_rx(hw_);
639 if (DmaStart(ctx->rx_dma_chan, ctx->dmadesc_rx) != ESP_OK)
641 return ErrorCode::INIT_ERR;
644#if CONFIG_IDF_TARGET_ESP32
648 spi_ll_dma_rx_enable(hw_,
true);
649 (void)DmaStart(ctx->rx_dma_chan,
nullptr);
653 spicommon_dma_desc_setup_link(ctx->dmadesc_tx, tx,
static_cast<int>(size),
false);
654 if (DmaReset(ctx->tx_dma_chan) != ESP_OK)
656 return ErrorCode::INIT_ERR;
658 spi_hal_hw_prepare_tx(hw_);
659 if (DmaStart(ctx->tx_dma_chan, ctx->dmadesc_tx) != ESP_OK)
661 return ErrorCode::INIT_ERR;
664#if CONFIG_IDF_TARGET_ESP32
665 spicommon_dmaworkaround_transfer_active(ctx->tx_dma_chan.chan_id);
669 read_back_ = read_back;
670 mem_read_ = mem_read;
671 async_dma_size_ = size;
672 async_dma_rx_enabled_ = rx_enabled;
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_);
685 if (op.type == OperationRW::OperationType::BLOCK)
687 return op.data.sem_info.sem->Wait(op.data.sem_info.timeout);
689 return ErrorCode::OK;
692ErrorCode ESP32SPI::ExecuteChunk(
const uint8_t* tx, uint8_t* rx,
size_t size,
695 if ((size == 0U) || (size > kMaxPollingTransferBytes))
697 return ErrorCode::SIZE_ERR;
700 static constexpr std::array<uint8_t, kMaxPollingTransferBytes> kZero = {};
701 const uint8_t* tx_data = (tx !=
nullptr) ? tx : kZero.data();
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_);
711 const uint64_t timeout_us = CalcPollingTimeoutUs(size, GetBusSpeed());
712 const uint64_t start_us = GetNowUs();
713 while (!spi_ll_usr_is_done(hw_))
715 if ((GetNowUs() - start_us) > timeout_us)
717 return ErrorCode::TIMEOUT;
721 if (enable_rx && (rx !=
nullptr))
723 spi_ll_read_buffer(hw_, rx, size * 8U);
725 spi_ll_clear_int_stat(hw_);
726 return ErrorCode::OK;
729ErrorCode ESP32SPI::ExecuteTransfer(
const uint8_t* tx, uint8_t* rx,
size_t size,
733 while (offset < size)
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;
740 const ErrorCode ec = ExecuteChunk(tx_chunk, rx_chunk, chunk, enable_rx);
741 if (ec != ErrorCode::OK)
748 return ErrorCode::OK;
754 const size_t need = std::max(read_data.
size_, write_data.
size_);
757 return CompleteZeroSize(op, in_isr);
760 const ErrorCode lock_ec = EnsureReadyAndAcquire();
761 if (lock_ec != ErrorCode::OK)
768 ASSERT(rx.
size_ >= need);
769 ASSERT(tx.
size_ >= need);
771 auto* tx_ptr =
static_cast<uint8_t*
>(tx.
addr_);
772 if (write_data.
size_ > 0U)
774 Memory::FastCopy(tx_ptr, write_data.
addr_, write_data.
size_);
776 if (need > write_data.
size_)
778 Memory::FastSet(tx_ptr + write_data.
size_, 0x00, need - write_data.
size_);
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);
790 ExecuteTransfer(tx_ptr,
static_cast<uint8_t*
>(rx.
addr_), need,
true);
791 if (ec == ErrorCode::OK)
793 if (read_data.
size_ > 0U)
801 return FinalizeSyncResult(op, in_isr, ec);
806 if (read_data.
size_ == 0U)
808 return CompleteZeroSize(op, in_isr);
811 const ErrorCode lock_ec = EnsureReadyAndAcquire();
812 if (lock_ec != ErrorCode::OK)
819 const size_t total = read_data.
size_ + 1U;
821 ASSERT(rx.
size_ >= total);
822 ASSERT(tx.
size_ >= total);
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_);
828 if (CanUseDma(total))
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);
837 ExecuteTransfer(tx_ptr,
static_cast<uint8_t*
>(rx.
addr_), total,
true);
838 if (ec == ErrorCode::OK)
840 auto* rx_ptr =
static_cast<uint8_t*
>(rx.
addr_);
841 Memory::FastCopy(read_data.
addr_, rx_ptr + 1, read_data.
size_);
846 return FinalizeSyncResult(op, in_isr, ec);
852 if (write_data.
size_ == 0U)
854 return CompleteZeroSize(op, in_isr);
857 const ErrorCode lock_ec = EnsureReadyAndAcquire();
858 if (lock_ec != ErrorCode::OK)
864 const size_t total = write_data.
size_ + 1U;
865 ASSERT(tx.
size_ >= total);
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_);
871 if (CanUseDma(total))
873 bool started =
false;
874 const ErrorCode ec = StartAsyncTransfer(tx_ptr,
nullptr, total,
false, {
nullptr, 0},
876 return ReturnAsyncStartResult(ec, started);
879 const ErrorCode ec = ExecuteTransfer(tx_ptr,
nullptr, total,
false);
880 if (ec == ErrorCode::OK)
886 return FinalizeSyncResult(op, in_isr, ec);
889ErrorCode ESP32SPI::Transfer(
size_t size,
OperationRW& op,
bool in_isr)
893 return CompleteZeroSize(op, in_isr);
896 const ErrorCode lock_ec = EnsureReadyAndAcquire();
897 if (lock_ec != ErrorCode::OK)
904 ASSERT(rx.
size_ >= size);
905 ASSERT(tx.
size_ >= size);
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);
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)
924 return FinalizeSyncResult(op, in_isr, ec);
927uint32_t ESP32SPI::GetMaxBusSpeed()
const {
return source_clock_hz_; }
常量原始数据封装类。 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.
@ EDGE_1
在第一个时钟边沿采样数据。Data sampled on the first clock edge.
ClockPolarity
定义 SPI 时钟极性。Defines the SPI clock polarity.
@ LOW
时钟空闲时为低电平。Clock idle low.
static Timebase * timebase
静态指针,用于存储全局时间基对象。 Static pointer storing the global timebase instance.
static MicrosecondTimestamp GetMicroseconds()
获取当前时间的微秒级时间戳。 Gets the current timestamp in microseconds.
存储 SPI 配置参数的结构体。Structure for storing SPI configuration parameters.
bool double_buffer
是否使用双缓冲区。Whether to use double buffer.
ClockPhase clock_phase
SPI 时钟相位。SPI clock phase.
Prescaler prescaler
SPI 分频系数。SPI prescaler.
ClockPolarity clock_polarity
SPI 时钟极性。SPI clock polarity.