STM32 温度采样定时器触发配置示例
在嵌入式系统中,传统的软件轮询加延时方式采集温度数据存在隐患。例如主循环延时期间 CPU 无法处理其他任务,且多任务环境下难以保证精确的采样周期。为了解决这些问题,可以采用硬件协同的方式。
为什么非要用硬件触发?
对于需要高实时性、长期稳定运行的应用,软件轮询是行不通的。工业级系统要求确定性的行为:第 n 次和第 n+1 次采样的间隔必须严格相等。
STM32 的高级定时器(如 TIM2/TIM3)不仅可以计时,还能输出一个叫 TRGO(Trigger Output)的信号。这个信号可以像'发令枪'一样,精准地告诉 ADC:'现在开始转换!'整个过程不需要 CPU 参与,即使主程序正在处理 Wi-Fi 协议栈或者跑 FreeRTOS 的任务,也不会影响采样节奏。
定时器怎么当'发令员'?一步步带你配置
我们以 TIM3 为例,目标是让它每 1ms 发出一次 TRGO 脉冲,驱动 ADC 启动一次转换。
假设系统主频 72MHz,APB1 总线时钟也是 72MHz,我们要实现 1kHz 的触发频率(即周期 1ms)。
关键参数计算如下:
- 计数器时钟 = 72MHz / (PSC + 1)
- 目标更新频率 = 1kHz → 周期 = 1ms = 1000μs
- 所以:(PSC + 1) × (ARR + 1) / 72,000,000 = 0.001
取 PSC = 71 → 分频后时钟为 1MHz(每个计数 1μs) 取 ARR = 999 → 溢出时间 = 1000 × 1μs = 1ms
接下来设置 TRGO 信号源为'更新事件'(Update Event),这样每次溢出就会输出一个上升沿。
void TIM3_ConfigForADC(void) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitTypeDef timerInit;
timerInit.TIM_Period = 1000 - 1; // ARR
timerInit.TIM_Prescaler = 72 - 1; // PSC
timerInit.TIM_ClockDivision = 0;
timerInit.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &timerInit);
// 关键一步:选择主模式触发源为'更新事件'
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
// 启动定时器
TIM_Cmd(TIM3, ENABLE);
}
这段代码执行完之后,TIM3 就开始自由运行了。它会自动每 1ms 打一枪,告诉 ADC:'该你干活了。'
注:除了更新事件,TRGO 还可以来自比较匹配、捕获等事件,适用于更复杂的同步场景。
ADC 准备好了吗?让它只听 TIM 的话
接下来轮到 ADC 登场。我们需要让 ADC 工作在'外部触发 + 单次转换'模式,并指定由 TIM3_TRGO 作为触发源。
STM32F1 系列中,ADC1 支持多个外部触发源,其中就包括 ADC_ExternalTrigConv_T3_TRGO。
此外,我们要采集的是内部温度传感器,它连接在 ADC 通道 16 上。使用前必须显式启用该功能。
void ADC1_TempSensor_Init(void) {
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef gpioInit;
gpioInit.GPIO_Pin = GPIO_Pin_0;
gpioInit.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &gpioInit);
ADC_InitTypeDef adcInit;
ADC_StructInit(&adcInit);
adcInit.ADC_Mode = ADC_Mode_Independent;
adcInit.ADC_ScanConvMode = DISABLE;
adcInit.ADC_ContinuousConvMode = DISABLE;
adcInit.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
adcInit.ADC_DataAlign = ADC_DataAlign_Right;
adcInit.ADC_NbrOfChannel = ;
ADC_Init(ADC1, &adcInit);
ADC_TempSensorVrefintCmd(ENABLE);
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, , ADC_SampleTime_239Cycles5);
ADC_ResetCalibration(ADC1);
(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
(ADC_GetCalibrationStatus(ADC1));
ADC_Cmd(ADC1, ENABLE);
}
