Merge branch 'change/bu_lp_i2c_c5' into 'master'

change(ulp): bring up lp i2c on esp32c5

Closes IDF-8634

See merge request espressif/esp-idf!32142
This commit is contained in:
Liu Xiao Yu
2024-08-05 16:58:20 +08:00
10 changed files with 147 additions and 28 deletions

View File

@ -19,6 +19,8 @@
#include "soc/pcr_struct.h" #include "soc/pcr_struct.h"
#include "hal/i2c_types.h" #include "hal/i2c_types.h"
#include "soc/clk_tree_defs.h" #include "soc/clk_tree_defs.h"
#include "soc/lp_clkrst_struct.h"
#include "soc/lpperi_struct.h"
#include "hal/misc.h" #include "hal/misc.h"
#ifdef __cplusplus #ifdef __cplusplus
@ -69,7 +71,7 @@ typedef enum {
} i2c_ll_slave_intr_t; } i2c_ll_slave_intr_t;
// Get the I2C hardware instance // Get the I2C hardware instance
#define I2C_LL_GET_HW(i2c_num) (&I2C0) #define I2C_LL_GET_HW(i2c_num) (((i2c_num) == I2C_NUM_0) ? (&I2C0) : (&LP_I2C))
#define I2C_LL_MASTER_EVENT_INTR (I2C_NACK_INT_ENA_M|I2C_TIME_OUT_INT_ENA_M|I2C_TRANS_COMPLETE_INT_ENA_M|I2C_ARBITRATION_LOST_INT_ENA_M|I2C_END_DETECT_INT_ENA_M) #define I2C_LL_MASTER_EVENT_INTR (I2C_NACK_INT_ENA_M|I2C_TIME_OUT_INT_ENA_M|I2C_TRANS_COMPLETE_INT_ENA_M|I2C_ARBITRATION_LOST_INT_ENA_M|I2C_END_DETECT_INT_ENA_M)
#define I2C_LL_SLAVE_EVENT_INTR (I2C_TRANS_COMPLETE_INT_ENA_M|I2C_TXFIFO_WM_INT_ENA_M|I2C_RXFIFO_WM_INT_ENA_M | I2C_SLAVE_STRETCH_INT_ENA_M) #define I2C_LL_SLAVE_EVENT_INTR (I2C_TRANS_COMPLETE_INT_ENA_M|I2C_TXFIFO_WM_INT_ENA_M|I2C_RXFIFO_WM_INT_ENA_M | I2C_SLAVE_STRETCH_INT_ENA_M)
#define I2C_LL_SLAVE_RX_EVENT_INTR (I2C_TRANS_COMPLETE_INT_ENA_M | I2C_RXFIFO_WM_INT_ENA_M | I2C_SLAVE_STRETCH_INT_ENA_M) #define I2C_LL_SLAVE_RX_EVENT_INTR (I2C_TRANS_COMPLETE_INT_ENA_M | I2C_RXFIFO_WM_INT_ENA_M | I2C_SLAVE_STRETCH_INT_ENA_M)
@ -77,6 +79,14 @@ typedef enum {
#define I2C_LL_RESET_SLV_SCL_PULSE_NUM_DEFAULT (9) #define I2C_LL_RESET_SLV_SCL_PULSE_NUM_DEFAULT (9)
#define I2C_LL_SCL_WAIT_US_VAL_DEFAULT (2500) // Approximate value for SCL timeout regs (in us). #define I2C_LL_SCL_WAIT_US_VAL_DEFAULT (2500) // Approximate value for SCL timeout regs (in us).
// Record for Pins usage logs
#define LP_I2C_SCL_PIN_ERR_LOG "SCL pin can only be configured as GPIO#7"
#define LP_I2C_SDA_PIN_ERR_LOG "SDA pin can only be configured as GPIO#6"
#define LP_I2C_SDA_IOMUX_PAD 6
#define LP_I2C_SCL_IOMUX_PAD 7
/** /**
* @brief Calculate I2C bus frequency * @brief Calculate I2C bus frequency
* Note that the clock accuracy is affected by the external pull-up resistor, * Note that the clock accuracy is affected by the external pull-up resistor,
@ -752,6 +762,20 @@ static inline void i2c_ll_master_clr_bus(i2c_dev_t *hw, uint32_t slave_pulses)
hw->ctr.conf_upgate = 1; hw->ctr.conf_upgate = 1;
} }
/**
* @brief Set the ACK level that the I2C master must send when the Rx FIFO count has reached the threshold value.
* ack_level: 1 (NACK)
* ack_level: 0 (ACK)
*
* @param hw Beginning address of the peripheral registers
*
* @return None
*/
static inline void i2c_ll_master_rx_full_ack_level(i2c_dev_t *hw, int ack_level)
{
hw->ctr.rx_full_ack_level = ack_level;
}
/** /**
* @brief Set I2C source clock * @brief Set I2C source clock
* *
@ -762,10 +786,73 @@ static inline void i2c_ll_master_clr_bus(i2c_dev_t *hw, uint32_t slave_pulses)
*/ */
static inline void i2c_ll_set_source_clk(i2c_dev_t *hw, i2c_clock_source_t src_clk) static inline void i2c_ll_set_source_clk(i2c_dev_t *hw, i2c_clock_source_t src_clk)
{ {
if (hw == &LP_I2C) {
// Do nothing
return;
}
// src_clk : (1) for RTC_CLK, (0) for XTAL // src_clk : (1) for RTC_CLK, (0) for XTAL
PCR.i2c[0].i2c_sclk_conf.i2c_sclk_sel = (src_clk == I2C_CLK_SRC_RC_FAST) ? 1 : 0; PCR.i2c[0].i2c_sclk_conf.i2c_sclk_sel = (src_clk == I2C_CLK_SRC_RC_FAST) ? 1 : 0;
} }
/**
* @brief Set LP I2C source clock
*
* @param hw Address offset of the LP I2C peripheral registers
* @param src_clk Source clock for the LP I2C peripheral
*
* @return None
*/
static inline void lp_i2c_ll_set_source_clk(i2c_dev_t *hw, soc_periph_lp_i2c_clk_src_t src_clk)
{
(void)hw;
// src_clk : (0) for LP_FAST_CLK (RTC Fast), (1) for XTAL_D2_CLK
switch (src_clk) {
case LP_I2C_SCLK_LP_FAST:
LP_CLKRST.lpperi.lp_i2c_clk_sel = 0;
break;
case LP_I2C_SCLK_XTAL_D2:
LP_CLKRST.lpperi.lp_i2c_clk_sel = 1;
break;
default:
// Invalid source clock selected
abort();
}
}
/// LP_CLKRST.lpperi is a shared register, so this function must be used in an atomic way
#define lp_i2c_ll_set_source_clk(...) (void)__DECLARE_RCC_ATOMIC_ENV; lp_i2c_ll_set_source_clk(__VA_ARGS__)
/**
* @brief Enable bus clock for the LP I2C module
*
* @param hw_id LP I2C instance ID
* @param enable True to enable, False to disable
*/
static inline void _lp_i2c_ll_enable_bus_clock(int hw_id, bool enable)
{
(void)hw_id;
LPPERI.clk_en.lp_ext_i2c_ck_en = enable;
}
/// LPPERI.clk_en is a shared register, so this function must be used in an atomic way
#define lp_i2c_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; _lp_i2c_ll_enable_bus_clock(__VA_ARGS__)
/**
* @brief Reset LP I2C module
*
* @param hw_id LP I2C instance ID
*/
static inline void lp_i2c_ll_reset_register(int hw_id)
{
(void)hw_id;
LPPERI.reset_en.lp_ext_i2c_reset_en = 1;
LPPERI.reset_en.lp_ext_i2c_reset_en = 0;
}
/// LPPERI.reset_en is a shared register, so this function must be used in an atomic way
#define lp_i2c_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; lp_i2c_ll_reset_register(__VA_ARGS__)
/** /**
* @brief Enable I2C peripheral controller clock * @brief Enable I2C peripheral controller clock
* *
@ -774,7 +861,11 @@ static inline void i2c_ll_set_source_clk(i2c_dev_t *hw, i2c_clock_source_t src_c
*/ */
static inline void i2c_ll_enable_controller_clock(i2c_dev_t *hw, bool en) static inline void i2c_ll_enable_controller_clock(i2c_dev_t *hw, bool en)
{ {
(void)hw; if (hw == &LP_I2C) {
// Do nothing
return;
}
PCR.i2c[0].i2c_sclk_conf.i2c_sclk_en = en; PCR.i2c[0].i2c_sclk_conf.i2c_sclk_en = en;
} }

View File

@ -10,13 +10,35 @@
/* /*
Bunch of constants for every I2C peripheral: GPIO signals, irqs, hw addr of registers etc Bunch of constants for every I2C peripheral: GPIO signals, irqs, hw addr of registers etc
*/ */
typedef enum
{
LP_I2C_MUX_FUNC = 0,
LP_GPIO_MUX_FUNC = 1,
LP_IO_MUX_FUNC_NUM = 2,
LP_MUX_FUNC_NOT_USED = 0xFF,
} lp_io_mux_func_t;
static_assert(SOC_I2C_NUM == (SOC_HP_I2C_NUM + SOC_LP_I2C_NUM));
const i2c_signal_conn_t i2c_periph_signal[SOC_I2C_NUM] = { const i2c_signal_conn_t i2c_periph_signal[SOC_I2C_NUM] = {
/* I2C_NUM_0*/
{ {
.sda_out_sig = I2CEXT0_SDA_OUT_IDX, .sda_out_sig = I2CEXT0_SDA_OUT_IDX,
.sda_in_sig = I2CEXT0_SDA_IN_IDX, .sda_in_sig = I2CEXT0_SDA_IN_IDX,
.scl_out_sig = I2CEXT0_SCL_OUT_IDX, .scl_out_sig = I2CEXT0_SCL_OUT_IDX,
.scl_in_sig = I2CEXT0_SCL_IN_IDX, .scl_in_sig = I2CEXT0_SCL_IN_IDX,
.iomux_func = (uint8_t)LP_MUX_FUNC_NOT_USED,
.irq = ETS_I2C_EXT0_INTR_SOURCE, .irq = ETS_I2C_EXT0_INTR_SOURCE,
.module = PERIPH_I2C0_MODULE, .module = PERIPH_I2C0_MODULE,
}, },
/* LP_I2C_NUM_0*/
{
.sda_out_sig = 0,
.sda_in_sig = 0,
.scl_out_sig = 0,
.scl_in_sig = 0,
.iomux_func = (uint8_t)LP_I2C_MUX_FUNC,
.irq = ETS_LP_I2C_INTR_SOURCE,
.module = PERIPH_LP_I2C0_MODULE,
},
}; };

View File

@ -167,6 +167,10 @@ config SOC_LP_PERIPHERALS_SUPPORTED
bool bool
default y default y
config SOC_LP_I2C_SUPPORTED
bool
default y
config SOC_ULP_LP_UART_SUPPORTED config SOC_ULP_LP_UART_SUPPORTED
bool bool
default y default y
@ -509,7 +513,7 @@ config SOC_DEDIC_PERIPH_ALWAYS_ENABLE
config SOC_I2C_NUM config SOC_I2C_NUM
int int
default 1 default 2
config SOC_HP_I2C_NUM config SOC_HP_I2C_NUM
int int
@ -559,6 +563,14 @@ config SOC_I2C_SLAVE_SUPPORT_SLAVE_UNMATCH
bool bool
default y default y
config SOC_LP_I2C_NUM
int
default 1
config SOC_LP_I2C_FIFO_LEN
int
default 16
config SOC_I2S_NUM config SOC_I2S_NUM
int int
default 1 default 1

View File

@ -340,15 +340,15 @@ typedef enum { // TODO: [ESP32C5] IDF-8694, IDF-8696 (inherit from C6)
/** /**
* @brief Array initializer for all supported clock sources of LP_I2C * @brief Array initializer for all supported clock sources of LP_I2C
*/ */
#define SOC_LP_I2C_CLKS {SOC_MOD_CLK_RTC_FAST, SOC_MOD_CLK_XTAL_D2} #define SOC_LP_I2C_CLKS {SOC_MOD_CLK_RC_FAST, SOC_MOD_CLK_XTAL_D2}
/** /**
* @brief Type of LP_I2C clock source. * @brief Type of LP_I2C clock source.
*/ */
typedef enum { // TODO: [ESP32C5] IDF-8695 (inherit from C6) typedef enum { // TODO: [ESP32C5] IDF-8695 (inherit from C6)
LP_I2C_SCLK_LP_FAST = SOC_MOD_CLK_RTC_FAST, /*!< LP_I2C source clock is RTC_FAST */ LP_I2C_SCLK_LP_FAST = SOC_MOD_CLK_RC_FAST, /*!< LP_I2C source clock is RC_FAST */
LP_I2C_SCLK_XTAL_D2 = SOC_MOD_CLK_XTAL_D2, /*!< LP_I2C source clock is XTAL_D2 */ LP_I2C_SCLK_XTAL_D2 = SOC_MOD_CLK_XTAL_D2, /*!< LP_I2C source clock is XTAL_D2 */
LP_I2C_SCLK_DEFAULT = SOC_MOD_CLK_RTC_FAST, /*!< LP_I2C source clock default choice is RTC_FAST */ LP_I2C_SCLK_DEFAULT = SOC_MOD_CLK_RC_FAST, /*!< LP_I2C source clock default choice is RC_FAST */
} soc_periph_lp_i2c_clk_src_t; } soc_periph_lp_i2c_clk_src_t;
/////////////////////////////////////////////////SPI//////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////SPI////////////////////////////////////////////////////////////////////

View File

@ -1103,6 +1103,7 @@ typedef struct {
} i2c_dev_t; } i2c_dev_t;
extern i2c_dev_t I2C0; extern i2c_dev_t I2C0;
extern i2c_dev_t LP_I2C;
#ifndef __cplusplus #ifndef __cplusplus
_Static_assert(sizeof(i2c_dev_t) == 0x200, "Invalid size of i2c_dev_t structure"); _Static_assert(sizeof(i2c_dev_t) == 0x200, "Invalid size of i2c_dev_t structure");

