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