STM32 ADC多通道轉換DMA模式與非DMA模式兩種方法(HAL庫)


 

一、非DMA模式(轉)

  說明:這個是自己剛做的時候百度出來的,不是我自己做出來的,因為感覺有用就保存下來做學習用,原文鏈接:https://blog.csdn.net/qq_24815615/article/details/70227385,下面第二部分我會補充自己的DMA模式的方法。

  Stm32 ADC 的轉換模式還是很靈活,很強大,模式種類很多,那么這也導致很多人使用的時候沒細心研究參考手冊的情況下容易混淆。不知道該用哪種方式來實現自己想要的功能。網上也可以搜到很多資料,但是大部分是針對之前老版本的標准庫的。昨天幫客戶解決這個問題,正好做個總結:使用stm32cubeMX配置生成多通道采集的例子。

軟件:STM32Cumebx  MDK

硬件:eemaker板(基於stm32F103c8的)

在百度搜索ADC多通道采集,大部分的都是基於采用dma模式才實現的。而我講的使用非dma方法。首先有幾個概念要搞清楚:

  掃描模式(想采集多通道必須開啟):是一次對所選中的通道進行轉換,比如開了ch0,ch1,ch4,ch5。Ch0轉換完以后就會自動轉換通道0,1,4,5直到轉換完。但是這種連續性並不是不能被打斷。這就引入了間斷模式,可以說是對掃描模式的一種補充。它可以把0,1,4,5這四個通道進行分組。可以分成0,1一組,4,5一組。也可以每個通道配置為一組。這樣每一組轉換之前都需要先觸發一次。

  Stm32 ADC的單次模式和連續模式。這兩中模式的概念是相對應的。這里的單次模式並不是指一個通道。假如你同時開了ch0,ch1,ch4,ch5這四個通道。單次模式轉換模式下會把這四個通道采集一邊就停止了。而連續模式就是這四個通道轉換完以后再循環過來再從ch0開始。

  另外還有規則組和注入組的概念,因為我這個例程只用到了規則組,就不多介紹這兩個概念,想要弄清楚請自行查閱手冊。

下面進入正題,配置stm32cubeMX。

Stm32cubeMx配置ADC多通道采集

先使能幾個通道,我這里設置為0、1、4、5.

然后就要配置ADC的參數:

Stm32cubeMx配置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采集。

 

二、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 在轉換,后來查看就是Rank1、2、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校准

  串口打印結果如下,至於怎樣串口打印這里就不多說了,想知道的可以看https://www.cnblogs.com/xingboy/p/9522940.html

   

補充:使用定時器與DMA中斷定時采集

  上面只是單純的一直采集的,如果想要用到中斷的話就可以按下面的方式來,ADC配置跟上面說的DMA模式一樣:

  先配置定時器中斷,怎么配置可以參考我的另一個文章https://www.cnblogs.com/xingboy/p/9897500.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采集參考:https://www.cnblogs.com/xingboy/p/10018749.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);

  打印結果如下

    

 

 

 

 

 

  


免責聲明!

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



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