View File

@ -63,7 +63,7 @@
#define SOC_LP_TIMER_SUPPORTED 1 #define SOC_LP_TIMER_SUPPORTED 1
// #define SOC_LP_AON_SUPPORTED 1 // TODO: [ESP32C5] IDF-8638 // #define SOC_LP_AON_SUPPORTED 1 // TODO: [ESP32C5] IDF-8638
#define SOC_LP_PERIPHERALS_SUPPORTED 1 #define SOC_LP_PERIPHERALS_SUPPORTED 1
// #define SOC_LP_I2C_SUPPORTED 1 // TODO: [ESP32C5] IDF-8634 #define SOC_LP_I2C_SUPPORTED 1
#define SOC_ULP_LP_UART_SUPPORTED 1 #define SOC_ULP_LP_UART_SUPPORTED 1
#define SOC_CLK_TREE_SUPPORTED 1 #define SOC_CLK_TREE_SUPPORTED 1
// #define SOC_ASSIST_DEBUG_SUPPORTED 1 // TODO: [ESP32C5] IDF-8663 // #define SOC_ASSIST_DEBUG_SUPPORTED 1 // TODO: [ESP32C5] IDF-8663
@ -243,8 +243,7 @@
#define SOC_DEDIC_PERIPH_ALWAYS_ENABLE (1) /*!< The dedicated GPIO (a.k.a. fast GPIO) is featured by some customized CPU instructions, which is always enabled */ #define SOC_DEDIC_PERIPH_ALWAYS_ENABLE (1) /*!< The dedicated GPIO (a.k.a. fast GPIO) is featured by some customized CPU instructions, which is always enabled */
/*-------------------------- I2C CAPS ----------------------------------------*/ /*-------------------------- I2C CAPS ----------------------------------------*/
// ESP32-C5 has 1 I2C #define SOC_I2C_NUM (2U)
#define SOC_I2C_NUM (1U)
#define SOC_HP_I2C_NUM (1U) #define SOC_HP_I2C_NUM (1U)
#define SOC_I2C_FIFO_LEN (32) /*!< I2C hardware FIFO depth */ #define SOC_I2C_FIFO_LEN (32) /*!< I2C hardware FIFO depth */
@ -264,9 +263,9 @@
/*-------------------------- LP_I2C CAPS -------------------------------------*/ /*-------------------------- LP_I2C CAPS -------------------------------------*/
// ESP32-C5 has 1 LP_I2C // ESP32-C5 has 1 LP_I2C
// #define SOC_LP_I2C_NUM (1U) #define SOC_LP_I2C_NUM (1U)
// #define SOC_LP_I2C_FIFO_LEN (16) /*!< LP_I2C hardware FIFO depth */ #define SOC_LP_I2C_FIFO_LEN (16) /*!< LP_I2C hardware FIFO depth */
/*-------------------------- I2S CAPS ----------------------------------------*/ /*-------------------------- I2S CAPS ----------------------------------------*/
#define SOC_I2S_NUM (1U) #define SOC_I2S_NUM (1U)

