在STM32的ADC模塊中,**采樣時機(Sampling Time)和轉換時機(Conversion Time)**是ADC工作流程中的兩個關鍵階段,直接影響采樣精度和系統實時性。以下是詳細解析:
1. 采樣時機(Sampling Time)
(1)定義
- 采樣階段:ADC對輸入信號進行保持和穩定的過程。
- 采樣時間:由
ADC_SMPRx
寄存器配置,決定采樣電容充電時間。
(2)配置參數
STM32F103的采樣時間可設置為:
typedef enum {ADC_SampleTime_1Cycles5, // 1.5周期ADC_SampleTime_7Cycles5, // 7.5周期ADC_SampleTime_13Cycles5, // 13.5周期ADC_SampleTime_28Cycles5, // 28.5周期ADC_SampleTime_41Cycles5, // 41.5周期ADC_SampleTime_55Cycles5, // 55.5周期ADC_SampleTime_71Cycles5, // 71.5周期ADC_SampleTime_239Cycles5 // 239.5周期(用于高阻抗信號)
} ADC_SampleTime;
(3)選擇原則
信號類型 | 推薦采樣時間 | 原因 |
---|---|---|
低阻抗信號 | 1.5~28.5周期 | 信號源阻抗低(如運放輸出),快速穩定。 |
高阻抗信號 | 55.5~239.5周期 | 信號源阻抗高(如溫度傳感器、分壓電路),需更長時間充電。 |
內部通道 | ≥239.5周期 | 內部溫度傳感器和VREFINT阻抗極高,必須延長采樣時間。 |
(4)代碼示例
// 配置PA0(低阻抗)和溫度傳感器(高阻抗)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5); // 快速采樣
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5); // 慢速采樣
2. 轉換時機(Conversion Time)
(1)定義
- 轉換階段:ADC核心將采樣到的模擬量轉換為數字量的過程。
- 固定耗時:12位分辨率下恒定為12.5個ADC時鐘周期(與采樣時間無關)。
(2)總轉換時間計算
[
T_{total} = (T_{sampling} + 12.5) \times \frac{1}{f_{ADC}}
]
- 示例:
- ADC時鐘 = 14 MHz,采樣時間 = 55.5周期
- 總時間 = (55.5 + 12.5) / 14MHz ≈ 4.86μs
(3)吞吐量限制
- 理論最大采樣率:
[
f_{max} = \frac{1}{T_{total}}
]- 若
T_total=4.86μs
,則f_max≈205kHz
(單通道)。
- 若
3. 采樣與轉換的時序圖
采樣階段 轉換階段
|-------- Sampling Time --------|-- 12.5 Cycles --|
|<----------- Total Conversion Time ------------>|
4. 關鍵影響因素
(1)ADC時鐘頻率
- 最大14MHz(STM32F103),需分頻APB2時鐘:
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 72MHz/6=12MHz
- 更高時鐘 → 更快轉換,但可能降低精度。
(2)通道切換延遲
- 多通道掃描時,每個通道需單獨配置采樣時間,切換通道會增加額外延遲。
(3)觸發方式
- 硬件觸發(如定時器):精確控制采樣間隔。
- 軟件觸發:靈活性高但時序不易控制。
5. 優化策略
(1)動態調整采樣時間
根據信號類型切換采樣時間:
void Set_ADC_SampleTime(ADC_TypeDef* ADCx, uint8_t channel, uint8_t isHighImpedance) {ADC_SampleTime time = isHighImpedance ? ADC_SampleTime_239Cycles5 : ADC_SampleTime_28Cycles5;ADC_RegularChannelConfig(ADCx, channel, 1, time);
}
(2)使用注入組中斷
高優先級信號通過注入組立即響應:
// 過壓時緊急采樣
void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_JEOC)) {uint16_t emergencyData = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);}
}
(3)DMA傳輸規則組數據
避免CPU輪詢,提高效率:
uint16_t adcValues[3];
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues;
ADC_DMACmd(ADC1, ENABLE);
6. 實際應用示例
電機電流監測系統
- 規則組:
- 通道PA0(電流傳感器),采樣時間7.5周期(低阻抗)。
- 定時器觸發1kHz采樣,DMA傳輸數據。
- 注入組:
- 比較器檢測過流時觸發PA8中斷,注入組采集PA1(備份傳感器)。
- 計算總延遲:
- 采樣+轉換時間 = (7.5 + 12.5) / 14MHz ≈ 1.43μs
- 1kHz采樣率下,CPU負載極低。
總結
- 采樣時機:由信號源阻抗決定,需足夠時間穩定信號。
- 轉換時機:固定12.5周期,與ADC時鐘頻率直接相關。
- 優化核心:在精度和速度間平衡,優先保證信號完整性。
在STM32的ADC編程中,采樣、轉換和數據獲取的流程分布在程序的不同位置,具體取決于觸發方式(軟件/硬件)和數據讀取方式(輪詢/DMA/中斷)。以下是基于STM32F103的詳細說明和代碼示例:
1. 采樣(Sampling)
觸發采樣的位置
- 軟件觸發:在代碼中顯式調用啟動轉換函數。
// 在需要采樣的地方(如主循環或定時器回調中) ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 啟動規則組采樣 ADC_SoftwareStartInjectedConv(ADC1); // 啟動注入組采樣
- 硬件觸發:由定時器、外部中斷等自動觸發,無需手動調用。
// 配置TIM2觸發規則組采樣(無需在主循環中操作) TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
采樣時間配置
在ADC初始化階段設置:
// 配置規則組通道的采樣時間(在ADC初始化時)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
2. 轉換(Conversion)
轉換的觸發
- 轉換由硬件自動完成,無需程序干預。
- 轉換時間固定為12.5個ADC時鐘周期(如14MHz時鐘下約0.89μs)。
判斷轉換完成
- 輪詢方式:檢查標志位。
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 規則組 while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC)); // 注入組
- 中斷方式:在中斷服務函數中處理。
void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_EOC)) {uint16_t data = ADC_GetConversionValue(ADC1); // 規則組數據ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);}if (ADC_GetITStatus(ADC1, ADC_IT_JEOC)) {uint16_t data = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);} }
3. 獲取數據(Data Readout)
規則組數據
- 輪詢讀取:
ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); uint16_t adcValue = ADC_GetConversionValue(ADC1); // 讀取規則組數據
- DMA傳輸(推薦多通道):
uint16_t adcValues[3]; // 存儲3個通道的數據 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues; ADC_DMACmd(ADC1, ENABLE); // 數據會自動更新到adcValues數組
注入組數據
- 中斷讀取:
void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_JEOC)) {uint16_t injData = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);} }
4. 完整流程示例(規則組+注入組)
初始化配置
void ADC_Init(void) {// 1. 時鐘和GPIO初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);// 2. 規則組配置(PA0)ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;ADC_Init(ADC1, &ADC_InitStructure);// 3. 注入組配置(PA1)ADC_InjectedChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_Ext_IT15);// 4. 啟用中斷ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLE);NVIC_EnableIRQ(ADC1_2_IRQn);// 5. 校準和啟動ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}
主程序中的采樣與讀取
int main(void) {ADC_Init();while (1) {// 規則組:手動觸發并讀取ADC_SoftwareStartConvCmd(ADC1, ENABLE);while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));uint16_t regularData = ADC_GetConversionValue(ADC1);// 注入組:由PA8按鍵觸發(中斷中自動處理)Delay_ms(100); // 模擬其他任務}
}
中斷服務函數
void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_JEOC)) {uint16_t injectedData = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);// 處理緊急數據(如過壓保護)}
}
5. 關鍵總結
操作 | 程序位置 | 常用函數/配置 |
---|---|---|
采樣觸發 | 主循環/定時器回調/中斷 | ADC_SoftwareStartConvCmd() |
采樣時間 | ADC初始化階段 | ADC_RegularChannelConfig() |
轉換等待 | 輪詢/中斷 | ADC_GetFlagStatus() 或中斷標志 |
數據讀取 | 輪詢/DMA/中斷 | ADC_GetConversionValue() 或DMA數組 |
- 規則組:適合周期性采樣,配合DMA提高效率。
- 注入組:用于緊急事件,中斷中即時響應。
- 硬件觸發:精確控制時序,減少CPU干預。
在STM32的ADC配置中,**連續掃描(Scan Mode)和連續轉換(Continuous Conversion Mode)**是兩個關鍵的工作模式,它們決定了ADC如何對多個通道進行采樣和數據更新。以下是詳細解釋:
1. 連續掃描模式(Scan Mode)
功能
- 多通道自動切換:ADC按預設順序依次采樣多個通道(規則組或注入組),無需手動切換。
- 數據存儲:需配合DMA或中斷將數據存入數組(規則組數據寄存器
ADC_DR
會被覆蓋)。
配置方法
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 啟用掃描模式
ADC_InitStructure.ADC_NbrOfChannel = 3; // 通道數量
ADC_Init(ADC1, &ADC_InitStructure);// 設置通道順序(規則組)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); // 第1通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); // 第2通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); // 第3通道
典型應用
- 同時監測多個傳感器(如溫度、電壓、電流)。
- 需配合DMA傳輸數據:
uint16_t adcValues[3]; // 存儲多通道數據 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues; ADC_DMACmd(ADC1, ENABLE);
2. 連續轉換模式(Continuous Conversion Mode)
功能
- 自動重啟轉換:完成一次轉換后立即開始下一次,無需手動觸發。
- 單通道/多通道:可與掃描模式組合使用。
配置方法
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 啟用連續轉換
ADC_Init(ADC1, &ADC_InitStructure);
典型應用
- 實時監控信號變化(如音頻采集)。
- 單通道連續采樣示例:
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 啟動后自動循環采樣
3. 組合使用場景
模式對比
模式組合 | 行為 | 適用場景 |
---|---|---|
掃描+單次轉換 | 按順序采樣所有通道后停止 | 定時觸發多通道采樣(如每1秒1次) |
掃描+連續轉換 | 循環采樣所有通道,數據持續更新 | 實時多通道監測(如電機控制) |
單通道+連續轉換 | 重復采樣同一通道 | 高速信號捕獲(如示波器) |
代碼示例(掃描+連續轉換)
// 初始化ADC(多通道循環采樣)
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 3;
ADC_Init(ADC1, &ADC_InitStructure);// 配置DMA自動傳輸數據
uint16_t adcValues[3];
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues;
ADC_DMACmd(ADC1, ENABLE);// 啟動ADC
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
4. 關鍵注意事項
-
數據覆蓋問題
- 規則組掃描時,
ADC_DR
寄存器會被新數據覆蓋,必須使用DMA或中斷及時讀取。
- 規則組掃描時,
-
觸發源選擇
- 連續轉換模式下,硬件觸發(如定時器)可精確控制采樣間隔。
-
功耗權衡
- 連續轉換模式功耗較高,電池供電設備建議使用單次轉換+定時觸發。
-
注入組優先級
- 即使規則組處于連續轉換中,注入組觸發仍會立即中斷當前轉換。
5. 常見問題解答
Q1:掃描模式必須用DMA嗎?
A1:不是必須,但強烈推薦。DMA可避免數據丟失,若用中斷讀取,需確保處理速度夠快。
Q2:如何暫停連續轉換?
A2:調用ADC_ContinuousConvModeCmd(ADC1, DISABLE)
,或直接關閉ADC:
ADC_Cmd(ADC1, DISABLE); // 完全停止ADC
Q3:規則組和注入組能同時用掃描模式嗎?
A3:可以!規則組和注入組的掃描相互獨立,但注入組會打斷規則組當前轉換。
總結
- 掃描模式:解決多通道自動切換問題,需注意數據存儲。
- 連續轉換:實現無縫采樣,適合實時性要求高的場景。
- 組合使用:靈活應對復雜需求(如實時監測多個傳感器)。
通過合理配置這兩種模式,可以充分發揮STM32 ADC的性能。
在STM32的ADC模塊中,**連續轉換模式(Continuous Conversion Mode)和單次轉換模式(Single Conversion Mode)**的選擇直接影響系統的實時性、功耗和采樣精度。以下是針對您提到的兩點的詳細解釋:
1. 連續轉換模式 + 硬件觸發的精確控制
工作原理
- 連續轉換模式:ADC完成一次轉換后,立即自動啟動下一次轉換,形成不間斷的采樣循環。
- 硬件觸發(如定時器TIM2):通過外部信號(如定時器更新事件)控制ADC的啟動時機,實現固定間隔采樣。
為何能精確控制采樣間隔?
- 硬件同步:定時器的時鐘源穩定(如內部72MHz晶振),觸發信號的間隔誤差極小(納秒級)。
- 規避軟件延遲:相比軟件觸發(需CPU干預),硬件觸發完全由外設自動完成,無調度延遲。
配置示例
// 配置TIM2觸發ADC(1kHz采樣率,即1ms間隔)
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_InitStructure.TIM_Period = 7200 - 1; // 72MHz / 7200 = 10kHz
TIM_InitStructure.TIM_Prescaler = 0;
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // 更新事件觸發ADC// 配置ADC為連續轉換+硬件觸發
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_Init(ADC1, &ADC_InitStructure);
效果:ADC以精確的1ms間隔采樣,適合需要嚴格周期性的應用(如數字濾波器、PWM控制)。
2. 連續轉換模式的功耗問題
高功耗原因
- ADC持續工作:連續轉換模式下,ADC核心和模擬電路(如采樣保持電路)始終處于活動狀態,電流消耗較大(STM32F103典型值約1mA@14MHz ADC時鐘)。
- 頻繁數據更新:即使無實際信號變化,ADC仍持續轉換,浪費功耗。
單次轉換+定時觸發的低功耗方案
(1)工作流程
- 單次轉換模式:每次觸發后只采樣一次,然后自動停止。
- 定時器觸發:按需喚醒ADC(如每秒1次),其余時間ADC處于低功耗狀態。
(2)配置代碼
// 配置ADC為單次轉換+硬件觸發
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 單次模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_Init(ADC1, &ADC_InitStructure);// 配置TIM2低速觸發(如1Hz)
TIM_InitStructure.TIM_Period = 72000000 - 1; // 72MHz / 72000000 = 1Hz
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
(3)功耗對比
模式 | 平均電流(示例) | 適用場景 |
---|---|---|
連續轉換(1kHz) | ~1.2mA | 實時控制、高速信號處理 |
單次轉換+定時(1Hz) | ~50μA | 電池供電的傳感器監測 |
3. 如何選擇?
選擇連續轉換模式當:
- 需要高實時性(如電機控制、音頻采樣)。
- 系統供電充足(如插電設備)。
選擇單次轉換+定時觸發當:
- 設備由電池供電(如IoT傳感器)。
- 信號變化緩慢(如溫度、濕度監測)。
折中方案
- 動態切換模式:根據任務需求靈活調整。
void Set_ADCMode(uint8_t isHighSpeed) {ADC_ContinuousConvModeCmd(ADC1, isHighSpeed ? ENABLE : DISABLE);TIM_SetAutoreload(TIM2, isHighSpeed ? 7200 - 1 : 72000000 - 1); // 切換采樣率 }
4. 實測建議
- 測量電流:用萬用表對比兩種模式的功耗差異(注意關閉未用外設)。
- 喚醒時間:單次模式首次轉換可能有額外延遲(需重新校準)。
通過合理選擇轉換模式,可平衡性能與功耗,滿足不同應用場景需求。
在STM32的ADC應用中,定時器觸發ADC采樣通常不需要在定時器中斷服務函數(如TIMx_IRQHandler
)中編寫代碼,而是直接通過定時器的硬件觸發輸出(如TRGO)自動控制ADC啟動。以下是具體說明和代碼示例:
1. 硬件觸發ADC的配置流程
(1)定時器配置
- 不依賴中斷:僅用定時器的更新事件(UEV)觸發ADC,無需進入中斷。
- 關鍵步驟:
- 設置定時器周期(決定采樣間隔)。
- 配置定時器觸發輸出(TRGO)。
// 配置TIM2觸發ADC(1kHz采樣率,72MHz主頻)
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_InitStructure.TIM_Period = 72000 - 1; // 72000 / 72MHz = 1ms (1kHz)
TIM_InitStructure.TIM_Prescaler = 0; // 不分頻
TIM_InitStructure.TIM_ClockDivision = 0;
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);// 配置TIM2更新事件觸發TRGO
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
TIM_Cmd(TIM2, ENABLE); // 啟動定時器
(2)ADC配置
- 設置為硬件觸發模式,選擇定時器作為觸發源。
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; // TIM2觸發
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 單次轉換模式(推薦低功耗)
ADC_Init(ADC1, &ADC_InitStructure);
2. 何時需要定時器中斷?
如果需要在定時器事件中執行其他任務(如數據處理、狀態檢查),才需啟用中斷。此時需:
(1)啟用定時器中斷
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 允許更新中斷
NVIC_EnableIRQ(TIM2_IRQn); // 啟用NVIC中斷
(2)中斷服務函數示例
void TIM2_IRQHandler(void) {if (TIM_GetITStatus(TIM2, TIM_IT_Update)) {// 此處可添加其他任務(如讀取ADC數據、控制邏輯)// 注意:ADC采樣已由硬件自動觸發,無需在此啟動!TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中斷標志}
}
3. 完整代碼示例(無中斷方案)
目標:用TIM2以1kHz觸發ADC采樣,DMA傳輸數據。
#include "stm32f10x.h"uint16_t adcValue; // 存儲ADC數據void ADC_TIM_Config(void) {// 1. 啟動時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);// 2. 配置GPIO(PA0為ADC輸入)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);// 3. 配置TIM2(1kHz觸發)TIM_TimeBaseInitTypeDef TIM_InitStructure;TIM_InitStructure.TIM_Period = 72000 - 1; // 1ms間隔TIM_InitStructure.TIM_Prescaler = 0;TIM_TimeBaseInit(TIM2, &TIM_InitStructure);TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // 更新事件觸發ADCTIM_Cmd(TIM2, ENABLE);// 4. 配置ADC1(單次轉換+定時器觸發)ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 單次轉換ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_Init(ADC1, &ADC_InitStructure);// 5. 配置ADC通道ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);// 6. 啟用ADCADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}int main(void) {ADC_TIM_Config();while (1) {// 數據通過DMA或中斷自動更新(此處為輪詢示例)if (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) {adcValue = ADC_GetConversionValue(ADC1);ADC_ClearFlag(ADC1, ADC_FLAG_EOC);}}
}
4. 關鍵注意事項
- 無需中斷觸發ADC:硬件觸發本身不需要CPU干預,直接由定時器信號啟動ADC。
- 中斷的用途:僅當需要在定時器事件中執行額外邏輯(如數據處理、狀態機更新)時才啟用中斷。
- DMA推薦:高頻采樣時,務必使用DMA傳輸數據,避免CPU輪詢開銷。
總結
- 純硬件觸發:配置定時器TRGO + ADC外部觸發,無需中斷,效率最高。
- 中斷的適用場景:同步執行其他任務(如報警檢查、數據打包)。
- 低功耗設計:單次轉換模式 + 定時器觸發,適合電池供電設備。
在STM32的ADC應用中,定時器觸發的是ADC的啟動時機,即控制何時開始一次完整的“采樣+轉換”過程。以下是關鍵概念的分步解釋和代碼實現:
1. 定時器觸發的本質
- 觸發的內容:定時器觸發的是ADC的完整轉換流程,包括:
- 采樣階段(Sampling Phase):ADC對輸入信號進行采樣保持(時間由
ADC_SampleTime
決定)。 - 轉換階段(Conversion Phase):ADC將采樣值轉換為數字量(固定12.5個ADC時鐘周期)。
- 采樣階段(Sampling Phase):ADC對輸入信號進行采樣保持(時間由
- 定時器的作用:精確控制每次“采樣+轉換”的啟動時刻,而非單獨控制采樣或轉換。
2. 數據獲取的位置
(1)DMA自動傳輸(推薦方式)
- 配置步驟:
uint16_t adcValue; // 存儲ADC數據// 啟用ADC DMA DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adcValue; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 1; DMA_Init(DMA1_Channel1, &DMA_InitStructure); ADC_DMACmd(ADC1, ENABLE);
- 數據獲取:
定時器觸發ADC后,轉換結果會自動通過DMA傳輸到adcValue
變量,無需程序干預。
(2)中斷中讀取
- 配置步驟:
// 啟用ADC轉換完成中斷 ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); NVIC_EnableIRQ(ADC1_2_IRQn);// 中斷服務函數 void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_EOC)) {adcValue = ADC_GetConversionValue(ADC1);ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);} }
- 數據獲取:
每次轉換完成后,在中斷中讀取ADC_DR
寄存器。
(3)輪詢方式(低效,僅用于測試)
// 主循環中等待轉換完成
while (1) {ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 定時器觸發時無需此句while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));adcValue = ADC_GetConversionValue(ADC1);
}
3. 完整代碼示例(定時器觸發+DMA)
目標:TIM2以1kHz觸發ADC1采樣PA0,DMA自動傳輸數據。
#include "stm32f10x.h"uint16_t adcValue; // 存儲ADC數據void ADC_Config(void) {// 1. 啟用時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// 2. 配置PA0為模擬輸入GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);// 3. 配置TIM2(1kHz觸發)TIM_TimeBaseInitTypeDef TIM_InitStructure;TIM_InitStructure.TIM_Period = 72000 - 1; // 72MHz / 72000 = 1kHzTIM_InitStructure.TIM_Prescaler = 0;TIM_TimeBaseInit(TIM2, &TIM_InitStructure);TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // 更新事件觸發ADCTIM_Cmd(TIM2, ENABLE);// 4. 配置DMADMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adcValue;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize = 1;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循環模式DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_Cmd(DMA1_Channel1, ENABLE);// 5. 配置ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 單次轉換ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_Init(ADC1, &ADC_InitStructure);// 6. 配置ADC通道ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);// 7. 啟用ADC和DMAADC_DMACmd(ADC1, ENABLE);ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}int main(void) {ADC_Config();while (1) {// adcValue已通過DMA自動更新,可直接使用float voltage = adcValue * 3.3f / 4095; // 轉換為電壓值}
}
4. 關鍵總結
問題 | 答案 |
---|---|
定時器觸發的內容 | 觸發完整的ADC轉換流程(采樣+轉換) |
采樣時刻控制 | 由定時器的觸發信號決定 |
數據獲取位置 | DMA自動傳輸到變量 / 中斷中讀取 / 輪詢ADC_DR 寄存器 |
最優方案 | 定時器硬件觸發 + DMA傳輸(無CPU干預,高效低功耗) |
通過這種方式,STM32可以實現精確的定時采樣,同時最小化CPU負載。
在STM32的ADC應用中,定時器觸發ADC采樣時,選擇單次轉換模式(Single Conversion)還是連續轉換模式(Continuous Conversion),取決于具體的應用場景和需求。以下是兩者的對比分析和配置建議:
1. 單次轉換模式(Single Conversion)
特點
- 工作流程:
每次定時器觸發后,ADC執行一次完整的采樣+轉換,然后自動停止,等待下一次觸發。 - 功耗:較低(僅在觸發時激活ADC)。
- 數據更新:每次觸發后數據更新一次。
適用場景
- 低頻采樣:如溫度監測(每秒幾次)。
- 電池供電設備:需要最小化功耗。
- 事件觸發采樣:如按鍵按下后采集一次數據。
配置代碼
// ADC初始化配置
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 單次轉換模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; // TIM2觸發
ADC_Init(ADC1, &ADC_InitStructure);// 啟動ADC(首次啟動后,后續由定時器自動觸發)
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
2. 連續轉換模式(Continuous Conversion)
特點
- 工作流程:
定時器首次觸發后,ADC持續循環執行采樣+轉換,直到手動停止。
(注:在硬件觸發下,連續轉換模式仍依賴定時器的周期性觸發。) - 功耗:較高(ADC持續運行)。
- 數據更新:數據持續刷新,速率由定時器觸發頻率決定。
適用場景
- 高頻實時采樣:如音頻信號處理(>1kHz)。
- 控制環路:如電機PID控制,需要嚴格周期性的數據更新。
- 多通道掃描:配合DMA實現無縫數據流。
配置代碼
// ADC初始化配置
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 連續轉換模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; // TIM2觸發
ADC_Init(ADC1, &ADC_InitStructure);// 啟動ADC(首次啟動后,ADC會持續運行,但每次轉換仍由定時器觸發)
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
3. 關鍵區別與選擇建議
特性 | 單次轉換模式 | 連續轉換模式 |
---|---|---|
觸發后行為 | 采樣一次后停止 | 持續采樣,直到手動停止 |
功耗 | 低(適合電池供電) | 高(適合持續供電場景) |
數據更新頻率 | 由定時器觸發頻率決定 | 由定時器觸發頻率決定 |
硬件資源占用 | ADC間歇工作 | ADC持續占用 |
典型應用 | 溫度傳感器、低速監測 | 電機控制、音頻采集 |
選擇原則
-
單次轉換模式:
- 需要低功耗或非連續采樣時使用。
- 即使定時器觸發頻率很高(如1kHz),每次觸發仍只采樣一次。
-
連續轉換模式:
- 需要無縫數據流或嚴格實時性時使用。
- 注意:在硬件觸發下,連續轉換模式的實際采樣率仍由定時器決定,但ADC核心會保持活躍狀態。
4. 常見問題解答
Q1:連續轉換模式下,定時器觸發還有意義嗎?
- 有意義!連續轉換模式只是保持ADC核心處于工作狀態,但每次轉換的啟動時刻仍由定時器精確控制。
- 若禁用硬件觸發(如用軟件觸發),ADC會以最大速度連續采樣(不推薦,可能丟失時序精度)。
Q2:單次模式是否會漏掉定時器觸發信號?
- 不會。每次定時器觸發都會啟動一次新的轉換,即使前一次轉換已完成。
- 但需確保兩次觸發的間隔大于單次轉換的總時間(采樣時間 + 12.5周期)。
Q3:如何動態切換模式?
// 運行時切換為單次模式
ADC_ContinuousConvModeCmd(ADC1, DISABLE);// 運行時切換為連續模式
ADC_ContinuousConvModeCmd(ADC1, ENABLE);
5. 完整示例(單次轉換 + 定時器觸發 + DMA)
#include "stm32f10x.h"uint16_t adcValue;void ADC_Config(void) {// 1. 啟用時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// 2. 配置PA0為模擬輸入GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);// 3. 配置TIM2(100Hz觸發)TIM_TimeBaseInitTypeDef TIM_InitStructure;TIM_InitStructure.TIM_Period = 720000 - 1; // 72MHz / 720000 = 100HzTIM_InitStructure.TIM_Prescaler = 0;TIM_TimeBaseInit(TIM2, &TIM_InitStructure);TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // 更新事件觸發ADCTIM_Cmd(TIM2, ENABLE);// 4. 配置DMA(單次傳輸)DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adcValue;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize = 1;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循環模式DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_Cmd(DMA1_Channel1, ENABLE);// 5. 配置ADC(單次轉換 + TIM2觸發)ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 單次模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_Init(ADC1, &ADC_InitStructure);// 6. 配置ADC通道ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);// 7. 啟用ADC和DMAADC_DMACmd(ADC1, ENABLE);ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}int main(void) {ADC_Config();while (1) {// adcValue每10ms自動更新一次(由TIM2觸發)float voltage = adcValue * 3.3f / 4095;}
}
總結
- 單次轉換模式:節能,適合低頻或事件觸發場景。
- 連續轉換模式:實時性強,適合高頻或控制環路。
- 硬件觸發:無論哪種模式,定時器均可精確控制采樣間隔。
- 推薦組合:
- 低功耗應用:單次轉換 + 定時器觸發 + DMA。
- 實時控制:連續轉換 + 定時器觸發 + DMA。