ADC簡介
??? STM32F103系列有3個ADC,精度為12位,每個ADC最多有16個外部通道。其中ADC1和ADC2都有16個外部通道,ADC3一般有8個外部通道,各通道的A/D轉換可以單次、連續、掃描或間斷執行,ADC轉換的結果可以左對齊或右對齊儲存在16位數據寄存器中。ADC的輸入時鐘不得超過14MHz,其時鐘頻率由PCLK2分頻產生。
ADC功能框圖講解
????學習STM32開發板上的外設時首先要了解其外設的功能框圖,如下:
????功能框圖可以大體分為7部分,下面一一講解:
電壓輸入范圍
??? ADC所能測量的電壓范圍就是VREF- ≤ VIN ≤ VREF+,把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的輸入電壓范圍為:0~3.3V。
輸入通道
??? ADC的信號輸入就是通過通道來實現的,信號通過通道輸入到單片機中,單片機經過轉換后,將模擬信號輸出為數字信號。STM32中的ADC有著18個通道,其中外部的16個通道已經在框圖中標出,如下:
????這16個通道對應著不同的IO口,此外ADC1/2/3 還有內部通道:ADC1 的通道 16 連接到了芯片內部的溫度傳感器, Vrefint 連接到了通道 17。ADC2 的模擬通道 16 和 17 連接到了內部的 VSS。
??? ADC的全部通道如下圖所示:
????外部的16個通道在轉換時又分為規則通道和注入通道,其中規則通道最多有16路,注入通道最多有4路(注入通道貌似使用不多),下面簡單介紹一下兩種通道:
????規則通道顧名思義就是,最平常的通道、也是最常用的通道,平時的ADC轉換都是用規則通道實現的。
????注入通道是相對于規則通道的,注入通道可以在規則通道轉換時,強行插入轉換,相當于一個“中斷通道”吧。當有注入通道需要轉換時,規則通道的轉換會停止,優先執行注入通道的轉換,當注入通道的轉換執行完畢后,再回到之前規則通道進行轉換。
轉換順序
????知道了ADC的轉換通道后,如果ADC只使用一個通道來轉換,那就很簡單,但如果是使用多個通道進行轉換就涉及到一個先后順序了,畢竟規則轉換通道只有一個數據寄存器。多個通道的使用順序分為倆種情況:規則通道的轉換順序和注入通道的轉換順序。
????規則通道中的轉換順序由三個寄存器控制:SQR1、SQR2、SQR3,它們都是32位寄存器。SQR寄存器控制著轉換通道的數目和轉換順序,只要在對應的寄存器位SQx中寫入相應的通道,這個通道就是第x個轉換。具體的對應關系如下:
????通過SQR1寄存器就能了解其轉換順序在寄存器上的實現了:
????和規則通道轉換順序的控制一樣,注入通道的轉換也是通過注入寄存器來控制,只不過只有一個JSQR寄存器來控制,控制關系如下:
????需要注意的是,只有當JL=4的時候,注入通道的轉換順序才會按照JSQ1、JSQ2、JSQ3、JSQ4的順序執行。當JL<4時,注入通道的轉換順序恰恰相反,也就是執行順序為:JSQ4、JSQ3、JSQ2、JSQ1。
????配置轉換順序的函數如下代碼所示:
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
/**
* @brief Configures for the selected ADC regular channel its corresponding
* rank in the sequencer and its sample time.
* @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
* @param ADC_Channel: the ADC channel to configure.
* This parameter can be one of the following values:
* @arg ADC_Channel_0: ADC Channel0 selected
* @arg ADC_Channel_1: ADC Channel1 selected
* @arg ADC_Channel_2: ADC Channel2 selected
* @arg ADC_Channel_3: ADC Channel3 selected
* @arg ADC_Channel_4: ADC Channel4 selected
* @arg ADC_Channel_5: ADC Channel5 selected
* @arg ADC_Channel_6: ADC Channel6 selected
* @arg ADC_Channel_7: ADC Channel7 selected
* @arg ADC_Channel_8: ADC Channel8 selected
* @arg ADC_Channel_9: ADC Channel9 selected
* @arg ADC_Channel_10: ADC Channel10 selected
* @arg ADC_Channel_11: ADC Channel11 selected
* @arg ADC_Channel_12: ADC Channel12 selected
* @arg ADC_Channel_13: ADC Channel13 selected
* @arg ADC_Channel_14: ADC Channel14 selected
* @arg ADC_Channel_15: ADC Channel15 selected
* @arg ADC_Channel_16: ADC Channel16 selected
* @arg ADC_Channel_17: ADC Channel17 selected
* @param Rank: The rank in the regular group sequencer. This parameter must be between 1 to 16.
* @param ADC_SampleTime: The sample time value to be set for the selected channel.
* This parameter can be one of the following values:
* @arg ADC_SampleTime_1Cycles5: Sample time equal to 1.5 cycles
* @arg ADC_SampleTime_7Cycles5: Sample time equal to 7.5 cycles
* @arg ADC_SampleTime_13Cycles5: Sample time equal to 13.5 cycles
* @arg ADC_SampleTime_28Cycles5: Sample time equal to 28.5 cycles
* @arg ADC_SampleTime_41Cycles5: Sample time equal to 41.5 cycles
* @arg ADC_SampleTime_55Cycles5: Sample time equal to 55.5 cycles
* @arg ADC_SampleTime_71Cycles5: Sample time equal to 71.5 cycles
* @arg ADC_SampleTime_239Cycles5: Sample time equal to 239.5 cycles
* @retval None
*/
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)
{
函數內容略;
}
觸發源
??? ADC轉換的輸入、通道、轉換順序都已經說明了,但ADC轉換是怎么觸發的呢?就像通信協議一樣,都要規定一個起始信號才能傳輸信息,ADC也需要一個觸發信號來實行模/數轉換。
????其一就是通過直接配置寄存器觸發,通過配置控制寄存器CR2的ADON位,寫1時開始轉換,寫0時停止轉換。在程序運行過程中只要調用庫函數,將CR2寄存器的ADON位置1就可以進行轉換,比較好理解。
????另外,還可以通過內部定時器或者外部IO觸發轉換,也就是說可以利用內部時鐘讓ADC進行周期性的轉換,也可以利用外部IO使ADC在需要時轉換,具體的觸發由控制寄存器CR2決定。
????在參考手冊中可以找到,ADC_CR2寄存器的詳情如下:
轉換時間
????還有一點,就是轉換時間的問題,ADC的每一次信號轉換都要時間,這個時間就是轉換時間,轉換時間由輸入時鐘和采樣周期來決定。
????由于ADC在STM32中是掛載在APB2總線上的,所以ADC得時鐘是由PCLK2(72MHz)經過分頻得到的,分頻因子由 RCC 時鐘配置寄存器RCC_CFGR 的位 15:14 ADCPRE[1:0]設置,可以是 2/4/6/8 分頻,一般配置分頻因子為8,即8分頻得到ADC的輸入時鐘頻率為9MHz。
????采樣周期是確立在輸入時鐘上的,配置采樣周期可以確定使用多少個ADC時鐘周期來對電壓進行采樣,采樣的周期數可通過 ADC采樣時間寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位設置,ADC_SMPR2 控制的是通道 0~9, ADC_SMPR1 控制的是通道 10~17。每個通道可以配置不同的采樣周期,但最小的采樣周期是1.5個周期,也就是說如果想最快時間采樣就設置采樣周期為1.5.
- ?
轉換時間=采樣時間+12.5個周期
??? 12.5個周期是固定的,一般我們設置 PCLK2=72M,經過 ADC 預分頻器能分頻到最大的時鐘只能是 12M,采樣周期設置為 1.5 個周期,算出最短的轉換時間為 1.17us。
數據寄存器
????轉換完成后的數據就存放在數據寄存器中,但數據的存放也分為規則通道轉換數據和注入通道轉換數據的。
????規則數據寄存器負責存放規則通道轉換的數據,通過32位寄存器ADC_DR來存放:
????當使用ADC獨立模式(也就是只使用一個ADC,可以使用多個通道)時,數據存放在低16位中,當使用ADC多模式時高16位存放ADC2的數據。需要注意的是ADC轉換的精度是12位,而寄存器中有16個位來存放數據,所以要規定數據存放是左對齊還是右對齊。
????當使用多個通道轉換數據時,會產生多個轉換數據,然鵝數據寄存器只有一個,多個數據存放在一個寄存器中會覆蓋數據導致ADC轉換錯誤,所以我們經常在一個通道轉換完成之后就立刻將數據取出來,方便下一個數據存放。一般開啟DMA模式將轉換的數據,傳輸在一個數組中,程序對數組讀操作就可以得到轉換的結果。
??? DMA的使用之前介紹過,請移步此處:DMA介紹。
????注入通道轉換的數據寄存器有4個,由于注入通道最多有4個,所以注入通道轉換的數據都有固定的存放位置,不會跟規則寄存器那樣產生數據覆蓋的問題。ADC_JDRx 是 32 位的,低 16 位有效,高 16 位保留,數據同樣分為左對齊和右對齊,具體是以哪一種方式存放,由ADC_CR2 的 11 位 ALIGN 設置。
中斷
? ??????從框圖中可以知道數據轉換完成之后可以產生中斷,有三種情況:
-
規則通道數據轉換完成之后,可以產生一個中斷,可以在中斷函數中讀取規則數據寄存器的值。這也是單通道時讀取數據的一種方法。
-
注入通道數據轉換完成之后,可以產生一個中斷,并且也可以在中斷中讀取注入數據寄存器的值,達到讀取數據的作用。
-
當輸入的模擬量(電壓)不再閾值范圍內就會產生看門狗事件,就是用來監視輸入的模擬量是否正常。
????以上中斷的配置都由ADC_SR寄存器決定:
? ? 當然,在轉換完成之后也可以產生DMA請求,從而將轉換好的數據從數據寄存器中讀取到內存中。
電壓轉換
????要知道,轉換后的數據是一個12位的二進制數,我們需要把這個二進制數代表的模擬量(電壓)用數字表示出來。比如測量的電壓范圍是0~3.3V,轉換后的二進制數是x,因為12位ADC在轉換時將電壓的范圍大?。ㄒ簿褪?.3)分為4096(2^12)份,所以轉換后的二進制數x代表的真實電壓的計算方法就是:
- ?
y=3.3* x / 4096
初始化結構體
????每個外設的核心就是其對應的初始化結構體了,ADC的初始化結構體代碼如下:
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
typedef struct
{
uint32_t ADC_Mode; // ADC 工作模式選擇
FunctionalState ADC_ScanConvMode; // ADC 掃描(多通道)或者單次(單通道)模式選擇
FunctionalState ADC_ContinuousConvMode; // ADC 單次轉換或者連續轉換選擇
uint32_t ADC_ExternalTrigConv; // ADC 轉換觸發信號選擇
uint32_t ADC_DataAlign; // ADC 數據寄存器對齊格式
uint8_t ADC_NbrOfChannel; // ADC 采集通道數
} ADC_InitTypeDef;
????通過配置初始化結構體來設置ADC的相關信息。
單通道電壓采集
????用這個程序來簡單熟練一下ADC的單通道電壓采集吧,程序使用了ADC1的通道11,對應的IO口是PC^1,因為博主的開發板上PC ^1引腳沒有任何復用,使用中斷,在中斷中讀取轉換的電壓。
頭文件
????為了提高文件的可移植性,頭文件中定義了一些與ADC和中斷相關的量,在移植程序的時候只需要修改頭文件中的定義即可。
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
/* 采用ADC1的通道11 引腳為PC^1 模式必須是模擬輸入*/
/* 配置與中斷有關的信息 */
/* 配置ADC初始化結構體的宏定義 */
/* 通道信息和采樣周期 */
/* 函數聲明 */
void ADC_COnfig(void);
void ADC_NVIC_Config(void);
void ADC_GPIO_Config(void);
void?ADCx_Init(void);
引腳配置函數
????首先配置相應的GPIO引腳,畢竟模擬信號是通過GPIO引腳傳輸到開發板的,注意的是,引腳的模式一定要是模擬輸入!
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
void ADC_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(ADC_GPIO_RCC,??ENABLE);
GPIO_InitStruct.GPIO_Pin = ADC_GPIO_PIN ;
GPIO_InitStruct.GPIO_Mode?=?ADC_GPIO_MODE?;
GPIO_Init(ADC_GPIO_PORT , &GPIO_InitStruct);
}
????配置引腳就是老套路:聲明結構體變量、開啟時鐘、寫入結構體、初始化GPIO。
NVIC配置函數
????因為我們是在轉換完成后利用中斷,在中斷函數中讀取數據,所以要首先配置中斷函數的優先級,因為程序中只有這一個中斷,所以優先級的配置就比較隨意。
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
void ADC_NVIC_Config(void)
{
NVIC_InitTypeDef?NVIC_InitStruct?;
/* 配置中斷優先級分組(設置搶占優先級和子優先級的分配),在函數在misc.c */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1)?;
/* 配置初始化結構體 在misc.h中 */
/* 配置中斷源 在stm32f10x.h中 */
NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn ;
/* 配置搶占優先級 */
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;
/* 配置子優先級 */
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ;
/* 使能中斷通道 */
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
/* 調用初始化函數 */
NVIC_Init(&NVIC_InitStruct) ;
}
ADC配置函數
??? ADC的配置函數是ADC的精髓,在這個函數中包含的內容有:ADC的初始化結構體配置、配置了時鐘分頻、配置了通道轉換順序、打開轉換中斷、進行校準、軟件觸發ADC采集等。
????函數中都有詳細的注釋:
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
void ADC_COnfig(void)
{
ADC_InitTypeDef ADC_InitStruct;
??RCC_APB2PeriphClockCmd(ADC_RCC,??ENABLE);
/* 配置初始化結構體,詳情見頭文件 */
ADC_InitStruct.ADC_ContinuousConvMode = ADCx_ContinuousConvMode ;
ADC_InitStruct.ADC_DataAlign = ADCx_DataAlign ;
ADC_InitStruct.ADC_ExternalTrigConv = ADCx_ExternalTrigConv ;
ADC_InitStruct.ADC_Mode = ADCx_Mode ;
ADC_InitStruct.ADC_NbrOfChannel = ADCx_NbrOfChannel ;
??ADC_InitStruct.ADC_ScanConvMode?=?ADCx_ScanConvMode?;
??ADC_Init(ADCx,?&ADC_InitStruct);
/* 配置ADC時鐘為8分頻,即9M */
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
/* 配置ADC通道轉換順序和時間 */
ADC_RegularChannelConfig(ADCx, ADC_Channel, 1, ADC_SampleTime );
/* 配置為轉換結束后產生中斷 在中斷中讀取信息 */
ADC_ITConfig(ADCx, ADC_IT_EOC,ENABLE);
/* 開啟ADC,進行轉換 */
ADC_Cmd(ADCx, ENABLE );
/* 重置ADC校準 */
ADC_ResetCalibration(ADCx);
/* 等待初始化完成 */
while(ADC_GetResetCalibrationStatus( ADCx))
/* 開始校準 */
ADC_StartCalibration(ADCx);
/* 等待校準完成 */
while (ADC_GetCalibrationStatus(ADCx));
/* 軟件觸發ADC轉換 */
??ADC_SoftwareStartConvCmd(ADCx,?ENABLE);
}
中斷函數
????在中斷函數中進行讀取數據,將數據存放在變量result中,此處使用關鍵字extern聲明,代表變量result已經在其他文件中定義,關于extern的介紹在之前發的文章中有的介紹,具體請移步此處:extern關鍵字。
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
extern?uint16_t?resurt;
void ADC1_2_IRQHandler(void)
{
/* 判斷產生中斷請求 */
while(ADC_GetITStatus(ADCx, ADC_IT_EOC) == SET)
resurt=ADC_GetConversionValue(ADCx);
/* 清除中斷標志 */
ADC_ClearITPendingBit(ADCx, ADC_IT_EOC);
}
主函數
????主函數負責接收轉換的值,并將其轉換為電壓值,然后通過串口打印在計算機上,便于調試。
????變量result是主函數中的全局變量,注意最后的結果應該轉換為浮點型。
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
uint16_t?result;
void delay(void)
{
uint16_t k=0xffff;
while(k--);
}
int main(void)
{
float voltage;
/* 串口調試函數 */
??DEBUG_USART_Config();
/* 與ADC相關的函數打包在此函數中 */
ADCx_Init();
while(1)
{
/* 強制轉換為浮點型 */
voltage = (float) result/4096*3.3;
printf(" 電壓值為:%f ",voltage);
delay();
??}
}
評論
查看更多