4. 定时器与PWM:SysTick定时器、通用定时器配置、PWM输出原理、风扇调速实现
各位同学,欢迎来到第四章。这一章我们聊聊定时器和PWM。说实话,在暖通空调主控里,定时器和PWM是真正干活的家伙。你想想看,风扇要转多快、阀门要开多大、压缩机要不要启动——这些动作背后,全是定时器和PWM在驱动。
我个人习惯把定时器比作MCU的「心跳」。没有它,整个系统就像没了节奏的乐队,各吹各的号。而PWM呢,说白了就是通过调节脉冲宽度来模拟模拟量输出。嗯,这里要注意,很多新手容易把PWM和真正的DAC搞混,后面我会细说。
4.1 SysTick定时器:系统的心跳
SysTick是ARM Cortex-M内核自带的一个24位递减计数器。它最大的特点就是——简单、可靠、所有Cortex-M芯片都有。我在项目中遇到过客户要求换MCU型号,但SysTick的代码几乎不用改,这就是它的优势。
SysTick主要用来做什么?
- 操作系统的心跳:FreeRTOS、uCOS这些RTOS,全靠SysTick来产生时间片中断。
- 精准延时:比如你要等100微秒再读传感器,用SysTick最合适。
- 软件定时器:在裸机编程里,我经常用SysTick来维护一个全局的毫秒计数器。
配置SysTick其实就几步:
// SysTick配置示例:产生1ms中断
void SysTick_Init(void)
{
// 1. 设置重装载值
// 假设系统时钟为72MHz,1ms需要72000个时钟周期
SysTick->LOAD = 72000 - 1;
// 2. 清零当前值
SysTick->VAL = 0;
// 3. 配置控制寄存器
// 使能SysTick、使能中断、使用系统时钟
SysTick->CTRL = 0x07;
}
// 中断服务函数
void SysTick_Handler(void)
{
static uint32_t tick_ms = 0;
tick_ms++;
// 每1000ms翻转一次LED
if(tick_ms % 1000 == 0)
{
GPIO_Toggle(LED_PIN);
}
}
4.2 通用定时器:真正的多功能选手
如果说SysTick是「基本款」,那通用定时器就是「豪华版」。以STM32为例,通用定时器(TIM2-TIM5)支持:
- 向上/向下计数模式
- 输入捕获(测量脉冲宽度)
- 输出比较(产生精确延时)
- PWM生成
- 编码器接口
配置通用定时器,我一般按这个流程走:
// 通用定时器配置示例:TIM2产生100Hz定时中断
void TIM2_Init(void)
{
// 1. 使能定时器时钟
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// 2. 设置预分频器
// 72MHz / 7200 = 10kHz
TIM2->PSC = 7200 - 1;
// 3. 设置自动重装载值
// 10kHz / 100 = 100Hz
TIM2->ARR = 100 - 1;
// 4. 更新事件产生中断
TIM2->DIER |= TIM_DIER_UIE;
// 5. 使能定时器
TIM2->CR1 |= TIM_CR1_CEN;
// 6. 配置NVIC
NVIC_EnableIRQ(TIM2_IRQn);
}
定时频率 = 系统时钟 / (PSC + 1) / (ARR + 1)记住这个公式,你就能算出任何想要的频率。
4.3 PWM输出原理:说白了就是占空比
PWM,全称Pulse Width Modulation,脉冲宽度调制。你想想看,一个方波信号,高电平占整个周期的比例,就是占空比。占空比从0%到100%,对应的等效电压就从0V到VCC。
为什么会这样?因为人眼和电机都有惯性。LED闪烁太快你看不出来,电机电压变化太快转子也反应不过来。所以PWM本质上是在「骗」负载——用高频开关来模拟连续电压。
在暖通空调里,PWM的应用场景:
- 风扇调速:改变占空比,风扇转速跟着变
- 阀门控制:比例阀的开度控制
- 加热器功率:电加热的功率调节
- 压缩机变频:通过PWM控制逆变器
配置PWM输出,以TIM3的通道1为例:
// PWM输出配置示例:TIM3_CH1输出50Hz PWM
void PWM_Init(void)
{
// 1. 使能时钟
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
// 2. 配置GPIO为复用功能
GPIOA->MODER |= GPIO_MODER_MODER6_1; // AF模式
GPIOA->AFR[0] |= 0x02 << 24; // AF2 = TIM3_CH1
// 3. 配置定时器
TIM3->PSC = 7200 - 1; // 72MHz/7200 = 10kHz
TIM3->ARR = 200 - 1; // 10kHz/200 = 50Hz
// 4. 配置PWM模式
TIM3->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; // PWM模式1
TIM3->CCMR1 |= TIM_CCMR1_OC1PE; // 预装载使能
// 5. 设置初始占空比50%
TIM3->CCR1 = 100;
// 6. 使能输出
TIM3->CCER |= TIM_CCER_CC1E;
// 7. 使能定时器
TIM3->CR1 |= TIM_CR1_CEN;
}
// 设置占空比函数
void PWM_SetDuty(uint16_t duty)
{
if(duty > 200) duty = 200; // 限幅
TIM3->CCR1 = duty;
}
4.4 风扇调速实现:从理论到实战
好了,前面铺垫了这么多,终于到实战环节了。暖通空调里的风扇,常见的有两种:
- 直流无刷风扇:4线制(电源、地、PWM输入、转速反馈)
- 交流风扇:通过可控硅或继电器控制
这里我们重点讲直流无刷风扇的PWM调速。这种风扇在暖通空调里用得最多,比如室内机的贯流风扇、室外机的轴流风扇。
完整的调速实现代码:
// 风扇控制结构体
typedef struct {
uint16_t target_speed; // 目标转速,单位RPM
uint16_t current_speed; // 当前转速
uint8_t duty_cycle; // 当前占空比 0-100%
uint8_t fan_status; // 风扇状态
} Fan_Control_t;
// 风扇初始化
void Fan_Init(void)
{
PWM_Init(); // 初始化PWM输出
// 配置转速反馈引脚为输入捕获
TIM4_IC_Init();
// 设置初始占空比为0,风扇停止
PWM_SetDuty(0);
}
// 风扇调速函数
void Fan_SetSpeed(uint16_t target_rpm)
{
uint8_t duty;
// 根据目标转速计算占空比
// 假设风扇额定转速3000RPM对应100%占空比
duty = (target_rpm * 100) / 3000;
// 限幅处理
if(duty > 100) duty = 100;
if(duty < 20) duty = 20; // 最低启动占空比
// 设置PWM占空比
PWM_SetDuty((duty * 200) / 100); // 转换为CCR值
// 记录目标转速
fan_control.target_speed = target_rpm;
fan_control.duty_cycle = duty;
}
// 转速反馈处理(在输入捕获中断中调用)
void Fan_Speed_Feedback(uint16_t period_us)
{
// 计算转速
// 假设风扇每转产生2个脉冲
// 转速 = 60 / (period_us * 2 / 1000000)
uint16_t rpm = 30000000 / period_us;
fan_control.current_speed = rpm;
// 闭环控制:如果实际转速与目标偏差太大,调整占空比
if(abs(rpm - fan_control.target_speed) > 50)
{
// 简单的PID调节
int16_t error = fan_control.target_speed - rpm;
int16_t adjust = error / 10; // 比例系数
fan_control.duty_cycle += adjust;
// 限幅
if(fan_control.duty_cycle > 100) fan_control.duty_cycle = 100;
if(fan_control.duty_cycle < 20) fan_control.duty_cycle = 20;
// 更新PWM
PWM_SetDuty((fan_control.duty_cycle * 200) / 100);
}
}
| 参数 | 推荐值 | 说明 |
|---|---|---|
| PWM频率 | 20-25kHz | 避免人耳可听噪声 |
| 最低占空比 | 20-30% | 低于此值风扇可能不启动 |
| 调速分辨率 | 8位(0-255) | 足够平滑调速 |
| 反馈采样周期 | 100ms | 太频繁会增加CPU负担 |
好了,这一章的内容就到这里。定时器和PWM是嵌入式开发的基石,掌握了它们,你就能控制暖通空调里的大部分执行器。下一章我们会讲ADC和传感器采集,到时候你会看到定时器如何与ADC配合,实现精准的采样时序。
记住,多动手写代码,多拿示波器看波形。纸上得来终觉浅,绝知此事要躬行。