STM32使用HAL庫實現ADC單通道轉換

 

   STM32的ADC轉換仍是很強大的,它具備多個通道選擇,這裏我就不細說,不瞭解的能夠自行百度,這裏只是選取單通道,實現ADC轉換。在文章開始以前,我說一下數據左對齊跟右對齊的差異,之前一直糊里糊塗的,記錄下來以避免之後本身忘記。12位二進制最大值爲 0x0FFF 左對齊操做後的結果是 0xFFF0,右對齊後仍是0x0FFF。反過來看 ,若寄存器裏左對齊的數據值X (至關於實際數據*16,因此左對齊轉換的值要/16纔是實際的值),則X>>4纔是實際的數據。而右對齊,則是數據保持不變,採集到多少就多少。至因而按左對齊保存到寄存器仍是按照右對齊,就看你的配置裏如何選了。html

  好了,下面就開始說明怎麼用STM32CUBEMX實現ADC單通道轉換吧。函數

利用中斷模式ui

一、配置ADC引腳spa

  

 二、開定時跟串口,定時器用來定時打開ADC轉換,這樣能夠達到1S內控制ADC轉換次數的目的,不過有個限制,這裏樣子控制ADC轉換次數的話,若是採樣次數多,配置ADC採樣速度時必定要夠  快,正常配置ADC的採樣頻率能夠經過改變其採樣速度來設置的,這裏我是爲了方便處理,就直接用定時器去開啓了;而串口則是打印轉換後的電壓用的。3d

  

三、配置時鐘code

  

四、配置ADC設置htm

  `blog

五、開啓中斷模式get

  

六、串口配置默認便可回調函數

  

七、定時器配置,定時器配置的是進入定時器中斷的頻率,定時時間能夠根據這個頻率換算出來,這裏定時器的頻率 = 72M / 72 /1000 =1000Hz,因此定時時間爲 T = 1S/f = 1S/1000 = 1ms,因此我這裏配置定時爲1ms。

  

八、基本配置咱們完成了,如今咱們生成工程用KEIL5打開

  

九、打開工程,咱們如今進入代碼部分

  這裏咱們只須要重寫定時器中斷回調函數跟,ADC轉換回調中斷函數便可。在main文件裏添加這下面這兩個函數

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)    //定時器中斷回調
{
    HAL_ADC_Start_IT(&hadc1); //定時器中斷裏面開啓ADC中斷轉換,1ms開啓一次採集    
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)    //ADC轉換完成回調
{
    HAL_ADC_Stop_IT(&hadc1);        //關閉ADC
    HAL_TIM_Base_Stop_IT(&htim3);    //關閉定時器
    AD_Value=HAL_ADC_GetValue(&hadc1);  //獲取ADC轉換的值
    Value_1=(float)(AD_Value*3.3/4096);     //ADC換算,這裏參考電壓3.3V,12位的ADC滿量程爲2^12=4096,轉換出來的單位是V
    printf("%.4f\r\n",Value_2[j-10000]);     //串口打印信息
    HAL_TIM_Base_Start_IT(&htim3);       //開啓定時器
}

  到這裏就完成單通道ADC中斷轉換的全部步驟啦,經過串口助手實測轉換結果偏差爲0.0008v。

  至於串口查看信息打印輸出重定向能夠看我這篇文章:http://www.javashuo.com/article/p-xgbapsoq-kz.html

 

不使用中斷模式

   不使用中斷模式的狀況下跟使用中斷的相似的,首先配置的過程當中不須要開啓中斷,至於定時器開不開看我的須要,想利用定時器定時採集的能夠開,不想的不用開,其餘的配置同樣。生成代碼後,在main文件的main函數中的while循環裏添加下面代碼:

  /* USER CODE BEGIN 3 */
        for(char n=0;n<22;n++) 
        {  //取22個值作濾波用
            HAL_ADC_Start(&hadc2);
            HAL_ADC_PollForConversion(&hadc2, 10);    //等待轉換完成,第二個參數表示超時時間,單位ms        
            if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc2), HAL_ADC_STATE_REG_EOC))
            {
                Value[n]=HAL_ADC_GetValue(&hadc2);
                AD_Value += Value[n];
            }                
        }
        max=Value[0];
        min=Value[0];
        for(char n=0;n<22;n++)//取最大值、最小值
        {
            max=(Value[n]<max)?max:Value[n];    
            min=(min<Value[n])?min:Value[n];
        }    
        printf("PC0 ADC : %.4f \r\n",(float)((AD_Value -max-min)/20)*(3.1/4096));        
        AD_tr=(float)((AD_Value -max-min)/20)*(3.1/4096);    //這裏我作了個去掉最大最小值後,取均值的軟件濾波   
        AD_Value=0;

  這裏面的一些變量就大家本身去定義了,我就不列出來了,實測偏差在0.001v之內。

 

補充注意事項:

   一、ADC初始化後要進行校準,使用下面函數校準,能夠放在ADC初始化函數後面校準

HAL_ADCEx_Calibration_Start(&hadc2);    //AD校準

  二、傳入ADC的電壓不能夠超過3.3V,就是不能夠超過你的參考電壓,否則結果不許,還有可能燒壞ADC引腳

 

 使用DMA模式【轉:http://www.stm32cube.com/article/37

 再次寫寫stm32cubemx中AD採集的問題,此次不用while裏面的查詢,也不用中斷採樣了,直接用DMA
先說下用DMA的好處:不管是中斷採樣仍是查詢採樣,都須要在主程序中佔用好多時間出來,嗯,你能夠這樣理解
那種採樣都須要調用HAL_ADC_GetValue()這個函數,,,就是要取得轉換後的值,中斷還好點,要是查詢的話,有可能會丟失數據啊. 用dma就能夠避免了
DMA用的事總線時間,無線cpu干預,額,這種說法貌似有點問題.管它呢
在AD轉換結束的時候自動鏈接你準備存取的變量的地址,數據一步到位.額,省了多少事..
使用stm32cubemx對AD的配置

5.png


而後對她的DMA配置,並開啓DMA的中斷

無標題.png


而後生成代碼吧
打開main.c文件,在這個地方添加代碼

/[i] USER CODE BEGIN 0 [/i]/
__IO uint16_t uhADCxConvertedValue = 0;
/[i] USER CODE END 0 [/i]/

在main()函數裏添加

  /[i] USER CODE BEGIN 2 [/i]/
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&uhADCxConvertedValue, 1);

  /[i] USER CODE END 2 [/i]/

意思是開啓dma傳輸,傳送一個字的數據到uhADCxConvertedValue這個變量裏面
而後再文件的末尾處添加

/[i] USER CODE BEGIN 4 [/i]/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
  /[i] Turn LED1 on: Transfer process is correct [/i]/
 // BSP_LED_On(LED1);
    HAL_GPIO_WritePin (GPIOF,GPIO_PIN_6,GPIO_PIN_SET );
}
/[i] USER CODE END 4 [/i]/

意思是AD轉換完成調用這個函數,函數裏使能led
也許,你會問,爲毛是HAL_ADC_ConvCpltCallback()這個函數啊,這個函數不是當開啓AD的中斷的時候才調用的嗎?
嗯,對,這個函數是這樣的,可是你仔細去分析下開啓AD的DMA中斷函數裏面,就會發現這個函數也在啊
以下圖.進入HAL_ADC_Start_DMA函數裏面,看到

2.png


在進入到圖中的ADC_DMAConvCplt函數裏面看到

3.png

OK,疑問解決,之後用到AD就能夠直接調用這個CALL了,不要糾結了.

相關文章
相關標籤/搜索