STM32F1之ADC使用

目錄

一、使用過程中問題

1、ADC數據會產生跳動解決辦法:

2、使用過程中ADC採集沒有中間值,要麼最大要麼最小:

二、ADC電壓採集 

電壓輸入範圍

輸入通道​

轉換順序觸發源轉換時間

數據寄存器

中斷

轉換結束中斷

模擬看門狗中斷

DMA 請求

電壓轉換

三、代碼使用

1、adc值讀取

2、ADC的DMA多通道讀取使用方法


一、使用過程中問題

1、ADC數據會產生跳動解決辦法:

①多去幾次求平均值(平均值會將毛刺及錯誤的值加入平均計算);

②多取幾次,去除最大值與最小值,其餘求平均值(中位值平均濾波法);

A、名稱:中位值平均濾波法(又稱防脈衝干擾平均濾波法)
B、方法:
    採一組隊列去掉最大值和最小值後取平均值,
    相當於“中位值濾波法”+“算術平均濾波法”。
    連續採樣N個數據,去掉一個最大值和一個最小值,
    然後計算N-2個數據的算術平均值。
    N值的選取:3-14。
C、優點:
    融合了“中位值濾波法”+“算術平均濾波法”兩種濾波法的優點。
    對於偶然出現的脈衝性干擾,可消除由其所引起的採樣值偏差。
    對週期干擾有良好的抑制作用。
    平滑度高,適於高頻振盪的系統。
D、缺點:
    計算速度較慢,和算術平均濾波法一樣。
    比較浪費RAM。

③採集數據進行排列,然後取中間值(中位值濾波法);

A、名稱:中位值濾波法
B、方法:
    連續採樣N次(N取奇數),把N次採樣值按大小排列,
    取中間值爲本次有效值。
C、優點:
    能有效克服因偶然因素引起的波動干擾;
    對溫度、液位的變化緩慢的被測參數有良好的濾波效果。
D、缺點:
    對流量、速度等快速變化的參數不宜。

④採用濾波算法②③似乎就是。

2、使用過程中ADC採集沒有中間值,要麼最大要麼最小:

首先就要查看基準電平是否焊接上。

使用過程:

比較神奇,就算端口引腳沒有初始化模擬輸入也可以使用adc採集,本來是PC0、PC2、PC3、PC4、PC5設置成PC10、PC12、PC13、PC14、PC15,也可以使用,

另外使用中,adc不能模擬採集,可能由於基準電壓引腳沒有焊接

二、ADC電壓採集 

電壓輸入範圍

ADC 輸入範圍爲: VREF- ≤ VIN ≤ VREF+。由 VREF-、 VREF+ 、 VDDA 、 VSSA、這四個外部引腳決定。
在設計原理圖的時候一般把VSSA和VREF-接地,把VREF+和VDDA 接3V3,得到ADC的輸入電壓範圍爲: 0~3.3V。
如果想讓輸入的電壓範圍變寬,去到可以測試負電壓或者更高的正電壓,我們可以在外部加一個電壓調理電路,把需要轉換的電壓擡升或者降壓到 0~3.3V,這樣 ADC 就可以測量了
 

輸入通道

轉換順序
觸發源
轉換時間

ADC 時鐘
ADC 輸入時鐘 ADC_CLK 由 PCLK2 經過分頻產生, 最大是 14M,分頻因子由 RCC 時鐘配置寄存器 RCC_CFGR 的位 15:14 ADCPRE[1:0]設置,可以是 2/4/6/8 分頻,注意這裏沒有 1 分頻。一般我們設置 PCLK2=HCLK=72M。
採樣時間
ADC 使用若干個 ADC_CLK 週期對輸入的電壓進行採樣,採樣的週期數可通過 ADC採樣時間寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位設置, ADC_SMPR2 控制的是通道 0~9, ADC_SMPR1 控制的是通道 10~17。每個通道可以分別用不同的時間採樣。其中採樣週期最小是 1.5 個,即如果我們要達到最快的採樣,那麼應該設置採樣週期爲 1.5 個週期,這裏說的週期就是 1/ADC_CLK
ADC 的轉換時間跟 ADC 的輸入時鐘和採樣時間有關,公式爲: Tconv = 採樣時間 +12.5 個週期。 當 ADCLK = 14MHZ (最高),採樣時間設置爲 1.5 週期(最快),那麼總的轉換時間(最短) Tconv = 1.5 週期 + 12.5 週期 = 14 週期 = 1us。
一般我們設置 PCLK2=72M,經過 ADC 預分頻器能分頻到最大的時鐘只能是 12M,採樣週期設置爲 1.5 個週期,算出最短的轉換時間爲 1.17us,這個纔是最常用的。
 


