本次我們來講一下ADC(Analog-to-Digital Converter)不是游戲里的AD Carry,我們將實現電池電壓的讀取。
ADC簡介
ADC的全稱為Analog-to-Digital Converter(模擬/數字轉換器)。
在單片機中傳輸的信號均為數字信號,通過離散的高低電平表示數字邏輯的 1 和 0,但是在現實的物理世界中只存在模擬信號,即連續變化的信號。將這些連續變化的信號——比如熱,光,聲音,速度通過各種傳感器轉化成連續的電信號,再通過 ADC 功能將連續的模擬信號轉化成離散的數字信號給單片機進行處理。
常見的ADC類型有并聯比較型、逐次逼近型。它們的特點如下
ADC有兩個重要參數,分辨率和轉換速率。
分辨率:每一個ADC模塊都會明確他的分辨率,通過bit來表示,一般的是8bit,10bit,12bit,16bit,24bit.bit越大,說明分辨率越高。
采樣率:采樣率就是ADC 采樣的速率。它是指在規定的時間內可以采集的次數,采樣率越高,采集到的點數就越多,那么對原始信號的還原率就越高。采樣率的單位是SPS(sample per sencond),每秒采樣次數。這個值越大,采樣速度越快。
下面簡單的講一下這兩種ADC的工作原理
并聯比較型
這個也是比較容易理解的一種。下圖為并聯比較型ADC的工作原理圖。
它分為分壓部分、比較部分、編碼部分。
一開始的一排電阻均為等阻值的電阻,它們將VREF(參考電壓)進行均分,也就代表這個該ADC的精度,電阻分壓后的電壓(Vr)我們將其接到比較器的。圖中V1為我們的待測電壓,當V1Vr時比較器輸出1。比如我們這個假設VREF為15V,V1為1.2V。那么在最下面的比較器中Vr為1V,V1>Vr,比較器輸出1,第二個比較器中Vr為3V,V1
之后我們在每個比較器后加上D觸發器,當我們把C(控制端)至1后,我們給D輸入1,則Q輸入1,輸入0,則Q輸出0。這里我們先給C1,此時比較器輸出的結果就被存儲到鎖存器中。然后再置0,那么無論我們的輸入電壓V1怎樣變,Q輸出的值都不變,也就保存了輸出值。這里我們改變控制C的速度很大程度決定了最大采樣率,當然最大采樣率還受比較器等器件最大頻率的限制。
但是這里輸出的還不是二進制數,因此我們需要后面加一個編碼器進行編碼。上面就是并聯比較型原理的簡單描述。
這里我們也可以看出,并聯比較型的缺點就是精度想要提升就必須增加相應的電阻、比較器、觸發器。這樣會增加物料成本與體積。但是優點就是速度很快。
逐級比較型
下圖為逐級比較型ADC的工作示意圖,我們使用的STM32F4也是使用的這種ADC。
它可以分為控制電路、數碼寄存器、D/A轉換器、電壓比較器四個部分
首先第一步在數碼寄存器進行編碼,將高位D2編碼置1,其他位置0,之后進入D/A轉換器,將編碼出來的數字量轉變為模擬量V0。之后V0和輸入電壓Vx到比較器進行比較。如果Vx大于V0,比較器輸出1,那么控制電路就會將高位D2鎖定不變,之后將D1置1進行第二輪比較。如果此時Vx還是大于V0我們也就說明了我們V0的值更加接近Vx。我們將D0置1繼續進行第三輪比較,比如Vx還是比V0大那么說明Vx大于了ADC的量程,如果Vx
這里給出另一個圖方便大家理解
與 1/2Vref 進行比較,Vin 大于 1/2Vref,則將第一位標記為 1
與 3/4Vref 進行比較,Vin 小于 3/4Vref,則將第二位標記為 0
與 5/8Vref 進行比較,Vin 小于 5/8Vref,則將第三位標記為 0。
圖中的 Vin 通過這個三位的 ADC 后輸出的結果為 100,轉換的結果為 1/2Vref。
這里我們看出由于它需要逐次逼近因此它的轉換速度是比較慢的。而且它的分辨率和采樣速度相互矛盾,分辨率越高,采樣速率就越低。
CubeMX配置
雖然這里bsp文件已經將我們需要的ADC1,ADC3開啟了,但是這里還是帶著大家學習一遍,便于大家使用其他STM32板子時快速配置。
首先還是看原理圖
這里可以看到電源ADC引腳為PF10,使用ADC3的通道8。
我們需要開啟ADC1和ADC3,ADC1在此處作用為讀取STM32內部的1.2V校準電壓Vrefint,這個操作在芯片內部實現沒有對應引腳。
ADC1的具體配置如下
下表簡單描述一下CuebMX中ADC設置中的功能。
ADC3開啟通道8,具體配置如ADC1一樣。
這里我們為什么需要使用ADC1的VREFINT呢?
VREFINT是ADC的內部參照電壓1.2V,一般來說在STM32我們會使用Vcc作為Vref,但是實際情況中Vcc可能存在較大波動導致Vref不穩定最終使得ADC采樣值不準確,因此我們使用已有的1.2V內部參考電壓先行進行多次采樣,計算平均值。將其與ADC采樣得出的值進行對比,計算出偏移的比例,得到單位數字電壓對應的模擬電壓值。
程序編寫
首先在RT-Thread Settings組件中打開ADC設備驅動程序
之后在硬件中打開ADC1與ADC3
新建一個app_adc.c文件
在使能設備之后有幾點需要我們注意
我們需要采樣內部參考電壓1.2V200次,取平均值并計算單位數字電壓對應的模擬電壓值
/* 采樣內部參考電壓200次 /
for(int i=0;i<200;i++)
{
totalvalue+=rt_adc_read(adc_ref,ADC_REF_CHANNEL);
}
/ 計算單位數字電壓對應的模擬電壓值 /
voltage_vrefint_proportion=2001.2f/totalvalue;
之后我們對ADC3通道8進行采樣,這里有兩個點需要注意
/* 讀取采樣值 */
value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
rt_kprintf("the value is :%d n", value);
/* 轉換為對應電壓值 */
vol=(double)value*voltage_vrefint_proportion*1009.0909090909090909090909090909f;
rt_kprintf("the voltage is :%d.%02d n", vol / 100, vol % 100);
我們重新看一下原理圖,我們這里采樣的是分壓電路分壓之后的值,因此我們需要計算出分壓之前的值才為我們所需的電池電壓。通過如下計算我們需要乘以10.09
并且RT-Thread中rt_kprintf是不能打印出浮點數的,如果是浮點數的話就不會打印出來,這個我之前也踩過坑,有兩個辦法,一是修改rt_kprintf函數實現,但是printf作為一個可重入的函數,打印浮點數是不安全的。因此我這里選擇方法二,我們想要保留小數點后兩位,那么我們就將值*100,之后在打印時再將值/100作為整數部分,值%100作為小數部分,如下所示。
rt_kprintf("the voltage is :%d.%02d n", vol / 100, vol % 100);
完整代碼如下,實現功能為輸入adc_vol_sample,輸出實際讀取到的轉換的原始數據和經過計算后的實際電壓值。
/*
Copyright (c) 2006-2021, RT-Thread Development Team
SPDX-License-Identifier: Apache-2.0
Change Logs:
Date Author Notes
2023-01-09 Goldengrandpa the first version
/
#include
#include
#define ADC_REF_NAME "adc1" / ADC 內部參考電壓設備名稱 /
#define ADC_REF_CHANNEL 17
#define ADC_DEV_NAME "adc3" / ADC 設備名稱 /
#define ADC_DEV_CHANNEL 8 / ADC 通道 /
static int adc_vol_sample(int argc, char argv[])
{
rt_adc_device_t adc_ref;
rt_adc_device_t adc_dev;
rt_uint32_t totalvalue;
rt_uint32_t value, vol;
double voltage_vrefint_proportion;
rt_err_t ret = RT_EOK;
/ 查找設備 /
adc_ref = (rt_adc_device_t) rt_device_find(ADC_REF_NAME);
if (adc_ref == RT_NULL)
{
rt_kprintf("adc sample run failed! can't find %s device!n", ADC_REF_NAME);
return RT_ERROR;
}
adc_dev = (rt_adc_device_t) rt_device_find(ADC_DEV_NAME);
if (adc_dev == RT_NULL)
{
rt_kprintf("adc sample run failed! can't find %s device!n", ADC_DEV_NAME);
return RT_ERROR;
}
/ 使能設備 /
ret = rt_adc_enable(adc_ref, ADC_REF_CHANNEL);
ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
/ 采樣內部參考電壓200次 /
for(int i=0;i<200;i++)
{
totalvalue+=rt_adc_read(adc_ref,ADC_REF_CHANNEL);
}
/ 計算單位數字電壓對應的模擬電壓值 /
voltage_vrefint_proportion=2001.2f/totalvalue;
/ 讀取采樣值 /
value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
rt_kprintf("the value is :%d n", value);
/ 轉換為對應電壓值 /
vol=(double)valuevoltage_vrefint_proportion1009.0909090909090909090909090909f;
rt_kprintf("the voltage is :%d.%02d n", vol / 100, vol % 100);
/ 關閉通道 /
ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
return ret;
}
/ 導出到 msh 命令列表中 */
MSH_CMD_EXPORT(adc_vol_sample, adc voltage convert sample);
硬件連接如下,這里使用的是大疆的6s電池,輸出電壓24V。
由于電池基本要沒電了因此計算出來的電壓為22.39V偏小屬于正常現象。
-
adc
+關注
關注
98文章
6452瀏覽量
544144 -
比較器
+關注
關注
14文章
1637瀏覽量
107118 -
D觸發器
+關注
關注
3文章
164瀏覽量
47867 -
STM32F4
+關注
關注
3文章
194瀏覽量
28003 -
ADC采樣
+關注
關注
0文章
134瀏覽量
12831 -
RT-Thread
+關注
關注
31文章
1274瀏覽量
39940
發布評論請先 登錄
相關推薦
評論