forked from espressif/esp-idf
Merge branch 'feat/lp_core_lp_spi_support' into 'master'
feat(lp-spi): Added support for LP SPI to the LP core Closes IDF-7538 and IDF-10210 See merge request espressif/esp-idf!31492
This commit is contained in:
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
@ -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")
|
||||
|
109
components/ulp/lp_core/include/lp_core_spi.h
Normal file
109
components/ulp/lp_core/include/lp_core_spi.h
Normal 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
|
65
components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h
Normal file
65
components/ulp/lp_core/lp_core/include/ulp_lp_core_spi.h
Normal 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
|
262
components/ulp/lp_core/lp_core/lp_core_spi.c
Normal file
262
components/ulp/lp_core/lp_core/lp_core_spi.c
Normal 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 */
|
306
components/ulp/lp_core/lp_core_spi.c
Normal file
306
components/ulp/lp_core/lp_core_spi.c
Normal 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;
|
||||
}
|
@ -8,6 +8,10 @@ if(CONFIG_SOC_ULP_LP_UART_SUPPORTED)
|
||||
list(APPEND app_sources "test_lp_core_uart.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_LP_SPI_SUPPORTED)
|
||||
list(APPEND app_sources "test_lp_core_spi.c")
|
||||
endif()
|
||||
|
||||
set(lp_core_sources "lp_core/test_main.c")
|
||||
set(lp_core_sources_counter "lp_core/test_main_counter.c")
|
||||
|
||||
@ -25,6 +29,11 @@ if(CONFIG_SOC_ULP_LP_UART_SUPPORTED)
|
||||
set(lp_core_sources_uart "lp_core/test_main_uart.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_LP_SPI_SUPPORTED)
|
||||
set(lp_core_sources_spi_master "lp_core/test_main_spi_master.c")
|
||||
set(lp_core_sources_spi_slave "lp_core/test_main_spi_slave.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "lp_core"
|
||||
REQUIRES ulp unity esp_timer test_utils
|
||||
@ -49,3 +58,8 @@ endif()
|
||||
if(CONFIG_SOC_ULP_LP_UART_SUPPORTED)
|
||||
ulp_embed_binary(lp_core_test_app_uart "${lp_core_sources_uart}" "${lp_core_exp_dep_srcs}")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_LP_SPI_SUPPORTED)
|
||||
ulp_embed_binary(lp_core_test_app_spi_master "${lp_core_sources_spi_master}" "${lp_core_exp_dep_srcs}")
|
||||
ulp_embed_binary(lp_core_test_app_spi_slave "${lp_core_sources_spi_slave}" "${lp_core_exp_dep_srcs}")
|
||||
endif()
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "ulp_lp_core_spi.h"
|
||||
#include "test_shared.h"
|
||||
|
||||
volatile lp_core_test_commands_t spi_test_cmd = LP_CORE_NO_COMMAND;
|
||||
|
||||
volatile uint8_t spi_master_tx_buf[100] = {0};
|
||||
volatile uint8_t spi_master_rx_buf[100] = {0};
|
||||
volatile uint32_t spi_tx_len = 0;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Wait for the HP core to start the test */
|
||||
while (spi_test_cmd == LP_CORE_NO_COMMAND) {
|
||||
|
||||
}
|
||||
|
||||
/* Setup SPI transaction */
|
||||
lp_spi_transaction_t trans_desc = {
|
||||
.tx_length = spi_tx_len,
|
||||
.rx_length = spi_tx_len,
|
||||
.tx_buffer = (uint8_t *)spi_master_tx_buf,
|
||||
.rx_buffer = (uint8_t *)spi_master_rx_buf,
|
||||
};
|
||||
|
||||
/* Transmit data */
|
||||
lp_core_lp_spi_master_transfer(&trans_desc, -1);
|
||||
|
||||
/* Synchronize with the HP core running the test */
|
||||
spi_test_cmd = LP_CORE_NO_COMMAND;
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "ulp_lp_core_spi.h"
|
||||
#include "test_shared.h"
|
||||
|
||||
volatile lp_core_test_command_reply_t spi_test_cmd_reply = LP_CORE_COMMAND_NOK;
|
||||
|
||||
volatile uint8_t spi_slave_tx_buf[100] = {0};
|
||||
volatile uint8_t spi_slave_rx_buf[100] = {0};
|
||||
volatile uint32_t spi_rx_len = 0;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Setup SPI transaction */
|
||||
lp_spi_transaction_t trans_desc = {
|
||||
.rx_length = spi_rx_len,
|
||||
.rx_buffer = (uint8_t *)spi_slave_rx_buf,
|
||||
.tx_buffer = NULL,
|
||||
};
|
||||
|
||||
/* Receive data */
|
||||
lp_core_lp_spi_slave_transfer(&trans_desc, -1);
|
||||
|
||||
/* Synchronize with the HP core running the test */
|
||||
spi_test_cmd_reply = LP_CORE_COMMAND_OK;
|
||||
|
||||
return 0;
|
||||
}
|
@ -24,6 +24,7 @@ typedef enum {
|
||||
LP_CORE_LP_UART_READ_TEST,
|
||||
LP_CORE_LP_UART_MULTI_BYTE_READ_TEST,
|
||||
LP_CORE_LP_UART_PRINT_TEST,
|
||||
LP_CORE_LP_SPI_WRITE_READ_TEST,
|
||||
LP_CORE_NO_COMMAND,
|
||||
} lp_core_test_commands_t;
|
||||
|
||||
|
254
components/ulp/test_apps/lp_core/main/test_lp_core_spi.c
Normal file
254
components/ulp/test_apps/lp_core/main/test_lp_core_spi.c
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "lp_core_test_app_spi_master.h"
|
||||
#include "lp_core_test_app_spi_slave.h"
|
||||
#include "ulp_lp_core.h"
|
||||
#include "lp_core_spi.h"
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_log.h"
|
||||
#include "test_shared.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
extern const uint8_t lp_core_main_spi_master_bin_start[] asm("_binary_lp_core_test_app_spi_master_bin_start");
|
||||
extern const uint8_t lp_core_main_spi_master_bin_end[] asm("_binary_lp_core_test_app_spi_master_bin_end");
|
||||
extern const uint8_t lp_core_main_spi_slave_bin_start[] asm("_binary_lp_core_test_app_spi_slave_bin_start");
|
||||
extern const uint8_t lp_core_main_spi_slave_bin_end[] asm("_binary_lp_core_test_app_spi_slave_bin_end");
|
||||
|
||||
static const char* TAG = "lp_core_spi_test";
|
||||
|
||||
#define TEST_GPIO_PIN_MISO 6
|
||||
#define TEST_GPIO_PIN_MOSI 7
|
||||
#define TEST_GPIO_PIN_CLK 8
|
||||
#define TEST_GPIO_PIN_CS 4
|
||||
|
||||
#define TEST_DATA_LEN_BYTES 42
|
||||
uint8_t expected_data[100] = {0};
|
||||
|
||||
static void load_and_start_lp_core_firmware(ulp_lp_core_cfg_t* cfg, const uint8_t* firmware_start, const uint8_t* firmware_end)
|
||||
{
|
||||
TEST_ASSERT(ulp_lp_core_load_binary(firmware_start, (firmware_end - firmware_start)) == ESP_OK);
|
||||
TEST_ASSERT(ulp_lp_core_run(cfg) == ESP_OK);
|
||||
}
|
||||
|
||||
static void setup_test_data(void)
|
||||
{
|
||||
uint8_t *tx_data = (uint8_t *)&ulp_spi_master_tx_buf;
|
||||
ulp_spi_tx_len = TEST_DATA_LEN_BYTES;
|
||||
|
||||
/* Setup test data */
|
||||
for (int i = 0; i < ulp_spi_tx_len; i++) {
|
||||
tx_data[i] = (i + 1) % 256;
|
||||
expected_data[i] = tx_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_expected_data(void)
|
||||
{
|
||||
ulp_spi_rx_len = TEST_DATA_LEN_BYTES;
|
||||
|
||||
/* Setup expected data */
|
||||
for (int i = 0; i < TEST_DATA_LEN_BYTES; i++) {
|
||||
expected_data[i] = (i + 1) % 256;
|
||||
}
|
||||
}
|
||||
|
||||
/* Base LP SPI bus settings */
|
||||
lp_spi_host_t host_id = 0;
|
||||
lp_spi_bus_config_t bus_config = {
|
||||
.miso_io_num = TEST_GPIO_PIN_MISO,
|
||||
.mosi_io_num = TEST_GPIO_PIN_MOSI,
|
||||
.sclk_io_num = TEST_GPIO_PIN_CLK,
|
||||
};
|
||||
|
||||
/* Base LP SPI device settings */
|
||||
lp_spi_device_config_t device = {
|
||||
.cs_io_num = TEST_GPIO_PIN_CS,
|
||||
.spi_mode = 0,
|
||||
.clock_speed_hz = 10 * 1000, // 10 MHz
|
||||
.duty_cycle = 128, // 50% duty cycle
|
||||
};
|
||||
|
||||
/* Base LP SPI slave device settings */
|
||||
lp_spi_slave_config_t slv_device = {
|
||||
.cs_io_num = TEST_GPIO_PIN_CS,
|
||||
.spi_mode = 0,
|
||||
};
|
||||
|
||||
static void lp_spi_master_init(int spi_flags, bool setup_master_loop_back)
|
||||
{
|
||||
/* Initialize LP SPI bus */
|
||||
/* Setup loop back for tests which do not use an LP SPI slave for looping back the data. */
|
||||
bus_config.miso_io_num = setup_master_loop_back ? TEST_GPIO_PIN_MOSI : TEST_GPIO_PIN_MISO;
|
||||
TEST_ASSERT(lp_core_lp_spi_bus_initialize(host_id, &bus_config) == ESP_OK);
|
||||
|
||||
/* Add LP SPI device */
|
||||
device.flags = spi_flags;
|
||||
TEST_ASSERT(lp_core_lp_spi_bus_add_device(host_id, &device) == ESP_OK);
|
||||
}
|
||||
|
||||
static void lp_spi_slave_init(int spi_flags)
|
||||
{
|
||||
/* Initialize LP SPI bus */
|
||||
TEST_ASSERT(lp_core_lp_spi_bus_initialize(host_id, &bus_config) == ESP_OK);
|
||||
|
||||
/* Add LP SPI slave device */
|
||||
if (spi_flags != 0) {
|
||||
slv_device.flags = spi_flags;
|
||||
}
|
||||
TEST_ASSERT(lp_core_lp_spi_slave_initialize(host_id, &slv_device) == ESP_OK);
|
||||
}
|
||||
|
||||
static void lp_spi_master_execute_test(bool wait_for_slave_ready)
|
||||
{
|
||||
/* Load and run the LP core firmware */
|
||||
ulp_lp_core_cfg_t lp_cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
};
|
||||
load_and_start_lp_core_firmware(&lp_cfg, lp_core_main_spi_master_bin_start, lp_core_main_spi_master_bin_end);
|
||||
|
||||
if (wait_for_slave_ready) {
|
||||
/* Wait for the HP SPI device to be initialized */
|
||||
unity_wait_for_signal("LP SPI slave ready");
|
||||
}
|
||||
|
||||
/* Setup test data */
|
||||
setup_test_data();
|
||||
|
||||
/* Start the test */
|
||||
ulp_spi_test_cmd = LP_CORE_LP_SPI_WRITE_READ_TEST;
|
||||
|
||||
while (ulp_spi_test_cmd != LP_CORE_NO_COMMAND) {
|
||||
/* Wait for the test to complete */
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
/* Verify the received data if we expect the data to be looped back from the LP SPI slave */
|
||||
uint8_t *rx_data = (uint8_t *)&ulp_spi_master_rx_buf;
|
||||
for (int i = 0; i < TEST_DATA_LEN_BYTES; i++) {
|
||||
ESP_LOGI(TAG, "LP SPI master received data: 0x%02x", rx_data[i]);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_data, rx_data, ulp_spi_tx_len);
|
||||
}
|
||||
|
||||
static void lp_spi_slave_execute_test(void)
|
||||
{
|
||||
/* Load and run the LP core firmware */
|
||||
ulp_lp_core_cfg_t lp_cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
};
|
||||
load_and_start_lp_core_firmware(&lp_cfg, lp_core_main_spi_slave_bin_start, lp_core_main_spi_slave_bin_end);
|
||||
|
||||
/* Setup expected test data */
|
||||
setup_expected_data();
|
||||
|
||||
/* Send signal to LP SPI master */
|
||||
unity_send_signal("LP SPI slave ready");
|
||||
|
||||
/* Wait for the test to complete */
|
||||
while (ulp_spi_test_cmd_reply != LP_CORE_COMMAND_OK) {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
/* Verify the received data */
|
||||
uint8_t *rx_data = (uint8_t *)&ulp_spi_slave_rx_buf;
|
||||
for (int i = 0; i < TEST_DATA_LEN_BYTES; i++) {
|
||||
ESP_LOGI(TAG, "LP SPI slave received data: 0x%02x", rx_data[i]);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_data, rx_data, TEST_DATA_LEN_BYTES);
|
||||
}
|
||||
|
||||
void test_lp_spi_master(void)
|
||||
{
|
||||
/* Initialize LP SPI in master mode */
|
||||
lp_spi_master_init(0, false);
|
||||
|
||||
/* Start the LP SPI master test */
|
||||
lp_spi_master_execute_test(true);
|
||||
}
|
||||
|
||||
void test_lp_spi_slave(void)
|
||||
{
|
||||
/* Initialize LP SPI in slave mode */
|
||||
lp_spi_slave_init(0);
|
||||
|
||||
/* Start the LP SPI slave test */
|
||||
lp_spi_slave_execute_test();
|
||||
}
|
||||
void test_lp_spi_master_3wire(void)
|
||||
{
|
||||
/* Initialize LP SPI in master mode */
|
||||
int spi_flags = LP_SPI_DEVICE_3WIRE;
|
||||
lp_spi_master_init(spi_flags, false);
|
||||
|
||||
/* Start the LP SPI master test */
|
||||
lp_spi_master_execute_test(true);
|
||||
}
|
||||
|
||||
void test_lp_spi_slave_3wire(void)
|
||||
{
|
||||
/* Initialize LP SPI in slave mode */
|
||||
int spi_flags = LP_SPI_DEVICE_3WIRE;
|
||||
lp_spi_slave_init(spi_flags);
|
||||
|
||||
/* Start the LP SPI slave test */
|
||||
lp_spi_slave_execute_test();
|
||||
}
|
||||
|
||||
void test_lp_spi_master_lsbfirst(void)
|
||||
{
|
||||
/* Initialize LP SPI in master mode */
|
||||
int spi_flags = LP_SPI_DEVICE_BIT_LSBFIRST;
|
||||
lp_spi_master_init(spi_flags, false);
|
||||
|
||||
/* Start the LP SPI master test */
|
||||
lp_spi_master_execute_test(true);
|
||||
}
|
||||
|
||||
void test_lp_spi_slave_lsbfirst(void)
|
||||
{
|
||||
/* Initialize LP SPI in slave mode */
|
||||
int spi_flags = LP_SPI_DEVICE_BIT_LSBFIRST;
|
||||
lp_spi_slave_init(spi_flags);
|
||||
|
||||
/* Start the LP SPI slave test */
|
||||
lp_spi_slave_execute_test();
|
||||
}
|
||||
|
||||
/* Test LP-SPI master loopback */
|
||||
TEST_CASE("LP-Core LP-SPI master loopback test", "[lp_core]")
|
||||
{
|
||||
/* Initialize LP SPI in master mode */
|
||||
lp_spi_master_init(0, true);
|
||||
|
||||
/* Start the LP SPI master test */
|
||||
lp_spi_master_execute_test(false);
|
||||
}
|
||||
|
||||
/* Test LP-SPI master loopback with active low CS line */
|
||||
TEST_CASE("LP-Core LP-SPI master loopback test with active high CS line", "[lp_core]")
|
||||
{
|
||||
/* Initialize LP SPI in master mode */
|
||||
int spi_flags = LP_SPI_DEVICE_CS_ACTIVE_HIGH;
|
||||
lp_spi_master_init(spi_flags, true);
|
||||
|
||||
/* Start the LP SPI master test */
|
||||
lp_spi_master_execute_test(false);
|
||||
}
|
||||
|
||||
/* Test LP-SPI master and LP-SPI slave communication */
|
||||
TEST_CASE_MULTIPLE_DEVICES("LP-Core LP-SPI master and LP-SPI slave read write test", "[lp_core_spi][test_env=generic_multi_device][timeout=150]", test_lp_spi_master, test_lp_spi_slave);
|
||||
|
||||
/* Test LP-SPI master in 3-Wire SPI mode */
|
||||
TEST_CASE_MULTIPLE_DEVICES("LP-Core LP-SPI master and LP-SPI slave in 3-Wire SPI mode", "[lp_core_spi][test_env=generic_multi_device][timeout=150]", test_lp_spi_master_3wire, test_lp_spi_slave_3wire);
|
||||
|
||||
/* Test LP-SPI master and LP-SPI slave in LSB first mode */
|
||||
TEST_CASE_MULTIPLE_DEVICES("LP-Core LP-SPI master and LP-SPI in LSB first SPI mode", "[lp_core_spi][test_env=generic_multi_device][timeout=150]", test_lp_spi_master_lsbfirst, test_lp_spi_slave_lsbfirst);
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user