1、非DMA模式(轉)html
說明:這個是本身剛作的時候百度出來的,不是我本身作出來的,由於感受有用就保存下來作學習用,原文連接:https://blog.csdn.net/qq_24815615/article/details/70227385,下面第二部分我會補充本身的DMA模式的方法。數組
Stm32 ADC 的轉換模式仍是很靈活,很強大,模式種類不少,那麼這也致使不少人使用的時候沒細心研究參考手冊的狀況下容易混淆。不知道該用哪一種方式來實現本身想要的功能。網上也能夠搜到不少資料,可是大部分是針對以前老版本的標準庫的。昨天幫客戶解決這個問題,正好作個總結:使用stm32cubeMX配置生成多通道採集的例子。函數
軟件:STM32Cumebx MDK學習
硬件:eemaker板(基於stm32F103c8的)測試
在百度搜索ADC多通道採集,大部分的都是基於採用dma模式才實現的。而我講的使用非dma方法。首先有幾個概念要搞清楚:ui
掃描模式(想採集多通道必須開啓):是一次對所選中的通道進行轉換,好比開了ch0,ch1,ch4,ch5。Ch0轉換完之後就會自動轉換通道0,1,4,5直到轉換完。可是這種連續性並非不能被打斷。這就引入了間斷模式,能夠說是對掃描模式的一種補充。它能夠把0,1,4,5這四個通道進行分組。能夠分紅0,1一組,4,5一組。也能夠每一個通道配置爲一組。這樣每一組轉換以前都須要先觸發一次。spa
Stm32 ADC的單次模式和連續模式。這兩中模式的概念是相對應的。這裏的單次模式並非指一個通道。假如你同時開了ch0,ch1,ch4,ch5這四個通道。單次模式轉換模式下會把這四個通道採集一邊就中止了。而連續模式就是這四個通道轉換完之後再循環過來再從ch0開始。.net
另外還有規則組和注入組的概念,由於我這個例程只用到了規則組,就很少介紹這兩個概念,想要弄清楚請自行查閱手冊。3d
下面進入正題,配置stm32cubeMX。調試
先使能幾個通道,我這裏設置爲0、一、四、5.
而後就要配置ADC的參數:
目前通過個人測試,要想用非dma和中斷模式只有這樣配置能夠正確進行多通道轉換:掃描模式+單次轉換模式+間斷轉換模式(每一個間斷組一個通道)。
分析配置成這樣的模式,掃描模式是在配置爲多個通道必須打開的,stm32cubeMX上也默認好了,只能enable。單次轉換模式是我不須要不停的去採集每一個通道值,而是把四個通道採集完之後就讓它中止。這裏間斷配置是關鍵,間斷模式可讓掃描的四個通道進行分紅四個組,stm32cubeMX參數裏面number of Discontinous Conversions是配置間斷組每一個組有幾個通道的,這裏必須配置爲1(不然在獲取ad值得時候只能讀取到每一個間斷組最後一個通道)。
生成mdk工程代碼。這時候尚未完成,只是實現了ADC的初始化,須要採集這四個通道值得函數還要本身寫。下面這個是我main函數的while循環:
for(i=1;i<5;i++) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1,0xffff);//等待ADC轉換完成 adcBuf[i]=HAL_ADC_GetValue(&hadc1); printf("------ch:%d--%d-------\r\n",i,adcBuf[i]); } HAL_ADC_Stop(&hadc1); HAL_Delay(1000);
調用hal庫接口函數也須要注意,HAL_ADC_Start必定要放在for裏面,即每個通道都要觸發。四個通道都採集完了,再去調用HAL_ADC_Stop(&hadc1);結束本次ADC採集。
2、DMA模式
下面就是我本身的DMA模式的ADC多通道轉換了。
先配置一些ADC的基本配置:
引腳
時鐘
這個時鐘能夠結合ADC設置裏配置的採樣時間結合計算出ADC轉換的時間,進而換算出頻率。
接着配置DMA
ADC是12位的,其實DMA只須要用Half Word就能夠了,但實際中HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
該函數中pData爲32位的,也就是DMA必須配置爲Word才能夠。
配置ADC基本設置
這裏要注意選擇對不一樣的通道,一開始我就是沒留意到這個問題,就只有一個通道 Channel10 在轉換,後來查看就是Rank一、二、3全配置成 Channel10 了,因此只有這個通道在轉換,這裏這個提醒你們注意一下。
中斷配置
最後在main文件的main函數裏的while循環里加入下面代碼
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&AD_DMA, 5); //啓用DMA的ADC轉換,AD_DMA 0~3 對應ADC 0~3,這裏注意最後一個參數的大小 printf("AD_DMA_0 = %d\r\n",AD_DMA[0]); printf("AD_DMA_1 = %d\r\n",AD_DMA[1]); printf("AD_DMA_2 = %d\r\n",AD_DMA[2]); HAL_Delay(500);
注意:在while循環前要加ADC校準
HAL_ADCEx_Calibration_Start(&hadc1); //AD校準
串口打印結果以下,至於怎樣串口打印這裏就很少說了,想知道的能夠看http://www.javashuo.com/article/p-xgbapsoq-kz.html
補充:使用定時器與DMA中判定時採集
上面只是單純的一直採集的,若是想要用到中斷的話就能夠按下面的方式來,ADC配置跟上面說的DMA模式同樣:
先配置定時器中斷,怎麼配置能夠參考個人另外一個文章http://www.javashuo.com/article/p-mooxlrej-mb.html
接着在 main 函數的 while 循環前打開定時器中斷
HAL_TIM_Base_Start_IT(&htim3); //啓動定時器中斷
而後重寫定時器中斷回調函數
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&AD_DMA, 5); //啓用DMA的ADC轉換,AD_DMA 0~3 對應ADC 0~3,這裏注意最後一個參數的大小 }
這裏要注意了,我調試的時候發現HAL_ADC_Start_DMA()函數中最後一個參數的大小起碼要比你定義的AD_DMA數組大2,不過不能大於2倍,前面的使用這個函數的時候也是要這樣,數據過小,會致使後面的AD通道採集不了數據,大於2倍程序會一直卡住,至於爲何這樣子我也還沒搞懂,知道的能夠告訴我一聲。【補充:關於這個參數大小的問題,我查了一些資料,通常ADC每次讀進來的數據都是2個字節大小的半字,因此3個通道讀進來的通常一次6個字節這樣,4個通道相似,而這裏的最後一個參數表明的就是要傳輸的字節數,因此這個參數要根據通道個數設置,一般ADC讀入一個半字,也就是uint16_t,你設爲Word,那麼會去讀一個uint32_t是4個字節,其實這個我也還不是很懂,不知道對不對的歡迎你們指出】
最後寫DMA中斷服務函數
void DMA1_Channel1_IRQHandler(void) { /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */ /*本身添加代碼部分*/ HAL_ADC_Stop_DMA(&hadc1); //中止DMA的ADC轉換,AD_DMA 0~3 對應ADC 0~3 HAL_TIM_Base_Stop_IT(&htim3);//關閉定時器 printf("AD_DMA_0 = %d\r\n",AD_DMA[0]); printf("AD_DMA_1 = %d\r\n",AD_DMA[1]); printf("AD_DMA_2 = %d\r\n",AD_DMA[2]); HAL_TIM_Base_Start_IT(&htim3); //從新開啓定時器 /* USER CODE END DMA1_Channel1_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_adc1); /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */ //__HAL_DMA_CLEAR_FLAG(&hdma_adc1, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_adc1)); //清楚標誌位 /* USER CODE END DMA1_Channel1_IRQn 1 */ }
這樣子,就能夠實現1S採集多少次ADC了,而不用單純控制採樣頻率來控制1S的ADC採集次數了,我的以爲單純控制採樣頻率比較難算。
補充:單通ADC採集參考:http://www.javashuo.com/article/p-cjjyldyp-hr.html
補充一個 4 通道採集 DMA 模式:
定義一個數組存放DMA數據
uint16_t AD_DMA[4];
直接在 main 函數的 while 前面開啓 ADC校驗跟採集
HAL_ADCEx_Calibration_Start(&hadc1); //AD校準 HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&AD_DMA, 8); //啓用DMA的ADC轉換,AD_DMA 0~3 對應ADC 0~3
while函數裏打印DMA的值
printf("AD0 = %d\r\n",AD_DMA[0]); printf("AD1 = %d\r\n",AD_DMA[1]); printf("AD2 = %d\r\n",AD_DMA[2]); printf("AD3 = %d\r\n",AD_DMA[3]); HAL_Delay(1000);
打印結果以下