IDF寫的還挺好,我覺得,這么多芯片可以縫縫補補的用一個SDK,牛的咧。
這個是最常見的宏定義
單次觸發的單元體
這個絕對是里面最常見的宏了
該宏定義接受以下參數:
a:要檢查的條件表達式。
err_code:在條件為假時返回的錯誤代碼。
goto_tag:條件為假時要跳轉到的標簽位置。
log_tag:用于記錄日志的標簽位置(可選)。
format:格式化字符串(可選)。
...:可變參數列表(可選)。
該宏的作用是執行以下操作:
如果提供了log_tag,則將其值打印到日志中。
檢查條件a是否為假。如果為假,則執行以下操作:
將錯誤代碼賦值給變量ret。
使用goto_tag跳轉到指定標簽位置。
這個宏通常用于在條件為假時進行錯誤處理或跳轉到特定的代碼塊。
在這里是錯誤的代碼的處理處
連著就2個
這是一個條件判斷的宏定義,用于檢查 init_config 和 ret_unit 是否為空指針。如果其中任何一個為空指針,則跳轉到指定的標簽位置 err,并打印錯誤信息 "invalid argument: null pointer"。
具體解釋如下:
init_config && ret_unit:這是一個邏輯與操作,用于檢查init_config和ret_unit是否都非空。如果兩者都非空,則表達式的結果為真;否則為假。
ESP_ERR_INVALID_ARG:這是錯誤代碼常量,表示參數無效的錯誤。
err:這是要跳轉到的標簽位置。
TAG:這是要打印日志的標簽位置。
"invalid argument: null pointer":這是要打印的錯誤信息字符串。
當 init_config 或 ret_unit 為空指針時,執行以下操作:
如果提供了TAG,則將其值打印到日志中。
將錯誤代碼賦值給變量ret。
使用err跳轉到指定標簽位置。
這個宏的作用是在參數無效時進行錯誤處理,并將錯誤信息打印到日志中。
這是一個C語言的函數,名為heap_caps_calloc。這個函數用于在指定的內存堆上分配一塊連續的、指定大小的內存空間,并初始化為0。
參數解析:
n:需要分配的元素數量。
size:每個元素的大?。ㄒ宰止潪閱挝唬?。
caps:內存分配器的容量屬性。這通常用于指定內存分配器可以處理的最大對象大小。
函數執行流程:
調用heap_caps_calloc_base(n, size, caps)函數嘗試在指定的內存堆上分配n * size字節的內存空間,并返回一個指向這塊內存的指針。
如果分配失?。磒tr為NULL),并且size大于0,那么調用heap_caps_alloc_failed(size, caps, __func__)函數報告內存分配失敗。
最后,返回分配的內存塊的指針。
這是一個C語言的函數,名為heap_caps_calloc_base。這個函數用于在指定的內存堆上分配一塊連續的、指定大小的內存空間,并初始化為0。
參數解析:
n:需要分配的元素數量。
size:每個元素的大?。ㄒ宰止潪閱挝唬?。
caps:內存分配器的容量屬性。這通常用于指定內存分配器可以處理的最大對象大小。
函數執行流程:
首先,函數檢查n * size是否會導致乘法溢出。如果會導致溢出,那么函數返回NULL,表示內存分配失敗。
這是通過調用__builtin_mul_overflow(n, size, &size_bytes)來實現的,這個內建函數會檢查乘法操作是否會導致溢出,如果會導致溢出,那么它會把溢出的大小存儲在size_bytes中,并返回非零值。
如果n * size不會導致溢出,那么函數就使用calloc函數來分配內存。calloc函數接受兩個參數:需要分配的元素數量和每個元素的大小,然后返回一個指向分配的內存的指針。
最后,函數返回calloc函數返回的指針。
讓我們來看看這個函數的樣子
這是一個C語言的函數,名為_calloc_r。這個函數是C標準庫函數calloc的一個包裝器,它用于在指定的內存堆上分配一塊連續的、指定大小的內存空間,并初始化為0。
參數解析:
struct _reent *r:一個指向_reent結構體的指針,這個結構體通常用于封裝某些與環境相關的信息。在這個函數中,我們并沒有使用到這個參數。
size_t nmemb:需要分配的元素數量。
size_t size:每個元素的大?。ㄒ宰止潪閱挝唬?/p>
函數執行流程:
首先,函數檢查nmemb * size是否會導致乘法溢出。如果會導致溢出,那么函數返回NULL,表示內存分配失敗。這是通過調用__builtin_mul_overflow(nmemb, size, &size_bytes)來實現的,這個內建函數會檢查乘法操作是否會導致溢出,如果會導致溢出,那么它會把溢出的大小存儲在size_bytes中,并返回非零值。
然后,函數調用heap_caps_malloc_default(size_bytes)來分配一塊內存。這個函數接受一個參數,表示需要分配的字節數,然后返回一個指向分配的內存的指針。
如果內存分配成功(即result != NULL),函數使用bzero(result, size_bytes)來將這塊內存的所有字節都設置為0。
最后,函數返回指向分配的內存的指針。
這是一個C語言的語句,它調用了之前定義的heap_caps_calloc函數來分配一塊內存,并將這塊內存的地址賦值給變量unit。
參數解析:
1:需要分配的元素數量。在這里,我們只分配一個元素,所以這個值是1。
sizeof(adc_oneshot_unit_ctx_t):每個元素的大小。這里,我們使用sizeof操作符來獲取adc_oneshot_unit_ctx_t類型數據的大?。ㄒ宰止潪閱挝唬?,然后把它作為calloc函數的第二個參數。這意味著我們想要分配一塊能夠存儲一個adc_oneshot_unit_ctx_t類型數據的內存。
ADC_MEM_ALLOC_CAPS:內存分配器的容量屬性。這通常用于指定內存分配器可以處理的最大對象大小。
在這里,我們使用ADC_MEM_ALLOC_CAPS作為這個參數,表示我們希望使用能夠處理最大對象大小為ADC_MEM_ALLOC_CAPS的內存分配器來分配這塊內存。
函數執行流程:
調用heap_caps_calloc函數,傳入參數1、sizeof(adc_oneshot_unit_ctx_t)和ADC_MEM_ALLOC_CAPS。
如果分配成功,heap_caps_calloc函數會返回一個指向分配的內存的指針,我們將這個指針賦值給變量unit。
如果分配失敗,heap_caps_calloc函數會返回NULL,我們將不會得到任何結果。
看出來沒有,每一個語句都要進行一次校驗。
解析:
_lock_acquire(&s_ctx.mutex);:這行代碼獲取名為s_ctx.mutex的互斥鎖,以確保在初始化過程中只有一個線程可以訪問該代碼塊。
s_ctx.units[init_config->unit_id] = unit;:這行代碼將unit指針存儲在s_ctx.units數組中,數組索引為init_config->unit_id。這意味著根據配置中的單元ID,將特定的單元與上下文關聯起來。
_lock_release(&s_ctx.mutex);:這行代碼釋放之前獲取的互斥鎖,允許其他線程訪問被保護的代碼塊。
unit->unit_id = init_config->unit_id;:這行代碼將單元的ID設置為配置中指定的單元ID,確保單元的唯一性。
unit->ulp_mode = init_config->ulp_mode;:這行代碼將單元的ULP(單位長度脈沖)模式設置為配置中指定的ULP模式,用于控制ADC的采樣率。
總結:這段代碼片段展示了一個使用互斥鎖保護的ADC單觸發模式的初始化過程,其中通過將單元ID和ULP模式與相應的配置關聯起來來初始化單元。
這些都是內聯函數
實現在C
這是一個C語言的代碼片段,它定義了一個名為_lock_acquire的函數,該函數用于獲取一個互斥鎖。這個函數是兼容舊版newlib鎖函數的。
解析:
typedef int _lock_t;:這行代碼定義了一個新的類型別名_lock_t,它是一個整數類型。
void IRAM_ATTR _lock_acquire(_lock_t *lock):這行代碼定義了一個函數_lock_acquire,它接受一個指向_lock_t類型的指針lock作為參數。IRAM_ATTR是一個編譯器指令,表示這個函數是"in-ram"屬性的,即它在編譯時會被嵌入到程序的ROM中,而不是在運行時從外部存儲器加載。
{ lock_acquire_generic(lock, portMAX_DELAY, queueQUEUE_TYPE_MUTEX); }:這行代碼調用了lock_acquire_generic函數,嘗試獲取由lock指向的互斥鎖。portMAX_DELAY和queueQUEUE_TYPE_MUTEX是傳遞給lock_acquire_generic函數的參數,它們可能是指定等待時間(以微秒為單位)和鎖的類型(在這個例子中是互斥鎖)。
這個是屬于我一樣看不懂的東西,但是大概率是一個去取值復制
這是我上次寫的單次觸發結構體,真尼瑪老母豬帶胸罩,一套一套的
那么這個結構體就是給了一個新變量吧
這個ADC單次觸發的內容?但是是這個是S_CTX的發源地
也就是最上面的這個結構體
這段代碼定義了一個名為adc_oneshot_ctx_t的結構體,用于存儲ADC(模數轉換器)單觸發模式的上下文信息。
解析:
typedef struct adc_oneshot_ctx_t:這是一個類型定義,將結構體adc_oneshot_ctx_t定義為一個新的類型名,以便在后續的代碼中引用。
{ _lock_t mutex; ... }:這是結構體的成員列表,列出了該結構體包含的所有成員變量。
adc_oneshot_unit_ctx_t *units[SOC_ADC_PERIPH_NUM];:這是一個指向adc_oneshot_unit_ctx_t類型的指針數組,用于存儲ADC單元的上下文信息。SOC_ADC_PERIPH_NUM是一個宏定義,表示ADC外設的數量。
int apb_periph_ref_cnts;:這是一個整型變量,用于記錄使用APB_SARADC外設的ADC單觸發模式芯片的引用計數。
看這個,其實就是線程安全的寫法
獲取這個結構體里面的mutex
這段代碼是將一個單元(unit)對象存儲到s_ctx.units數組中,數組的索引由init_config->unit_id指定。
根據代碼片段提供的信息,我們可以推斷出以下內容:
s_ctx是一個全局變量或結構體的成員變量,它包含了與ADC單觸發模式相關的上下文信息。
units是s_ctx中的一個成員變量,它是一個指針數組,用于存儲ADC單元的上下文信息。
init_config是一個指向初始化配置結構體的指針,其中包含了要存儲的單元的ID和相關配置信息。
unit_id是init_config中的一個成員變量,它指定了要存儲的單元的ID。
unit是要存儲的單元對象。
根據上述分析,該代碼的作用是將單元對象存儲到s_ctx.units數組中,以便在后續的操作中使用。
unit->unit_id = init_config->unit_id;:這行代碼將init_config中的unit_id值賦給unit對象的unit_id成員變量。
unit->ulp_mode = init_config->ulp_mode;:這行代碼將init_config中的ulp_mode值賦給unit對象的ulp_mode成員變量。
這段代碼定義了一個名為clk_src的變量,類型為adc_oneshot_clk_src_t,并將其初始化為ADC_DIGI_CLK_SRC_DEFAULT。
然后,代碼檢查init_config結構體中的clk_src成員變量是否存在有效的值。如果存在有效值,則將clk_src變量的值更新為init_config->clk_src。
接著就是設置時鐘以及看看沒有設置成功
里面的這個代碼呢是在每個芯片里面都有的
來看參數
第一個是soc的clk模塊
這段代碼定義了一個枚舉類型soc_module_clk_t,用于表示不同的時鐘源。以下是每個時鐘源的含義:
SOC_MOD_CLK_CPU:CPU時鐘可以從XTAL、PLL、RC_FAST或APLL中獲取,通過配置soc_cpu_clk_src_t來指定。
SOC_MOD_CLK_RTC_FAST:RTC快速時鐘可以從XTAL_D4或RC_FAST中獲取,通過配置soc_rtc_fast_clk_src_t來指定。
SOC_MOD_CLK_RTC_SLOW:RTC慢速時鐘可以從RC_SLOW、XTAL32K或RC_FAST_D256中獲取,通過配置soc_rtc_slow_clk_src_t來指定。
SOC_MOD_CLK_APB:APB時鐘高度依賴于CPU時鐘源。
SOC_MOD_CLK_PLL_D2:PLL_D2時鐘從PLL派生,具有固定的分頻器為2。
SOC_MOD_CLK_PLL_F160M:PLL_F160M時鐘從PLL派生,具有固定的頻率為160MHz。
SOC_MOD_CLK_XTAL32K:XTAL32K時鐘來自外部32kHz晶體,將時鐘門控傳遞給外設。
SOC_MOD_CLK_RC_FAST:RC_FAST時鐘來自內部8MHz rc振蕩器,將時鐘門控傳遞給外設。
SOC_MOD_CLK_RC_FAST_D256:RC_FAST_D256時鐘來自內部8MHz rc振蕩器,分頻器為256,將時鐘門控傳遞給外設。
SOC_MOD_CLK_XTAL:XTAL時鐘來自外部晶體(2~40MHz)。
SOC_MOD_CLK_REF_TICK:REF_TICK從APB派生,即使APB頻率改變,其頻率也固定為1MHz。
SOC_MOD_CLK_APLL:APLL從PLL獲取,其頻率可以通過APLL配置寄存器進行配置。
SOC_MOD_CLK_INVALID:表示可用模塊時鐘源的結束。
這個枚舉類型用于在系統中確定和配置各個模塊的時鐘源。
這段代碼定義了一個枚舉類型esp_clk_tree_src_freq_precision_t,用于表示不同的時鐘源精度。以下是每個精度的含義:
ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED:從驅動程序緩存的數據中獲取值;如果數據為0,則執行校準操作。
ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX:獲取其近似頻率值。
ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT:始終執行校準操作。
ESP_CLK_TREE_SRC_FREQ_PRECISION_INVALID:無效的精度級別。
這個枚舉類型用于在系統中確定和配置各個模塊的時鐘源精度。
其實著才開始配置最后一個,ULP
隨意看吧
大概就是所有結構體都要拋頭露面
終于來了初始化了
這段代碼定義了一個名為adc_oneshot_hal_ctx_t的結構體,用于表示ADC(模數轉換器)的單次觸發模式的硬件抽象層(HAL)上下文。
以下是每個成員的解釋:
dev:ADC SoC(系統級芯片)層的句柄,用于與ADC SoC進行交互。
unit:表示ADC單元的變量。
work_mode:表示ADC的工作模式,可能是連續模式、單次觸發模式等。
chan_configs:表示每個ADC通道的配置數組。
clk_src:表示時鐘源的選擇。
clk_src_freq_hz:表示時鐘源的頻率,以Hz為單位
ADC的初始化
這段代碼片段展示了一個名為adc1_handle的變量,類型為adc_oneshot_unit_handle_t。它被用于存儲ADC(模數轉換器)單元的句柄。
接下來,代碼定義了一個名為init_config1的結構體變量,類型為adc_oneshot_unit_init_cfg_t。這個結構體包含了ADC單元的配置信息。在這個例子中,unit_id成員被設置為ADC_UNIT_1,表示當前配置的是第一個ADC單元。
這段代碼的作用是初始化ADC單元1,并將其句柄存儲在adc1_handle變量中。
設置 ADC 的初始配置后,使用adc_oneshot_new_unit()準備好的adc_oneshot_unit_init_cfg_t.如果分配成功,該函數將返回 ADC 單元句柄。
通道的設置
擴展通道的時候衰減系數
位寬
狠狠的注入
折騰這么久就是為了這個read,我可真想死
寫完就跑
一個好的函數從參數開始
搞對象都沒有這么費勁
進入現場,保護一下
下面就是要轉換了
函數接受三個參數:
handle:指向adc_oneshot_unit_handle_t類型的指針,表示ADC單元的句柄。
chan:表示要讀取的ADC通道。
out_raw:指向整數類型的指針,用于存儲轉換結果的原始值。
函數返回一個esp_err_t類型的錯誤碼,表示操作的結果。如果操作成功,返回ESP_OK;否則,返回相應的錯誤碼。
函數的具體實現如下:
首先,檢查輸入參數的有效性。如果handle或out_raw為空指針,或者chan超出了有效范圍,將返回相應的錯誤碼。
然后,嘗試獲取ADC單元的鎖。如果獲取失敗,返回ESP_ERR_TIMEOUT。
進入臨界區,使用rtc_spinlock進行保護。
調用adc_oneshot_hal_setup函數,設置ADC單元的相關參數。
如果支持ADC校準功能(版本1),則獲取當前通道的衰減值,并初始化ADC硬件校準。
調用adc_oneshot_hal_convert函數,執行實際的ADC轉換操作,并將結果存儲在out_raw中。
退出臨界區,釋放ADC單元的鎖。
根據轉換結果的有效性,返回相應的錯誤碼。
差不多就是這樣,獲取參數以后來判斷,在操作系統的監督下進行一個安全的操作,然后就釋放資源。
這個就是轉換函數,感覺又是一篇文章,死了
這段代碼是一個函數定義,函數名為adc_oneshot_hal_convert。它的作用是執行ADC(模數轉換器)的單次轉換操作,并返回轉換結果的有效性。
函數接受兩個參數:
hal:指向adc_oneshot_hal_ctx_t類型的指針,表示ADC單元的上下文信息。
out_raw:指向整數類型的指針,用于存儲轉換結果的原始值。
函數返回一個布爾值,表示操作的結果。如果操作成功,返回true;否則,返回false。
函數的具體實現如下:
首先,根據hal->unit的值確定要使用的ADC通道。如果hal->unit等于ADC_UNIT_1,則使用通道1;否則,使用通道2。
清除指定通道的事件標志位。
禁用所有ADC單元。
啟用指定的ADC通道。
啟動ADC單元的單次轉換操作,設置時鐘源頻率為hal->clk_src_freq_hz。
等待直到指定通道的事件被觸發。
讀取轉換結果的原始值,并將其存儲在out_raw中。
如果ADC單元的數量為2(即SOC_ADC_PERIPH_NUM == 2),則對轉換結果進行進一步的校驗。如果校驗失敗,將out_raw設置為-1。
再次禁用所有ADC單元。
返回轉換結果的有效性(valid)
看著簡單,但是實現確實復雜
最終的數據在這里獲取
@brief宏強制在外設寄存器上進行32位讀取
@注:這個宏只能在xxx結構體的注冊域上調用。當前實現讀入uint32_t類型。
最后居然是怎么個東西
在寄存器上干活了
也可以開啟這個功能
說實話我沒有看懂
不用你操心的原因是因為別人已經操心過了
這些地方是ADC的封裝處
ADC 數字控制器模式配置
ADC數字控制器(DMA模式)輸出數據格式。用于分析采集到的ADC(DMA)數據。
上面我說不明白的結構體在文檔里面都有
寫程序試試呢?
審核編輯:劉清
-
adc
+關注
關注
99文章
6606瀏覽量
547542 -
C語言
+關注
關注
180文章
7624瀏覽量
139487 -
分配器
+關注
關注
0文章
202瀏覽量
26048 -
ESP
+關注
關注
0文章
187瀏覽量
34457 -
觸發器
+關注
關注
14文章
2029瀏覽量
61716
原文標題:?ESP32-S3 ADC外設.2-單次觸發模式
文章出處:【微信號:TT1827652464,微信公眾號:云深之無跡】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
ESP32-S3芯片與ESP32及ESP32-S2比較好在哪里呢
淺談ESP32-C3與ESP32-S3芯片
合宙ESP32-S3開發板特性解讀
啟明去端分享| ESP32-S3如何實現tcp_client和tcp_server

新品上市 | 合宙ESP32-S3開發板

基于ESP32-S3的高性能開發板介紹

評論