C51入门(十四)定时器编程-软件产生PWM(续)

文章下方附学习资源 请自主领取

1. 条条大路通罗马

通过前面的学习(传送门),我们了解了PWM的基本概念和产生原理,还学会了用51单片机的定时器产生1kHz的PWM以及用PWM控制LED的亮度。可惜的是产生的1kHz PWM实际上只有139Hz,误差太大了。还好,LED开关频率(PWM频率)在50Hz以上,人眼就察觉不到闪烁了(看到的不一定是真实的)。

那么,用51单片机的定时器能不能产生频率更精确的PWM信 呢?答案是肯定的。
仔细观察如图1所示的PWM波形,可以发现t1和t2相互转换时,输出波形翻转,且周期T=t1+t2。

图1 PWM波形解密
对占空比和频率固定的PWM来说,t1和t2时间是固定的。那么好了,固定的时间都可以用定时器来实现。
于是,我们应该可以用一个定时器来产生PWM,具体思路如下:
(1)设置PWM输出一个初始电平(如高电平)。
(2)用定时器定时时间t1,启动定时器。
(3)t1定时到,触发定时器中断。在定时器中断函数里,做两件事情:
a. 翻转PWM输出;
b. 设置定时器定时时间t2。
(4)t2定时到,触发定时器中断。在定时器中断函数里,做两件事情:
a. 翻转PWM输出;
b. 设置定时器定时时间t1。
(5)重复(3)~(4)。

为了理清思路,画出流程图草图如图2所示,为下一步的编程做准备。

图2 PWM产生流程图
那么问题来了,在定时器中断函数里怎么判断下次定时时间是t1还是t2呢?这就需要引入记忆功能了。你想到什么办法吗?

嵌入式物联 需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!

无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。

点击这里找小助理0元领取:嵌入式物联 学习资料(头条)

2. 走自己的路

根据流程图设计的代码如下。

#include<reg51.h>//按键和led接口定义sbit key = P3^2;sbit pwm_out = P1^0;  //LEDbit timer_stage = 0;  //0:高电平输出状态;1:低电平输出状态unsigned int code timer0_X[2][2] ={(65536-600)%256,(65536-600)/256,(65536-400)%256,(65536-400)/256};void timer0_init(); //T0定时器初始化声明void main(){  EA =1;  EX0 = 1; //外部中断0打开  IT0 = 1; //外部中断下降沿触发(按键按下触发)  PX0 = 1; //外部中断0优先级更高  timer0_init();  pwm_out = 1; //正脉宽  while(1);}//T0定时器初始化函数实现void timer0_init(){    TMOD = 0x01;  //定时器方式2    TL0 = timer0_X[0][0]; //PWM脉宽对应的定时器初值    TH0 = timer0_X[0][1];    EA = 1 ;  //总中断打开    ET0 = 1; //T0中断打开    TR0 = 1; //启动T0    pwm_out = 1; //PWM初始电平为高电平}//T0中断函数void isr_timer0() interrupt 1{        pwm_out=~pwm_out; //取反        timer_stage =~timer_stage;        if(timer_stage == 0)    //t1输出        {           TL0 = timer0_X[0][0]; //对应的定时器初值           TH0 = timer0_X[0][1];    }        else        {            TL0 = timer0_X[1][0]; //t2对应的定时器初值             TH0 = timer0_X[1][1];        }}

有了上次经验,我们先在Proteus里仿真。PWM的理想频率为1kHz,占空比为60%。仿真结果如图3所示。

图3 定时器产生PWM波形仿真

输出的PWM波形很完美,理论1kHz PWM频率为962Hz,大大超过了我们的预期。
下载程序到开发板运行,用示波器观测到的波形如图4所示。

图4 PWM示波器实测
PWM频率实测为882Hz,占空比为59.61%。占空比误差不大,频率误差为118Hz。
且慢,开发板晶振为11.0592MHz,计数1000次产生的PWM周期和频率计算公式如下:

  • Tpwm=1000*(12/11.0592MHz)
  • Fpwm=11.0592MHz/12000=921Hz
    因此,PWM频率误差实际为Δf=(921-882)Hz=39Hz。
    谁吃掉了我们的PWM频率?是定时器的中断函数里的代码吗?还是不知名的巫婆?还是前阵子的 红黑洞?
  • 3. 烧脑

    (1)初值赋值对输出PWM有何影响。
    仔细分析源代码,你可能会发现定时器初值怎么放在一个预先定义好的二维数组里:

    unsigned int code timer0_X[2][2] ={(65536-600)%256,(65536-600)/256,                             (65536-400)%256,(65536-400)/256};
    为了便于理解,用一个示意图画出二维数组的结构,如图5所示。图5 定时器初值二维数组示意图试一试:
  • 把code去掉,观察PWM频率的变化。
  • 把初值直接写到定时器的中断函数里,观察PWM频率的变化。
    有答案了吗?以上两行内容仅供参考。
  • (2)修改程序,增加一个按键动态改变PWM的占空比。
      高能提示:请不要试图在定时器中断函数里用(65536-X)%256计算初值。如果这么做了,请观察PWM频率的变化。

    (3)完善程序,实现PWM呼吸灯。


    原文链接:
    https://mp.weixin.qq.com/s/spaMgknWYRZ3CzkBiaqIWA

    ?

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

    上一篇 2022年8月27日
    下一篇 2022年8月27日

    相关推荐