stm32 ADC 簡介
stm32的ADC是 12位逐次逼近型 模擬數字轉換器;它包括18個通道,可以用來測量16個外部通道和2個內部通道.ADC轉換的結果存放在16位數據寄存器(ADC規則數據寄存器,ADC_DR 和 ADC注入數據寄存器,ADC_JDCx)中,這個數據寄存器可以設置對齊方式為左對齊或右對齊.
ADC通道與GPIO對應表(圖片來自整點原子STM32F1開發指南庫函數版本)
規則通道組和注入通道組
注入通道可打斷規則通道的轉換
所謂規則通道組和注入通道組其實應該就是通道的分組吧,按照OOP的思想來理解,通道組是一個基類,注入通道組和規則通道組派生自通道組這個基類,通道組這個基類中包含了一個保存各個通道的數組.
為什么要對通道進行分組呢,這個有待深究,以后再說.
ADC相關的寄存器
ADC_CR1
各個位描述如下圖:
- scan位:
設置掃描模式,1為使用掃描模式,0則關閉.掃描模式下,有ADC_SQRx或ADC_JSQRx寄存器選中的通道被轉換,此時如果設置了EOCIE或JEOCIE,則只有在最后一個通道轉換完畢后才會產生EOC或JEOC中斷 - DUALMOD位:
設置ADC的操作模式,詳細的看下面的來自<<stm32中文參考手冊>>截圖
ADC_CR2
各個位描述如下圖:
- ADON位:用於開關AD轉換器
- CONT位:用於設置是否進行連續轉換,使用單次轉換CONT位必須設置為0.
- EXTSEL[2:0]:用於選擇啟動規則轉換組轉換的外部事件
如果需要使用軟件觸發,就將這三個位設置為 111
ADC采樣事件寄存器(ADC_SMPR1和ADC_SMPR2)
各個位描述如下圖:
這兩個寄存器用來設置通道0~17的采樣時間,每個通道要占3位.
對於每個要轉換的通道,采樣時間盡量長一點,以獲得較高的准確度,但是會降低ADC的轉換速率.ADC的轉換時間可以由以下公式計算:
Tconv = 采樣時間 + 12.5周期
ADC規則序列寄存器(ADC_SQR1~3)
這幾個寄存器功能都差不多,不一一詳細說明了.
- L[3:0]:用於存儲規則序列的長度
ADC數據寄存器(ADC_DR)
這個沒什么好說的,用來存放AD轉換后的結果
要注意可以通過ADC_CR2的ALIGN位設置這個寄存器是左對齊還是右對齊
ADC狀態寄存器(ADC_SR)
沒啥好說的,保存了各種狀態,看圖吧.
通過庫函數配置ADC1通道1進行AD轉換
查看手冊可以知道ADC1通道1對應着PA1,如下圖
1.外設使能
- STM32F103ZET6的ADC通道1在PA1上所以我們先要使能 PORTA的時鍾 和 ADC1時鍾,然后設置 PA1為模擬輸入.
2.復位ADC1,同時設置ADC1的分頻因子
- 開啟了ADC1的時鍾后,要復位ADC1,將ADC1的全部寄存器重設位缺省值,然后通過RCC_CFGR設置ADC1的分頻因子,並且分頻因子要確保ADC1的時鍾不超過14MHz!
//ADC復位 ADC_DeInit();
RCC_ADCCLKConfig(RCC_PCLK2_DIV6); //設置分配因子的庫函數
3.初始化ADC1參數,設置ADC1的工作模式以及規則序列的相關信息.
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);// ADC初始化
4.使能ADC並校准
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState); //使能指定的ADC
void ADC_ResetCalibration(ADC_TypeDef* ADCx);//復位校准
while(ADC_GetResetCalibrationStatus(ADCx));//等待復位校准結束
void ADC_StartCalibration(ADC_TypeDef* ADCx);//執行ADC校准
while(ADC_GetCalibrationStatus(ADCx));//等待AD校准結束
5.設置規則序列1里面的通道,讀取ADC的值
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); //設置規則序列通道以及采樣周期
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);//允許軟件開啟ADC轉換
while(!ADC_GetFlagStatus(ADCx,ADC_FLAG_EOC));//等待轉換結束
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);//獲取轉 換結果數據
MDK5中實現代碼
-
配置stm32f103庫函數編程環境,我用的是正點原子的那一套,不多記錄了;
-
ADC1_Init() 函數
void ADC1_Init(void)
/* --------------------------------------------- ADC1_IN1 -> PA.1 --------------------------------------------- */ void ADC1_Init(void) { GPIO_InitTypeDef gpioInitStruct; ADC_InitTypeDef adcInitStruct; //Enable periph clock RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE); //配置ADC的時鍾,不要超過14MHz; RCC_ADCCLKConfig(RCC_PCLK2_Div6); //Congfig PA.1 gpioInitStruct.GPIO_Mode = GPIO_Mode_AIN; //設置PA.1的輸入模式為模擬輸入 gpioInitStruct.GPIO_Pin = GPIO_Pin_1; gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&gpioInitStruct); //ADC1 復位 ADC_DeInit(ADC1); //initialize adc1 adcInitStruct.ADC_Mode = ADC_Mode_Independent; //ADC模式:ADC獨立模式 adcInitStruct.ADC_DataAlign = ADC_DataAlign_Right; //ADC_DR寄存器的數據對齊方式:右對齊 adcInitStruct.ADC_ScanConvMode = DISABLE; //是否允許連續掃描模式:不允許,使用單通道模式 adcInitStruct.ADC_ContinuousConvMode = DISABLE; //單次轉換模式 adcInitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //ADC觸發方式:不需要外部觸發源,由軟件觸發 adcInitStruct.ADC_NbrOfChannel = 1; //進行轉換的ACD通道數目 ADC_Init(ADC1,&adcInitStruct); //Enable ADC1 ADC_Cmd(ADC1,ENABLE); //ADC1校准 ADC_ResetCalibration(ADC1); //復位校准 while(ADC_GetResetCalibrationStatus(ADC1));//等待復位校准完成 ADC_StartCalibration(ADC1); //ACD校准 while(ADC_GetCalibrationStatus(ADC1)); //等待ADC校准完成 }
-
ADC_GetConvValue()
u16 ADC_GetConvValue(u8 channel)
/** * 獲取指定的通道adc的轉換結果 * @param channel: 指定adc的通道,必須在1~3之間 */ u16 ADC_GetConvValue(u8 channel) { //Config sample channel //采樣周期盡量設置長一些,以確保精度 ADC_RegularChannelConfig(ADC1,channel,1,ADC_SampleTime_239Cycles5); //使能軟件轉換 ADC_SoftwareStartConvCmd(ADC1,ENABLE); while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)); return ADC_GetConversionValue(ADC1); }
-
ADC_GetAverageConvValue()
u16 ADC_GetAverageConvValue(u8 channel,u8 times)
u16 ADC_GetAverageConvValue(u8 channel,u8 times) { u8 i = 0; u32 temp = 0; for(;i < times;i++) { temp += ADC_GetConvValue(channel); } return (u16)(temp/times); }
-
ADC_SampleValue2ReadableValue()
double ADC_SampleValue2ReadableValue(u16 sampleValue)
/** * 描述:將AD轉換后存放在ADC_DR寄存器中的值轉換為有意義的值, * 可以通過宏定義 REF 來設置參考電壓 * @param sampleValue : 要轉換的值 * @retval 轉換的結果,是一個double類型的浮點數 */ double ADC_SampleValue2ReadableValue(u16 sampleValue) { return (double)sampleValue*((double)REF/4096.0); }
-
main.c文件代碼
main.c
#include"ADC.h" #include"delay.h" #include"usart.h"
void Init(void);
void Loop(void);int main(void)
{
Init();
while(1)
Loop();
}void Init(void)
{
delay_init();
USART_Debug_Init();
ADC1_Init();
USART_WriteLine("系統初始化完成!將開始AD采集.");
}void Loop(void)
{
u16 sampledValue;
sampledValue = ADC_GetAverageConvValue(1,5);
USART_WriteLine("采樣值: %d ; 計算所得電壓值: %.2f ",sampledValue,ADC_SampleValue2ReadableValue(sampledValue));
delay_ms(250);
} -
在main.c中使用的 USART_WriteLine(const char*str,...)函數 是我自己封裝的串口函數,也可以使用正點原子 System文件夾 下提供的printf()函數;
總結庫函數配置ADC的步驟
1.使能需要用到的GPIO 和 ADC的時鍾;
2.配置ADC的時鍾分頻:
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
2.配置IO口,輸入模式為 GPIO_Mode_AIN,模擬輸入模式;
3.調用ADC_DeInit();
對ADC進行復位;
4.調用ADC_Init();
初始化ADC
5.調用ADC_Cmd(ADCx,ENABLE);
,使能ADC
6.校准ADC:
ADC_ResetCalibration(ADC1); //復位校准 while(ADC_GetResetCalibrationStatus(ADC1));//等待復位校准完成 ADC_StartCalibration(ADC1); //ACD校准 while(ADC_GetCalibrationStatus(ADC1)); //等待ADC校准完成
7.獲取AD轉換的結果
</div>