Merge branch 'feature/s31_lp_spi' into 'master'

feat(ulp/lp_spi): bring up LP SPI on ESP32-S31

Closes IDF-14736

See merge request espressif/esp-idf!47833
This commit is contained in:
Marius Vikhammer
2026-04-28 13:34:40 +08:00
13 changed files with 880 additions and 381 deletions

View File

@@ -0,0 +1,375 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use it in application code.
******************************************************************************/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "soc/lp_spi_struct.h"
#include "soc/lpperi_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef lp_spi_dev_t lp_spi_ll_dev_t;
#define LP_SPI_LL_GET_HW() (&LP_SPI)
#define LP_SPI_LL_MAX_BUFFER_SIZE 64
/**
* @brief Enable the LP SPI peripheral clock gate
*/
static inline void lp_spi_ll_enable_clock(lp_spi_ll_dev_t *hw)
{
(void)hw;
lpperi_dev_t *lp_peri_dev = &LPPERI;
lp_peri_dev->clk_en.ck_en_lp_spi = 1;
}
/**
* @brief Write a 32-bit word to data buffer register at index n
*/
static inline void lp_spi_ll_write_buffer_word(lp_spi_ll_dev_t *hw, int n, uint32_t val)
{
hw->data_buf[n].reg_buf = val;
}
/**
* @brief Read a 32-bit word from data buffer register at index n
*/
static inline uint32_t lp_spi_ll_read_buffer_word(lp_spi_ll_dev_t *hw, int n)
{
return hw->data_buf[n].reg_buf;
}
/**
* @brief Reset RX and TX AFIFOs
*/
static inline void lp_spi_ll_reset_fifos(lp_spi_ll_dev_t *hw)
{
hw->spi_dma_conf.reg_rx_afifo_rst = 1;
hw->spi_dma_conf.reg_rx_afifo_rst = 0;
hw->spi_dma_conf.reg_buf_afifo_rst = 1;
hw->spi_dma_conf.reg_buf_afifo_rst = 0;
}
/**
* @brief Clear the transaction-done interrupt
*/
static inline void lp_spi_ll_clear_int_trans_done(lp_spi_ll_dev_t *hw)
{
hw->spi_dma_int_clr.reg_trans_done_int_clr = 1;
}
/**
* @brief Return true if a transaction is currently in progress
*/
static inline bool lp_spi_ll_is_busy(lp_spi_ll_dev_t *hw)
{
return hw->spi_cmd.reg_usr != 0;
}
/**
* @brief Return true if the transaction-done interrupt raw bit is set
*/
static inline bool lp_spi_ll_get_int_trans_done(lp_spi_ll_dev_t *hw)
{
return hw->spi_dma_int_raw.reg_trans_done_int_raw != 0;
}
/**
* @brief Enable or disable the dummy phase
*/
static inline void lp_spi_ll_set_dummy_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_dummy = enable ? 1 : 0;
}
/**
* @brief Set the number of dummy cycles (value = cycles - 1)
*/
static inline void lp_spi_ll_set_dummy_cyclelen(lp_spi_ll_dev_t *hw, uint32_t cyclelen)
{
hw->spi_user1.reg_usr_dummy_cyclelen = cyclelen - 1;
}
/**
* @brief Enable or disable the command phase
*/
static inline void lp_spi_ll_set_command_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_command = enable ? 1 : 0;
}
/**
* @brief Set the command bit length (value = bits - 1)
*/
static inline void lp_spi_ll_set_command_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen)
{
hw->spi_user2.reg_usr_command_bitlen = bitlen - 1;
}
/**
* @brief Set the command value
*/
static inline void lp_spi_ll_set_command_value(lp_spi_ll_dev_t *hw, uint32_t value)
{
hw->spi_user2.reg_usr_command_value = value;
}
/**
* @brief Enable or disable the address phase
*/
static inline void lp_spi_ll_set_address_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_addr = enable ? 1 : 0;
}
/**
* @brief Set the address bit length (value = bits - 1)
*/
static inline void lp_spi_ll_set_address_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen)
{
hw->spi_user1.reg_usr_addr_bitlen = bitlen;
}
/**
* @brief Set the address value
*/
static inline void lp_spi_ll_set_address_value(lp_spi_ll_dev_t *hw, uint32_t value)
{
hw->spi_addr.reg_usr_addr_value = value;
}
/**
* @brief Enable or disable the MOSI (write-data) phase
*/
static inline void lp_spi_ll_set_mosi_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_mosi = enable ? 1 : 0;
}
/**
* @brief Enable or disable the MISO (read-data) phase
*/
static inline void lp_spi_ll_set_miso_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_miso = enable ? 1 : 0;
}
/**
* @brief Set the data bit length for master/slave transfers (value = bits - 1)
*/
static inline void lp_spi_ll_set_data_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen)
{
hw->spi_ms_dlen.reg_ms_data_bitlen = bitlen;
}
/**
* @brief Trigger a configuration update (master mode, synchronises APB->SPI domain)
*/
static inline void lp_spi_ll_apply_config(lp_spi_ll_dev_t *hw)
{
hw->spi_cmd.reg_update = 1;
while (hw->spi_cmd.reg_update) {
;
}
}
/**
* @brief Start a user-defined SPI transaction
*/
static inline void lp_spi_ll_start_user_transaction(lp_spi_ll_dev_t *hw)
{
hw->spi_cmd.reg_usr = 1;
}
/**
* @brief Get the number of bits received during the last slave transfer
*/
static inline uint32_t lp_spi_ll_get_slave_rcv_bitlen(lp_spi_ll_dev_t *hw)
{
return hw->spi_slave1.reg_slv_data_bitlen;
}
/**
* @brief Return true if wr_bit_order (MOSI LSB-first) is set
*/
static inline bool lp_spi_ll_get_wr_bit_order(lp_spi_ll_dev_t *hw)
{
return hw->spi_ctrl.reg_wr_bit_order != 0;
}
/**
* @brief Set MOSI/MISO bit order (0 = MSB first, 1 = LSB first)
*/
static inline void lp_spi_ll_set_bit_order(lp_spi_ll_dev_t *hw, bool rd_lsb_first, bool wr_lsb_first)
{
hw->spi_ctrl.reg_rd_bit_order = rd_lsb_first ? 1 : 0;
hw->spi_ctrl.reg_wr_bit_order = wr_lsb_first ? 1 : 0;
}
/**
* @brief Set SPI clock polarity (CPOL) idle edge
*/
static inline void lp_spi_ll_set_ck_idle_edge(lp_spi_ll_dev_t *hw, bool idle_high)
{
hw->spi_misc.reg_ck_idle_edge = idle_high ? 1 : 0;
}
/**
* @brief Set clock-out edge (used with CPOL/CPHA in master mode)
*/
static inline void lp_spi_ll_set_ck_out_edge(lp_spi_ll_dev_t *hw, bool edge)
{
hw->spi_user.reg_ck_out_edge = edge ? 1 : 0;
}
/**
* @brief Set slave mode rising/falling clock edge for Rx and Tx sampling
*/
static inline void lp_spi_ll_set_slave_clk_edges(lp_spi_ll_dev_t *hw, bool rsck_i_edge, bool tsck_i_edge)
{
hw->spi_user.reg_rsck_i_edge = rsck_i_edge ? 1 : 0;
hw->spi_user.reg_tsck_i_edge = tsck_i_edge ? 1 : 0;
}
/**
* @brief Set slave clock mode 1/3 support bit
*/
static inline void lp_spi_ll_set_slave_clk_mode_13(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_slave.reg_clk_mode_13 = enable ? 1 : 0;
}
/**
* @brief Set master CS polarity (active high / active low)
*/
static inline void lp_spi_ll_set_master_cs_pol(lp_spi_ll_dev_t *hw, bool active_high)
{
hw->spi_misc.reg_master_cs_pol = active_high ? 1 : 0;
}
/**
* @brief Set slave CS polarity (0 = active low, 1 = inverted)
*/
static inline void lp_spi_ll_set_slave_cs_pol(lp_spi_ll_dev_t *hw, bool inverted)
{
hw->spi_misc.reg_slave_cs_pol = inverted ? 1 : 0;
}
/**
* @brief Enable or disable full-duplex mode (doutdin)
*/
static inline void lp_spi_ll_set_full_duplex(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_doutdin = enable ? 1 : 0;
}
/**
* @brief Enable or disable 3-wire half-duplex (SIO) mode
*/
static inline void lp_spi_ll_set_sio_mode(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_sio = enable ? 1 : 0;
}
/**
* @brief Configure CS setup (pre-transaction) timing
*/
static inline void lp_spi_ll_set_cs_setup(lp_spi_ll_dev_t *hw, bool enable, uint32_t setup_time)
{
hw->spi_user.reg_cs_setup = enable ? 1 : 0;
hw->spi_user1.reg_cs_setup_time = setup_time;
}
/**
* @brief Configure CS hold (post-transaction) timing
*/
static inline void lp_spi_ll_set_cs_hold(lp_spi_ll_dev_t *hw, bool enable, uint32_t hold_time)
{
hw->spi_user.reg_cs_hold = enable ? 1 : 0;
hw->spi_user1.reg_cs_hold_time = hold_time;
}
/**
* @brief Enable CS0 (disable the CS0_DIS bit)
*/
static inline void lp_spi_ll_enable_cs0(lp_spi_ll_dev_t *hw)
{
hw->spi_misc.reg_cs0_dis = 0;
}
/**
* @brief Disable MOSI/MISO high-part buffer access
*/
static inline void lp_spi_ll_disable_highpart(lp_spi_ll_dev_t *hw)
{
hw->spi_user.reg_usr_mosi_highpart = 0;
hw->spi_user.reg_usr_miso_highpart = 0;
}
/**
* @brief Set slave mode enable bit
*/
static inline void lp_spi_ll_set_slave_mode(lp_spi_ll_dev_t *hw, bool slave)
{
hw->spi_slave.reg_slave_mode = slave ? 1 : 0;
}
/**
* @brief Set slave clock mode (clk_mode field)
*/
static inline void lp_spi_ll_set_slave_clk_mode(lp_spi_ll_dev_t *hw, uint32_t clk_mode)
{
hw->spi_slave.reg_clk_mode = clk_mode;
}
/**
* @brief Issue a software reset of the SPI peripheral
*/
static inline void lp_spi_ll_soft_reset(lp_spi_ll_dev_t *hw)
{
hw->spi_slave.reg_soft_reset = 1;
hw->spi_slave.reg_soft_reset = 0;
}
/**
* @brief Write the raw clock register value (used with spi_ll_master_cal_clock output)
*/
static inline void lp_spi_ll_set_clock_val(lp_spi_ll_dev_t *hw, uint32_t clock_val)
{
hw->spi_clock.val = clock_val;
}
/**
* @brief Zero out the clock, user, and ctrl registers (slave init)
*/
static inline void lp_spi_ll_reset_slave_regs(lp_spi_ll_dev_t *hw)
{
hw->spi_clock.val = 0;
hw->spi_user.val = 0;
hw->spi_ctrl.val = 0;
}
/**
* @brief Reset CS timing registers in master mode
*/
static inline void lp_spi_ll_reset_cs_timing(lp_spi_ll_dev_t *hw)
{
hw->spi_user1.reg_cs_setup_time = 0;
hw->spi_user1.reg_cs_hold_time = 0;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,375 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use it in application code.
******************************************************************************/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "soc/lp_spi_struct.h"
#include "soc/lp_peri_clkrst_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef lp_dev_t lp_spi_ll_dev_t;
#define LP_SPI_LL_GET_HW() (&LP_SPI)
#define LP_SPI_LL_MAX_BUFFER_SIZE 64
/**
* @brief Enable the LP SPI peripheral clock gate
*/
static inline void lp_spi_ll_enable_clock(lp_spi_ll_dev_t *hw)
{
LP_PERI_CLKRST.spi_ctrl.lp_spi_clk_en = 1;
hw->spi_clk_gate.reg_clk_en = 1;
hw->spi_clk_gate.reg_mst_clk_active = 1;
}
/**
* @brief Write a 32-bit word to data buffer register at index n
*/
static inline void lp_spi_ll_write_buffer_word(lp_spi_ll_dev_t *hw, int n, uint32_t val)
{
hw->data_buf[n].val = val;
}
/**
* @brief Read a 32-bit word from data buffer register at index n
*/
static inline uint32_t lp_spi_ll_read_buffer_word(lp_spi_ll_dev_t *hw, int n)
{
return hw->data_buf[n].val;
}
/**
* @brief Reset RX and TX AFIFOs
*/
static inline void lp_spi_ll_reset_fifos(lp_spi_ll_dev_t *hw)
{
hw->spi_dma_conf.reg_rx_afifo_rst = 1;
hw->spi_dma_conf.reg_rx_afifo_rst = 0;
hw->spi_dma_conf.reg_buf_afifo_rst = 1;
hw->spi_dma_conf.reg_buf_afifo_rst = 0;
}
/**
* @brief Clear the transaction-done interrupt
*/
static inline void lp_spi_ll_clear_int_trans_done(lp_spi_ll_dev_t *hw)
{
hw->spi_dma_int_clr.reg_trans_done_int_clr = 1;
}
/**
* @brief Return true if a transaction is currently in progress
*/
static inline bool lp_spi_ll_is_busy(lp_spi_ll_dev_t *hw)
{
return hw->spi_cmd.reg_usr != 0;
}
/**
* @brief Return true if the transaction-done interrupt raw bit is set
*/
static inline bool lp_spi_ll_get_int_trans_done(lp_spi_ll_dev_t *hw)
{
return hw->spi_dma_int_raw.reg_trans_done_int_raw != 0;
}
/**
* @brief Enable or disable the dummy phase
*/
static inline void lp_spi_ll_set_dummy_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_dummy = enable ? 1 : 0;
}
/**
* @brief Set the number of dummy cycles (value = cycles - 1)
*/
static inline void lp_spi_ll_set_dummy_cyclelen(lp_spi_ll_dev_t *hw, uint32_t cyclelen)
{
hw->spi_user1.reg_usr_dummy_cyclelen = cyclelen - 1;
}
/**
* @brief Enable or disable the command phase
*/
static inline void lp_spi_ll_set_command_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_command = enable ? 1 : 0;
}
/**
* @brief Set the command bit length (value = bits - 1)
*/
static inline void lp_spi_ll_set_command_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen)
{
hw->spi_user2.reg_usr_command_bitlen = bitlen - 1;
}
/**
* @brief Set the command value
*/
static inline void lp_spi_ll_set_command_value(lp_spi_ll_dev_t *hw, uint32_t value)
{
hw->spi_user2.reg_usr_command_value = value;
}
/**
* @brief Enable or disable the address phase
*/
static inline void lp_spi_ll_set_address_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_addr = enable ? 1 : 0;
}
/**
* @brief Set the address bit length (value = bits - 1)
*/
static inline void lp_spi_ll_set_address_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen)
{
hw->spi_user1.reg_usr_addr_bitlen = bitlen;
}
/**
* @brief Set the address value
*/
static inline void lp_spi_ll_set_address_value(lp_spi_ll_dev_t *hw, uint32_t value)
{
hw->spi_addr.reg_usr_addr_value = value;
}
/**
* @brief Enable or disable the MOSI (write-data) phase
*/
static inline void lp_spi_ll_set_mosi_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_mosi = enable ? 1 : 0;
}
/**
* @brief Enable or disable the MISO (read-data) phase
*/
static inline void lp_spi_ll_set_miso_en(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_usr_miso = enable ? 1 : 0;
}
/**
* @brief Set the data bit length for master/slave transfers (value = bits - 1)
*/
static inline void lp_spi_ll_set_data_bitlen(lp_spi_ll_dev_t *hw, uint32_t bitlen)
{
hw->spi_ms_dlen.reg_ms_data_bitlen = bitlen;
}
/**
* @brief Trigger a configuration update (master mode, synchronises APB->SPI domain)
*/
static inline void lp_spi_ll_apply_config(lp_spi_ll_dev_t *hw)
{
hw->spi_cmd.reg_update = 1;
while (hw->spi_cmd.reg_update) {
;
}
}
/**
* @brief Start a user-defined SPI transaction
*/
static inline void lp_spi_ll_start_user_transaction(lp_spi_ll_dev_t *hw)
{
hw->spi_cmd.reg_usr = 1;
}
/**
* @brief Get the number of bits received during the last slave transfer
*/
static inline uint32_t lp_spi_ll_get_slave_rcv_bitlen(lp_spi_ll_dev_t *hw)
{
return hw->spi_slave1.reg_slv_data_bitlen;
}
/**
* @brief Return true if wr_bit_order (MOSI LSB-first) is set
*/
static inline bool lp_spi_ll_get_wr_bit_order(lp_spi_ll_dev_t *hw)
{
return hw->spi_ctrl.reg_wr_bit_order != 0;
}
/**
* @brief Set MOSI/MISO bit order (0 = MSB first, 1 = LSB first)
*/
static inline void lp_spi_ll_set_bit_order(lp_spi_ll_dev_t *hw, bool rd_lsb_first, bool wr_lsb_first)
{
hw->spi_ctrl.reg_rd_bit_order = rd_lsb_first ? 1 : 0;
hw->spi_ctrl.reg_wr_bit_order = wr_lsb_first ? 1 : 0;
}
/**
* @brief Set SPI clock polarity (CPOL) idle edge
*/
static inline void lp_spi_ll_set_ck_idle_edge(lp_spi_ll_dev_t *hw, bool idle_high)
{
hw->spi_misc.reg_ck_idle_edge = idle_high ? 1 : 0;
}
/**
* @brief Set clock-out edge (used with CPOL/CPHA in master mode)
*/
static inline void lp_spi_ll_set_ck_out_edge(lp_spi_ll_dev_t *hw, bool edge)
{
hw->spi_user.reg_ck_out_edge = edge ? 1 : 0;
}
/**
* @brief Set slave mode rising/falling clock edge for Rx and Tx sampling
*/
static inline void lp_spi_ll_set_slave_clk_edges(lp_spi_ll_dev_t *hw, bool rsck_i_edge, bool tsck_i_edge)
{
hw->spi_user.reg_rsck_i_edge = rsck_i_edge ? 1 : 0;
hw->spi_user.reg_tsck_i_edge = tsck_i_edge ? 1 : 0;
}
/**
* @brief Set slave clock mode 1/3 support bit
*/
static inline void lp_spi_ll_set_slave_clk_mode_13(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_slave.reg_clk_mode_13 = enable ? 1 : 0;
}
/**
* @brief Set master CS polarity (active high / active low)
*/
static inline void lp_spi_ll_set_master_cs_pol(lp_spi_ll_dev_t *hw, bool active_high)
{
hw->spi_misc.reg_master_cs_pol = active_high ? 1 : 0;
}
/**
* @brief Set slave CS polarity (0 = active low, 1 = inverted)
*/
static inline void lp_spi_ll_set_slave_cs_pol(lp_spi_ll_dev_t *hw, bool inverted)
{
hw->spi_misc.reg_slave_cs_pol = inverted ? 1 : 0;
}
/**
* @brief Enable or disable full-duplex mode (doutdin)
*/
static inline void lp_spi_ll_set_full_duplex(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_doutdin = enable ? 1 : 0;
}
/**
* @brief Enable or disable 3-wire half-duplex (SIO) mode
*/
static inline void lp_spi_ll_set_sio_mode(lp_spi_ll_dev_t *hw, bool enable)
{
hw->spi_user.reg_sio = enable ? 1 : 0;
}
/**
* @brief Configure CS setup (pre-transaction) timing
*/
static inline void lp_spi_ll_set_cs_setup(lp_spi_ll_dev_t *hw, bool enable, uint32_t setup_time)
{
hw->spi_user.reg_cs_setup = enable ? 1 : 0;
hw->spi_user1.reg_cs_setup_time = setup_time;
}
/**
* @brief Configure CS hold (post-transaction) timing
*/
static inline void lp_spi_ll_set_cs_hold(lp_spi_ll_dev_t *hw, bool enable, uint32_t hold_time)
{
hw->spi_user.reg_cs_hold = enable ? 1 : 0;
hw->spi_user1.reg_cs_hold_time = hold_time;
}
/**
* @brief Enable CS0 (disable the CS0_DIS bit)
*/
static inline void lp_spi_ll_enable_cs0(lp_spi_ll_dev_t *hw)
{
hw->spi_misc.reg_cs0_dis = 0;
}
/**
* @brief Disable MOSI/MISO high-part buffer access
*/
static inline void lp_spi_ll_disable_highpart(lp_spi_ll_dev_t *hw)
{
hw->spi_user.reg_usr_mosi_highpart = 0;
hw->spi_user.reg_usr_miso_highpart = 0;
}
/**
* @brief Set slave mode enable bit
*/
static inline void lp_spi_ll_set_slave_mode(lp_spi_ll_dev_t *hw, bool slave)
{
hw->spi_slave.reg_slave_mode = slave ? 1 : 0;
}
/**
* @brief Set slave clock mode (clk_mode field)
*/
static inline void lp_spi_ll_set_slave_clk_mode(lp_spi_ll_dev_t *hw, uint32_t clk_mode)
{
hw->spi_slave.reg_clk_mode = clk_mode;
}
/**
* @brief Issue a software reset of the SPI peripheral
*/
static inline void lp_spi_ll_soft_reset(lp_spi_ll_dev_t *hw)
{
hw->spi_slave.reg_soft_reset = 1;
hw->spi_slave.reg_soft_reset = 0;
}
/**
* @brief Write the raw clock register value (used with spi_ll_master_cal_clock output)
*/
static inline void lp_spi_ll_set_clock_val(lp_spi_ll_dev_t *hw, uint32_t clock_val)
{
hw->spi_clock.val = clock_val;
}
/**
* @brief Zero out the clock, user, and ctrl registers (slave init)
*/
static inline void lp_spi_ll_reset_slave_regs(lp_spi_ll_dev_t *hw)
{
hw->spi_clock.val = 0;
hw->spi_user.val = 0;
hw->spi_ctrl.val = 0;
}
/**
* @brief Reset CS timing registers in master mode
*/
static inline void lp_spi_ll_reset_cs_timing(lp_spi_ll_dev_t *hw)
{
hw->spi_user1.reg_cs_setup_time = 0;
hw->spi_user1.reg_cs_hold_time = 0;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1343,10 +1343,6 @@ config SOC_SPI_SUPPORT_OCT
bool
default y
config SOC_LP_SPI_MAXIMUM_BUFFER_SIZE
int
default 64
config SOC_SPIRAM_XIP_SUPPORTED
bool
default y

View File

@@ -512,9 +512,6 @@
#define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1
#define SOC_SPI_SUPPORT_OCT 1
/*-------------------------- LP SPI CAPS ----------------------------------------*/
#define SOC_LP_SPI_MAXIMUM_BUFFER_SIZE 64
/*-------------------------- SPIRAM CAPS ----------------------------------------*/
#define SOC_SPIRAM_XIP_SUPPORTED 1

View File

@@ -183,6 +183,10 @@ config SOC_LP_I2C_SUPPORTED
bool
default y
config SOC_LP_SPI_SUPPORTED
bool
default y
config SOC_SPIRAM_SUPPORTED
bool
default y

View File

@@ -83,7 +83,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 // TODO: [ESP32S31] IDF-14639
#define SOC_LP_SPI_SUPPORTED 1
#define SOC_SPIRAM_SUPPORTED 1
#define SOC_PSRAM_DMA_CAPABLE 1
// #define SOC_SDMMC_HOST_SUPPORTED 1 // TODO: [ESP32S31] IDF-14705

View File

@@ -1,5 +1,5 @@
/**
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -813,244 +813,19 @@ typedef union {
} lp_spi_sleep_conf1_reg_t;
/** Group: LP SPI W0 REG */
/** Type of spi_w0 register
/** Group: LP SPI Wn REG */
/** Type of spi_wn register
* SPI CPU-controlled buffer0
*/
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 */
@@ -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;
@@ -1265,6 +1025,7 @@ typedef struct {
volatile lp_rnd_eco_high_reg_t rnd_eco_high;
} lp_dev_t;
extern lp_dev_t LP_SPI;
#ifndef __cplusplus
_Static_assert(sizeof(lp_dev_t) == 0x100, "Invalid size of lp_dev_t structure");

View File

@@ -13,18 +13,16 @@
#include "esp_err.h"
#include "ulp_lp_core_spi.h"
#include "ulp_lp_core_utils.h"
#include "soc/lp_spi_struct.h"
#include "hal/lp_spi_ll.h"
/* Use the register structure to access LP_SPI module registers */
lp_spi_dev_t *lp_spi_dev = &LP_SPI;
static lp_spi_ll_dev_t *lp_spi_dev = LP_SPI_LL_GET_HW();
static inline esp_err_t lp_core_spi_wait_for_interrupt(int32_t cycles_to_wait)
{
uint32_t timeout_start = ulp_lp_core_get_cpu_cycles();
while (!lp_spi_dev->spi_dma_int_raw.reg_trans_done_int_raw) {
while (!lp_spi_ll_get_int_trans_done(lp_spi_dev)) {
if (ulp_lp_core_is_timeout_elapsed(timeout_start, cycles_to_wait)) {
/* Clear interrupt bits */
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
lp_spi_ll_clear_int_trans_done(lp_spi_dev);
return ESP_ERR_TIMEOUT;
}
}
@@ -48,48 +46,51 @@ esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32
}
/* 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;
lp_spi_ll_reset_fifos(lp_spi_dev);
/* 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;
lp_spi_ll_clear_int_trans_done(lp_spi_dev);
/* Make sure that we do not have any ongoing transactions */
if (lp_spi_dev->spi_cmd.reg_usr) {
if (lp_spi_ll_is_busy(lp_spi_dev)) {
return ESP_ERR_INVALID_STATE;
}
/* Configure dummy bits */
lp_spi_dev->spi_user.reg_usr_dummy = trans_desc->dummy_bits ? 1 : 0;
lp_spi_ll_set_dummy_en(lp_spi_dev, trans_desc->dummy_bits != 0);
if (trans_desc->dummy_bits) {
lp_spi_dev->spi_user1.reg_usr_dummy_cyclelen = trans_desc->dummy_bits - 1;
lp_spi_ll_set_dummy_cyclelen(lp_spi_dev, trans_desc->dummy_bits);
}
/* Configure the command and command bit length */
lp_spi_dev->spi_user.reg_usr_command = trans_desc->command_bits ? 1 : 0;
lp_spi_ll_set_command_en(lp_spi_dev, trans_desc->command_bits != 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));
lp_spi_ll_set_command_bitlen(lp_spi_dev, trans_desc->command_bits);
lp_spi_ll_set_command_value(lp_spi_dev,
lp_spi_ll_get_wr_bit_order(lp_spi_dev)
? 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;
lp_spi_ll_set_address_en(lp_spi_dev, trans_desc->address_bits != 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);
lp_spi_ll_set_address_bitlen(lp_spi_dev, trans_desc->address_bits - 1);
lp_spi_ll_set_address_value(lp_spi_dev,
lp_spi_ll_get_wr_bit_order(lp_spi_dev)
? __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;
lp_spi_ll_set_mosi_en(lp_spi_dev, true);
lp_spi_ll_set_miso_en(lp_spi_dev, trans_desc->rx_buffer != NULL);
/* 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;
lp_spi_ll_set_data_bitlen(lp_spi_dev, tx_bitlen - 1);
/* Prepare the data to be transmitted */
uint32_t tx_idx = 0;
@@ -106,23 +107,20 @@ esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32
* 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_reg_num = (LP_SPI_LL_MAX_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);
lp_spi_ll_write_buffer_word(lp_spi_dev, (tx_idx / 4) & max_data_reg_num, *(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) {
;
}
lp_spi_ll_apply_config(lp_spi_dev);
/* Start the transaction */
lp_spi_dev->spi_cmd.reg_usr = 1;
lp_spi_ll_start_user_transaction(lp_spi_dev);
/* Wait for the transaction to complete */
ret = lp_core_spi_wait_for_interrupt(cycles_to_wait);
@@ -131,12 +129,12 @@ esp_err_t lp_core_lp_spi_master_transfer(lp_spi_transaction_t *trans_desc, int32
}
/* Clear the transaction done interrupt */
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
lp_spi_ll_clear_int_trans_done(lp_spi_dev);
/* 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;
*(uint32_t *)(trans_desc->rx_buffer + rx_idx) = lp_spi_ll_read_buffer_word(lp_spi_dev, (rx_idx / 4) & max_data_reg_num);
rx_idx += 4;
// This loop would exit even if we haven't received all the data.
}
@@ -159,23 +157,20 @@ esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_
}
/* 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;
lp_spi_ll_reset_fifos(lp_spi_dev);
/* 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;
lp_spi_ll_clear_int_trans_done(lp_spi_dev);
/* Set data lines */
lp_spi_dev->spi_user.reg_usr_mosi = 1;
lp_spi_dev->spi_user.reg_usr_miso = 1;
lp_spi_ll_set_mosi_en(lp_spi_dev, true);
lp_spi_ll_set_miso_en(lp_spi_dev, true);
/* 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;
lp_spi_ll_set_data_bitlen(lp_spi_dev, rx_bitlen - 1);
/* Prepare the data to be received */
uint32_t rx_idx = 0;
@@ -191,9 +186,9 @@ esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_
* 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) {
if (trans_desc->rx_length > LP_SPI_LL_MAX_BUFFER_SIZE) {
/* Truncate the length to the maximum buffer size */
length_in_bytes = SOC_LP_SPI_MAXIMUM_BUFFER_SIZE;
length_in_bytes = LP_SPI_LL_MAX_BUFFER_SIZE;
}
while (rx_idx < length_in_bytes) {
@@ -204,42 +199,40 @@ esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_
}
/* 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;
uint32_t slave_bitlen = lp_spi_ll_get_slave_rcv_bitlen(lp_spi_dev);
rcvd_bitlen = slave_bitlen > (trans_desc->rx_length * 8) ? (trans_desc->rx_length * 8) : slave_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;
*(uint32_t *)(trans_desc->rx_buffer + rx_idx) = lp_spi_ll_read_buffer_word(lp_spi_dev, rx_idx / 4);
rx_idx += 4;
}
/* Clear the transaction done interrupt */
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
lp_spi_ll_clear_int_trans_done(lp_spi_dev);
}
/* 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) {
if (length_in_bytes > LP_SPI_LL_MAX_BUFFER_SIZE) {
/* Truncate the length to the maximum buffer size */
length_in_bytes = SOC_LP_SPI_MAXIMUM_BUFFER_SIZE;
length_in_bytes = LP_SPI_LL_MAX_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);
lp_spi_ll_write_buffer_word(lp_spi_dev, tx_idx / 4, *(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) {
;
}
lp_spi_ll_apply_config(lp_spi_dev);
/* Start the transaction */
lp_spi_dev->spi_cmd.reg_usr = 1;
lp_spi_ll_start_user_transaction(lp_spi_dev);
/* Wait for the transaction to complete */
ret = lp_core_spi_wait_for_interrupt(cycles_to_wait);
@@ -248,7 +241,7 @@ esp_err_t lp_core_lp_spi_slave_transfer(lp_spi_transaction_t *trans_desc, int32_
}
/* Clear the transaction done interrupt */
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
lp_spi_ll_clear_int_trans_done(lp_spi_dev);
}
return ret;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,17 +10,15 @@
#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 "hal/lp_spi_ll.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 lp_spi_ll_dev_t *lp_spi_dev = LP_SPI_LL_GET_HW();
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)
{
@@ -87,10 +85,9 @@ static esp_err_t lp_spi_cs_pin_init(int cs_io_num)
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;
lp_spi_ll_enable_clock(lp_spi_dev);
}
}
@@ -108,135 +105,125 @@ static esp_err_t lp_spi_clock_init(const lp_spi_device_config_t *dev_config)
/* 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;
lp_spi_ll_set_clock_val(lp_spi_dev, 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;
lp_spi_ll_set_slave_mode(lp_spi_dev, false);
lp_spi_ll_set_slave_clk_mode(lp_spi_dev, 0);
/* Reset CS timing */
lp_spi_dev->spi_user1.reg_cs_setup_time = 0;
lp_spi_dev->spi_user1.reg_cs_hold_time = 0;
lp_spi_ll_reset_cs_timing(lp_spi_dev);
/* 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;
lp_spi_ll_disable_highpart(lp_spi_dev);
}
static void lp_spi_slave_init(void)
{
/* Set Slave mode */
lp_spi_dev->spi_slave.reg_slave_mode = 1;
lp_spi_ll_set_slave_mode(lp_spi_dev, true);
/* Reset the SPI peripheral */
lp_spi_dev->spi_slave.reg_soft_reset = 1;
lp_spi_dev->spi_slave.reg_soft_reset = 0;
lp_spi_ll_soft_reset(lp_spi_dev);
/* 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;
lp_spi_ll_reset_slave_regs(lp_spi_dev);
lp_spi_ll_set_full_duplex(lp_spi_dev, true);
lp_spi_ll_set_sio_mode(lp_spi_dev, false);
/* 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;
lp_spi_ll_disable_highpart(lp_spi_dev);
}
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;
lp_spi_ll_set_bit_order(lp_spi_dev,
(dev_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST) != 0,
(dev_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST) != 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;
lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false);
lp_spi_ll_set_ck_out_edge(lp_spi_dev, false);
} 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;
lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false);
lp_spi_ll_set_ck_out_edge(lp_spi_dev, true);
} 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;
lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true);
lp_spi_ll_set_ck_out_edge(lp_spi_dev, true);
} 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;
lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true);
lp_spi_ll_set_ck_out_edge(lp_spi_dev, false);
}
/* 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;
lp_spi_ll_set_master_cs_pol(lp_spi_dev, (dev_config->flags & LP_SPI_DEVICE_CS_ACTIVE_HIGH) != 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;
lp_spi_ll_set_full_duplex(lp_spi_dev, (dev_config->flags & LP_SPI_DEVICE_HALF_DUPLEX) == 0);
/* Configure 3-Wire half-duplex mode */
lp_spi_dev->spi_user.reg_sio = dev_config->flags & LP_SPI_DEVICE_3WIRE ? 1 : 0;
lp_spi_ll_set_sio_mode(lp_spi_dev, (dev_config->flags & LP_SPI_DEVICE_3WIRE) != 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;
lp_spi_ll_set_cs_setup(lp_spi_dev,
dev_config->cs_ena_pretrans != 0,
dev_config->cs_ena_pretrans == 0 ? 0 : dev_config->cs_ena_pretrans - 1);
lp_spi_ll_set_cs_hold(lp_spi_dev,
dev_config->cs_ena_posttrans != 0,
dev_config->cs_ena_posttrans);
/* Select the CS pin */
lp_spi_dev->spi_misc.reg_cs0_dis = 0;
lp_spi_ll_enable_cs0(lp_spi_dev);
}
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;
lp_spi_ll_set_bit_order(lp_spi_dev,
(slave_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST) != 0,
(slave_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST) != 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;
lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false);
lp_spi_ll_set_slave_clk_edges(lp_spi_dev, false, false);
lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, false);
} 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;
lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false);
lp_spi_ll_set_slave_clk_edges(lp_spi_dev, true, true);
lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, true);
} 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;
lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true);
lp_spi_ll_set_slave_clk_edges(lp_spi_dev, true, true);
lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, false);
} 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;
lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true);
lp_spi_ll_set_slave_clk_edges(lp_spi_dev, false, false);
lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, true);
}
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;
lp_spi_ll_set_slave_cs_pol(lp_spi_dev, false);
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;
lp_spi_ll_set_full_duplex(lp_spi_dev, true);
/* Configure 3-Wire half-duplex mode */
lp_spi_dev->spi_user.reg_sio = slave_config->flags & LP_SPI_DEVICE_3WIRE ? 1 : 0;
lp_spi_ll_set_sio_mode(lp_spi_dev, (slave_config->flags & LP_SPI_DEVICE_3WIRE) != 0);
/* Select the CS pin */
lp_spi_dev->spi_misc.reg_cs0_dis = 0;
lp_spi_ll_enable_cs0(lp_spi_dev);
}
//////////////////////////////////////////////////////////////////////////////////

