本帖最后由 huo_hu 于 2016-4-19 09:53 编辑
pwm(脉宽调制)在电源、电机控制中有着广泛的应用,本文讲述pwm实现的基本原理以及pwm相关的技术参数,希望对大家有用。
关键字:pwm,c语言,stc51,stm32
先从一段pwm定时器软件实现的C语言程序开始吧。
void main (){
定时器初始化为5us中断一次。。。省略;
}
uint8 cnt=0,ch1=0,ch2=0;
#define ch1iobit ... //定义通道1和2的输出管脚... 略去
#define ch2iobit ...
定时器溢出中断服务程序 {
cnt++;
if (cnt>=5) cnt=0; //cnt=0~4
//pwm第一路
if (cnt>ch1)
ch1iobit=1;
else
ch1iobit=0;
//pwm第二路
if (cnt>ch2)
ch2iobit=1;
else
ch2iobit=0;
}
ch1和ch2两个变量保存两个通道的占空比数值,有效值为0~4,ch1和2的iobit你自己定义一下51单片机可以sbit,stm32可以用位操作。
主要讲原理,程序怎么写不重要,我们来分析一下这段程序,首先是两个频率,一个是定时器的溢出频率,上面程序为5us即200KHZ这个是pwm的基频,也就是一个pwm所有可能的开关点,这个频率越高则软件执行开销越大。另一个是pwm的输出频率,上面程序为200K/5=40K,pwm的输出频率和基频成成比和溢出回0值成反比。另外这个溢出回0值影响调控精度,上面程序只能做0~4共5级的pwm调整,由此可以看出pwm频率受基频和控制精度共同决定。即便是硬件实现也是这个道理,不要单纯问某个单片机pwm频率最高多少这种问题了。
然后是pwm不能全开关的问题,我们把所有波形画出来,我这里就用01代替了
cnt大于ch1比较
ch1 cnt=0 1 2 3 4
0 0 1 1 1 1
1 0 0 1 1 1
2 0 0 0 1 1
3 0 0 0 0 1
4 0 0 0 0 0
可以看到当ch1的值为4时pwm可以全部关闭,但ch1=0时不能全部开启,那么换成比较式为if (cnt>=ch1)行不行呢?实际上也不行,上面真值表的对角线的0全部换成1就是大于等于真值,这时ch=0可以全开,但ch=4不能全关。同理小于比较和小于等于比较等同于0和1交换也不行,交换比较结果输出也不行,究其原因是pwm输出N个占空比,不能表示出N+1个状态,所以无论哪一个pwm全开和全关必须舍弃一个。这个问题在溢出周期比较大的时候可以忽略,但是如果想要彻底解决必须引入其他的位来控制,这个就是stc12c5a单片机pca模块pwm部分EPCNH和EPCNL作用的由来。
还有一个更新的问题,ch1和ch2是两个全局变量,在程序的任何地方都可以修改其结果可能会出现意外的状况,从波形上看pwm会抖动。虽然这种意外情况只是一瞬间但是对于桥控制这样的电路需要两路完全互补的pwm,这样的错误会引起全桥短路,所以要防止意外的状况。唯一最适合更新的时刻是在溢出回0的时间点上,这个时候每一路的输出状态和通道值无关,软件实现上需要另外增加两个缓存变量暂存外部对占空比值的修改,计数器回0的时刻再把缓存更新到ch1和ch2.
if (cnt>=5) {
cnt=0; //cnt=0~4
ch1=ch1_buf;
ch2=ch2_buf;
}
这部分功能在stm32的单片机上叫做预装载功能,它除了对通道寄存器进行保护还对计数周期进行保护,只在计数器回0时更新。
上面是pwm的软件实现,软件实现的好处是不受io管脚的限制并且输出通道数不受限制,为了实现高精度和高频率的需要不得不提高基频,缺点自然就是开销大。如果硬件实现需要两个逻辑器件,一个是计数器,一个是比较器(每通道一个)。
计数器实现上还有一个更简单的办法就是掩位,也就是stc单片机的实现方式。如果用C程序写就这样:
初始化略。。。
定时器溢出中断服务程序 {
cnt++;
//pwm第一路
if ((cnt&3 ) > (ch1 & 3))
ch1iobit=1;
else
ch1iobit=0;
//pwm第二路。。。
}
这个程序不再有从新赋值的操作,ch1只有低两位有效,通过与操作只比较cnt的低两位和ch1的低两位,这个程序就可以用简单的逻辑器件来实现了。cnt++对应一个计数器,if里的逻辑条件对应一个比较器,它的比较结果就是输出。新stc的pca可以设计成6位比较或7位比较或8位比较,比较位数越少则频率越高,但是可控精度打对折。硬件输出的好处是初始化完就不用管了,比较输出不再需要软件参与没有执行开销。(完)
|