Stm32的Adc具有12位的精度,共有16個外部通道和2個內(nèi)部通道。不同通道的 A/D 轉(zhuǎn)換可以在單一、連續(xù)、掃描或者間斷模式下進(jìn)行。它的其他特性還包括支持模擬看門狗和DMA。
1.2 Adc初始化和大多數(shù)外設(shè)一樣,Adc在使用前必須初始化時鐘源,并從掉電模式喚醒該設(shè)備。建議在初始化Adc后立即運(yùn)行一次校準(zhǔn),以減少準(zhǔn)確性錯誤。
1.3 通道的選擇對于16個可復(fù)用的通道,可以將通道分成兩種類型的組。常規(guī)組和注入組,組序列保存在寄存器ADC_SQRx和ADC_JSQR中。常規(guī)組可以包含最多16個通道,注入組最多包含4個通道。
注入組可以理解為常規(guī)組的一種中斷,當(dāng)注入組的采集被觸發(fā)時,常規(guī)組的采集會被中斷。直到注入組采集完之后,常規(guī)組才開始繼續(xù)采集。
如果只想采集一個通道的數(shù)值,只將一個通道寫入組里。想采集多個通道的數(shù)值,就將多個通道寫入組里。當(dāng)一個組包含多個通道時,要開啟掃描模式, adc對組中的每一個通道根據(jù)寄存器里的序列進(jìn)行一次轉(zhuǎn)換。
1.4 觸發(fā)方式要觸發(fā)一次ad轉(zhuǎn)換,可以由內(nèi)部軟件觸發(fā),或者外部觸發(fā)。要不要使用外部觸發(fā)由控制寄存器里的EXTTRIG位來指定。
內(nèi)部觸發(fā)自然是通過寫控制寄存器里的相應(yīng)位來觸發(fā)。而外部觸發(fā)則可以有八種觸發(fā)源可供選擇,所以常規(guī)組和注入組在控制寄存器里各有3個位來指定哪個外部事件可觸ad轉(zhuǎn)換。外部觸發(fā)源一般是定時器或者是外部中斷線事件。
1.5 單一和連續(xù)一次采集被由內(nèi)部或者外部觸發(fā)后,可以只采集一次,也可以一直連續(xù)采集。如果是一組通道,采集完最后一個通道時,會回到序列中的第一個通道繼續(xù)采集。
每次采集完成后都會將轉(zhuǎn)換后的數(shù)據(jù)存在一個寄存器里,并置位采集完成位,根據(jù)設(shè)定產(chǎn)生中斷。
1.6 間斷模式間斷模式是將常規(guī)和注入組里的序列再切割成更小的組。
比如一個常規(guī)組含有9個頻道,利用間斷模式并設(shè)置控制寄存器的位,可以將9個頻道分成3組。這樣一次觸發(fā)只會采集3個通道,而不是采集9個通道,連續(xù)觸發(fā)3次才能采集完9個通道。
1.7 采樣頻率Adc模塊允許對采樣頻率進(jìn)行修改,以滿足對采樣速率的不同需求??紤]到Adc時序里需要一個穩(wěn)定期,實際使用時還要面臨各種DMA請求和中斷嵌套,建議不要使用過高的采樣頻率,并考慮系統(tǒng)運(yùn)行時負(fù)載的臨界值。
可以通過修改ADC_SMPR1 和 ADC_SMPR2 寄存器中的 SMP[2:0]來設(shè)置采樣頻率。
1.8 模擬看門狗設(shè)置相應(yīng)的寄存器可以為Adc指定上下閥值,當(dāng)采集到電壓值超過閥值時,系統(tǒng)會產(chǎn)生中斷。
1.9 DMA當(dāng)在掃描模式和連續(xù)模式下,顯然每次都將保存采集值的寄存器數(shù)據(jù)讀出來處理是來不及的。設(shè)置Adc的DMA使能,可以讓每次轉(zhuǎn)換過的數(shù)值都經(jīng)DMA傳到指定的內(nèi)存空間里。
1.10 字節(jié)序對于轉(zhuǎn)換后的數(shù)據(jù),可以設(shè)置左對齊或是右對齊,以支持不同的處理和傳輸?shù)男枰?/p>1.11 雙adc模式
具有兩個adc的設(shè)備可以使用的模式。在這種模式下,通常adc1做主設(shè)備,adc2做從設(shè)備。從設(shè)備的觸發(fā)有主設(shè)備來發(fā)起。
主設(shè)備的常規(guī)組/注入組的觸發(fā)成為從設(shè)備觸發(fā)的條件,而主設(shè)備可以被設(shè)置為等待從設(shè)備采集一段時間后才開始采集,雙adc模式下有許多種方式可以支持不同的主從同步。
二、Adc的庫函數(shù)HAL里的Adc函數(shù)相較stmlib里的結(jié)構(gòu)更加簡單,它的私有成員就不做敘述了,因為如何實現(xiàn)HAL的結(jié)構(gòu)性代碼并不是我們關(guān)心的問題。這里只描述一下Adc的輸出函數(shù)。
2.1 初始化函數(shù)同GPIO的初始化一樣,Adc的初始化使用幾個結(jié)構(gòu)體來進(jìn)行。不同的是由于Adc較之GPIO更加復(fù)雜,所以對Adc屬性的描述分成了幾個不同的結(jié)構(gòu)體。
ADC_InitTypeDef結(jié)構(gòu)體原型
typedef struct
{
uint32_t DataAlign;
uint32_t ScanConvMode;
uint32_t ContinuousConvMode;
uint32_t NbrOfConversion;
uint32_t DiscontinuousConvMode;
uint32_t NbrOfDiscConversion;
uint32_t ExternalTrigConv;
}ADC_InitTypeDef;
從上到下依次描述:
字節(jié)序、是否使用掃描模式、單一/連續(xù)模式、常規(guī)組序列、是否使用間斷模式、間斷模式中一個組的通道數(shù)量、外部觸發(fā)的方式。
HAL_ADC_init函數(shù)的參數(shù)是一個指向ADC_HandleTypeDef結(jié)構(gòu)體的指針,這個結(jié)構(gòu)體的第一個元素,就是一個指向ADC_InitTypeDef結(jié)構(gòu)體的指針。所以初始化ADC前,要先定義一個ADC_InitTypeDef結(jié)構(gòu)體,賦值給每個成員變量。然后把它的地址寫入一個ADC_HandleTypeDef結(jié)構(gòu)體的成員。
ADC_InitTypeDef結(jié)構(gòu)體原型
/**
* @brief ADC handle Structure definition
*/
typedef struct
{
ADC_TypeDef *Instance; /*!< Register base address */
ADC_InitTypeDef Init; /*!< ADC required parameters */
__IO uint32_t NbrOfConversionRank ; /*!< ADC conversion rank counter */
DMA_HandleTypeDef *DMA_Handle; /*!< Pointer DMA Handler */
HAL_LockTypeDef Lock; /*!< ADC locking object */
__IO HAL_ADC_StateTypeDef State; /*!< ADC communication state */
__IO uint32_t ErrorCode; /*!< ADC Error code */
}ADC_HandleTypeDef;
可以看到它除了包含一個指向ADC_InitTypeDef的指針之外,還包含指向寄存器的指針、互斥鎖、一個描述狀態(tài)的變量、一個保存錯誤代碼的變量、一個指向DMA結(jié)構(gòu)體的指針。所有與對Adc進(jìn)行操作的函數(shù)都使用這個結(jié)構(gòu)體的指針作為參數(shù)。
HAL有一個自己的鎖,這樣即使程序員在多個線程里沒有使用信號量來對外設(shè)進(jìn)行讀寫控制,HAL層也會獨(dú)立保證對外設(shè)進(jìn)行特殊操作時的原子性。
ADC_InitTypeDef結(jié)構(gòu)體只描述了一部分Adc的屬性。所以有ADC_ChannelConfTypeDef結(jié)構(gòu)體來描述單個通道在組序列里的排序位置及它的工作速率,ADC_AnalogWDFConfTypeDef結(jié)構(gòu)體來描述一個模擬看門狗的閥值和中斷模式。而HAL_ADC_ConfigChannel和HAL_ADC_AnalogWDGconfig()函數(shù)用來處理這兩個結(jié)構(gòu)體。
在初始化Adc時鐘和使能設(shè)備后,至少需要設(shè)置四個結(jié)構(gòu)體,調(diào)用三個函數(shù),才能將Adc初始化完畢。
2.2 Adc的操作開始與結(jié)束
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);
HAL_ADC_START()、HAL_ADC_START_DMA()、HAL_ADC_START_IT()三個函數(shù)分別表示開始一次采集(直接軟觸發(fā)或者等待外部觸發(fā)),后兩個函數(shù)表示數(shù)據(jù)用DMA傳輸和使能中斷。中斷處理函數(shù)是HAL_ADC_IRQHander()。
callback函數(shù)
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc);
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc);
void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc);
Adc中斷、DMA傳輸、看門狗超過閥值、發(fā)生Adc錯誤,這些函數(shù)返回前都調(diào)用了Callback函數(shù),用來在非中斷模式下處理Adc數(shù)據(jù),如果想進(jìn)行一些操作可以直接修改Callback函數(shù)。
取轉(zhuǎn)換后的數(shù)值
/* ADC retrieve conversion value intended to be used with polling or interruption */
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
HAL_ADC_GetValue()用來區(qū)寄存器值。
等待函數(shù)
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout);
HAL_StatusTypeDef HAL_ADC_PollForEvent(ADC_HandleTypeDef* hadc, uint32_t EventType, uint32_t Timeout);
1
2
在非中斷和DMA模式下,采集數(shù)據(jù)需要等待一次采集的完成,或者某些事件的發(fā)生。HAL_ADC_PollForConversion函數(shù)和HAL_ADC_PollForEvent函數(shù)用來等待這兩者。
三、例子
只用一個通道采集的初始化例子
static void ADC_Config(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_AnalogWDGConfTypeDef AnalogWDGConfig;
/* Configuration of ADCx init structure: ADC parameters and regular group */
AdcHandle.Instance = ADCx;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.ScanConvMode = ADC_SCAN_DISABLE;