View File

@@ -25,7 +25,11 @@ static const char* TAG = "lp_core_spi_test";
#define TEST_GPIO_PIN_MISO 6
#define TEST_GPIO_PIN_MOSI 7
#if CONFIG_IDF_TARGET_ESP32S31
#define TEST_GPIO_PIN_CLK 5 // S31 only has RTC GPIOs 0-7; GPIO 8 is not a valid LP IO
#else
#define TEST_GPIO_PIN_CLK 8
#endif
#define TEST_GPIO_PIN_CS 4
#define TEST_DATA_LEN_BYTES 42

View File

@@ -14,10 +14,12 @@ INPUT += \
$(PROJECT_PATH)/components/bt/include/esp32s31/include/esp_bt.h \
$(PROJECT_PATH)/components/bt/include/esp32s31/include/esp_bt_vs.h \
$(PROJECT_PATH)/components/ulp/lp_core/include/lp_core_etm.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/shared/include/ulp_lp_core_lp_uart_shared.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_print.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 \

View File

@@ -1,7 +1,7 @@
| Supported Targets | ESP32-P4 |
| ----------------- | -------- |
# LP I2C Example
# LP SPI Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
@@ -22,6 +22,7 @@ To run this example, you should have an ESP based development board that support
| | SDI(MISO) | SDO(MOSI) | SCK | CSB (CS) |
| ----------------------- | ----------| ----------| ----- | -------- |
| ESP32-P4 LP SPI Master | GPIO6 | GPIO7 | GPIO8 | GPIO4 |
| ESP32-S31 LP SPI Master | GPIO6 | GPIO7 | GPIO3 | GPIO4 |
### Build and Flash

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -17,7 +17,11 @@ extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end"
#define LP_SPI_MOSI_PIN 7
#define LP_SPI_MISO_PIN 6
#if CONFIG_IDF_TARGET_ESP32S31
#define LP_SPI_SCLK_PIN 3
#else
#define LP_SPI_SCLK_PIN 8
#endif
#define LP_SPI_CS_PIN 4
#define LP_CORE_WAKEUP_PERIOD_US 1*1000*1000
@@ -58,7 +62,7 @@ static void lp_spi_init(void)
/* Base LP SPI device settings */
lp_spi_device_config_t device = {
.cs_io_num = LP_SPI_CS_PIN,
.clock_speed_hz = 10 * 1000, // 10 MHz
.clock_speed_hz = 10 * 1000, // 10 kHz
.duty_cycle = 128, // 50% duty cycle
};
@@ -79,7 +83,7 @@ void app_main(void)
vTaskDelay(pdMS_TO_TICKS(1000));
uint32_t causes = esp_sleep_get_wakeup_causes();
if (esp_sleep_get_wakeup_causes() & BIT(ESP_SLEEP_WAKEUP_ULP)) {
if (causes & BIT(ESP_SLEEP_WAKEUP_ULP)) {
printf("LP core woke up the main CPU\n");
printf("Temperature %.2f degree celsius, humidity %.2f%%RH\n", ulp_temperature / 100.0, ulp_humidity / 1024.0);
} else {