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

Add LP SPI HAL LL layer for ESP32-S31 and ESP32-P4, enable the
peripheral in soc_caps, and port the HP-side and LP-core-side SPI
drivers to use the new LL abstraction.
This commit is contained in:
Marius Vikhammer
2026-04-28 10:20:02 +08:00
parent e17f46e240
commit defe1cde71
13 changed files with 880 additions and 381 deletions
@@ -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
@@ -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
@@ -1343,10 +1343,6 @@ config SOC_SPI_SUPPORT_OCT
bool bool
default y default y
config SOC_LP_SPI_MAXIMUM_BUFFER_SIZE
int
default 64
config SOC_SPIRAM_XIP_SUPPORTED config SOC_SPIRAM_XIP_SUPPORTED
bool bool
default y default y
@@ -512,9 +512,6 @@
#define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1 #define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1
#define SOC_SPI_SUPPORT_OCT 1 #define SOC_SPI_SUPPORT_OCT 1
/*-------------------------- LP SPI CAPS ----------------------------------------*/
#define SOC_LP_SPI_MAXIMUM_BUFFER_SIZE 64
/*-------------------------- SPIRAM CAPS ----------------------------------------*/ /*-------------------------- SPIRAM CAPS ----------------------------------------*/
#define SOC_SPIRAM_XIP_SUPPORTED 1 #define SOC_SPIRAM_XIP_SUPPORTED 1
@@ -183,6 +183,10 @@ config SOC_LP_I2C_SUPPORTED
bool bool
default y default y
config SOC_LP_SPI_SUPPORTED
bool
default y
config SOC_SPIRAM_SUPPORTED config SOC_SPIRAM_SUPPORTED
bool bool
default y default y
@@ -83,7 +83,7 @@
#define SOC_LP_GPIO_MATRIX_SUPPORTED 1 #define SOC_LP_GPIO_MATRIX_SUPPORTED 1
#define SOC_LP_PERIPHERALS_SUPPORTED 1 #define SOC_LP_PERIPHERALS_SUPPORTED 1
#define SOC_LP_I2C_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_SPIRAM_SUPPORTED 1
#define SOC_PSRAM_DMA_CAPABLE 1 #define SOC_PSRAM_DMA_CAPABLE 1
// #define SOC_SDMMC_HOST_SUPPORTED 1 // TODO: [ESP32S31] IDF-14705 // #define SOC_SDMMC_HOST_SUPPORTED 1 // TODO: [ESP32S31] IDF-14705
@@ -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 * SPDX-License-Identifier: Apache-2.0 OR MIT
*/ */
@@ -813,244 +813,19 @@ typedef union {
} lp_spi_sleep_conf1_reg_t; } lp_spi_sleep_conf1_reg_t;
/** Group: LP SPI W0 REG */ /** Group: LP SPI Wn REG */
/** Type of spi_w0 register /** Type of spi_wn register
* SPI CPU-controlled buffer0 * SPI CPU-controlled buffer0
*/ */
typedef union { typedef union {
struct { struct {
/** reg_buf0 : R/W/SS; bitpos: [31:0]; default: 0; /** reg_buf : R/W/SS; bitpos: [31:0]; default: 0;
* data buffer * data buffer
*/ */
uint32_t reg_buf0:32; uint32_t reg_buf:32;
}; };
uint32_t val; uint32_t val;
} lp_spi_w0_reg_t; } lp_spi_wn_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;
/** Group: LP SPI SLAVE REG */ /** Group: LP SPI SLAVE REG */
@@ -1238,22 +1013,7 @@ typedef struct {
volatile lp_spi_sleep_conf1_reg_t spi_sleep_conf1; volatile lp_spi_sleep_conf1_reg_t spi_sleep_conf1;
volatile lp_spi_dma_int_set_reg_t spi_dma_int_set; volatile lp_spi_dma_int_set_reg_t spi_dma_int_set;
uint32_t reserved_050[18]; uint32_t reserved_050[18];
volatile lp_spi_w0_reg_t spi_w0; volatile lp_spi_wn_reg_t data_buf[16];
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;
uint32_t reserved_0d8[2]; uint32_t reserved_0d8[2];
volatile lp_spi_slave_reg_t spi_slave; volatile lp_spi_slave_reg_t spi_slave;
volatile lp_spi_slave1_reg_t spi_slave1; volatile lp_spi_slave1_reg_t spi_slave1;
@@ -1265,6 +1025,7 @@ typedef struct {
volatile lp_rnd_eco_high_reg_t rnd_eco_high; volatile lp_rnd_eco_high_reg_t rnd_eco_high;
} lp_dev_t; } lp_dev_t;
extern lp_dev_t LP_SPI;
#ifndef __cplusplus #ifndef __cplusplus
_Static_assert(sizeof(lp_dev_t) == 0x100, "Invalid size of lp_dev_t structure"); _Static_assert(sizeof(lp_dev_t) == 0x100, "Invalid size of lp_dev_t structure");
+47 -54
View File
@@ -13,18 +13,16 @@
#include "esp_err.h" #include "esp_err.h"
#include "ulp_lp_core_spi.h" #include "ulp_lp_core_spi.h"
#include "ulp_lp_core_utils.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 */ static lp_spi_ll_dev_t *lp_spi_dev = LP_SPI_LL_GET_HW();
lp_spi_dev_t *lp_spi_dev = &LP_SPI;
static inline esp_err_t lp_core_spi_wait_for_interrupt(int32_t cycles_to_wait) 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(); 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)) { if (ulp_lp_core_is_timeout_elapsed(timeout_start, cycles_to_wait)) {
/* Clear interrupt bits */ lp_spi_ll_clear_int_trans_done(lp_spi_dev);
lp_spi_dev->spi_dma_int_clr.reg_trans_done_int_clr = 1;
return ESP_ERR_TIMEOUT; 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 */ /* Reset the Tx and Rx FIFOs */
lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 1; lp_spi_ll_reset_fifos(lp_spi_dev);
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. /* Clear any previous interrupts.
* Note: LP SPI does not have any DMA access but the interrupt bit lives in the DMA interrupt register. * 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 */ /* 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; return ESP_ERR_INVALID_STATE;
} }
/* Configure dummy bits */ /* 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) { 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 */ /* 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) { if (trans_desc->command_bits) {
lp_spi_dev->spi_user2.reg_usr_command_bitlen = trans_desc->command_bits - 1; lp_spi_ll_set_command_bitlen(lp_spi_dev, trans_desc->command_bits);
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_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 */ /* 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) { if (trans_desc->address_bits) {
lp_spi_dev->spi_user1.reg_usr_addr_bitlen = trans_desc->address_bits - 1; lp_spi_ll_set_address_bitlen(lp_spi_dev, 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_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 */ /* Set data lines */
lp_spi_dev->spi_user.reg_usr_mosi = 1; lp_spi_ll_set_mosi_en(lp_spi_dev, true);
lp_spi_dev->spi_user.reg_usr_miso = trans_desc->rx_buffer ? 1 : 0; lp_spi_ll_set_miso_en(lp_spi_dev, trans_desc->rx_buffer != NULL);
/* Configure the transaction bit length */ /* Configure the transaction bit length */
int tx_bitlen = trans_desc->tx_length * 8; 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 */ /* Prepare the data to be transmitted */
uint32_t tx_idx = 0; 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 * 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. * 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 uint8_t max_data_chunk_size = max_data_reg_num * 4; // 60
while (tx_idx < trans_desc->tx_length) { while (tx_idx < trans_desc->tx_length) {
/* Store 4 bytes of data in the data buffer registers serially. */ /* 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; 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 */ /* 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) { if ((tx_idx >= trans_desc->tx_length) || (tx_idx % max_data_chunk_size) == 0) {
/* Apply the configuration */ /* Apply the configuration */
lp_spi_dev->spi_cmd.reg_update = 1; lp_spi_ll_apply_config(lp_spi_dev);
while (lp_spi_dev->spi_cmd.reg_update) {
;
}
/* Start the transaction */ /* 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 */ /* Wait for the transaction to complete */
ret = lp_core_spi_wait_for_interrupt(cycles_to_wait); 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 */ /* 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 */ /* Fetch the received data if an Rx buffer is provided */
if (trans_desc->rx_buffer != NULL) { if (trans_desc->rx_buffer != NULL) {
while (rx_idx < tx_idx) { 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; rx_idx += 4;
// This loop would exit even if we haven't received all the data. // 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 */ /* Reset the Tx and Rx FIFOs */
lp_spi_dev->spi_dma_conf.reg_rx_afifo_rst = 1; lp_spi_ll_reset_fifos(lp_spi_dev);
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. /* Clear any previous interrupts.
* Note: LP SPI does not have any DMA access but the interrupt bit lives in the DMA interrupt register. * 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 */ /* Set data lines */
lp_spi_dev->spi_user.reg_usr_mosi = 1; lp_spi_ll_set_mosi_en(lp_spi_dev, true);
lp_spi_dev->spi_user.reg_usr_miso = 1; lp_spi_ll_set_miso_en(lp_spi_dev, true);
/* Configure the transaction bit length */ /* Configure the transaction bit length */
int rx_bitlen = trans_desc->rx_length * 8; 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 */ /* Prepare the data to be received */
uint32_t rx_idx = 0; 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. * driver to receive up to 64 bytes of data at a time.
*/ */
uint32_t length_in_bytes = trans_desc->rx_length; 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 */ /* 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) { 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 */ /* 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; rcvd_length_in_bytes = (rcvd_bitlen + 7) / 8;
/* Read the received data */ /* Read the received data */
while (rx_idx < rcvd_length_in_bytes) { 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; rx_idx += 4;
} }
/* Clear the transaction done interrupt */ /* 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 */ /* Prepare data for transmission if a Tx buffer is provided */
if (trans_desc->tx_buffer != NULL) { if (trans_desc->tx_buffer != NULL) {
uint32_t tx_idx = 0; uint32_t tx_idx = 0;
uint32_t length_in_bytes = trans_desc->tx_length; 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 */ /* 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) { while (tx_idx < length_in_bytes) {
/* Store 4 bytes of data in the data buffer registers serially. */ /* 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; tx_idx += 4;
} }
/* Apply the configuration */ /* Apply the configuration */
lp_spi_dev->spi_cmd.reg_update = 1; lp_spi_ll_apply_config(lp_spi_dev);
while (lp_spi_dev->spi_cmd.reg_update) {
;
}
/* Start the transaction */ /* 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 */ /* Wait for the transaction to complete */
ret = lp_core_spi_wait_for_interrupt(cycles_to_wait); 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 */ /* 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; return ret;
+55 -68
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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -10,17 +10,15 @@
#include "driver/lp_io.h" #include "driver/lp_io.h"
#include "hal/rtc_io_types.h" #include "hal/rtc_io_types.h"
#include "include/lp_core_spi.h" #include "include/lp_core_spi.h"
#include "soc/lp_spi_struct.h"
#include "soc/lp_gpio_sig_map.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/periph_ctrl.h"
#include "esp_private/esp_clk_tree_common.h" #include "esp_private/esp_clk_tree_common.h"
#include "hal/spi_ll.h" #include "hal/spi_ll.h"
static const char *LP_SPI_TAG = "lp_spi"; static const char *LP_SPI_TAG = "lp_spi";
/* Use the LP SPI register structure to access peripheral registers */ static lp_spi_ll_dev_t *lp_spi_dev = LP_SPI_LL_GET_HW();
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) 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) static void lp_spi_enable_clock_gate(void)
{ {
lpperi_dev_t *lp_peri_dev = &LPPERI;
PERIPH_RCC_ATOMIC() { PERIPH_RCC_ATOMIC() {
(void)__DECLARE_RCC_ATOMIC_ENV; // Avoid warnings for unused variable __DECLARE_RCC_ATOMIC_ENV (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. */ /* 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_clock_val_t spi_clock;
spi_ll_master_cal_clock(max_clock_source_hz, dev_config->clock_speed_hz, duty_cycle, &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; return ret;
} }
static void lp_spi_master_init(void) 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 */ /* Clear Slave mode to enable Master mode */
lp_spi_dev->spi_slave.reg_slave_mode = 0; lp_spi_ll_set_slave_mode(lp_spi_dev, false);
lp_spi_dev->spi_slave.reg_clk_mode = 0; lp_spi_ll_set_slave_clk_mode(lp_spi_dev, 0);
/* Reset CS timing */ /* Reset CS timing */
lp_spi_dev->spi_user1.reg_cs_setup_time = 0; lp_spi_ll_reset_cs_timing(lp_spi_dev);
lp_spi_dev->spi_user1.reg_cs_hold_time = 0;
/* Use all 64 bytes of the Tx/Rx buffers in CPU controlled transfer */ /* 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_ll_disable_highpart(lp_spi_dev);
lp_spi_dev->spi_user.reg_usr_miso_highpart = 0;
} }
static void lp_spi_slave_init(void) static void lp_spi_slave_init(void)
{ {
/* Set Slave mode */ /* 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 */ /* Reset the SPI peripheral */
lp_spi_dev->spi_slave.reg_soft_reset = 1; lp_spi_ll_soft_reset(lp_spi_dev);
lp_spi_dev->spi_slave.reg_soft_reset = 0;
/* Configure slave */ /* Configure slave */
lp_spi_dev->spi_clock.val = 0; lp_spi_ll_reset_slave_regs(lp_spi_dev);
lp_spi_dev->spi_user.val = 0; lp_spi_ll_set_full_duplex(lp_spi_dev, true);
lp_spi_dev->spi_ctrl.val = 0; lp_spi_ll_set_sio_mode(lp_spi_dev, false);
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 */ /* 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_ll_disable_highpart(lp_spi_dev);
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) static void lp_spi_master_setup_device(const lp_spi_device_config_t *dev_config)
{ {
/* Configure transmission bit order */ /* 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_ll_set_bit_order(lp_spi_dev,
lp_spi_dev->spi_ctrl.reg_wr_bit_order = dev_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0; (dev_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST) != 0,
(dev_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST) != 0);
/* Configure SPI mode in master mode */ /* Configure SPI mode in master mode */
if (dev_config->spi_mode == 0) { if (dev_config->spi_mode == 0) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 0; lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false);
lp_spi_dev->spi_user.reg_ck_out_edge = 0; lp_spi_ll_set_ck_out_edge(lp_spi_dev, false);
} else if (dev_config->spi_mode == 1) { } else if (dev_config->spi_mode == 1) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 0; lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false);
lp_spi_dev->spi_user.reg_ck_out_edge = 1; lp_spi_ll_set_ck_out_edge(lp_spi_dev, true);
} else if (dev_config->spi_mode == 2) { } else if (dev_config->spi_mode == 2) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 1; lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true);
lp_spi_dev->spi_user.reg_ck_out_edge = 1; lp_spi_ll_set_ck_out_edge(lp_spi_dev, true);
} else if (dev_config->spi_mode == 3) { } else if (dev_config->spi_mode == 3) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 1; lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true);
lp_spi_dev->spi_user.reg_ck_out_edge = 0; lp_spi_ll_set_ck_out_edge(lp_spi_dev, false);
} }
/* Configure the polarity of the CS line */ /* 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 */ /* 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 */ /* 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 */ /* 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_ll_set_cs_setup(lp_spi_dev,
lp_spi_dev->spi_user.reg_cs_setup = dev_config->cs_ena_pretrans ? 1 : 0; dev_config->cs_ena_pretrans != 0,
lp_spi_dev->spi_user1.reg_cs_hold_time = dev_config->cs_ena_posttrans; dev_config->cs_ena_pretrans == 0 ? 0 : dev_config->cs_ena_pretrans - 1);
lp_spi_dev->spi_user.reg_cs_hold = dev_config->cs_ena_posttrans ? 1 : 0; lp_spi_ll_set_cs_hold(lp_spi_dev,
dev_config->cs_ena_posttrans != 0,
dev_config->cs_ena_posttrans);
/* Select the CS pin */ /* 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) static void lp_spi_slave_setup_device(const lp_spi_slave_config_t *slave_config)
{ {
/* Configure transmission bit order */ /* 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_ll_set_bit_order(lp_spi_dev,
lp_spi_dev->spi_ctrl.reg_wr_bit_order = slave_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0; (slave_config->flags & LP_SPI_DEVICE_RXBIT_LSBFIRST) != 0,
(slave_config->flags & LP_SPI_DEVICE_TXBIT_LSBFIRST) != 0);
/* Configure SPI mode in slave mode */ /* Configure SPI mode in slave mode */
if (slave_config->spi_mode == 0) { if (slave_config->spi_mode == 0) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 0; lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false);
lp_spi_dev->spi_user.reg_rsck_i_edge = 0; lp_spi_ll_set_slave_clk_edges(lp_spi_dev, false, false);
lp_spi_dev->spi_user.reg_tsck_i_edge = 0; lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, false);
lp_spi_dev->spi_slave.reg_clk_mode_13 = 0;
} else if (slave_config->spi_mode == 1) { } else if (slave_config->spi_mode == 1) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 0; lp_spi_ll_set_ck_idle_edge(lp_spi_dev, false);
lp_spi_dev->spi_user.reg_rsck_i_edge = 1; lp_spi_ll_set_slave_clk_edges(lp_spi_dev, true, true);
lp_spi_dev->spi_user.reg_tsck_i_edge = 1; lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, true);
lp_spi_dev->spi_slave.reg_clk_mode_13 = 1;
} else if (slave_config->spi_mode == 2) { } else if (slave_config->spi_mode == 2) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 1; lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true);
lp_spi_dev->spi_user.reg_rsck_i_edge = 1; lp_spi_ll_set_slave_clk_edges(lp_spi_dev, true, true);
lp_spi_dev->spi_user.reg_tsck_i_edge = 1; lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, false);
lp_spi_dev->spi_slave.reg_clk_mode_13 = 0;
} else if (slave_config->spi_mode == 3) { } else if (slave_config->spi_mode == 3) {
lp_spi_dev->spi_misc.reg_ck_idle_edge = 1; lp_spi_ll_set_ck_idle_edge(lp_spi_dev, true);
lp_spi_dev->spi_user.reg_rsck_i_edge = 0; lp_spi_ll_set_slave_clk_edges(lp_spi_dev, false, false);
lp_spi_dev->spi_user.reg_tsck_i_edge = 0; lp_spi_ll_set_slave_clk_mode_13(lp_spi_dev, true);
lp_spi_dev->spi_slave.reg_clk_mode_13 = 1;
} }
if (slave_config->flags & LP_SPI_DEVICE_CS_ACTIVE_HIGH) { 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."); 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) { 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."); 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 */ /* 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 */ /* Select the CS pin */
lp_spi_dev->spi_misc.reg_cs0_dis = 0; lp_spi_ll_enable_cs0(lp_spi_dev);
} }
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////
@@ -25,7 +25,11 @@ static const char* TAG = "lp_core_spi_test";
#define TEST_GPIO_PIN_MISO 6 #define TEST_GPIO_PIN_MISO 6
#define TEST_GPIO_PIN_MOSI 7 #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 #define TEST_GPIO_PIN_CLK 8
#endif
#define TEST_GPIO_PIN_CS 4 #define TEST_GPIO_PIN_CS 4
#define TEST_DATA_LEN_BYTES 42 #define TEST_DATA_LEN_BYTES 42
+2
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.h \
$(PROJECT_PATH)/components/bt/include/esp32s31/include/esp_bt_vs.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_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/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/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_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_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_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_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/ulp/ulp_common/include/ulp_common.h \
+2 -1
View File
@@ -1,7 +1,7 @@
| Supported Targets | ESP32-P4 | | 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.) (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) | | | SDI(MISO) | SDO(MOSI) | SCK | CSB (CS) |
| ----------------------- | ----------| ----------| ----- | -------- | | ----------------------- | ----------| ----------| ----- | -------- |
| ESP32-P4 LP SPI Master | GPIO6 | GPIO7 | GPIO8 | GPIO4 | | ESP32-P4 LP SPI Master | GPIO6 | GPIO7 | GPIO8 | GPIO4 |
| ESP32-S31 LP SPI Master | GPIO6 | GPIO7 | GPIO3 | GPIO4 |
### Build and Flash ### Build and Flash
@@ -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 * 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_MOSI_PIN 7
#define LP_SPI_MISO_PIN 6 #define LP_SPI_MISO_PIN 6
#if CONFIG_IDF_TARGET_ESP32S31
#define LP_SPI_SCLK_PIN 3
#else
#define LP_SPI_SCLK_PIN 8 #define LP_SPI_SCLK_PIN 8
#endif
#define LP_SPI_CS_PIN 4 #define LP_SPI_CS_PIN 4
#define LP_CORE_WAKEUP_PERIOD_US 1*1000*1000 #define LP_CORE_WAKEUP_PERIOD_US 1*1000*1000
@@ -58,7 +62,7 @@ static void lp_spi_init(void)
/* Base LP SPI device settings */ /* Base LP SPI device settings */
lp_spi_device_config_t device = { lp_spi_device_config_t device = {
.cs_io_num = LP_SPI_CS_PIN, .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 .duty_cycle = 128, // 50% duty cycle
}; };
@@ -79,7 +83,7 @@ void app_main(void)
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
uint32_t causes = esp_sleep_get_wakeup_causes(); 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("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); printf("Temperature %.2f degree celsius, humidity %.2f%%RH\n", ulp_temperature / 100.0, ulp_humidity / 1024.0);
} else { } else {