STM32定時器觸發ADC多通道連續採樣,DMA緩存結果

STM32的ADC使用非常靈活,採樣觸發方面:既支持軟件觸發,定時器或其他硬件電路自動觸發,也支持轉換完成後自動觸發下一通道/輪轉換。轉換結果存儲方面:既支持軟件讀取和轉存,也支持DMA自動存儲轉換結果。STM32書籍介紹的最多的是“軟件觸發 + 查詢法讀取轉換結果的方式”,對採集溫度、溼度這樣近乎直流的信號而言,這種方法足夠應付。但當應用需要提升A/D轉換的採樣率時,這種做法就逐漸無法滿足求了:1、軟件需要通過頻繁的查詢或中斷來確定在採樣間隔時到達時及時觸發下一輪A/D轉換,處理器的其他工作被頻繁打斷,軟件開發難度增加。2、定時器定時中斷/查詢法的定時精度/抖動時間大概是處理器執行數條指令的時間,對於STM32而言,當採樣率大於10KSPS以後,採樣定時抖動造成信噪比降低將使A/D的信噪比降低到與10位A/D相當(具體數學分析詳見本人之前博文:(https://www.cnblogs.com/helesheng/p/8880492.html)。
STM32提供兩種方法來解決這個問題:方法一:讓ADC不停歇的連續進行轉換,轉換結果則通過DMA直接搬運到內存中。由於ADC進行一次轉換的時間可以由ADC時鐘ADCCLK頻率和採樣時間精確確定,這種方法有效的降低了轉換間隔時間的孔徑抖動,提高了信噪比,尤其適合200KSPS以上的高採樣率。有興趣的讀者可以參考我在博文:https://www.cnblogs.com/helesheng/p/8880492.html中給出的代碼。由於無法連續調節ADCCLK頻率和採樣時間,但這種方法的缺陷是無法連續調節採樣率,如20.05KSPS,44.1KSPS等常用但非整數的採樣率是無法被產生的。本文介紹的方法二,是由定時器3的TRGO信號直接出發A/D轉換的方法可以有效的在低於200KSPS以下時實現採樣率的連續調節。由於使用定時器硬件直接出發A/D轉換,無需軟件參與,這種方法也能夠有效的避免採樣間隔的孔徑抖動。另外,爲了避免軟件頻繁參與轉換結果讀取造成的執行效率降低和開發難度增大問題,本文提供了通過DMA讀取自動連續轉換結果的代碼。
以下原創內容歡迎網友轉載,但請註明出處: https://www.cnblogs.com/helesheng
 
一、ADC模塊配置
 1 RCC_ADCCLKConfig(RCC_PCLK2_Div8);   //設置ADC分頻因子8 72M/8=9,ADC最大時間不能超過14M  
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
//ADC1工作在獨立模式 2 3 ADC_InitStructure.ADC_ScanConvMode = ENABLE;//模數轉換工作在掃描模式(多通道) 4 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//ADC工作在非連續模式 5 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;//定時器3的TRGO觸發轉換 6 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//ADC數據右對齊 7 ADC_InitStructure.ADC_NbrOfChannel = 2;//轉換的ADC通道的數目爲2 8 ADC_Init(ADC1, &ADC_InitStructure);//要把以下參數初始化ADC_InitStructure 9 ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_13Cycles5);//ADC1通道6 ,採樣時間爲13.5個週期 10 ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 2, ADC_SampleTime_13Cycles5);//ADC1通道7 ,採樣時間爲13.5個週期 11 ADC_DMACmd(ADC1, ENABLE); //使能ADC1的DMA傳輸方式 12 ADC_Cmd(ADC1, ENABLE);//使能ADC1 13 ADC_ResetCalibration(ADC1);//重置ADC1的校準寄存器 14 while(ADC_GetResetCalibrationStatus(ADC1)); 15 ADC_StartCalibration(ADC1); //開始校準ADC1 16 while(ADC_GetCalibrationStatus(ADC1)); //等待校準完成 17 ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能ADC1軟件轉換
 其中值得注意的是:
1、ADC被配置爲由外部信號觸發,而觸發信號是TIM3產生的TRGO。注意STM32不支持其他定時器的TRGO作爲ADC的觸發源。
2、ADC被配置爲非連續工作模式( ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;)所謂“連續工作模式”,就是前面提到了通過配置每次A/D轉換時間實現採樣定時的工作方式。如果該模式被使能,就意味着ADC會在被一次觸發後就逐通道、不停歇的進行連續的轉換,而不會等到下次定時器觸發信號TRGO再啓動A/D轉換。
3、ADC被配置爲多通道掃描模式(ADC_InitStructure.ADC_ScanConvMode = ENABLE;),這樣ADC會在每次被TIM3觸發後依次完成規則通道組中每個通道的一輪轉換
二、DMA的配置
 1   DMA_DeInit(DMA1_Channel1);
 2   DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//傳輸的源頭地址
 3   DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;//目標地址
 4   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外設作源頭
 5   DMA_InitStructure.DMA_BufferSize = 2000;//數據長度爲2000
 6   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設地址寄存器不遞增
 7   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//內存地址遞增
 8   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外設傳輸以字節爲單位
 9   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//內存以字爲單位
10   DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//循環模式
11   DMA_InitStructure.DMA_Priority = DMA_Priority_High;//4優先級之一的(高優先)
12   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非內存到內存
13   DMA_Init(DMA1_Channel1, &DMA_InitStructure);//根據以上參數初始化DMA_InitStructure
14 
15   DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);//配置DMA1通道1傳輸完成中斷 
16   DMA_Cmd(DMA1_Channel1, ENABLE);//使能DMA1
其中值得注意的是:
1、DMA被配置爲外設到內存方式,每次傳輸的數據寬度是半字(16位)。
2、緩衝長度爲2000個半字(DMA_InitStructure.DMA_BufferSize = 2000;),由於前面配置了兩個通道,緩衝區將一次能存放1000次採樣的結果。
2、使用循環模式(DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;),使能了DMA1中斷(DMA_Init(DMA1_Channel1, &DMA_InitStructure);)這樣將在完成1000次採樣後觸發DMA中斷,以便軟件一次性讀取這1000次轉換的結果。而下一次ADC轉換完成後結果將從頭覆蓋這個緩衝區的內容。
與之配套的中斷控制器配置程序如下:
1 NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
2 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//搶佔優先級設置爲1
3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//子優先級設置爲1
4 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中斷使能
5 NVIC_Init(&NVIC_InitStructure);//按指定參數初始化中斷     
其中搶佔優先級和子優先級可以根據應用實際需要確定。
DMA中斷服務程序如下:
 1 void DMA1_Channel1_IRQHandler(void)
 2 {    
 3     if(DMA_GetITStatus(DMA1_IT_TC1))//判斷通道1是否傳輸完成
 4      {
 5         DMA_ClearITPendingBit(DMA1_IT_TC1);    //清除通道1傳輸完成標誌位
 6 
 7     ////////////此處應編寫代碼從DMA指向的內存區讀走數據,否則可能被覆蓋//////////
 8 
 9 
10      }
11 }
三、TIM3配置
TIM3配置代碼如下:
 1 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能TIM3時鐘
 2 
 3 TIM_TimeBaseStructure.TIM_Period = 1000-1;
 4 TIM_TimeBaseStructure.TIM_Prescaler = 72-1;
 5 TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;      //採樣分頻TIM_CKD_DIV1
 6 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;     //向上計數
 7 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
 8 
 9 TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);//設置輸出TRGO信號
10 TIM_Cmd(TIM3, ENABLE);
其中值得注意的是:
1、週期寄存器和預分頻寄存器配置後,將TIM3的溢出頻率確定爲1KHz。而這些值可以根據應用需要的採樣率自己確定。
2、TIM3溢出後將輸出TRGO信號(TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);)。
 
 更多關於STM32定時器和ADC的高級使用方法,歡迎大家購買我的新書《基於STM32的嵌入式系統原理及應用》(科學出版社出版 ISBN:9787030697974)或我的B站賬號“何樂生0

 

 

 

 

 

 

 

 

 

 

 
 
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章