PWM,即脉宽调制(PWM,Pulse Width Modulation),在这篇文章中,暂时将之简单的理解为: 在一段时间内,控制单片机输出高低电平的手段。
在单片机中,经常用PWM实现变速马达,渐变灯,这两者均是通过控制一段时间内单片机输出高电平或低电平而实现想要的功能,以渐变灯为例。
假如现需要一个LED灯以 90HZ 的频率,在 3.5S 内从灭到亮,单片机的中断周期为 32.5us,假使LED为高推(高电平点亮)。
当看到这个题目时,首先要想到的是 需要用PWM实现。目前市面上的单片机多数都提供了若干通道的硬件PWM,我们仅需要设定寄存器就可以实现我们所需的功能,但这篇文章,要讲解 软件 实现PWM。这就需要引入 占空比 和 周期计数 两个概念。
Period_cnt = T周期 / T中断周期 = 11ms / 32.5us ≈ 338 次中断。
接着来分析这一题目。90HZ 的频率决定了LED灯的周期,频率影响着灯变化的细腻程度,频率越高,人眼看到灯的变化就越柔和。回想在文章开头提到PWM的概念: 在一段时间内,控制单片机输出高低电平的手段。 既然有提到一段时间内,那么我们就要想,是哪一段时间?又根据什么判断应该 输出高 还是 输出低 ?
Duty = T周期 / T中断周期 = 11ms / 32.5us ≈ 338
在算出占空比后,我们还需要明白一件事情,即: 中断多少次,占空比增加1?可参考 下方公式,即每中断318次,占空比 + 1。
Duty_interrupt = T渐变时长 / T中断周期 / CNT占空比 = 3.5s / 32.5us / 338 ≈ 318
至此,我们已然把计算工作做完,接下来,就是利用代码实现PWM。在代码中会详细讲解 占空比及周期计数的关系。
/*
PWM本质上是利用周期计数和占空比相比较, 从而输出高电平 或者 低电平(假若高电平驱动外部设备)
假如说一个周期是100ms, 要求占空比是20%, 那么在一个周期内的 0-20ms 间要输出高电平, 20 - 100ms
要输出低电平. 在此后的若干个周期内按此循环。
在该文章的题目中, 要求LED渐亮, 所以占空比要逐渐的增加.
*/
struct PWM {
unsigned int duty; // 记录当前的占空比, 范围: 0 - 338
// duty_interrupt 和 period_cnt 均为每中断一次就 +1
// 记录中断了多少次, 每中断 318 次清零, duty ++
unsigned int duty_interrupt;
// 周期计数, 中断 338 次代表一个周期到, 故每338次中断需要清零
unsigned int period_cnt;
} pwm;
void main() {
while (1) {
// Your code
}
}
// void interrupt 为 32.5us 中断函数
void interrupt() {
// 每次进入中断,
pwm.duty_interrupt++;
pwm.period_cnt++;
// 每中断 318 次, 占空比 ++, 同时 duty_interrupt 清零
if (pwm.duty_interrupt >= 318) {
pwm.duty_interrupt = 0;
pwm.duty++;
}
// 中断338次表示一个周期到, 清零
if (pwm.period_cnt >= 338) {
pwm.period_cnt = 0;
}
// 周期计数大于占空比, 熄灭LED; 否则点亮LED
if (pwm.period_cnt >= pwm.duty) {
// PA1 为控制LED的IO口
PA1 = 0;
}
else {
PA1 = 1;
}
}
至此,就已经完成了软件实现PWM。若文章没有讲清楚,可通过以下方式联系共同探讨。
QQ: 1535747461
Email: CubicJar@outlook.com
计算占空比、周期计数的工具: https://www.aliyundrive.com/s/sMEpMxf6D6y