View File

@ -881,7 +881,7 @@ typedef struct lp_i2c_dev_t {
volatile lp_i2c_rxfifo_start_addr_reg_t rxfifo_start_addr; volatile lp_i2c_rxfifo_start_addr_reg_t rxfifo_start_addr;
} lp_i2c_dev_t; } lp_i2c_dev_t;
// We map the LP_I2C instance to the i2c_dev_t struct for convinience of using the same HAL/LL. See soc/i2c_struct.h // We map the LP_I2C instance to the i2c_dev_t struct for convenience of using the same HAL/LL. See soc/i2c_struct.h
//extern lp_i2c_dev_t LP_I2C; //extern lp_i2c_dev_t LP_I2C;
#ifndef __cplusplus #ifndef __cplusplus

View File

@ -14,12 +14,7 @@
static const char *LPI2C_TAG = "lp_core_i2c"; static const char *LPI2C_TAG = "lp_core_i2c";
#if !SOC_LP_GPIO_MATRIX_SUPPORTED #if SOC_LP_GPIO_MATRIX_SUPPORTED
#include "soc/lp_io_struct.h"
/* Use the register structure to access LP_IO module registers */
lp_io_dev_t *lp_io_dev = &LP_IO;
#else
#include "driver/lp_io.h" #include "driver/lp_io.h"
#include "soc/lp_gpio_sig_map.h" #include "soc/lp_gpio_sig_map.h"
#endif /* !SOC_LP_GPIO_MATRIX_SUPPORTED */ #endif /* !SOC_LP_GPIO_MATRIX_SUPPORTED */
@ -94,11 +89,9 @@ static esp_err_t lp_i2c_set_pin(const lp_core_i2c_cfg_t *cfg)
ESP_RETURN_ON_ERROR(lp_i2c_configure_io(sda_io_num, sda_pullup_en), LPI2C_TAG, "LP I2C SDA pin config failed"); ESP_RETURN_ON_ERROR(lp_i2c_configure_io(sda_io_num, sda_pullup_en), LPI2C_TAG, "LP I2C SDA pin config failed");
#if !SOC_LP_GPIO_MATRIX_SUPPORTED #if !SOC_LP_GPIO_MATRIX_SUPPORTED
/* Select LP I2C function for the SDA Pin */ const i2c_signal_conn_t *p_i2c_pin = &i2c_periph_signal[LP_I2C_NUM_0];
lp_io_dev->gpio[sda_io_num].mcu_sel = 1; ret = rtc_gpio_iomux_func_sel(sda_io_num, p_i2c_pin->iomux_func);
ret = rtc_gpio_iomux_func_sel(scl_io_num, p_i2c_pin->iomux_func);
/* Select LP I2C function for the SCL Pin */
lp_io_dev->gpio[scl_io_num].mcu_sel = 1;
#else #else
/* Connect the SDA pin of the LP_I2C peripheral to the LP_IO Matrix */ /* Connect the SDA pin of the LP_I2C peripheral to the LP_IO Matrix */
ret = lp_gpio_connect_out_signal(sda_io_num, LP_I2C_SDA_PAD_OUT_IDX, 0, 0); ret = lp_gpio_connect_out_signal(sda_io_num, LP_I2C_SDA_PAD_OUT_IDX, 0, 0);