數據寄存器

一切準備就緒後, ADC 轉換後的數據根據轉換組的不同,規則組的數據放在 ADC_DR寄存器,注入組的數據放在 JDRx。
 

中斷

轉換結束中斷

數據轉換結束後,可以產生中斷,中斷分爲三種:規則通道轉換結束中斷,注入轉換通道轉換結束中斷,模擬看門狗中斷。其中轉換結束中斷很好理解,跟我們平時接觸的中斷一樣,有相應的中斷標誌位和中斷使能位,我們還可以根據中斷類型寫相應配套的中斷服務程序。

模擬看門狗中斷

當被 ADC 轉換的模擬電壓低於低閾值或者高於高閾值時,就會產生中斷,前提是我們開啓了模擬看門狗中斷,其中低閾值和高閾值由 ADC_LTR 和 ADC_HTR 設置。例如我們設置高閾值是 2.5V,那麼模擬電壓超過 2.5V的時候,就會產生模擬看門狗中斷,反之低閾值也一樣。

DMA 請求

規則和注入通道轉換結束後,除了產生中斷外,還可以產生 DMA 請求,把轉換好的數據直接存儲在內存裏面。要注意的是隻有 ADC1 和 ADC3 可以產生 DMA 請求。一般我們在使用 ADC 的時候都會開啓 DMA 傳輸。

電壓轉換

模擬電壓經過 ADC 轉換後,是一個 12 位的數字值,如果通過串口以 16 進制打印出來的話,可讀性比較差,那麼有時候我們就需要把數字電壓轉換成模擬電壓,也可以跟實際的模擬電壓(用萬用表測)對比,看看轉換是否準確。
我們一般在設計原理圖的時候會把 ADC 的輸入電壓範圍設定在: 0~3.3v,因爲 ADC是 12 位的,那麼 12 位滿量程對應的就是 3.3V, 12 位滿量程對應的數字值是: 2^12。數值0 對應的就是 0V。 如果轉換後的數值爲 X , X 對應的模擬電壓爲 Y,那麼會有這麼一個等式成立: 2^12 / 3.3 = X / Y, => Y = (3.3 * X )
 

三、代碼使用

1、adc值讀取

adc.c



#include "main.h"
#include "stm32f10x_dma.h"
#include "bsp_adc.h"




void ADC_IN_Init(){
	ADC_InitTypeDef ADC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);//使能ADC時鐘和相關GPIO時鐘
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADC時鐘72M/6=12,最大不能超過14
	
	//GPIO配置
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_10|GPIO_Pin_10|GPIO_Pin_10|GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;

	GPIO_Init(GPIOC,&GPIO_InitStructure);

	ADC_DeInit(ADC1);//復位ADC
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//獨立工作模式
	ADC_InitStructure.ADC_ScanConvMode=DISABLE;//啓動多通道掃描(單通道不要打開)
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//啓動連續轉換
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//軟件觸發
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//右對齊
	ADC_InitStructure.ADC_NbrOfChannel=1;//掃描8個通道
	ADC_Init(ADC1,&ADC_InitStructure);//根據參數初始化ADC1



	
	ADC_Cmd(ADC1,ENABLE);//使能ADC
	ADC_ResetCalibration(ADC1);//使能復位校準
	while(ADC_GetResetCalibrationStatus(ADC1));//等待校準完成
	ADC_StartCalibration(ADC1);//使能ADC校準
	while(ADC_GetCalibrationStatus(ADC1));//等待校準完成
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//啓動一次ADC轉換
}


