refactor(i2c): Make i2c hal layer independent

This commit is contained in:
C.S.M
2025-08-27 16:47:50 +08:00
parent ab2829d65f
commit 5ce39e8878
58 changed files with 125 additions and 43 deletions

View File

@@ -94,6 +94,7 @@
/components/esp_eth/ @esp-idf-codeowners/network /components/esp_eth/ @esp-idf-codeowners/network
/components/esp_event/ @esp-idf-codeowners/system /components/esp_event/ @esp-idf-codeowners/system
/components/esp_gdbstub/ @esp-idf-codeowners/debugging /components/esp_gdbstub/ @esp-idf-codeowners/debugging
/components/esp_hal_*/ @esp-idf-codeowners/peripherals
/components/esp_hid/ @esp-idf-codeowners/bluetooth /components/esp_hid/ @esp-idf-codeowners/bluetooth
/components/esp_http_client/ @esp-idf-codeowners/app-utilities /components/esp_http_client/ @esp-idf-codeowners/app-utilities
/components/esp_http_server/ @esp-idf-codeowners/app-utilities /components/esp_http_server/ @esp-idf-codeowners/app-utilities

View File

@@ -19,6 +19,7 @@ set(ldfragments "")
# I2C related source files # I2C related source files
if(CONFIG_SOC_I2C_SUPPORTED) if(CONFIG_SOC_I2C_SUPPORTED)
list(APPEND srcs "i2c/i2c.c") list(APPEND srcs "i2c/i2c.c")
list(APPEND ldfragments "i2c/linker.lf")
endif() endif()
# RMT legacy driver # RMT legacy driver

View File

@@ -0,0 +1,4 @@
[mapping:esp_hal_i2c]
archive: libesp_hal_i2c.a
entries:
i2c_hal_iram (noflash)

View File

@@ -15,6 +15,7 @@ if(CONFIG_SOC_I2C_SUPPORTED)
endif() endif()
set(requires esp_hal_i2c)
if(${target} STREQUAL "linux") if(${target} STREQUAL "linux")
set(priv_requires esp_ringbuf) set(priv_requires esp_ringbuf)
else() else()
@@ -23,6 +24,7 @@ endif()
idf_component_register(SRCS ${srcs} idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include} INCLUDE_DIRS ${include}
REQUIRES "${requires}"
PRIV_REQUIRES "${priv_requires}" PRIV_REQUIRES "${priv_requires}"
LDFRAGMENTS "linker.lf" LDFRAGMENTS "linker.lf"
) )

View File

@@ -10,7 +10,7 @@ entries:
i2c_master: s_i2c_start_end_command (noflash) i2c_master: s_i2c_start_end_command (noflash)
[mapping:i2c_hal] [mapping:i2c_hal]
archive: libhal.a archive: libesp_hal_i2c.a
entries: entries:
if I2C_ISR_IRAM_SAFE = y: if I2C_ISR_IRAM_SAFE = y:
i2c_hal: i2c_hal_master_trans_start (noflash) i2c_hal: i2c_hal_master_trans_start (noflash)

View File

@@ -0,0 +1,17 @@
idf_build_get_property(target IDF_TARGET)
list(APPEND includes "${target}/include")
list(APPEND includes "include")
set(srcs)
set(include "include")
set(target_folder "${target}")
# I2C related source files
if(CONFIG_SOC_I2C_SUPPORTED)
list(APPEND srcs "i2c_hal.c" "i2c_hal_iram.c" "${target_folder}/i2c_periph.c")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${includes}
REQUIRES soc hal)

View File

@@ -0,0 +1,9 @@
# `esp_hal_i2c`
⚠️ This HAL component is still under heavy development at the moment, so we don't guarantee the stability and backward-compatibility among versions.
The `esp_hal_i2c` component provides a **Hardware Abstraction Layer** of I2C for all targets supported by ESP-IDF.
In a broad sense, the HAL layer consists of two sub-layers: HAL (upper) and Low-Level(bottom). The HAL layer defines the steps and data that is required to operate a peripheral (e.g. initialization, start and stop). The low-level is a translation layer above the register files under the `soc` component, it only covers general conceptions to register configurations.
The functions in this file mainly provide hardware abstraction for IDF peripheral drivers. For advanced developers, the HAL layer functions can also be directly used to assist in implementing their own drivers. However, it needs to be mentioned again that the interfaces here do not guarantee stability.

View File

@@ -449,7 +449,6 @@ static inline void i2c_ll_get_sda_timing(i2c_dev_t *hw, int *sda_sample, int *sd
*sda_sample = hw->sda_sample.time; *sda_sample = hw->sda_sample.time;
} }
/** /**
* @brief Check if the I2C bus is busy * @brief Check if the I2C bus is busy
* *
@@ -557,7 +556,7 @@ __attribute__((always_inline))
static inline void i2c_ll_write_txfifo(i2c_dev_t *hw, const uint8_t *ptr, uint8_t len) static inline void i2c_ll_write_txfifo(i2c_dev_t *hw, const uint8_t *ptr, uint8_t len)
{ {
uint32_t fifo_addr = (hw == &I2C0) ? 0x6001301c : 0x6002701c; uint32_t fifo_addr = (hw == &I2C0) ? 0x6001301c : 0x6002701c;
for(int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
WRITE_PERI_REG(fifo_addr, ptr[i]); WRITE_PERI_REG(fifo_addr, ptr[i]);
} }
} }
@@ -574,7 +573,7 @@ static inline void i2c_ll_write_txfifo(i2c_dev_t *hw, const uint8_t *ptr, uint8_
__attribute__((always_inline)) __attribute__((always_inline))
static inline void i2c_ll_read_rxfifo(i2c_dev_t *hw, uint8_t *ptr, uint8_t len) static inline void i2c_ll_read_rxfifo(i2c_dev_t *hw, uint8_t *ptr, uint8_t len)
{ {
for(int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
// Known issue that hardware read fifo will cause data lose, (fifo pointer jump over a random address) // Known issue that hardware read fifo will cause data lose, (fifo pointer jump over a random address)
// use `DPORT_REG_READ` can avoid this issue. // use `DPORT_REG_READ` can avoid this issue.
ptr[i] = DPORT_REG_READ((uint32_t)&hw->fifo_data); ptr[i] = DPORT_REG_READ((uint32_t)&hw->fifo_data);
@@ -592,7 +591,7 @@ static inline void i2c_ll_read_rxfifo(i2c_dev_t *hw, uint8_t *ptr, uint8_t len)
*/ */
static inline void i2c_ll_master_set_filter(i2c_dev_t *hw, uint8_t filter_num) static inline void i2c_ll_master_set_filter(i2c_dev_t *hw, uint8_t filter_num)
{ {
if(filter_num > 0) { if (filter_num > 0) {
hw->scl_filter_cfg.thres = filter_num; hw->scl_filter_cfg.thres = filter_num;
hw->sda_filter_cfg.thres = filter_num; hw->sda_filter_cfg.thres = filter_num;
hw->scl_filter_cfg.en = 1; hw->scl_filter_cfg.en = 1;

View File

@@ -21,7 +21,6 @@
extern "C" { extern "C" {
#endif #endif
/** /**
* @brief I2C hardware cmd register fields. * @brief I2C hardware cmd register fields.
*/ */
@@ -177,7 +176,6 @@ static inline void i2c_ll_master_restore_clock_configurations(i2c_dev_t *hw, uin
// Not supported on ESP32S2 // Not supported on ESP32S2
} }
/** /**
* @brief Reset I2C txFIFO * @brief Reset I2C txFIFO
* *
@@ -1058,7 +1056,6 @@ static inline void i2c_ll_slave_get_event(i2c_dev_t *hw, i2c_intr_event_t *event
} }
} }
/** /**
* @brief Enable I2C master TX interrupt * @brief Enable I2C master TX interrupt
* *

View File

@@ -55,7 +55,7 @@ typedef struct {
uint16_t tout; /*!< I2C bus timeout period */ uint16_t tout; /*!< I2C bus timeout period */
} i2c_hal_clk_config_t; } i2c_hal_clk_config_t;
typedef enum{ typedef enum {
#if SOC_I2C_SUPPORT_SLAVE #if SOC_I2C_SUPPORT_SLAVE
I2C_MODE_SLAVE = 0, /*!< I2C slave mode */ I2C_MODE_SLAVE = 0, /*!< I2C slave mode */
#endif #endif
@@ -131,7 +131,6 @@ typedef soc_periph_lp_i2c_clk_src_t lp_i2c_clock_source_t;
typedef int i2c_clock_source_t; typedef int i2c_clock_source_t;
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -0,0 +1,3 @@
components/esp_hal_i2c/test_apps/hal_i2c:
disable:
- if: SOC_I2C_SUPPORTED != 1

View File

@@ -0,0 +1,26 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# Check G1 component dependencies using tools/cmake template
idf_build_set_property(__BUILD_COMPONENT_DEPGRAPH_ENABLED 1)
project(hal_i2c)
set(comp_deps_dot "${CMAKE_BINARY_DIR}/component_deps.dot")
idf_build_get_property(target IDF_TARGET)
execute_process(
COMMAND ${CMAKE_COMMAND} -E echo "Checking esp_hal_i2c dependency violations"
COMMAND python "${IDF_PATH}/tools/test_apps/system/g1_components/check_dependencies.py"
--component_deps_file ${comp_deps_dot}
--target ${target}
RESULT_VARIABLE result
)
if(NOT result EQUAL 0)
message(WARNING "Found esp_hal_i2c dependency violations. Please check G1 component dependencies.")
endif()

View File

@@ -1,2 +1,3 @@
idf_component_register(SRCS "hal_i2c.c" idf_component_register(SRCS "hal_i2c.c"
PRIV_REQUIRES esp_hal_i2c
INCLUDE_DIRS ".") INCLUDE_DIRS ".")

View File

@@ -28,7 +28,7 @@
static inline uint32_t time_get_us_by_ccount(uint32_t counter) static inline uint32_t time_get_us_by_ccount(uint32_t counter)
{ {
return counter/CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ; return counter / CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
} }
#define ACK_VALUE (0) #define ACK_VALUE (0)

View File

@@ -2,4 +2,4 @@ set(srcs "hal_i2c_main.c")
idf_component_register(SRCS ${srcs} idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "." INCLUDE_DIRS "."
PRIV_REQUIRES hal_i2c) PRIV_REQUIRES hal_i2c esp_hal_i2c)

View File

@@ -77,6 +77,9 @@ else()
# should be removable once using component init functions # should be removable once using component init functions
# link-time registration is used. # link-time registration is used.
bootloader_support esp_pm esp_usb_cdc_rom_console bootloader_support esp_pm esp_usb_cdc_rom_console
# [REFACTOR-TODO] Provide system hook to release dependency reversion.
# IDF-13980
esp_hal_i2c
LDFRAGMENTS "linker.lf" "app.lf") LDFRAGMENTS "linker.lf" "app.lf")
add_subdirectory(port) add_subdirectory(port)

View File

@@ -4,10 +4,6 @@ components/hal/test_apps/crypto:
- mbedtls - mbedtls
- esp_security - esp_security
components/hal/test_apps/hal_i2c:
disable:
- if: SOC_I2C_SUPPORTED != 1
components/hal/test_apps/hal_utils: components/hal/test_apps/hal_utils:
enable: enable:
- if: IDF_TARGET == "linux" - if: IDF_TARGET == "linux"

View File

@@ -102,10 +102,6 @@ elseif(NOT BOOTLOADER_BUILD)
list(APPEND srcs "ledc_hal.c" "ledc_hal_iram.c") list(APPEND srcs "ledc_hal.c" "ledc_hal_iram.c")
endif() endif()
if(CONFIG_SOC_I2C_SUPPORTED)
list(APPEND srcs "i2c_hal.c" "i2c_hal_iram.c")
endif()
if(CONFIG_SOC_I3C_MASTER_SUPPORTED) if(CONFIG_SOC_I3C_MASTER_SUPPORTED)
list(APPEND srcs "i3c_master_hal.c") list(APPEND srcs "i3c_master_hal.c")
endif() endif()

View File

@@ -7,8 +7,6 @@ entries:
cache_hal_esp32 (noflash) cache_hal_esp32 (noflash)
else: else:
cache_hal (noflash) cache_hal (noflash)
if SOC_I2C_SUPPORTED = y:
i2c_hal_iram (noflash)
if HAL_WDT_USE_ROM_IMPL = n: if HAL_WDT_USE_ROM_IMPL = n:
wdt_hal_iram (noflash) wdt_hal_iram (noflash)
if SOC_SYSTIMER_SUPPORTED = y && HAL_SYSTIMER_USE_ROM_IMPL = n: if SOC_SYSTIMER_SUPPORTED = y && HAL_SYSTIMER_USE_ROM_IMPL = n:

View File

@@ -1,8 +0,0 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(hal_i2c)

View File

@@ -124,10 +124,6 @@ if(CONFIG_SOC_I2S_SUPPORTED)
list(APPEND srcs "${target_folder}/i2s_periph.c") list(APPEND srcs "${target_folder}/i2s_periph.c")
endif() endif()
if(CONFIG_SOC_I2C_SUPPORTED)
list(APPEND srcs "${target_folder}/i2c_periph.c")
endif()
if(CONFIG_SOC_I3C_MASTER_SUPPORTED) if(CONFIG_SOC_I3C_MASTER_SUPPORTED)
list(APPEND srcs "${target_folder}/i3c_master_periph.c") list(APPEND srcs "${target_folder}/i3c_master_periph.c")
endif() endif()

View File

@@ -161,6 +161,7 @@ INPUT = \
$(PROJECT_PATH)/components/esp_eth/include/esp_eth.h \ $(PROJECT_PATH)/components/esp_eth/include/esp_eth.h \
$(PROJECT_PATH)/components/esp_event/include/esp_event_base.h \ $(PROJECT_PATH)/components/esp_event/include/esp_event_base.h \
$(PROJECT_PATH)/components/esp_event/include/esp_event.h \ $(PROJECT_PATH)/components/esp_event/include/esp_event.h \
$(PROJECT_PATH)/components/esp_hal_i2c/include/hal/i2c_types.h \
$(PROJECT_PATH)/components/esp_http_client/include/esp_http_client.h \ $(PROJECT_PATH)/components/esp_http_client/include/esp_http_client.h \
$(PROJECT_PATH)/components/esp_http_server/include/esp_http_server.h \ $(PROJECT_PATH)/components/esp_http_server/include/esp_http_server.h \
$(PROJECT_PATH)/components/esp_https_ota/include/esp_https_ota.h \ $(PROJECT_PATH)/components/esp_https_ota/include/esp_https_ota.h \
@@ -251,7 +252,6 @@ INPUT = \
$(PROJECT_PATH)/components/hal/include/hal/dac_types.h \ $(PROJECT_PATH)/components/hal/include/hal/dac_types.h \
$(PROJECT_PATH)/components/hal/include/hal/esp_flash_err.h \ $(PROJECT_PATH)/components/hal/include/hal/esp_flash_err.h \
$(PROJECT_PATH)/components/hal/include/hal/gpio_types.h \ $(PROJECT_PATH)/components/hal/include/hal/gpio_types.h \
$(PROJECT_PATH)/components/hal/include/hal/i2c_types.h \
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \ $(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
$(PROJECT_PATH)/components/hal/include/hal/lcd_types.h \ $(PROJECT_PATH)/components/hal/include/hal/lcd_types.h \
$(PROJECT_PATH)/components/hal/include/hal/ledc_types.h \ $(PROJECT_PATH)/components/hal/include/hal/ledc_types.h \

View File

@@ -30,7 +30,7 @@ Hardware abstraction in ESP-IDF is comprised of the following layers, ordered fr
- Hardware Abstraction Layer (HAL) - Hardware Abstraction Layer (HAL)
- Driver Layers - Driver Layers
The LL Layer, and HAL are entirely contained within the ``hal`` component. Each layer is dependent on the layer below it, i.e, driver depends on HAL, HAL depends on LL, LL depends on the register header files. Each hardware module has its own independent ``esp_hal_xxx`` component, which contains both the LL and the HAL of that module. Each layer is dependent on the layer below it, i.e, driver depends on HAL, HAL depends on LL, LL depends on the register header files.
For a particular peripheral ``xxx``, its hardware abstraction generally consists of the header files described in the table below. Files that are **Target Specific** have a separate implementation for each target, i.e., a separate copy for each chip. However, the ``#include`` directive is still target-independent, i.e., is the same for different targets, as the build system automatically includes the correct version of the header and source files. For a particular peripheral ``xxx``, its hardware abstraction generally consists of the header files described in the table below. Files that are **Target Specific** have a separate implementation for each target, i.e., a separate copy for each chip. However, the ``#include`` directive is still target-independent, i.e., is the same for different targets, as the build system automatically includes the correct version of the header and source files.

View File

@@ -682,4 +682,4 @@ API Reference
.. include-build-file:: inc/i2c_slave.inc .. include-build-file:: inc/i2c_slave.inc
.. include-build-file:: inc/components/esp_driver_i2c/include/driver/i2c_types.inc .. include-build-file:: inc/components/esp_driver_i2c/include/driver/i2c_types.inc
.. include-build-file:: inc/components/hal/include/hal/i2c_types.inc .. include-build-file:: inc/components/esp_hal_i2c/include/hal/i2c_types.inc

View File

@@ -30,7 +30,7 @@ ESP-IDF 的硬件抽象由以下层级各组成,从接近硬件的低层级抽
- 硬件抽象层 (HAL) - 硬件抽象层 (HAL)
- 驱动层 - 驱动层
LL 层和 HAL 完全包含在 ``hal`` 组件中,每一层都依赖于其下方的层级,即驱动层依赖于 HAL 层HAL 层依赖于 LL 层LL 层依赖于寄存器头文件。 每个硬件模块都有独立的 ``esp_hal_xxx`` 组件,其中同时包含该模块的 LL 层 和 HAL 层,每一层都依赖于其下方的层级,即驱动层依赖于 HAL 层HAL 层依赖于 LL 层LL 层依赖于寄存器头文件。
对于特定外设 ``xxx``,其硬件抽象通常由下表中的头文件组成。其中 **特定目标** 指的是文件对于不同目标(即芯片)有不同的实现。然而,对于不同的目标,``#include`` 指令相同,构建系统会自动包含正确版本的头文件和源文件。 对于特定外设 ``xxx``,其硬件抽象通常由下表中的头文件组成。其中 **特定目标** 指的是文件对于不同目标(即芯片)有不同的实现。然而,对于不同的目标,``#include`` 指令相同,构建系统会自动包含正确版本的头文件和源文件。

View File

@@ -682,4 +682,4 @@ API 参考
.. include-build-file:: inc/i2c_slave.inc .. include-build-file:: inc/i2c_slave.inc
.. include-build-file:: inc/components/esp_driver_i2c/include/driver/i2c_types.inc .. include-build-file:: inc/components/esp_driver_i2c/include/driver/i2c_types.inc
.. include-build-file:: inc/components/hal/include/hal/i2c_types.inc .. include-build-file:: inc/components/esp_hal_i2c/include/hal/i2c_types.inc

View File

@@ -18,6 +18,17 @@ set(extra_allowed_components
${CONFIG_IDF_TARGET_ARCH} ${CONFIG_IDF_TARGET_ARCH}
) )
# Since esp_hal_* components are split from original hal component, so we can allow they are g1 components.
# But in the future, esp_hal_* components should be removed from common build. IDF-13980
file(GLOB esp_hal_component_dirs "${idf_path}/components/esp_hal_*")
set(esp_hal_components "")
foreach(hal_dir ${esp_hal_component_dirs})
if(IS_DIRECTORY ${hal_dir} AND EXISTS "${hal_dir}/CMakeLists.txt")
get_filename_component(hal_name ${hal_dir} NAME)
list(APPEND esp_hal_components ${hal_name})
endif()
endforeach()
# These components are currently included into "G1" build, but shouldn't. # These components are currently included into "G1" build, but shouldn't.
# After removing the extra dependencies, remove the components from this list as well. # After removing the extra dependencies, remove the components from this list as well.
set(extra_components_which_shouldnt_be_included set(extra_components_which_shouldnt_be_included
@@ -104,6 +115,7 @@ set(expected_components
${COMPONENTS} ${COMPONENTS}
${extra_allowed_components} ${extra_allowed_components}
${extra_components_which_shouldnt_be_included} ${extra_components_which_shouldnt_be_included}
${esp_hal_components}
) )
list(SORT expected_components) list(SORT expected_components)

View File

@@ -1,9 +1,12 @@
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0 # SPDX-License-Identifier: Unlicense OR CC0-1.0
import argparse import argparse
import glob
import logging import logging
import os
g1_g0_components = [ # Base G1/G0 components (static list)
g1_g0_components_base = [
'hal', 'hal',
'cxx', 'cxx',
'esp_libc', 'esp_libc',
@@ -21,6 +24,33 @@ g1_g0_components = [
'esp_mm', 'esp_mm',
] ]
def get_all_esp_hal_components() -> list[str]:
"""Dynamically discover all esp_hal_* components"""
esp_hal_components = []
# Try to get IDF_PATH from environment
idf_path = os.environ.get('IDF_PATH')
if idf_path is None:
# Fallback: assume script is in IDF_PATH/tools/test_apps/system/g1_components/
script_dir = os.path.dirname(os.path.abspath(__file__))
idf_path = os.path.join(script_dir, '../../../../..')
components_dir = os.path.join(idf_path, 'components')
if os.path.exists(components_dir):
# Find all esp_hal_* directories
esp_hal_dirs = glob.glob(os.path.join(components_dir, 'esp_hal_*'))
for hal_dir in esp_hal_dirs:
if os.path.isdir(hal_dir):
component_name = os.path.basename(hal_dir)
esp_hal_components.append(component_name)
return sorted(esp_hal_components)
# Build complete G1/G0 components list (base + dynamic esp_hal_* components)
g1_g0_components = g1_g0_components_base + get_all_esp_hal_components()
# Global expected dependency violations that apply to all targets # Global expected dependency violations that apply to all targets
expected_dep_violations = { expected_dep_violations = {
'esp_system': ['esp_timer', 'bootloader_support', 'esp_pm', 'esp_usb_cdc_rom_console'], 'esp_system': ['esp_timer', 'bootloader_support', 'esp_pm', 'esp_usb_cdc_rom_console'],