stm32cubemx adc_STM32CubeMX__Exp5_ADC1_2CH_DMA_TIM3_Trig__简明指导文件__jyb
用定時(shí)器TIM3觸發(fā)DMA方式的雙通道ADC定時(shí)采樣:
拷貝STM32CubeMX工程文件LED_Flash_PC12.ioc,修改為:Exp5_ADC1_2CH_DMA_TIM3_Trig.ioc
(1)配置ADC1的通道和參數(shù)
配置ADC通道參數(shù)
(2)配置ADC1的DMA
①通過點(diǎn)"Add"按鈕,添加ADC1---DMA1 Channel 1。選擇后ADC1后自動(dòng)添加其DMA通道。
② DMA Request Settings:配置結(jié)果如下圖所示。
Mode:Circular;設(shè)置DMA的傳輸模式為連續(xù)不斷的循環(huán)模式。若只想訪問一次后就不要訪問了(或按指令操作來反問,也就是想要它訪問的時(shí)候就訪問,不要它訪問的時(shí)候就停止),可以設(shè)置成通用模式:DMA_Mode_Normal。
Peripheral:Increment Address:不勾選。如果DMA通道有外設(shè),可以通過DMA通道將數(shù)據(jù)輸出。
Memory:勾選。DMA通過地址遞增方式將數(shù)據(jù)存儲(chǔ)到內(nèi)部數(shù)據(jù)存儲(chǔ)器中。
Data Width:Word。Word是32bits,Half Word是16bits。選擇要與ADC轉(zhuǎn)換結(jié)果的數(shù)據(jù)寬度相同。
配置ADC的DMA
(3)配置ADC1的NVIC:不做任何選擇,按默認(rèn)即可,如下圖所示。DMA1中斷已經(jīng)默認(rèn)強(qiáng)制選擇了。我們?cè)谶@里是采用TIM3的定時(shí)溢出事件觸發(fā)ADC轉(zhuǎn)換的,在DMA中斷服務(wù)程序中讀取數(shù)據(jù),所以不需要使能ADC的中斷。
配置ADC的NVIC
(4)"User Constants"和"GPIO Settings"按默認(rèn)即可。
(5)配置TIM3。配置結(jié)果如下圖所示。
配置TIM3
用其更新事件作為TRGO觸發(fā)ADC。用鼠標(biāo)點(diǎn)"Pinout & Configuration"點(diǎn)"Timers"點(diǎn)"TIM3""Mode"選項(xiàng)卡中,"Clock Source"選"Internal Clock""TIM3 Mode and Configuration"的"Configuration"菜單欄中,點(diǎn)"Parameter Settings""Trigger Output(TRGO)Parameters"下拉選項(xiàng)中,"Trigger Event Selection"選擇"Update Event"。這樣就為ADC的啟動(dòng)提供觸發(fā)信號(hào)。72MHz的時(shí)鐘信號(hào)經(jīng)過(7199+1)和(39999+1)分頻后,頻率為0.25Hz,其周期為4秒,也就是說每4秒觸發(fā)一次ADC轉(zhuǎn)換。
(6) 為了觀察程序運(yùn)行,添加PC12接LED。
(7) ADC的時(shí)鐘為12MHz
配置ADC的時(shí)鐘
(8)配置完成,保存STM32CubeMX工程文件,點(diǎn)擊"GENERATE CODE",生成代碼工程框架并打開。
添加代碼
(1) 在main.c里面添加ADC轉(zhuǎn)換的相關(guān)變量
/* USER CODE BEGIN PV */
uint32_t ADC_Value[10]; //通道IN6、IN7采樣5次的值
uint8_t i,j,ADC_DMA_ConvCpltFlag=0; //ADC1_DMA方式轉(zhuǎn)換結(jié)束標(biāo)志
uint32_t IN6_Value[5],IN7_Value[5]; //從DMA轉(zhuǎn)換值中分離IN6和IN7的值
uint32_t IN6_AverageValue,IN7_AverageValue; //IN6和IN7的平均值
/* USER CODE END PV */
(2)開啟定時(shí)器TIM3,通過TIM3啟動(dòng)ADC。開啟DMA方式的ADC1
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim3); //啟動(dòng)TIM3基本定時(shí)功能,定時(shí)到觸發(fā)ADC啟動(dòng)
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_Value, 10); //啟動(dòng)DMA方式的ADC轉(zhuǎn)換,采樣到10個(gè)之后觸發(fā)DMA方式的ADC中斷
/* USER CODE END 2 */
(3)在中斷回調(diào)函數(shù)中做簡單的數(shù)據(jù)處理
/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle) //DMA方式的ADC中斷回調(diào)函數(shù)
{
// HAL_TIM_Base_Stop(&htim3);
// HAL_ADC_Stop_DMA(&hadc1);
j=0; //將采樣到的10個(gè)ADC轉(zhuǎn)換值分離給IN6和IN7
for(i = 0; i < 10;i++)
{
IN6_Value[j]=ADC_Value[i];
i++;
IN7_Value[j]=ADC_Value[i];
j++;
}
ADC_DMA_ConvCpltFlag=1; //置DMA方式的ADC轉(zhuǎn)換結(jié)束標(biāo)志
}
/* USER CODE END 4 */
(4)在主程序中做復(fù)雜些的數(shù)據(jù)處理
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(GPIOB,LED1_Pin); //用LED1指示主程序運(yùn)行
HAL_Delay(200); //每200msLED1閃爍一次
if(ADC_DMA_ConvCpltFlag==1) //判斷DMA方式的ADC轉(zhuǎn)換結(jié)束了沒有
{
IN6_AverageValue=0; //一次DMA方式的ADC轉(zhuǎn)換結(jié)束,計(jì)算兩個(gè)通道的平均值
IN7_AverageValue=0;
for(i =0;i <5;i++)
{
IN6_AverageValue+=IN6_Value[i];
IN7_AverageValue+=IN7_Value[i];
}
IN6_AverageValue=IN6_AverageValue/5;
IN7_AverageValue=IN7_AverageValue/5;
// HAL_TIM_Base_Start(&htim3);
// HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_Value, 10);
ADC_DMA_ConvCpltFlag=0; //清除轉(zhuǎn)換結(jié)束標(biāo)志,以便判斷下次中斷
}
}
/* USER CODE END 3 */
在啟動(dòng)了TIM3定時(shí)器后,TIM3計(jì)數(shù)溢出事件將觸發(fā)ADC啟動(dòng)轉(zhuǎn)換,ADC轉(zhuǎn)換按照規(guī)定的DMA方式進(jìn)行,先轉(zhuǎn)換IN6通道,再轉(zhuǎn)換IN7通道,這就是掃描轉(zhuǎn)換各個(gè)通道一次,等到下一次TIM3溢出事件再次啟動(dòng)ADC轉(zhuǎn)換,這樣反復(fù)5次,轉(zhuǎn)換得到10個(gè)ADC轉(zhuǎn)換值,將觸發(fā)DMA中斷,在DMA中斷回調(diào)函數(shù)中做簡單的數(shù)據(jù)處理,置DMA中斷標(biāo)志。在主程序中,通過LED1指示主程序的運(yùn)行情況,檢測到DMA中斷后對(duì)采樣到的數(shù)據(jù)做處理,并復(fù)位DMA中斷標(biāo)志。
這里你也許會(huì)問,DMA中斷為什么是void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)這個(gè)函數(shù)呀?這個(gè)函數(shù)不是當(dāng)開啟AD的中斷的時(shí)候才調(diào)用的嗎? 對(duì),是這樣的。我們仔細(xì)分析一下開啟AD的DMA中斷函數(shù),在里面就會(huì)發(fā)現(xiàn)這個(gè)函數(shù)也在啊。在main.c中找到HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_Value, 10);,在HAL_ADC_Start_DMA上點(diǎn)鼠標(biāo)右鍵,跟蹤其定義可以找到函數(shù):HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length),該函數(shù)中有一句:
/* Set the DMA transfer complete callback */
hadc->DMA_Handle->XferCpltCallback = ADC_DMAConvCplt;
DMA傳輸完成,自動(dòng)調(diào)用名字為ADC_DMAConvCplt函數(shù),在ADC_DMAConvCplt上點(diǎn)鼠標(biāo)右鍵,跟蹤其定義,進(jìn)入到void ADC_DMAConvCplt(DMA_HandleTypeDef *hdma)函數(shù)里面可以找到
/* Conversion complete callback */
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
hadc->ConvCpltCallback(hadc);
#else
HAL_ADC_ConvCpltCallback(hadc);
#endif /* USE_HAL_ADC_REGISTER_CALLBACKS */
發(fā)現(xiàn)DMA方式的ADC轉(zhuǎn)換,按照開辟的數(shù)據(jù)區(qū)大小,轉(zhuǎn)換結(jié)果將數(shù)據(jù)區(qū)填滿后,轉(zhuǎn)換完成,還是調(diào)用HAL_ADC_ConvCpltCallback(hadc); 這個(gè)回調(diào)函數(shù),在回調(diào)函數(shù)中對(duì)數(shù)據(jù)做初步處理。今后用到AD,不論是中斷方式還是DMA方式,都可以直接調(diào)用這個(gè)回調(diào)函數(shù)了,不用再糾結(jié)了。需要注意的是,中斷方式的ADC在回調(diào)函數(shù)中需要通過uhADCxConvertedValue = HAL_ADC_GetValue(AdcHandle); 獲得ADC的轉(zhuǎn)換值,而DMA方式的ADC,則通過DMA直接將轉(zhuǎn)換值存放在用數(shù)組名開辟的片內(nèi)RAM中,當(dāng)數(shù)組存滿數(shù)據(jù)后會(huì)觸發(fā)DMA中斷,在回調(diào)函數(shù)中直接從數(shù)組中取轉(zhuǎn)換結(jié)果即可。
以上程序是連續(xù)啟動(dòng)ADC轉(zhuǎn)換的,如果要想控制這個(gè)轉(zhuǎn)換過程,可以通過以下語句實(shí)現(xiàn):
HAL_TIM_Base_Stop(&htim3); //關(guān)閉定時(shí)器,停止溢出事件觸發(fā)ADC
HAL_ADC_Stop_DMA(&hadc1); //停止DMA方式的ADC轉(zhuǎn)換
HAL_TIM_Base_Start(&htim3); //啟動(dòng)定時(shí)器,定時(shí)溢出事件觸發(fā)ADC
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_Value, 10); //啟動(dòng)DMA方式的ADC轉(zhuǎn)換,得到10個(gè)轉(zhuǎn)換值后中斷
第三步:編譯、下載、運(yùn)行
為觀察到變化效果,可以先將PA6(IN6)和PA7(IN7)懸空,此時(shí)測量到是干擾。程序運(yùn)行后,可以看到LED持續(xù)閃爍,表明主程序一直在運(yùn)行,不用設(shè)置斷點(diǎn),全速運(yùn)行程序,在觀察窗口中添加變量ADC_Value、IN6_Value、IN7_Value、IN6_AverageValue、IN7_AverageValue、i、j,可以看到,每隔4秒鐘ADC_Value的值以組(IN6和IN7)為單位變化一次,因?yàn)門IM3定時(shí)4秒,所以每隔4秒觸發(fā)一次ADC轉(zhuǎn)換,轉(zhuǎn)換結(jié)果通過DMA送給ADC_Value數(shù)組。需要20秒后,ADC_Value填滿,觸發(fā)DMA中斷,IN6_Value和IN7_Value在中斷回調(diào)函數(shù)中得到各自的轉(zhuǎn)換結(jié)果,如下圖所示。也可以將PA6和PA7接GND、3.3V,做進(jìn)一步觀察。
觀察運(yùn)行結(jié)果
總結(jié)
以上是生活随笔為你收集整理的stm32cubemx adc_STM32CubeMX__Exp5_ADC1_2CH_DMA_TIM3_Trig__简明指导文件__jyb的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java servlet 3_java
- 下一篇: java的reentrantlock_J