//讀取指定通道值


uint16_t read_adc_value(uint8_t ch)   
{
    uint16_t adc_val = 0;
    
    ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_55Cycles5 );    
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);        
    while(ADC_GetSoftwareStartConvStatus(ADC1)){};
    delay_ms(10);
    adc_val =  100 - ADC_GetConversionValue(ADC1)*100/4096;    
			
    ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_55Cycles5 );    
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);        
    while(ADC_GetSoftwareStartConvStatus(ADC1)){};
    delay_ms(10);
    adc_val +=  100 - ADC_GetConversionValue(ADC1)*100/4096;
      
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_55Cycles5 );    
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);        
    while(ADC_GetSoftwareStartConvStatus(ADC1)){};
    delay_ms(10);
    adc_val +=  100 - ADC_GetConversionValue(ADC1)*100/4096;

	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_55Cycles5 );    
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);        
    while(ADC_GetSoftwareStartConvStatus(ADC1)){};
    delay_ms(10);
    adc_val += 100 - ADC_GetConversionValue(ADC1)*100/4096;
			
	return adc_val/4;


}



 

adc.h


#ifndef BSP_ADC_H
#define BSP_ADC_H
#include "stm32f10x_adc.h"


int getAdcValue(char channle);
void ADC_IN_Init(void);
uint16_t read_adc_value(uint8_t ch);


#endif

2、ADC的DMA多通道讀取使用方法

adc.h

#ifndef __ADC_H
#define	__ADC_H


#include "stm32f10x.h"

/********************ADC1輸入通道(引腳)配置**************************/
#define    ADC_APBxClock_FUN             RCC_APB2PeriphClockCmd
#define    ADC_CLK                       RCC_APB2Periph_ADC1

#define    ADC_GPIO_APBxClock_FUN        RCC_APB2PeriphClockCmd
#define    ADC_GPIO_CLK                  RCC_APB2Periph_GPIOC  
#define    ADC_PORT                      GPIOC



// 轉換通道個數
#define    NOFCHANEL										 6

#define    ADC_PIN1                      GPIO_Pin_0
#define    ADC_CHANNEL1                  ADC_Channel_10

#define    ADC_PIN2                      GPIO_Pin_1
#define    ADC_CHANNEL2                  ADC_Channel_11

#define    ADC_PIN3                      GPIO_Pin_2
#define    ADC_CHANNEL3                  ADC_Channel_12

#define    ADC_PIN4                      GPIO_Pin_3
#define    ADC_CHANNEL4                  ADC_Channel_13

#define    ADC_PIN5                      GPIO_Pin_4
#define    ADC_CHANNEL5                  ADC_Channel_14

#define    ADC_PIN6                      GPIO_Pin_5
#define    ADC_CHANNEL6                  ADC_Channel_15


// ADC1 對應 DMA1通道1,ADC3對應DMA2通道5,ADC2沒有DMA功能
#define    ADC_x                         ADC1
#define    ADC_DMA_CHANNEL               DMA1_Channel1
#define    ADC_DMA_CLK                   RCC_AHBPeriph_DMA1


/**************************函數聲明********************************/
void               ADCx_Init                               (void);


#endif /* __ADC_H */

adc.c

#include "bsp_adc.h"

__IO uint16_t ADC_ConvertedValue[NOFCHANEL]={0,0,0,0,0,0};

/**
  * @brief  ADC GPIO 初始化
  * @param  無
  * @retval 無
  */
static void ADCx_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	// 打開 ADC IO端口時鐘
	ADC_GPIO_APBxClock_FUN ( ADC_GPIO_CLK, ENABLE );
	
	// 配置 ADC IO 引腳模式
	GPIO_InitStructure.GPIO_Pin = 	ADC_PIN1|
																		ADC_PIN2|
																		ADC_PIN3|
																		ADC_PIN4|
																		ADC_PIN5|
																		ADC_PIN6;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	
	// 初始化 ADC IO
	GPIO_Init(ADC_PORT, &GPIO_InitStructure);				
}

/**
  * @brief  配置ADC工作模式
  * @param  無
  * @retval 無
  */
static void ADCx_Mode_Config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;
	
	// 打開DMA時鐘
	RCC_AHBPeriphClockCmd(ADC_DMA_CLK, ENABLE);
	// 打開ADC時鐘
	ADC_APBxClock_FUN ( ADC_CLK, ENABLE );
	
	// 復位DMA控制器
	DMA_DeInit(ADC_DMA_CHANNEL);
	
	// 配置 DMA 初始化結構體
	// 外設基址爲:ADC 數據寄存器地址
	DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 ) ( & ( ADC_x->DR ) );
	
	// 存儲器地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;
	
	// 數據源來自外設
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	
	// 緩衝區大小,應該等於數據目的地的大小
	DMA_InitStructure.DMA_BufferSize = NOFCHANEL;
	
	// 外設寄存器只有一個,地址不用遞增
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

	// 存儲器地址遞增
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 
	
	// 外設數據大小爲半字,即兩個字節
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	
	// 內存數據大小也爲半字,跟外設數據大小相同
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	
	// 循環傳輸模式
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	

	// DMA 傳輸通道優先級爲高,當使用一個DMA通道時,優先級設置不影響
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	
	// 禁止存儲器到存儲器模式,因爲是從外設到存儲器
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	
	// 初始化DMA
	DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
	
	// 使能 DMA 通道
	DMA_Cmd(ADC_DMA_CHANNEL , ENABLE);
	
	// ADC 模式配置
	// 只使用一個ADC,屬於單模式
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	
	// 掃描模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE ; 

	// 連續轉換模式
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

	// 不用外部觸發轉換,軟件開啓即可
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;

	// 轉換結果右對齊
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	
	// 轉換通道個數
	ADC_InitStructure.ADC_NbrOfChannel = NOFCHANEL;	
		
	// 初始化ADC
	ADC_Init(ADC_x, &ADC_InitStructure);
	
	// 配置ADC時鐘N狿CLK2的8分頻,即9MHz
	RCC_ADCCLKConfig(RCC_PCLK2_Div8); 
	
	// 配置ADC 通道的轉換順序和採樣時間
	ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL1, 1, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL2, 2, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL3, 3, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL4, 4, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL5, 5, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL6, 6, ADC_SampleTime_55Cycles5);
	
	// 使能ADC DMA 請求
	ADC_DMACmd(ADC_x, ENABLE);
	
	// 開啓ADC ,並開始轉換
	ADC_Cmd(ADC_x, ENABLE);
	
	// 初始化ADC 校準寄存器  
	ADC_ResetCalibration(ADC_x);
	// 等待校準寄存器初始化完成
	while(ADC_GetResetCalibrationStatus(ADC_x));
	
	// ADC開始校準
	ADC_StartCalibration(ADC_x);
	// 等待校準完成
	while(ADC_GetCalibrationStatus(ADC_x));
	
	// 由於沒有采用外部觸發,所以使用軟件觸發ADC轉換 
	ADC_SoftwareStartConvCmd(ADC_x, ENABLE);
}

/**
  * @brief  ADC初始化
  * @param  無
  * @retval 無
  */
void ADCx_Init(void)
{
	ADCx_GPIO_Config();
	ADCx_Mode_Config();
}
/*********************************************END OF FILE**********************/

數據讀取

void read_value(1)
{	
    
    ADC_ConvertedValueLocal[0] =(float) ADC_ConvertedValue[0]/4096*3.3;
    ADC_ConvertedValueLocal[1] =(float) ADC_ConvertedValue[1]/4096*3.3;
    ADC_ConvertedValueLocal[2] =(float) ADC_ConvertedValue[2]/4096*3.3;
    ADC_ConvertedValueLocal[3] =(float) ADC_ConvertedValue[3]/4096*3.3;
    ADC_ConvertedValueLocal[4] =(float) ADC_ConvertedValue[4]/4096*3.3;
    ADC_ConvertedValueLocal[5] =(float) ADC_ConvertedValue[5]/4096*3.3;
	
    Delay_s(1);		 
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章