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 CACHE_SYNC_FLAG_UNALIGNED = (1 << 1);
85constexpr int CACHE_SYNC_FLAG_DIR_C2M = (1 << 2);
86constexpr int CACHE_SYNC_FLAG_DIR_M2C = (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 ? CACHE_SYNC_FLAG_DIR_C2M : CACHE_SYNC_FLAG_DIR_M2C;
103 flags |= CACHE_SYNC_FLAG_UNALIGNED;
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() {
return !busy_.TestAndSet(); }
173void ESP32SPI::Release() { busy_.Clear(); }
179 return ErrorCode::OK;
182 if ((host_ <= SPI1_HOST) || (host_ >= SPI_HOST_MAX) ||
183 (
static_cast<int>(host_) >= SOC_SPI_PERIPH_NUM))
185 return ErrorCode::ARG_ERR;
188 const auto& signal = spi_periph_signal[host_];
192 return ErrorCode::NOT_SUPPORT;
197 (void)__DECLARE_RCC_ATOMIC_ENV;
198 spi_ll_enable_bus_clock(host_,
true);
199 spi_ll_reset_register(host_);
201 spi_ll_enable_clock(host_,
true);
202 spi_ll_master_init(hw_);
204 const spi_line_mode_t line_mode = {
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;
224 spi_ll_set_clk_source(hw_, SPI_CLK_SRC_DEFAULT);
225 if (ResolveClockSource(source_clock_hz_) != ErrorCode::OK)
227 return ErrorCode::INIT_ERR;
230 spi_ll_disable_int(hw_);
231 spi_ll_clear_int_stat(hw_);
233 return ErrorCode::OK;
238 if (!initialized_ || (hw_ ==
nullptr))
240 return ErrorCode::STATE_ERR;
243 const auto& signal = spi_periph_signal[host_];
247 if (!GPIO_IS_VALID_OUTPUT_GPIO(sclk_pin_))
249 return ErrorCode::ARG_ERR;
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);
259 if (!GPIO_IS_VALID_OUTPUT_GPIO(mosi_pin_))
261 return ErrorCode::ARG_ERR;
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);
271 if (!GPIO_IS_VALID_GPIO(miso_pin_))
273 return ErrorCode::ARG_ERR;
275 gpio_input_enable(
static_cast<gpio_num_t
>(miso_pin_));
276 esp_rom_gpio_connect_in_signal(miso_pin_, signal.spiq_in,
false);
279 return ErrorCode::OK;
282ErrorCode ESP32SPI::ResolveClockSource(uint32_t& source_hz)
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))
290 return ErrorCode::INIT_ERR;
292 return ErrorCode::OK;
299 return ErrorCode::OK;
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)
306 intr_handle_ =
nullptr;
307 return ErrorCode::INIT_ERR;
310 intr_installed_ =
true;
311 return ErrorCode::OK;
318 return ErrorCode::OK;
321 spi_dma_ctx_t* ctx =
nullptr;
322 if (spicommon_dma_chan_alloc(host_, SPI_DMA_CH_AUTO, &ctx) != ESP_OK)
324 return ErrorCode::INIT_ERR;
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) !=
332 (void)spicommon_dma_chan_free(ctx);
333 return ErrorCode::INIT_ERR;
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});
342 if (dma_max_transfer_bytes_ == 0U)
344 return ErrorCode::INIT_ERR;
347 return ErrorCode::OK;
350void IRAM_ATTR ESP32SPI::SpiIsrEntry(
void* arg)
352 auto* spi =
static_cast<ESP32SPI*
>(arg);
355 spi->HandleInterrupt();
359void IRAM_ATTR ESP32SPI::HandleInterrupt()
361 if ((hw_ ==
nullptr) || !initialized_)
368 spi_ll_clear_int_stat(hw_);
372 if (!spi_ll_usr_is_done(hw_))
377 FinishAsync(
true, ErrorCode::OK);
380void IRAM_ATTR ESP32SPI::FinishAsync(
bool in_isr, ErrorCode ec)
387#if CONFIG_IDF_TARGET_ESP32
391 spi_dma_ctx_t* ctx = ToDmaCtx(dma_ctx_);
394 spicommon_dmaworkaround_idle(ctx->tx_dma_chan.chan_id);
399 spi_ll_disable_int(hw_);
400 spi_ll_clear_int_stat(hw_);
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_)
408#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
409 if (!CacheSyncDmaBuffer(rx.addr_, async_dma_size_,
false))
411 ec = ErrorCode::FAILED;
416 if ((ec == ErrorCode::OK) && (read_back.size_ > 0U))
418 if (rx.addr_ ==
nullptr)
422 uint8_t* src =
static_cast<uint8_t*
>(rx.addr_);
425 ASSERT(rx.size_ >= (read_back.size_ + 1U));
430 ASSERT(rx.size_ >= read_back.size_);
432 Memory::FastCopy(read_back.addr_, src, read_back.size_);
435 if (ec == ErrorCode::OK)
440 async_running_ =
false;
441 async_dma_size_ = 0U;
442 async_dma_rx_enabled_ =
false;
444 read_back_ = {
nullptr, 0};
446 if (rw_op_.type == OperationRW::OperationType::BLOCK)
448 (void)block_wait_.TryPost(in_isr, ec);
452 rw_op_.UpdateStatus(in_isr, ec);
458 if (!initialized_ || (hw_ ==
nullptr))
460 return ErrorCode::STATE_ERR;
464 return ErrorCode::BUSY;
467 if (config.
prescaler == Prescaler::UNKNOWN)
469 return ErrorCode::ARG_ERR;
472 ((dbuf_rx_block_size_ == 0U) || (dbuf_tx_block_size_ == 0U)))
474 return ErrorCode::ARG_ERR;
477 const uint32_t div = PrescalerToDiv(config.
prescaler);
478 if ((div == 0) || (source_clock_hz_ == 0))
480 return ErrorCode::ARG_ERR;
483 const uint32_t target_hz = source_clock_hz_ / div;
486 return ErrorCode::ARG_ERR;
490 spi_ll_master_set_mode(hw_, mode);
492 const int applied_hz = spi_ll_master_set_clock(hw_,
static_cast<int>(source_clock_hz_),
493 static_cast<int>(target_hz), 128);
496 return ErrorCode::INIT_ERR;
499 spi_ll_apply_config(hw_);
500 GetConfig() = config;
502 dbuf_active_block_ = 0U;
504 return ErrorCode::OK;
507bool ESP32SPI::UseLocalDoubleBuffer()
const
509 return dbuf_enabled_ && (dbuf_rx_block_size_ > 0U) && (dbuf_tx_block_size_ > 0U);
512RawData ESP32SPI::GetRxBuffer()
514 if (UseLocalDoubleBuffer())
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_};
523RawData ESP32SPI::GetTxBuffer()
525 if (UseLocalDoubleBuffer())
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_};
534void ESP32SPI::SwitchBufferLocal()
536 if (UseLocalDoubleBuffer())
538 dbuf_active_block_ ^= 1U;
542bool ESP32SPI::CanUseDma(
size_t size)
const
544 return dma_requested_ && dma_enabled_ && (dma_ctx_ !=
nullptr) &&
545 (size > dma_enable_min_size_) && (size <= dma_max_transfer_bytes_);
548ErrorCode ESP32SPI::EnsureReadyAndAcquire()
552 return ErrorCode::INIT_ERR;
556 return ErrorCode::BUSY;
558 return ErrorCode::OK;
561ErrorCode ESP32SPI::FinalizeSyncResult(OperationRW& op,
bool in_isr, ErrorCode ec)
563 if (op.type != OperationRW::OperationType::BLOCK)
565 op.UpdateStatus(in_isr, ec);
570ErrorCode ESP32SPI::CompleteZeroSize(OperationRW& op,
bool in_isr)
572 return FinalizeSyncResult(op, in_isr, ErrorCode::OK);
575ErrorCode ESP32SPI::ReturnAsyncStartResult(ErrorCode ec,
bool started)
584void ESP32SPI::ConfigureTransferRegisters(
size_t size)
586 static constexpr spi_line_mode_t LINE_MODE = {
591 const size_t bitlen = size * 8U;
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;
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)
610 if (!CanUseDma(size))
612 return ErrorCode::NOT_SUPPORT;
614 if ((tx ==
nullptr) || (size == 0U))
616 return ErrorCode::ARG_ERR;
619 spi_dma_ctx_t* ctx = ToDmaCtx(dma_ctx_);
622 return ErrorCode::INIT_ERR;
625 const bool rx_enabled = enable_rx && (rx !=
nullptr);
626 ConfigureTransferRegisters(size);
628#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
629 if (!CacheSyncDmaBuffer(tx, size,
true))
631 return ErrorCode::FAILED;
635 if (enable_rx && (rx !=
nullptr))
637 spicommon_dma_desc_setup_link(ctx->dmadesc_rx, rx,
static_cast<int>(size),
true);
638 if (DmaReset(ctx->rx_dma_chan) != ESP_OK)
640 return ErrorCode::INIT_ERR;
642 spi_hal_hw_prepare_rx(hw_);
643 if (DmaStart(ctx->rx_dma_chan, ctx->dmadesc_rx) != ESP_OK)
645 return ErrorCode::INIT_ERR;
648#if CONFIG_IDF_TARGET_ESP32
652 spi_ll_dma_rx_enable(hw_,
true);
653 (void)DmaStart(ctx->rx_dma_chan,
nullptr);
657 spicommon_dma_desc_setup_link(ctx->dmadesc_tx, tx,
static_cast<int>(size),
false);
658 if (DmaReset(ctx->tx_dma_chan) != ESP_OK)
660 return ErrorCode::INIT_ERR;
662 spi_hal_hw_prepare_tx(hw_);
663 if (DmaStart(ctx->tx_dma_chan, ctx->dmadesc_tx) != ESP_OK)
665 return ErrorCode::INIT_ERR;
668#if CONFIG_IDF_TARGET_ESP32
669 spicommon_dmaworkaround_transfer_active(ctx->tx_dma_chan.chan_id);
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)
679 block_wait_.Start(*op.data.sem_info.sem);
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_);
693 if (op.type == OperationRW::OperationType::BLOCK)
695 return block_wait_.Wait(op.data.sem_info.timeout);
697 return ErrorCode::OK;
700ErrorCode ESP32SPI::ExecuteChunk(
const uint8_t* tx, uint8_t* rx,
size_t size,
703 if ((size == 0U) || (size > MAX_POLLING_TRANSFER_BYTES))
705 return ErrorCode::SIZE_ERR;
708 static constexpr std::array<uint8_t, MAX_POLLING_TRANSFER_BYTES> ZERO = {};
709 const uint8_t* tx_data = (tx !=
nullptr) ? tx : ZERO.data();
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_);
719 const uint64_t timeout_us = CalcPollingTimeoutUs(size, GetBusSpeed());
720 const uint64_t start_us = GetNowUs();
721 while (!spi_ll_usr_is_done(hw_))
723 if ((GetNowUs() - start_us) > timeout_us)
725 return ErrorCode::TIMEOUT;
729 if (enable_rx && (rx !=
nullptr))
731 spi_ll_read_buffer(hw_, rx, size * 8U);
733 spi_ll_clear_int_stat(hw_);
734 return ErrorCode::OK;
737ErrorCode ESP32SPI::ExecuteTransfer(
const uint8_t* tx, uint8_t* rx,
size_t size,
741 while (offset < size)
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;
748 const ErrorCode ec = ExecuteChunk(tx_chunk, rx_chunk, chunk, enable_rx);
749 if (ec != ErrorCode::OK)
756 return ErrorCode::OK;
762 const size_t need = std::max(read_data.
size_, write_data.
size_);
765 return CompleteZeroSize(op, in_isr);
768 const ErrorCode lock_ec = EnsureReadyAndAcquire();
769 if (lock_ec != ErrorCode::OK)
776 ASSERT(rx.
size_ >= need);
777 ASSERT(tx.
size_ >= need);
779 auto* tx_ptr =
static_cast<uint8_t*
>(tx.
addr_);
780 if (write_data.
size_ > 0U)
782 Memory::FastCopy(tx_ptr, write_data.
addr_, write_data.
size_);
784 if (need > write_data.
size_)
786 Memory::FastSet(tx_ptr + write_data.
size_, 0x00, need - write_data.
size_);
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);
798 ExecuteTransfer(tx_ptr,
static_cast<uint8_t*
>(rx.
addr_), need,
true);
799 if (ec == ErrorCode::OK)
801 if (read_data.
size_ > 0U)
809 return FinalizeSyncResult(op, in_isr, ec);
814 if (read_data.
size_ == 0U)
816 return CompleteZeroSize(op, in_isr);
819 const ErrorCode lock_ec = EnsureReadyAndAcquire();
820 if (lock_ec != ErrorCode::OK)
827 const size_t total = read_data.
size_ + 1U;
829 ASSERT(rx.
size_ >= total);
830 ASSERT(tx.
size_ >= total);
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_);
836 if (CanUseDma(total))
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);
845 ExecuteTransfer(tx_ptr,
static_cast<uint8_t*
>(rx.
addr_), total,
true);
846 if (ec == ErrorCode::OK)
848 auto* rx_ptr =
static_cast<uint8_t*
>(rx.
addr_);
849 Memory::FastCopy(read_data.
addr_, rx_ptr + 1, read_data.
size_);
854 return FinalizeSyncResult(op, in_isr, ec);
860 if (write_data.
size_ == 0U)
862 return CompleteZeroSize(op, in_isr);
865 const ErrorCode lock_ec = EnsureReadyAndAcquire();
866 if (lock_ec != ErrorCode::OK)
872 const size_t total = write_data.
size_ + 1U;
873 ASSERT(tx.
size_ >= total);
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_);
879 if (CanUseDma(total))
881 bool started =
false;
882 const ErrorCode ec = StartAsyncTransfer(tx_ptr,
nullptr, total,
false, {
nullptr, 0},
884 return ReturnAsyncStartResult(ec, started);
887 const ErrorCode ec = ExecuteTransfer(tx_ptr,
nullptr, total,
false);
888 if (ec == ErrorCode::OK)
894 return FinalizeSyncResult(op, in_isr, ec);
901 return CompleteZeroSize(op, in_isr);
904 const ErrorCode lock_ec = EnsureReadyAndAcquire();
905 if (lock_ec != ErrorCode::OK)
912 ASSERT(rx.
size_ >= size);
913 ASSERT(tx.
size_ >= size);
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);
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)
932 return FinalizeSyncResult(op, in_isr, ec);
935uint32_t ESP32SPI::GetMaxBusSpeed()
const {
return source_clock_hz_; }
只读原始数据视图 / 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.
@ 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.