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


 

   STM32的ADC轉換還是很強大的,它具有多個通道選擇,這里我就不細說,不了解的可以自行百度,這里只是選取單通道,實現ADC轉換。在文章開始之前,我說一下數據左對齊跟右對齊的差別,以前一直糊里糊塗的,記錄下來以免以后自己忘記。12位二進制最大值為 0x0FFF 左對齊操作后的結果是 0xFFF0,右對齊后還是0x0FFF。反過來看 ,若寄存器里左對齊的數據值X (相當於實際數據*16,所以左對齊轉換的值要/16才是實際的值),則X>>4才是實際的數據。而右對齊,則是數據保持不變,采集到多少就多少。至於是按左對齊保存到寄存器還是按照右對齊,就看你的配置里如何選了。

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

利用中斷模式

1、配置ADC引腳

  

 2、開定時跟串口,定時器用來定時打開ADC轉換,這樣可以達到1S內控制ADC轉換次數的目的,不過有個限制,這里樣子控制ADC轉換次數的話,如果采樣次數多,配置ADC采樣速度時一定要夠  快,正常配置ADC的采樣頻率可以通過改變其采樣速度來設置的,這里我是為了方便處理,就直接用定時器去開啟了;而串口則是打印轉換后的電壓用的。

  

3、配置時鍾

  

4、配置ADC設置

  `

5、開啟中斷模式

  

6、串口配置默認即可

  

7、定時器配置,定時器配置的是進入定時器中斷的頻率,定時時間可以根據這個頻率換算出來,這里定時器的頻率 = 72M / 72 /1000 =1000Hz,所以定時時間為 T = 1S/f = 1S/1000 = 1ms,所以我這里配置定時為1ms。

  

8、基本配置我們完成了,現在我們生成工程用KEIL5打開

  

9、打開工程,我們現在進入代碼部分

  這里我們只需要重寫定時器中斷回調函數跟,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。

  至於串口查看信息打印輸出重定向可以看我這篇文章:https://www.cnblogs.com/xingboy/p/9522940.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以內。

 

補充注意事項:

   1、ADC初始化后要進行校准,使用下面函數校准,可以放在ADC初始化函數后面校准

HAL_ADCEx_Calibration_Start(&hadc2);    //AD校准

  2、傳入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了,不要糾結了.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM