stm32.cube(四)——HAL.ADC
Stm32的Adc具有12位的精度,共有16個(gè)外部通道和2個(gè)內(nèi)部通道。不同通道的 A/D 轉(zhuǎn)換可以在單一、連續(xù)、掃描或者間斷模式下進(jìn)行。它的其他特性還包括支持模擬看門狗和DMA。
1.2 Adc初始化和大多數(shù)外設(shè)一樣,Adc在使用前必須初始化時(shí)鐘源,并從掉電模式喚醒該設(shè)備。建議在初始化Adc后立即運(yùn)行一次校準(zhǔn),以減少準(zhǔn)確性錯(cuò)誤。
1.3 通道的選擇對(duì)于16個(gè)可復(fù)用的通道,可以將通道分成兩種類型的組。常規(guī)組和注入組,組序列保存在寄存器ADC_SQRx和ADC_JSQR中。常規(guī)組可以包含最多16個(gè)通道,注入組最多包含4個(gè)通道。
注入組可以理解為常規(guī)組的一種中斷,當(dāng)注入組的采集被觸發(fā)時(shí),常規(guī)組的采集會(huì)被中斷。直到注入組采集完之后,常規(guī)組才開始繼續(xù)采集。
如果只想采集一個(gè)通道的數(shù)值,只將一個(gè)通道寫入組里。想采集多個(gè)通道的數(shù)值,就將多個(gè)通道寫入組里。當(dāng)一個(gè)組包含多個(gè)通道時(shí),要開啟掃描模式, adc對(duì)組中的每一個(gè)通道根據(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個(gè)位來指定哪個(gè)外部事件可觸ad轉(zhuǎn)換。外部觸發(fā)源一般是定時(shí)器或者是外部中斷線事件。
1.5 單一和連續(xù)一次采集被由內(nèi)部或者外部觸發(fā)后,可以只采集一次,也可以一直連續(xù)采集。如果是一組通道,采集完最后一個(gè)通道時(shí),會(huì)回到序列中的第一個(gè)通道繼續(xù)采集。
每次采集完成后都會(huì)將轉(zhuǎn)換后的數(shù)據(jù)存在一個(gè)寄存器里,并置位采集完成位,根據(jù)設(shè)定產(chǎn)生中斷。
1.6 間斷模式間斷模式是將常規(guī)和注入組里的序列再切割成更小的組。
比如一個(gè)常規(guī)組含有9個(gè)頻道,利用間斷模式并設(shè)置控制寄存器的位,可以將9個(gè)頻道分成3組。這樣一次觸發(fā)只會(huì)采集3個(gè)通道,而不是采集9個(gè)通道,連續(xù)觸發(fā)3次才能采集完9個(gè)通道。
1.7 采樣頻率Adc模塊允許對(duì)采樣頻率進(jìn)行修改,以滿足對(duì)采樣速率的不同需求。考慮到Adc時(shí)序里需要一個(gè)穩(wěn)定期,實(shí)際使用時(shí)還要面臨各種DMA請(qǐng)求和中斷嵌套,建議不要使用過高的采樣頻率,并考慮系統(tǒng)運(yùn)行時(shí)負(fù)載的臨界值。
可以通過修改ADC_SMPR1 和 ADC_SMPR2 寄存器中的 SMP[2:0]來設(shè)置采樣頻率。
1.8 模擬看門狗設(shè)置相應(yīng)的寄存器可以為Adc指定上下閥值,當(dāng)采集到電壓值超過閥值時(shí),系統(tǒng)會(huì)產(chǎn)生中斷。
1.9 DMA當(dāng)在掃描模式和連續(xù)模式下,顯然每次都將保存采集值的寄存器數(shù)據(jù)讀出來處理是來不及的。設(shè)置Adc的DMA使能,可以讓每次轉(zhuǎn)換過的數(shù)值都經(jīng)DMA傳到指定的內(nèi)存空間里。
1.10 字節(jié)序對(duì)于轉(zhuǎn)換后的數(shù)據(jù),可以設(shè)置左對(duì)齊或是右對(duì)齊,以支持不同的處理和傳輸?shù)男枰?/p>1.11 雙adc模式
具有兩個(gè)adc的設(shè)備可以使用的模式。在這種模式下,通常adc1做主設(shè)備,adc2做從設(shè)備。從設(shè)備的觸發(fā)有主設(shè)備來發(fā)起。
主設(shè)備的常規(guī)組/注入組的觸發(fā)成為從設(shè)備觸發(fā)的條件,而主設(shè)備可以被設(shè)置為等待從設(shè)備采集一段時(shí)間后才開始采集,雙adc模式下有許多種方式可以支持不同的主從同步。
二、Adc的庫(kù)函數(shù)HAL里的Adc函數(shù)相較stmlib里的結(jié)構(gòu)更加簡(jiǎn)單,它的私有成員就不做敘述了,因?yàn)槿绾螌?shí)現(xiàn)HAL的結(jié)構(gòu)性代碼并不是我們關(guān)心的問題。這里只描述一下Adc的輸出函數(shù)。
2.1 初始化函數(shù)同GPIO的初始化一樣,Adc的初始化使用幾個(gè)結(jié)構(gòu)體來進(jìn)行。不同的是由于Adc較之GPIO更加復(fù)雜,所以對(duì)Adc屬性的描述分成了幾個(gè)不同的結(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ī)組序列、是否使用間斷模式、間斷模式中一個(gè)組的通道數(shù)量、外部觸發(fā)的方式。
HAL_ADC_init函數(shù)的參數(shù)是一個(gè)指向ADC_HandleTypeDef結(jié)構(gòu)體的指針,這個(gè)結(jié)構(gòu)體的第一個(gè)元素,就是一個(gè)指向ADC_InitTypeDef結(jié)構(gòu)體的指針。所以初始化ADC前,要先定義一個(gè)ADC_InitTypeDef結(jié)構(gòu)體,賦值給每個(gè)成員變量。然后把它的地址寫入一個(gè)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;
可以看到它除了包含一個(gè)指向ADC_InitTypeDef的指針之外,還包含指向寄存器的指針、互斥鎖、一個(gè)描述狀態(tài)的變量、一個(gè)保存錯(cuò)誤代碼的變量、一個(gè)指向DMA結(jié)構(gòu)體的指針。所有與對(duì)Adc進(jìn)行操作的函數(shù)都使用這個(gè)結(jié)構(gòu)體的指針作為參數(shù)。
HAL有一個(gè)自己的鎖,這樣即使程序員在多個(gè)線程里沒有使用信號(hào)量來對(duì)外設(shè)進(jìn)行讀寫控制,HAL層也會(huì)獨(dú)立保證對(duì)外設(shè)進(jìn)行特殊操作時(shí)的原子性。
ADC_InitTypeDef結(jié)構(gòu)體只描述了一部分Adc的屬性。所以有ADC_ChannelConfTypeDef結(jié)構(gòu)體來描述單個(gè)通道在組序列里的排序位置及它的工作速率,ADC_AnalogWDFConfTypeDef結(jié)構(gòu)體來描述一個(gè)模擬看門狗的閥值和中斷模式。而HAL_ADC_ConfigChannel和HAL_ADC_AnalogWDGconfig()函數(shù)用來處理這兩個(gè)結(jié)構(gòu)體。
在初始化Adc時(shí)鐘和使能設(shè)備后,至少需要設(shè)置四個(gè)結(jié)構(gòu)體,調(diào)用三個(gè)函數(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()三個(gè)函數(shù)分別表示開始一次采集(直接軟觸發(fā)或者等待外部觸發(fā)),后兩個(gè)函數(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錯(cuò)誤,這些函數(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ù)用來等待這兩者。
三、例子
只用一個(gè)通道采集的初始化例子
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;