feat(lp-spi): Added support for LP SPI driver to the LP Core on esp32p4

This commit adds LP SPI master and LP SPI slave support for the LP Core
on the esp32p4.
This commit is contained in:
Sudeep Mohanty 2024-06-19 10:09:40 +02:00
parent ef63ae2cfc
commit 6e85d744a8
13 changed files with 799 additions and 253 deletions

View File

@ -243,6 +243,10 @@ config SOC_LP_I2C_SUPPORTED
bool
default y
config SOC_LP_SPI_SUPPORTED
bool
default y
config SOC_SPIRAM_SUPPORTED
bool
default y
@ -1279,6 +1283,14 @@ config SOC_SPI_MAX_PRE_DIVIDER
int
default 16
config SOC_LP_SPI_PERIPH_NUM
bool
default y
config SOC_LP_SPI_MAXIMUM_BUFFER_SIZE
int
default 64
config SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE
bool
default y

View File

@ -1,5 +1,5 @@
/**
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -116,7 +116,7 @@ typedef union {
uint32_t reg_clkdiv_pre:4;
uint32_t reserved_22:9;
/** reg_clk_equ_sysclk : R/W; bitpos: [31]; default: 1;
* In the master mode 1: spi_clk is eqaul to system 0: spi_clk is divided from system
* In the master mode 1: spi_clk is equal to system 0: spi_clk is divided from system
* clock. Can be configured in CONF state.
*/
uint32_t reg_clk_equ_sysclk:1;
@ -813,244 +813,19 @@ typedef union {
} lp_spi_sleep_conf1_reg_t;
/** Group: LP SPI W0 REG */
/** Type of spi_w0 register
* SPI CPU-controlled buffer0
/** Group: LP SPI Wn REG */
/** Type of spi_wn register
* SPI CPU-controlled buffer
*/
typedef union {
struct {
/** reg_buf0 : R/W/SS; bitpos: [31:0]; default: 0;
/** reg_buf : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf0:32;
uint32_t reg_buf:32;
};
uint32_t val;
} lp_spi_w0_reg_t;
/** Group: LP SPI W1 REG */
/** Type of spi_w1 register
* SPI CPU-controlled buffer1
*/
typedef union {
struct {
/** reg_buf1 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf1:32;
};
uint32_t val;
} lp_spi_w1_reg_t;
/** Group: LP SPI W2 REG */
/** Type of spi_w2 register
* SPI CPU-controlled buffer2
*/
typedef union {
struct {
/** reg_buf2 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf2:32;
};
uint32_t val;
} lp_spi_w2_reg_t;
/** Group: LP SPI W3 REG */
/** Type of spi_w3 register
* SPI CPU-controlled buffer3
*/
typedef union {
struct {
/** reg_buf3 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf3:32;
};
uint32_t val;
} lp_spi_w3_reg_t;
/** Group: LP SPI W4 REG */
/** Type of spi_w4 register
* SPI CPU-controlled buffer4
*/
typedef union {
struct {
/** reg_buf4 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf4:32;
};
uint32_t val;
} lp_spi_w4_reg_t;
/** Group: LP SPI W5 REG */
/** Type of spi_w5 register
* SPI CPU-controlled buffer5
*/
typedef union {
struct {
/** reg_buf5 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf5:32;
};
uint32_t val;
} lp_spi_w5_reg_t;
/** Group: LP SPI W6 REG */
/** Type of spi_w6 register
* SPI CPU-controlled buffer6
*/
typedef union {
struct {
/** reg_buf6 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf6:32;
};
uint32_t val;
} lp_spi_w6_reg_t;
/** Group: LP SPI W7 REG */
/** Type of spi_w7 register
* SPI CPU-controlled buffer7
*/
typedef union {
struct {
/** reg_buf7 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf7:32;
};
uint32_t val;
} lp_spi_w7_reg_t;
/** Group: LP SPI W8 REG */
/** Type of spi_w8 register
* SPI CPU-controlled buffer8
*/
typedef union {
struct {
/** reg_buf8 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf8:32;
};
uint32_t val;
} lp_spi_w8_reg_t;
/** Group: LP SPI W9 REG */
/** Type of spi_w9 register
* SPI CPU-controlled buffer9
*/
typedef union {
struct {
/** reg_buf9 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf9:32;
};
uint32_t val;
} lp_spi_w9_reg_t;
/** Group: LP SPI W10 REG */
/** Type of spi_w10 register
* SPI CPU-controlled buffer10
*/
typedef union {
struct {
/** reg_buf10 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf10:32;
};
uint32_t val;
} lp_spi_w10_reg_t;
/** Group: LP SPI W11 REG */
/** Type of spi_w11 register
* SPI CPU-controlled buffer11
*/
typedef union {
struct {
/** reg_buf11 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf11:32;
};
uint32_t val;
} lp_spi_w11_reg_t;
/** Group: LP SPI W12 REG */
/** Type of spi_w12 register
* SPI CPU-controlled buffer12
*/
typedef union {
struct {
/** reg_buf12 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf12:32;
};
uint32_t val;
} lp_spi_w12_reg_t;
/** Group: LP SPI W13 REG */
/** Type of spi_w13 register
* SPI CPU-controlled buffer13
*/
typedef union {
struct {
/** reg_buf13 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf13:32;
};
uint32_t val;
} lp_spi_w13_reg_t;
/** Group: LP SPI W14 REG */
/** Type of spi_w14 register
* SPI CPU-controlled buffer14
*/
typedef union {
struct {
/** reg_buf14 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf14:32;
};
uint32_t val;
} lp_spi_w14_reg_t;
/** Group: LP SPI W15 REG */
/** Type of spi_w15 register
* SPI CPU-controlled buffer15
*/
typedef union {
struct {
/** reg_buf15 : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer
*/
uint32_t reg_buf15:32;
};
uint32_t val;
} lp_spi_w15_reg_t;
} lp_spi_wn_reg_t;
/** Group: LP SPI SLAVE REG */
@ -1062,7 +837,7 @@ typedef union {
/** reg_clk_mode : R/W; bitpos: [1:0]; default: 0;
* SPI clock mode bits. 0: SPI clock is off when CS inactive 1: SPI clock is delayed
* one cycle after CS inactive 2: SPI clock is delayed two cycles after CS inactive 3:
* SPI clock is alwasy on. Can be configured in CONF state.
* SPI clock is always on. Can be configured in CONF state.
*/
uint32_t reg_clk_mode:2;
/** reg_clk_mode_13 : R/W; bitpos: [2]; default: 0;
@ -1238,22 +1013,7 @@ typedef struct {
volatile lp_spi_sleep_conf1_reg_t spi_sleep_conf1;
volatile lp_spi_dma_int_set_reg_t spi_dma_int_set;
uint32_t reserved_050[18];
volatile lp_spi_w0_reg_t spi_w0;
volatile lp_spi_w1_reg_t spi_w1;
volatile lp_spi_w2_reg_t spi_w2;
volatile lp_spi_w3_reg_t spi_w3;
volatile lp_spi_w4_reg_t spi_w4;
volatile lp_spi_w5_reg_t spi_w5;
volatile lp_spi_w6_reg_t spi_w6;
volatile lp_spi_w7_reg_t spi_w7;
volatile lp_spi_w8_reg_t spi_w8;
volatile lp_spi_w9_reg_t spi_w9;
volatile lp_spi_w10_reg_t spi_w10;
volatile lp_spi_w11_reg_t spi_w11;
volatile lp_spi_w12_reg_t spi_w12;
volatile lp_spi_w13_reg_t spi_w13;
volatile lp_spi_w14_reg_t spi_w14;
volatile lp_spi_w15_reg_t spi_w15;
volatile lp_spi_wn_reg_t data_buf[16];
uint32_t reserved_0d8[2];
volatile lp_spi_slave_reg_t spi_slave;
volatile lp_spi_slave1_reg_t spi_slave1;
@ -1263,11 +1023,12 @@ typedef struct {
volatile lp_rnd_eco_cs_reg_t rnd_eco_cs;
volatile lp_rnd_eco_low_reg_t rnd_eco_low;
volatile lp_rnd_eco_high_reg_t rnd_eco_high;
} lp_dev_t;
} lp_spi_dev_t;
extern lp_spi_dev_t LP_SPI;
#ifndef __cplusplus
_Static_assert(sizeof(lp_dev_t) == 0x100, "Invalid size of lp_dev_t structure");
_Static_assert(sizeof(lp_spi_dev_t) == 0x100, "Invalid size of lp_dev_t structure");
#endif
#ifdef __cplusplus

View File

@ -78,6 +78,7 @@
#define SOC_LP_GPIO_MATRIX_SUPPORTED 1
#define SOC_LP_PERIPHERALS_SUPPORTED 1
#define SOC_LP_I2C_SUPPORTED 1
#define SOC_LP_SPI_SUPPORTED 1
#define SOC_SPIRAM_SUPPORTED 1
#define SOC_PSRAM_DMA_CAPABLE 1
// #define SOC_ULP_SUPPORTED 1 //TODO: IDF-7534
@ -504,6 +505,10 @@
#define SOC_MEMSPI_IS_INDEPENDENT 1
#define SOC_SPI_MAX_PRE_DIVIDER 16
/*-------------------------- LP SPI CAPS ----------------------------------------*/
#define SOC_LP_SPI_PERIPH_NUM 1
#define SOC_LP_SPI_MAXIMUM_BUFFER_SIZE 64
/*-------------------------- SPI MEM CAPS ---------------------------------------*/
#define SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE (1)
#define SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND (1)

View File

@ -65,6 +65,10 @@ if(CONFIG_ULP_COPROC_TYPE_LP_CORE)
if(CONFIG_SOC_LP_TIMER_SUPPORTED)
list(APPEND srcs "lp_core/shared/ulp_lp_core_lp_timer_shared.c")
endif()
if(CONFIG_SOC_LP_SPI_SUPPORTED)
list(APPEND srcs "lp_core/lp_core_spi.c")
endif()
endif()
idf_component_register(SRCS ${srcs}

View File

@ -114,7 +114,8 @@ elseif(ULP_COCPU_IS_LP_CORE)
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_print.c"
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_panic.c"
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_interrupt.c"
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c")
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c"
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_spi.c")
target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles")
target_link_options(${ULP_APP_NAME} PRIVATE "-Wl,--no-warn-rwx-segments")

View File

@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LP SPI peripheral
* Since we have just one LP SPI peripheral, we can define it as a uint32_t type for now, instead of an enum.
*/
typedef uint32_t lp_spi_host_t;
/**
* @brief LP SPI device configuration flags
*/
#define LP_SPI_DEVICE_TXBIT_LSBFIRST (1<<0) /*!< Transmit command/address/data LSB first instead of the default MSB first */
#define LP_SPI_DEVICE_RXBIT_LSBFIRST (1<<1) /*!< Receive data LSB first instead of the default MSB first */
#define LP_SPI_DEVICE_BIT_LSBFIRST (LP_SPI_DEVICE_TXBIT_LSBFIRST|LP_SPI_DEVICE_RXBIT_LSBFIRST) /*!< Transmit and receive LSB first */
#define LP_SPI_DEVICE_3WIRE (1<<2) /*!< Use MOSI (=spid) for both sending and receiving data */
#define LP_SPI_DEVICE_CS_ACTIVE_HIGH (1<<3) /*!< Make CS line active-high during a transanction instead of the default active-low state. Only available in SPI master mode. */
#define LP_SPI_DEVICE_HALF_DUPLEX (1<<4) /*!< Transmit data before receiving it, instead of simultaneously. Only available in SPI master mode. */
/**
* @brief LP SPI bus configuration parameters
*/
typedef struct {
int mosi_io_num; /*!< GPIO pin for Master out, Slave In signal, a.k.a, SPI_D. */
int miso_io_num; /*!< GPIO pin for Master in, Slave Out signal, a.k.a, SPI_Q. */
int sclk_io_num; /*!< GPIO pin for LP SPI Clock signal. */
} lp_spi_bus_config_t;
/**
* @brief LP SPI device configuration parameters
*/
typedef struct {
int cs_io_num; /*!< GPIO pin for the device Chip Select (CS) signal. */
int clock_speed_hz; /*!< SPI clock speed in Hz. */
int spi_mode; /*!< SPI mode, representing a pair of Clock Polarity (CPOL) and Clock Phase (CPHA) configuration:
- SPI Mode 0: (0, 0)
- SPI Mode 1: (0, 1)
- SPI Mode 2: (1, 0)
- SPI Mode 3: (1, 1)
*/
int duty_cycle; /*!< Duty cycle of positive SPI clock, in 1/256th increments (128 = 50% duty cycle). Setting this to 0 (=not setting it) is equivalent to setting this to 128. */
int flags; /*!< Bitwise OR of LP_SPI_DEVICE_* flags */
int cs_ena_pretrans; /*!< Amount of SPI bit-cycles the CS should be active for, before the transmission (0-16). This only works on half-duplex transactions. */
int cs_ena_posttrans; /*!< Amount of SPI bit-cycles the CS should stay active for, after the transmission (0-16). This only works on half-duplex transactions. */
} lp_spi_device_config_t;
/**
* @brief LP SPI slave configuration parameters
*/
typedef struct {
int cs_io_num; /*!< GPIO pin for the device Chip Select (CS) signal. */
int spi_mode; /*!< SPI mode, representing a pair of Clock Polarity (CPOL) and Clock Phase (CPHA) configuration:
- SPI Mode 0: (0, 0)
- SPI Mode 1: (0, 1)
- SPI Mode 2: (1, 0)
- SPI Mode 3: (1, 1)
*/
int flags; /*!< Bitwise OR of LP_SPI_DEVICE_* flags */
} lp_spi_slave_config_t;
/**
* @brief Initialize the LP SPI bus for use by the LP core
*
* @param host_id LP SPI host number
* @param bus_config LP SPI bus configuration parameters
*
* @return esp_err_t ESP_OK when successful
* ESP_ERR_INVALID_ARG if the configuration is invalid
*/
esp_err_t lp_core_lp_spi_bus_initialize(lp_spi_host_t host_id, const lp_spi_bus_config_t *bus_config);
/**
* @brief Initialize the LP SPI controller in master mode and add an SPI device to the LP SPI bus.
*
* @param host_id LP SPI host number
* @param dev_config LP SPI device configuration parameters
*
* @return esp_err_t ESP_OK when successful
* ESP_ERR_INVALID_ARG if the configuration is invalid
* ESP_FAIL if the device could not be added
*/
esp_err_t lp_core_lp_spi_bus_add_device(lp_spi_host_t host_id, const lp_spi_device_config_t *dev_config);
/**
* @brief Initialize the LP SPI controller in slave mode
*
* @param host_id LP SPI host number
* @param slave_config LP SPI slave configuration parameters
*
* @return esp_err_t ESP_OK when successful
* ESP_FAIL if the SPI controller could not be initialized in slave mode
*/
esp_err_t lp_core_lp_spi_slave_initialize(lp_spi_host_t host_id, const lp_spi_slave_config_t *slave_config);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,65 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* The LP SPI bus identifier to initiate a transaction on.
*/
typedef uint32_t lp_spi_bus_t;
/**
* This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes.
*/
typedef struct {
uint32_t tx_length; /*!< Total data length to transmit in bytes */
uint32_t rx_length; /*!< Total data length to receive in bytes */
const void *tx_buffer; /*!< Pointer to the transmit buffer. Must be set for master mode transactions. Can be NULL for slave mode transactions. */
void *rx_buffer; /*!< Pointer to the receive buffer. Must be set for slave mode transactions. Can be NULL for master mode transactions. */
lp_spi_bus_t bus; /*!< The LP SPI bus to transmit the data on */
// The following are only used in master mode transactions
int command; /*!< Command data, of which the length is set in the ``command_bits`` field of this structure. */
uint32_t address; /*!< Address data, of which the length is set in the ``address_bits`` field of this structure. */
uint8_t command_bits; /*!< Default amount of bits in command phase */
uint8_t address_bits; /*!< Default amount of bits in address phase */
uint8_t dummy_bits; /*!< Amount of dummy bits to insert between address and data phase. */
} lp_spi_transaction_t;
/**
* @brief Initiate an LP SPI transaction in master mode to transmit device to an SPI device and optionally receive data
* from the device.
*
* @param trans_desc LP SPI transaction configuration descriptor
* @param ticks_to_wait Operation timeout in CPU cycles. Set to -1 to wait forever.
*
* @return esp_err_t ESP_OK when successful
* ESP_ERR_INVALID_ARG if the configuration is invalid
* ESP_ERR_TIMEOUT when the operation times out
*/
esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32_t ticks_to_wait);
/**
* @brief Initiate an LP SPI transaction in slave mode to receive data from an SPI master and optionally transmit data
* back to the master.
*
* @param trans_desc LP SPI transaction configuration descriptor
* @param ticks_to_wait Operation timeout in CPU cycles. Set to -1 to wait forever.
*
* @return esp_err_t ESP_OK when successful
* ESP_ERR_INVALID_ARG if the configuration is invalid
* ESP_ERR_TIMEOUT when the operation times out
*/
esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_t ticks_to_wait);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,262 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc_caps.h"
#if SOC_LP_SPI_SUPPORTED
#include <stdint.h>
#include <string.h>
#include "esp_err.h"
#include "ulp_lp_core_spi.h"
#include "soc/lp_spi_struct.h"
/* Use the register structure to access LP_SPI module registers */
lp_spi_dev_t *lp_spi_dev = &LP_SPI;
static inline esp_err_t lp_core_spi_wait_for_interrupt(int32_t ticks_to_wait)
{
uint32_t to = 0;
while (!lp_spi_dev->spi_dma_int_raw.reg_trans_done_int_raw) {
if (ticks_to_wait > -1) {
/* If the ticks_to_wait value is not -1, keep track of ticks and
* break from the loop once the timeout is reached.
*/
to++;
if (to >= ticks_to_wait) {
/* Clear interrupt bits */
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
return ESP_ERR_TIMEOUT;
}
}
}
return ESP_OK;
}
//////////////////////////////////////////////////////////////////////////////////
////////////////////////////////// Public APIs ///////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32_t ticks_to_wait)
{
esp_err_t ret = ESP_OK;
/* Argument sanity check
* Note: The Tx buffer is mandatory for this API.
*/
if (trans_desc == NULL || trans_desc->tx_buffer == NULL || trans_desc->tx_length == 0) {
return ESP_ERR_INVALID_ARG;
}
/* Reset the Tx and Rx FIFOs */
lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 1;
lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 0;
lp_spi_dev->spi_dma_conf.reg_buf_afifo_rst = 1;
lp_spi_dev->spi_dma_conf.reg_buf_afifo_rst = 0;
/* Clear any previous interrupts.
* Note: LP SPI does not have any DMA access but the interrupt bit lives in the DMA interrupt register.
*/
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
/* Make sure that we do not have any ongoing transactions */
if (lp_spi_dev->spi_cmd.reg_usr) {
return ESP_ERR_INVALID_STATE;
}
/* Configure dummy bits */
lp_spi_dev->spi_user.reg_usr_dummy = trans_desc->dummy_bits ? 1 : 0;
if (trans_desc->dummy_bits) {
lp_spi_dev->spi_user1.reg_usr_dummy_cyclelen = trans_desc->dummy_bits - 1;
}
/* Configure the command and command bit length */
lp_spi_dev->spi_user.reg_usr_command = trans_desc->command_bits ? 1 : 0;
if (trans_desc->command_bits) {
lp_spi_dev->spi_user2.reg_usr_command_bitlen = trans_desc->command_bits - 1;
lp_spi_dev->spi_user2.reg_usr_command_value = lp_spi_dev->spi_ctrl.reg_wr_bit_order ? trans_desc->command : __builtin_bswap32(trans_desc->command << (32 - trans_desc->command_bits));
}
/* Configure the address and address bit length */
lp_spi_dev->spi_user.reg_usr_addr = trans_desc->address_bits ? 1 : 0;
if (trans_desc->address_bits) {
lp_spi_dev->spi_user1.reg_usr_addr_bitlen = trans_desc->address_bits - 1;
lp_spi_dev->spi_addr.reg_usr_addr_value = lp_spi_dev->spi_ctrl.reg_wr_bit_order ? __builtin_bswap32(trans_desc->address) : trans_desc->address << (32 - trans_desc->address_bits);
}
/* Set data lines */
lp_spi_dev->spi_user.reg_usr_mosi = 1;
lp_spi_dev->spi_user.reg_usr_miso = trans_desc->rx_buffer ? 1 : 0;
/* Configure the transaction bit length */
int tx_bitlen = trans_desc->tx_length * 8;
lp_spi_dev->spi_ms_dlen.reg_ms_data_bitlen = tx_bitlen - 1;
/* Prepare the data to be transmitted */
uint32_t tx_idx = 0;
uint32_t rx_idx = 0;
/* The TRM suggests that the data is sent from and received in the LP_SPI_W0_REG ~ LP_SPI_W15_REG registers.
* The following rules apply:
* 1. The first 64 bytes are sent from/received in LP_SPI_W0_REG ~ LP_SPI_W15_REG
* 2. Bytes 64 - 255 are repeatedly sent from or received in LP_SPI_W15_REG[31:24]
* 3. Subsequent blocks of 256 bytes of data continue to follow the above rules
*
* This driver, however, avoids using the LP_SPI_W15_REG altogether. In other words,
* this driver sends or receives data in chunks of 60 bytes (LP_SPI_W0_REG ~ LP_SPI_W14_REG)
* and does not handle the repeated use of the high-byte of LP_SPI_W15_REG. This design approach
* has been chosen to simplify the data handling logic.
*/
uint8_t max_data_reg_num = (SOC_LP_SPI_MAXIMUM_BUFFER_SIZE / 4) - 1; // 15
uint8_t max_data_chunk_size = max_data_reg_num * 4; // 60
while (tx_idx < trans_desc->tx_length) {
/* Store 4 bytes of data in the data buffer registers serially. */
lp_spi_dev->data_buf[(tx_idx / 4) & max_data_reg_num].reg_buf = *(uint32_t *)(trans_desc->tx_buffer + tx_idx);
tx_idx += 4;
/* Begin transmission of the data if we have pushed all the data or if we have reached the maximum data chunk size */
if ((tx_idx >= trans_desc->tx_length) || (tx_idx % max_data_chunk_size) == 0) {
/* Apply the configuration */
lp_spi_dev->spi_cmd.reg_update = 1;
while (lp_spi_dev->spi_cmd.reg_update) {
;
}
/* Start the transaction */
lp_spi_dev->spi_cmd.reg_usr = 1;
/* Wait for the transaction to complete */
ret = lp_core_spi_wait_for_interrupt(ticks_to_wait);
if (ret != ESP_OK) {
return ret;
}
/* Clear the transaction done interrupt */
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
/* Fetch the received data if an Rx buffer is provided */
if (trans_desc->rx_buffer != NULL) {
while (rx_idx < tx_idx) {
*(uint32_t *)(trans_desc->rx_buffer + rx_idx) = lp_spi_dev->data_buf[(rx_idx / 4) & max_data_reg_num].reg_buf;
rx_idx += 4;
// This loop would exit even if we haven't received all the data.
}
}
}
}
return ret;
}
esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_t ticks_to_wait)
{
esp_err_t ret = ESP_OK;
/* Argument sanity check
* Note: The Rx buffer is mandatory for this API.
*/
if (trans_desc == NULL || trans_desc->rx_buffer == NULL || trans_desc->rx_length == 0) {
return ESP_ERR_INVALID_ARG;
}
/* Reset the Tx and Rx FIFOs */
lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 1;
lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 0;
lp_spi_dev->spi_dma_conf.reg_buf_afifo_rst = 1;
lp_spi_dev->spi_dma_conf.reg_buf_afifo_rst = 0;
/* Clear any previous interrupts.
* Note: LP SPI does not have any DMA access but the interrupt bit lives in the DMA interrupt register.
*/
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
/* Set data lines */
lp_spi_dev->spi_user.reg_usr_mosi = 1;
lp_spi_dev->spi_user.reg_usr_miso = 1;
/* Configure the transaction bit length */
int rx_bitlen = trans_desc->rx_length * 8;
lp_spi_dev->spi_ms_dlen.reg_ms_data_bitlen = rx_bitlen - 1;
/* Prepare the data to be received */
uint32_t rx_idx = 0;
uint32_t rcvd_bitlen = 0;
uint32_t rcvd_length_in_bytes = 0;
/* The LP SPI slave receives data in the LP_SPI_W0_REG ~ LP_SPI_W15_REG registers.
* The following rules apply:
* 1. The first 64 bytes are received in LP_SPI_W0_REG ~ LP_SPI_W15_REG
* 2. The next 64 bytes are overwritten in LP_SPI_W0_REG ~ LP_SPI_W15_REG
*
* Since the peripheral has no protection against overwriting the data, we restrict the
* driver to receive up to 64 bytes of data at a time.
*/
uint32_t length_in_bytes = trans_desc->rx_length;
if (trans_desc->rx_length > SOC_LP_SPI_MAXIMUM_BUFFER_SIZE) {
/* Truncate the length to the maximum buffer size */
length_in_bytes = SOC_LP_SPI_MAXIMUM_BUFFER_SIZE;
}
while (rx_idx < length_in_bytes) {
/* Wait for the transmission to complete */
ret = lp_core_spi_wait_for_interrupt(ticks_to_wait);
if (ret != ESP_OK) {
return ret;
}
/* Fetch the received bit length */
rcvd_bitlen = lp_spi_dev->spi_slave1.reg_slv_data_bitlen > (trans_desc->rx_length * 8) ? (trans_desc->rx_length * 8) : lp_spi_dev->spi_slave1.reg_slv_data_bitlen;
rcvd_length_in_bytes = (rcvd_bitlen + 7) / 8;
/* Read the received data */
while (rx_idx < rcvd_length_in_bytes) {
*(uint32_t *)(trans_desc->rx_buffer + rx_idx) = lp_spi_dev->data_buf[(rx_idx / 4)].reg_buf;
rx_idx += 4;
}
/* Clear the transaction done interrupt */
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
}
/* Prepare data for transmission if a Tx buffer is provided */
if (trans_desc->tx_buffer != NULL) {
uint32_t tx_idx = 0;
uint32_t length_in_bytes = trans_desc->tx_length;
if (length_in_bytes > SOC_LP_SPI_MAXIMUM_BUFFER_SIZE) {
/* Truncate the length to the maximum buffer size */
length_in_bytes = SOC_LP_SPI_MAXIMUM_BUFFER_SIZE;
}
while (tx_idx < length_in_bytes) {
/* Store 4 bytes of data in the data buffer registers serially. */
lp_spi_dev->data_buf[(tx_idx / 4)].reg_buf = *(uint32_t *)(trans_desc->tx_buffer + tx_idx);
tx_idx += 4;
}
/* Apply the configuration */
lp_spi_dev->spi_cmd.reg_update = 1;
while (lp_spi_dev->spi_cmd.reg_update) {
;
}
/* Start the transaction */
lp_spi_dev->spi_cmd.reg_usr = 1;
/* Wait for the transaction to complete */
ret = lp_core_spi_wait_for_interrupt(ticks_to_wait);
if (ret != ESP_OK) {
return ret;
}
/* Clear the transaction done interrupt */
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
}
return ret;
}
#endif /* SOC_LP_SPI_SUPPORTED */

View File

@ -0,0 +1,306 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_check.h"
#include "lp_core_spi.h"
#include "driver/rtc_io.h"
#include "driver/lp_io.h"
#include "hal/rtc_io_types.h"
#include "include/lp_core_spi.h"
#include "soc/lp_spi_struct.h"
#include "soc/lp_gpio_sig_map.h"
#include "soc/lpperi_struct.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/esp_clk_tree_common.h"
#include "hal/spi_ll.h"
static const char *LP_SPI_TAG = "lp_spi";
/* Use the LP SPI register structure to access peripheral registers */
lp_spi_dev_t *lp_spi_dev = &LP_SPI;
static esp_err_t lp_spi_config_io(gpio_num_t pin, rtc_gpio_mode_t direction, uint32_t out_pad_idx, uint32_t in_pad_idx)
{
esp_err_t ret = ESP_OK;
/* If pin is -1, then it is not connected to any LP_IO */
if (pin == -1) {
return ESP_OK;
}
/* Initialize LP_IO */
ESP_RETURN_ON_ERROR(rtc_gpio_init(pin), LP_SPI_TAG, "LP IO Init failed for GPIO %d", pin);
/* Set LP_IO direction */
ESP_RETURN_ON_ERROR(rtc_gpio_set_direction(pin, direction), LP_SPI_TAG, "LP IO Set direction failed for %d", pin);
/* Connect the LP SPI signals to the LP_IO Matrix */
ESP_RETURN_ON_ERROR(lp_gpio_connect_out_signal(pin, out_pad_idx, 0, 0), LP_SPI_TAG, "LP IO Matrix connect out signal failed for %d", pin);
ESP_RETURN_ON_ERROR(lp_gpio_connect_in_signal(pin, in_pad_idx, 0), LP_SPI_TAG, "LP IO Matrix connect in signal failed for %d", pin);
return ret;
}
static esp_err_t lp_spi_bus_init_io(const lp_spi_bus_config_t *bus_config)
{
esp_err_t ret = ESP_OK;
/* Argument sanity check */
#if SOC_LP_GPIO_MATRIX_SUPPORTED
/* LP SPI signals can be routed to any LP_IO */
ESP_RETURN_ON_FALSE((rtc_gpio_is_valid_gpio(bus_config->mosi_io_num)), ESP_FAIL, LP_SPI_TAG, "mosi_io_num error");
ESP_RETURN_ON_FALSE((bus_config->miso_io_num == -1) || (rtc_gpio_is_valid_gpio(bus_config->miso_io_num)), ESP_FAIL, LP_SPI_TAG, "miso_io_num error");
ESP_RETURN_ON_FALSE((rtc_gpio_is_valid_gpio(bus_config->sclk_io_num)), ESP_FAIL, LP_SPI_TAG, "sclk_io_num error");
/* Configure miso pin*/
ret = lp_spi_config_io(bus_config->miso_io_num, RTC_GPIO_MODE_INPUT_OUTPUT, LP_SPI_Q_PAD_OUT_IDX, LP_SPI_Q_PAD_IN_IDX);
/* Configure mosi pin */
ret = lp_spi_config_io(bus_config->mosi_io_num, RTC_GPIO_MODE_INPUT_OUTPUT, LP_SPI_D_PAD_OUT_IDX, LP_SPI_D_PAD_IN_IDX);
/* Configure sclk pin */
ret = lp_spi_config_io(bus_config->sclk_io_num, RTC_GPIO_MODE_INPUT_OUTPUT, LP_SPI_CK_PAD_OUT_IDX, LP_SPI_CK_PAD_IN_IDX);
#else
#error "LP SPI bus initialization is not supported without LP GPIO Matrix."
#endif /* SOC_LP_GPIO_MATRIX_SUPPORTED */
return ret;
}
static esp_err_t lp_spi_cs_pin_init(int cs_io_num)
{
esp_err_t ret = ESP_OK;
#if SOC_LP_GPIO_MATRIX_SUPPORTED
/* CS signal can be routed to any LP_IO */
ESP_RETURN_ON_FALSE((rtc_gpio_is_valid_gpio(cs_io_num)), ESP_FAIL, LP_SPI_TAG, "cs_io_num error");
/* Configure CS pin */
ret = lp_spi_config_io(cs_io_num, RTC_GPIO_MODE_INPUT_OUTPUT, LP_SPI_CS_PAD_OUT_IDX, LP_SPI_CS_PAD_IN_IDX);
#else
#error "LP SPI device Chip Select (CS) initialization is not supported without LP GPIO Matrix."
#endif /* SOC_LP_GPIO_MATRIX_SUPPORTED */
return ret;
}
static void lp_spi_enable_clock_gate(void)
{
lpperi_dev_t *lp_peri_dev = &LPPERI;
PERIPH_RCC_ATOMIC() {
(void)__DECLARE_RCC_ATOMIC_ENV; // Avoid warnings for unused variable __DECLARE_RCC_ATOMIC_ENV
lp_peri_dev->clk_en.ck_en_lp_spi = 1;
}
}
static esp_err_t lp_spi_clock_init(const lp_spi_device_config_t *dev_config)
{
esp_err_t ret = ESP_OK;
/* Max requested clock frequency cannot be more than the LP_FAST_CLK frequency */
uint32_t max_clock_source_hz = esp_clk_tree_lp_fast_get_freq_hz(ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX);
ESP_RETURN_ON_FALSE(dev_config->clock_speed_hz <= max_clock_source_hz, ESP_ERR_INVALID_ARG, LP_SPI_TAG, "Invalid clock speed for SPI device. Max allowed = %ld Hz", max_clock_source_hz);
/* Set the duty cycle. If not specified, use 50% */
int duty_cycle = dev_config->duty_cycle ? dev_config->duty_cycle : 128;
/* Calculate the clock pre-div values. We use the HP SPI LL function here for the calculation. */
spi_ll_clock_val_t spi_clock;
spi_ll_master_cal_clock(max_clock_source_hz, dev_config->clock_speed_hz, duty_cycle, &spi_clock);
lp_spi_dev->spi_clock.val = spi_clock;
return ret;
}
static void lp_spi_master_init(void)
{
/* Initialize the LP SPI in master mode.
* (We do not have a HAL/LL layer for LP SPI, yet, so let's use the LP SPI registers directly).
*/
/* Clear Slave mode to enable Master mode */
lp_spi_dev->spi_slave.reg_slave_mode = 0;
lp_spi_dev->spi_slave.reg_clk_mode = 0;
/* Reset CS timing */
lp_spi_dev->spi_user1.reg_cs_setup_time = 0;
lp_spi_dev->spi_user1.reg_cs_hold_time = 0;
/* Use all 64 bytes of the Tx/Rx buffers in CPU controlled transfer */
lp_spi_dev->spi_user.reg_usr_mosi_highpart = 0;
lp_spi_dev->spi_user.reg_usr_miso_highpart = 0;
}
static void lp_spi_slave_init(void)
{
/* Set Slave mode */
lp_spi_dev->spi_slave.reg_slave_mode = 1;
/* Reset the SPI peripheral */
lp_spi_dev->spi_slave.reg_soft_reset = 1;
lp_spi_dev->spi_slave.reg_soft_reset = 0;
/* Configure slave */
lp_spi_dev->spi_clock.val = 0;
lp_spi_dev->spi_user.val = 0;
lp_spi_dev->spi_ctrl.val = 0;
lp_spi_dev->spi_user.reg_doutdin = 1; //we only support full duplex
lp_spi_dev->spi_user.reg_sio = 0;
/* Use all 64 bytes of the Tx/Rx buffers in CPU controlled transfer */
lp_spi_dev->spi_user.reg_usr_miso_highpart = 0;
lp_spi_dev->spi_user.reg_usr_mosi_highpart = 0;
}
static void lp_spi_master_setup_device(const lp_spi_device_config_t *dev_config)
{
/* Configure transmission bit order */
lp_spi_dev->spi_ctrl.reg_rd_bit_order = dev_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST ? 1 : 0;
lp_spi_dev->spi_ctrl.reg_wr_bit_order = dev_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0;
/* Configure SPI mode in master mode */
if (dev_config->spi_mode == 0) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 0;
lp_spi_dev->spi_user.reg_ck_out_edge = 0;
} else if (dev_config->spi_mode == 1) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 0;
lp_spi_dev->spi_user.reg_ck_out_edge = 1;
} else if (dev_config->spi_mode == 2) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 1;
lp_spi_dev->spi_user.reg_ck_out_edge = 1;
} else if (dev_config->spi_mode == 3) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 1;
lp_spi_dev->spi_user.reg_ck_out_edge = 0;
}
/* Configure the polarity of the CS line */
lp_spi_dev->spi_misc.reg_master_cs_pol = dev_config->flags & LP_SPI_DEVICE_CS_ACTIVE_HIGH ? 1 : 0;
/* Configure half-duplex (0) or full-duplex (1) mode for LP SPI master */
lp_spi_dev->spi_user.reg_doutdin = dev_config->flags & LP_SPI_DEVICE_HALF_DUPLEX ? 0 : 1;
/* Configure 3-Wire half-duplex mode */
lp_spi_dev->spi_user.reg_sio = dev_config->flags & LP_SPI_DEVICE_3WIRE ? 1 : 0;
/* Configure CS setup and hold times */
lp_spi_dev->spi_user1.reg_cs_setup_time = dev_config->cs_ena_pretrans == 0 ? 0 : dev_config->cs_ena_pretrans - 1;
lp_spi_dev->spi_user.reg_cs_setup = dev_config->cs_ena_pretrans ? 1 : 0;
lp_spi_dev->spi_user1.reg_cs_hold_time = dev_config->cs_ena_posttrans;
lp_spi_dev->spi_user.reg_cs_hold = dev_config->cs_ena_posttrans ? 1 : 0;
/* Select the CS pin */
lp_spi_dev->spi_misc.reg_cs0_dis = 0;
}
static void lp_spi_slave_setup_device(const lp_spi_slave_config_t *slave_config)
{
/* Configure transmission bit order */
lp_spi_dev->spi_ctrl.reg_rd_bit_order = slave_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST ? 1 : 0;
lp_spi_dev->spi_ctrl.reg_wr_bit_order = slave_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0;
/* Configure SPI mode in slave mode */
if (slave_config->spi_mode == 0) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 0;
lp_spi_dev->spi_user.reg_rsck_i_edge = 0;
lp_spi_dev->spi_user.reg_tsck_i_edge = 0;
lp_spi_dev->spi_slave.reg_clk_mode_13 = 0;
} else if (slave_config->spi_mode == 1) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 0;
lp_spi_dev->spi_user.reg_rsck_i_edge = 1;
lp_spi_dev->spi_user.reg_tsck_i_edge = 1;
lp_spi_dev->spi_slave.reg_clk_mode_13 = 1;
} else if (slave_config->spi_mode == 2) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 1;
lp_spi_dev->spi_user.reg_rsck_i_edge = 1;
lp_spi_dev->spi_user.reg_tsck_i_edge = 1;
lp_spi_dev->spi_slave.reg_clk_mode_13 = 0;
} else if (slave_config->spi_mode == 3) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 1;
lp_spi_dev->spi_user.reg_rsck_i_edge = 0;
lp_spi_dev->spi_user.reg_tsck_i_edge = 0;
lp_spi_dev->spi_slave.reg_clk_mode_13 = 1;
}
if (slave_config->flags & LP_SPI_DEVICE_CS_ACTIVE_HIGH) {
ESP_LOGW(LP_SPI_TAG, "Active high CS line is not supported in slave mode. Using active low CS line.");
}
lp_spi_dev->spi_misc.reg_slave_cs_pol = 0;
if (slave_config->flags & LP_SPI_DEVICE_HALF_DUPLEX) {
ESP_LOGW(LP_SPI_TAG, "Half-duplex mode is not supported in slave mode. Using full-duplex mode.");
}
lp_spi_dev->spi_user.reg_doutdin = 1;
/* Configure 3-Wire half-duplex mode */
lp_spi_dev->spi_user.reg_sio = slave_config->flags & LP_SPI_DEVICE_3WIRE ? 1 : 0;
/* Select the CS pin */
lp_spi_dev->spi_misc.reg_cs0_dis = 0;
}
//////////////////////////////////////////////////////////////////////////////////
////////////////////////////////// Public APIs ///////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
esp_err_t lp_core_lp_spi_bus_initialize(lp_spi_host_t host_id, const lp_spi_bus_config_t *bus_config)
{
(void)host_id;
/* Sanity check arguments */
if (bus_config == NULL) {
return ESP_ERR_INVALID_ARG;
}
/* Connect the LP SPI peripheral to a "bus", i.e. a set of
* GPIO pins defined in the bus_config structure.
*/
esp_err_t ret = lp_spi_bus_init_io(bus_config);
return ret;
}
esp_err_t lp_core_lp_spi_bus_add_device(lp_spi_host_t host_id, const lp_spi_device_config_t *dev_config)
{
(void)host_id;
esp_err_t ret = ESP_OK;
/* Configure the CS pin */
ESP_RETURN_ON_ERROR(lp_spi_cs_pin_init(dev_config->cs_io_num), LP_SPI_TAG, "CS pin initialization failed");
/* Enable the LP SPI clock gate */
lp_spi_enable_clock_gate();
/* Lazy initialize the LP SPI in master mode */
lp_spi_master_init();
/* Configure clock */
ESP_RETURN_ON_ERROR(lp_spi_clock_init(dev_config), LP_SPI_TAG, "Clock initialization failed");
/* Setup the SPI device */
lp_spi_master_setup_device(dev_config);
return ret;
}
esp_err_t lp_core_lp_spi_slave_initialize(lp_spi_host_t host_id, const lp_spi_slave_config_t *slave_config)
{
(void)host_id;
esp_err_t ret = ESP_OK;
/* Configure the CS pin */
ESP_RETURN_ON_ERROR(lp_spi_cs_pin_init(slave_config->cs_io_num), LP_SPI_TAG, "CS pin initialization failed");
/* Enable the LP SPI clock gate */
lp_spi_enable_clock_gate();
/* Initialize the LP SPI in slave mode */
lp_spi_slave_init();
/* Setup the SPI device */
lp_spi_slave_setup_device(slave_config);
return ret;
}

View File

@ -7,6 +7,7 @@ components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h
components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h
components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h
components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h
components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h
# ESSL headers do not belong to any IDF component, in a user project it will come from a managed component
components/driver/test_apps/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h
components/driver/test_apps/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_spi.h

View File

@ -1,6 +1,7 @@
INPUT += \
$(PROJECT_PATH)/components/ulp/lp_core/include/lp_core_i2c.h \
$(PROJECT_PATH)/components/ulp/lp_core/include/lp_core_uart.h \
$(PROJECT_PATH)/components/ulp/lp_core/include/lp_core_spi.h \
$(PROJECT_PATH)/components/ulp/lp_core/include/ulp_lp_core.h \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_gpio.h \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_i2c.h \
@ -8,6 +9,7 @@ INPUT += \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h \
$(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h \
$(PROJECT_PATH)/components/ulp/ulp_common/include/ulp_common.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_helpers.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_host.h \

View File

@ -168,6 +168,7 @@ To enhance the capabilities of the ULP LP-Core coprocessor, it has access to per
* LP IO
* LP I2C
* LP UART
:SOC_LP_SPI_SUPPORTED: * LP SPI
.. only:: CONFIG_ESP_ROM_HAS_LP_ROM
@ -240,6 +241,10 @@ Main CPU API Reference
.. include-build-file:: inc/lp_core_i2c.inc
.. include-build-file:: inc/lp_core_uart.inc
.. only:: CONFIG_SOC_LP_SPI_SUPPORTED
.. include-build-file:: inc/lp_core_spi.inc
LP Core API Reference
~~~~~~~~~~~~~~~~~~~~~~
@ -250,4 +255,8 @@ LP Core API Reference
.. include-build-file:: inc/ulp_lp_core_print.inc
.. include-build-file:: inc/ulp_lp_core_interrupts.inc
.. only:: CONFIG_SOC_LP_SPI_SUPPORTED
.. include-build-file:: inc/ulp_lp_core_spi.inc
.. _esp-idf-monitor: https://github.com/espressif/esp-idf-monitor

View File

@ -164,6 +164,7 @@ ULP LP-Core 支持的外设
* LP IO
* LP I2C
* LP UART
:SOC_LP_SPI_SUPPORTED: * LP SPI
.. only:: CONFIG_ESP_ROM_HAS_LP_ROM
@ -236,6 +237,10 @@ API 参考
.. include-build-file:: inc/lp_core_i2c.inc
.. include-build-file:: inc/lp_core_uart.inc
.. only:: CONFIG_SOC_LP_SPI_SUPPORTED
.. include-build-file:: inc/lp_core_spi.inc
LP 内核 API 参考
~~~~~~~~~~~~~~~~~~~~~~
@ -246,4 +251,8 @@ LP 内核 API 参考
.. include-build-file:: inc/ulp_lp_core_print.inc
.. include-build-file:: inc/ulp_lp_core_interrupts.inc
.. only:: CONFIG_SOC_LP_SPI_SUPPORTED
.. include-build-file:: inc/ulp_lp_core_spi.inc
.. _esp-idf-monitor: https://github.com/espressif/esp-idf-monitor