View File

@ -301,7 +301,7 @@ examples/system/ulp/lp_core/interrupt:
examples/system/ulp/lp_core/lp_i2c: examples/system/ulp/lp_core/lp_i2c:
enable: enable:
- if: SOC_LP_I2C_SUPPORTED == 1 - if: SOC_LP_I2C_SUPPORTED == 1 and SOC_DEEP_SLEEP_SUPPORTED == 1
disable: disable:
- if: IDF_TARGET == "esp32p4" - if: IDF_TARGET == "esp32p4"
temporary: true temporary: true

View File

@ -7,13 +7,13 @@
## Overview ## Overview
This example demonstrates basic usage of the LP I2C driver from the LP core by reading to and writing from a sensor connected over I2C. This example demonstrates the basic usage of the LP I2C driver from the LP core by reading to and writing from a sensor connected over I2C.
## How to use example ## How to use example
### Hardware Required ### Hardware Required
To run this example, you should have a ESP32-C6 based development board as well as a BH1750 sensor. BH1750 is an ambient light sensor. More information about it can be found in the [BH1750 datasheet](https://www.mouser.com/datasheet/2/348/bh1750fvi-e-186247.pdf). To run this example, you should have an ESP based development board that supports the LP I2C peripheral on the LP Core as well as a BH1750 sensor. BH1750 is an ambient light sensor. More information about it can be found in the [BH1750 datasheet](https://www.mouser.com/datasheet/2/348/bh1750fvi-e-186247.pdf).
#### Pin Assignment: #### Pin Assignment:
@ -22,9 +22,10 @@ To run this example, you should have a ESP32-C6 based development board as well
| | SDA | SCL | | | SDA | SCL |
| ----------------------- | ------| ------| | ----------------------- | ------| ------|
| ESP32-C6 LP I2C Master | GPIO6 | GPIO7 | | ESP32-C6 LP I2C Master | GPIO6 | GPIO7 |
| ESP32-C5 LP I2C Master | GPIO6 | GPIO7 |
| BH1750 Sensor | SDA | SCL | | BH1750 Sensor | SDA | SCL |
**Note:** There's no need to add an external pull-up resistors for SDA/SCL pin, because the driver enables the internal pull-up resistors. **Note:** There's no need to add external pull-up resistors for SDA/SCL pin, because the driver enables the internal pull-up resistors.
### Build and Flash ### Build and Flash
@ -51,7 +52,7 @@ LP core woke up the main CPU
Lux = 3 Lux = 3
Entering deep sleep... Entering deep sleep...
(When the BH1750 sensor is exposed to a direct light source, the Lux value should be larger and the LP core should wakup the main CPU) (When the BH1750 sensor is exposed to a direct light source, the Lux value should be larger and the LP core should wakeup the main CPU)
LP core woke up the main CPU LP core woke up the main CPU
Lux = 1222 Lux = 1222