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的配置
然后對她的DMA配置,並開啟DMA的中斷
然后生成代碼吧
打開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函數里面,看到
在進入到圖中的ADC_DMAConvCplt函數里面看到
OK,疑問解決,
以后用到AD就可以直接調用這個CALL了,不要糾結了.