下面的內容編寫時間跨度有點大,亂了得一團,也沒ai整理。食之無味,棄之可惜。
推薦筆記:ESP32 之 ESP-IDF 教學(十八)—— 組件配置(KConfig)
推薦筆記:Kconfig 拓展
樂鑫組件庫,網頁在線。
一、準備工程
- 先重新弄一個工程,也算是復習一下上一節課的內容。這次換一種方式創建。
- 選擇模板
- 最后結果如下圖. 編譯下載監聽調試都沒有問題。再開始下一步。
二、添加或創建組件
1) 添加組件
- 樂鑫官方有一個在線庫,可以方便 添加組件 ,使用以下指令打開主頁。
ESP-IDF: 樂鑫組件注冊表
ESP-IDF: Show ESP Component Registry
- 添加 點燈組件
led_strip
和 按鈕組件button
,注意版本,不同版本直接差別可能較大!!! - 兩個組件的官方介紹頁面為:LED 指示燈 和 按鍵 。
- 官方組件添加很方便,直接下載即可。編譯沒有問題。
- 雖然只添加了兩個組件,但是下載了三個,其中一個應該是依賴,被一同下載了。
- 官方組件是不允許修改的,每次編譯都會檢查和確保哈希值。一般創建自己的組件,然后依賴,在其基礎上實現功能或改動。
應該是不建議直接復制一份然后修改。
2) 創建組件
- 下面使用vscode的一鍵 創建組件 功能,快速弄兩個模板組件。
- 如何就可以看到大致結構如下,創建兩個組件實現對提示燈和按鍵的測試或調用。
- 創建多一些測試文件,最后結構如下:
自動創建的
.h
頭文件居然沒有ifndef
宏定義保護。我個人還是喜歡加保護。這樣以防萬一。在個人工程里,嵌套調用還是很頻繁的。
- 往代碼里填寫一些測試內容,按鈕的測試內容如下,閃燈的內容類似。
- 注意
CMakeLists.txt
簡單理解,添加編譯路徑,指定參與編譯的文件。
/* 以下是 CMakeLists.txt 內容 */
idf_component_register(SRCS "button_test.c" "examples/button_examples.c" INCLUDE_DIRS "include")
// 因為 CMakeLists.txt 中沒有添加 examples 文件路徑, 所以下面使用相對路徑/* 以下是 button_examples.c 內容 */
#include <stdio.h>
#include "button_examples.h"
#include "esp_log.h"static const char *TAG = "button_examples.c";void button_examples_func(void)
{ESP_LOGI(TAG, "button_examples_func Start!");}/* 以下是 button_test.c 內容 */
#include <stdio.h>
#include "button_test.h"
#include "examples/button_examples.h" // 注意這里是使用了相對路徑
#include "esp_log.h" // 組件默認包含了 esp-idf 庫, 直接引用static const char *TAG = "button_test.c";void button_test_func(void)
{ESP_LOGI(TAG, "button_test_func Start!");button_examples_func();
}/* 以下是 main.c 內容 */
#include <stdio.h>
#include "esp_log.h"#include "led_test.h"
#include "button_test.h"
// #include "examples/button_examples.h" 無法導入 button_examples.h 文件,起到隔離保護作用const char *TAG = "main.c";void app_main(void)
{ESP_LOGI(TAG, "app_main Start!");led_test_func();button_test_func();
}/* 編譯運行監聽內容如下 */
I (236) main_task: Started on CPU0
I (246) main_task: Calling app_main()
I (246) main.c: app_main Start!
I (246) led_test.c: led_test_func Start!
I (246) led_examples.c: led_examples_func Start!
I (256) button_test.c: button_test_func Start!
I (256) button_examples.c: button_examples_func Start!
I (266) main_task: Returned from app_main()
四、組件路徑依賴
本文只介紹 ESP-IDF 推薦的規范格式,有一些非規范的操作也可以靈活實現目的,并不推薦也不介紹。
1) 組件路徑/目錄
-
ESP-IDF
默認僅從以下位置查找組件:$IDF_PATH/components/
(ESP-IDF 內置組件,如driver
、esp_wifi
等)$PROJECT_PATH/components/
(當前項目的components
目錄)$PROJECT_PATH/managed_components/
(當前項目的managed_components
目錄)
-
有一些第三方組件(開發板的驅動),不合適重復放在工程中,也不能放在 ESP-IDF安裝目錄里,就可以在
CMakeLists.txt
中顯式設置EXTRA_COMPONENT_DIRS
,該定義默認是空,不會添加任何額外的組件目錄。
# ? 必須在 項目根目錄的 CMakeLists.txt 中設置
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) # 必須放在 set 之前list(APPEND EXTRA_COMPONENT_DIRS # 額外組件路徑(可以是絕對路徑或相對于項目目錄的路徑)# "../shared_components" # 上級目錄的共享組件# "C:/your/custom_components" # 絕對路徑組件目錄
)# ? EXTRA_COMPONENT_DIRS 必須在這里設置(在 project() 之前)
project(hello_component) # 初始化 ESP-IDF 項目
- 重名組件會被替換,優先級從小到大,和局部變量類似。
- 初始編譯時所有組件都會被編譯,所以需要很久。
2) 組件依賴
- 重點:永遠通過組件名而非路徑建立依賴關系,這是 ESP-IDF 模塊化設計的核心原則!
- 組件之間的依賴通過
CMakeLists.txt
修改。 - 例如上例中,按鈕組件依賴閃燈組件,就可以導入閃燈組件的頭文件,調用函數。
idf_component_register(SRCS "led_test.c" // 單獨指定每一個參與編譯的 .cINCLUDE_DIRS "include" // 只限定對外開放 這個文件夾內的頭文件# REQUIRES # button_test # 明確依賴的組件名(非路徑)PRIV_REQUIRES button_test # 僅當前組件內部使用,對外不可見# "$($ENV{IDF_TARGET})" # 根據目標芯片選擇依賴,相當于可配置更改)
- 頭文件不添加在
INCLUDE_DIRS
: 用相對路徑,作用域僅限于當前組件內部。 - 頭文件添加在
INCLUDE_DIRS
: 將路徑變為公共 API,允許跨組件直接引用。 - 黃金法則:最小化暴露原則——僅將必要的頭文件路徑加入
INCLUDE_DIRS
,內部實現細節用相對路徑隔離。
- 為什么 .c 文件需要逐個指定? .c 文件在編譯時會經歷以下過程:
- 編譯(Compile):每個 .c 文件都會獨立編譯成對象文件(.o)。
- 鏈接(Link):所有 .o 文件合并生成最終的庫或固件。
- 因此需要 顯式列出每個 .c 文件;
- 避免誤編譯:確保只編譯需要參與構建的源文件(而不是目錄下所有文件)。
- 精確控制依賴:可以針對特定 .c 文件設置編譯選項(如 CFLAGS)。
- 構建系統優化:僅重新編譯修改過的 .c 文件。
五、菜單配置項
問DS:為什么esp中,有Kconfig和Kconfig.projbuild兩種不同的格式?
在 ESP-IDF 開發框架中,Kconfig
和 Kconfig.projbuild
是兩種不同用途的配置文件格式,它們的主要區別在于 作用范圍 和 設計目的。以下是詳細解釋:
1. Kconfig
(組件級配置)
- 作用對象:用于 單個組件(Component) 內部的配置。
- 功能:定義組件自身需要的可配置選項(如硬件參數、功能開關等)。
- 文件位置:位于組件的根目錄(如
components/your_component/Kconfig
)。 - 特點:
- 僅在組件的編譯過程中生效。
- 選項對其他組件不可見(除非顯式暴露)。
- 遵循 ESP-IDF 的組件隔離原則,保證模塊化。
示例場景:
比如一個 SPI 驅動組件可能需要配置默認時鐘頻率,此時會在 Kconfig
中定義:
config SPI_CLOCK_SPEEDint "SPI clock speed (Hz)"default 1000000helpSet the SPI master clock frequency.
2. Kconfig.projbuild
(項目級全局配置)
- 作用對象:影響 整個項目 或 多個組件 的全局配置。
- 功能:覆蓋或擴展項目范圍的配置(如 SDK 工具鏈選項、系統級參數)。
- 文件位置:可存在于組件目錄或項目根目錄(優先級高于普通
Kconfig
)。 - 特點:
- 配置選項會直接出現在頂層
menuconfig
菜單中。 - 可能破壞組件隔離性,需謹慎使用。
- 通常用于硬件適配或特殊需求(如切換目標芯片型號)。
- 配置選項會直接出現在頂層
示例場景:
項目需要強制指定所有組件的日志級別,可以在 Kconfig.projbuild
中:
config LOG_DEFAULT_LEVELint "Global log level"default 3range 0 4
關鍵區別總結
特性 | Kconfig | Kconfig.projbuild |
---|---|---|
作用范圍 | 組件內部 | 整個項目全局 |
使用場景 | 組件私有配置 | 跨組件或系統級配置 |
隔離性 | 嚴格隔離 | 可能影響其他組件 |
推薦使用頻率 | 高頻(標準做法) | 低頻(特殊需求) |
為什么設計兩種格式?
-
模塊化需求:
ESP-IDF 強調組件化開發,Kconfig
確保每個組件的配置獨立,避免命名沖突。 -
靈活性的平衡:
Kconfig.projbuild
為特殊場景(如硬件適配、全局優化)提供“后門”,但需手動啟用。 -
優先級控制:
當兩者沖突時,Kconfig.projbuild
的配置會覆蓋普通Kconfig
,方便項目定制。
最佳實踐建議
- 優先使用
Kconfig
:保持組件解耦。 - 慎用
Kconfig.projbuild
:僅在需要全局覆蓋時使用,并添加詳細注釋說明。 - 命名約定:在組件中避免定義全局選項(如以
CONFIG_
開頭的選項應謹慎)。
通過這種設計,ESP-IDF 實現了配置的靈活性和模塊化的平衡。
- 修改
Kconfig
文件,一定要刪除build
文件才會同步sdkmenuconfig
菜單,否則最多只是刷新了宏定義,不會刷新菜單選項。 - 嘗試修改例程,添加以下測試代碼。分別添加全局和組件配置,然后刷新菜單。就能看到宏定義被創建,菜單也出現選項。
/* main/Kconfig.projbuild 文件內容 */
menu "Example Configuration main.c"config MAIN_TESTint "main.c test"range 10 3600000default 1000helpDefine the main test.endmenu/* components/button_test/Kconfig 文件內容 */
menu "Example Configuration button.c"config BUTTON_TESTint "button.c test"range 10 3600000default 1000helpDefine the button test.endmenu/* components/led_test/Kconfig 文件內容 */
menu "Example Configuration led.c"config LED_TESTint "led.c test"range 10 3600000default 1000helpDefine the led test.endmenu
main
配置菜單出現在一級菜單,而組件配置菜單位于Component config
下面的二級菜單。- 如果希望本地組件的菜單在
main
菜單下面,可以修改為以下內容。可以輸入中文,但終端顯示錯位,VSCode
中就能顯示正常。
menu "Example Configuration main.c"rsource "../components/button_test/Kconfig" # 相對路徑rsource "../components/led_test/Kconfig"config MAIN_TESTint "main.c test 中文" # 名字 range 10 3600000default 1000helpmain.c test 中文. # 幫助endmenu
問 DS:
source
、rsource
和orsource
的區別?
以下內容貌似瞎說的,我試了而一下覺得路徑沒成功,相對路徑成功了。而另一個工程使用source
搭配相對路徑也成功了。所以有點疑惑。
在 Kconfig 系統中,source
、rsource
和 orsource
是用于導入其他 Kconfig 文件的命令,但它們的行為和用途有所不同。以下是它們的詳細解釋和區別:
1. source
- 功能:從指定的路徑導入另一個 Kconfig 文件。
- 路徑解析規則:
- 絕對路徑:直接使用給定的路徑(如
source "/path/to/Kconfig"
)。 - 相對路徑:路徑是**相對于
$srctree
(源碼根目錄)**解析的,而不是當前文件所在目錄。
- 絕對路徑:直接使用給定的路徑(如
- 典型用途:
用于導入項目中或 ESP-IDF 內置的組件 Kconfig 文件。 - 失敗行為:
如果文件不存在,會報錯并終止解析(如你遇到的問題)。
2. rsource
- 功能:相對路徑導入(Relative-source),路徑是相對于當前 Kconfig 文件所在目錄解析的。
- 路徑解析規則:
相對路徑基于當前文件的位置,而非$srctree
。
(這是與source
的關鍵區別!) - 典型用途:
當需要從當前組件目錄中導入子模塊的 Kconfig 文件時。 - 失敗行為:
文件不存在時會報錯。
3. orsource
- 功能:可選導入(Optional-source),如果文件不存在,則靜默忽略,不會報錯。
- 路徑解析規則:
與source
相同(相對于$srctree
)。 - 典型用途:
導入可能不存在的可選配置文件,避免因文件缺失導致構建失敗。 - 失敗行為:
文件不存在時不會報錯。
三者的對比總結
命令 | 路徑解析基準 | 文件不存在時的行為 | 典型場景 |
---|---|---|---|
source | $srctree | 報錯并終止 | 強制依賴的組件或核心配置 |
rsource | 當前文件目錄 | 報錯并終止 | 組件內部的子模塊配置 |
orsource | $srctree | 靜默忽略 | 可選的自定義或平臺特定配置 |
- 最后,如果修改了配置文件,為了避免每次重復配置,可以使用指令
idf.py save-defconfig
保 存當前的已修改配置,留作下次使用。
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 5.4.1 Project Minimal Configuration
#
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_BUTTON_TEST=999
CONFIG_LED_TEST=666
CONFIG_MAIN_TEST=555
六、實戰
- 將組件的示例內容實現,路徑類似:
managed_components\espressif__button\test_apps\main
。里面有多個例子,拷貝出來進行仿制,然后對組件進行二次打包,作為個人組件方便調用。搭配Kconfig
配置就更加規范了。
- 提一句按鈕的使用,單個按鍵沒啥好講的,主要是adc按鍵,使用
excel
表格方便計算,舉例3000mV,分成5個按鈕。 - 參考例程給的思路,分成
n+1
,已知所需電壓,假設上拉電阻R0,計算理論R1~R5,然后查電阻本尋找合適的電阻,然后再計算實際觸發電壓,然后編寫注冊按鈕的程序。