一篇关于PWM波频率与占空可调的好代码

2019-07-15 12:44发布

前日看到一篇很好的代码。有好代码就要拿出来与坛友分享嘛
程序可以利用51单片机输出频率和占空比可调的PWM波程序。作者写的很详细,程序写的很漂亮!!!但我还是有地方不明白
现在咱们大家来探讨探讨吧!!!


我有三个问题,恳求网友不吝赐教。
1.  频率计算函数,当频率变化,计算出定时器0初值;      
2.  脉宽计算函数,脉宽变化时,计算出定时器1初值;
3.  关定时器1,一定要这一步,因为定时器1的定时短于定时器0;



//------------------------------------------------------------------------------------
//程序功能简介:本程序产生15HZ~~~50KHZ的方波,并且实现频率和脉宽的独立调制,即可
//在改变频率的同时不改变脉宽,再改变脉宽的同时不改变频率;同时设置
//两个调节步长------在KEY键按下时,粗调,没有按下时,细调;
//程序思路: 本程序用到两个定时器------定时器0和定时器1,其中定时器0工作在定时方式下,
//决定方波的频率;定时器1,同样工作在定时方式下,用于设定脉宽;
//------------------------------------------------------------------------------------

#include <reg52.h>
#include <math.h>
#define  uchar unsigned char
#define  uint unsigned int
#define  ALL  65536     //定时器工作方式1时,最大基数长度 65536;
#define  F_osc 12000000    //晶振频率12M;

sbit KEY_F_UP=P0^2;     //频率上调按钮;
sbit KEY_F_DOWN=P0^3;   //频率下调按钮;
sbit KEY_W_UP=P0^4;     //脉宽上调按钮;
sbit KEY_W_DOWN=P0^5;   //脉宽下调按钮;
sbit KEY=P0^6;          //粗细调节按钮-----按下为粗调,否则为细调;
sbit OUTPUT=P1^0;       //波形输出;

uchar tiMER0_H,TIMER0_L,TIMER1_H,TIMER1_L;       //定时器0和定时器1的初值设置;
uchar PERCENT=50;                                                 //初始占空比;
uchar FLAG_F=0,FLAG_W=0;                                    //频率调节标志和脉宽调制标志;
uint  FREQ=50000;                                                   //初始频率;
float temp;                                                              //临时全局变量,用于数据传递;
void delay(uchar t);                                                  //延时函数,用于按键去抖;
void init();                                                               //初始化函数,用于定时器的初始化;
void calculate_F();                                                   //频率计算函数,当频率变化,计算出定时器0初值;
void calculate_W();                                                  //脉宽计算函数,脉宽变化时,计算出定时器1初值;
void key_scan();                                                      //按键扫描函数;
void timer0();                                                          //定时器0中断函数;
void timer1();                                                         //定时器1中断函数;

void delay(uchar t)
{
     uchar i,j;
     while(t--)             //每个脉冲为1us
      {
       for(i=0;i<100;i++)
        for(j=0;j<100;j++);
      }
   }

void calculate_F()
{
     temp = ALL - F_osc/12.0/FREQ;
     TIMER0_H = (uint)temp/256;
     TIMER0_L = (uint)temp%256;
}

void calculate_W()
{
     float TEMP;
     TEMP = (1 - PERCENT/100.0)*ALL + temp*PERCENT/100.0;
     TIMER1_H = (uint)TEMP/256;
     TIMER1_L = (uint)TEMP%256;
   }

   void key_scan()
   {
     delay(4);

     if(!KEY_F_UP)     //频率上调键按下;
     {
       FLAG_F=1;    //置标志位;

       if(!KEY)
         FREQ+=10;

       else
         FREQ++;

       if(FREQ>50000)
         FREQ=1;
     }

     else if(!KEY_F_DOWN)   //频率下调键按下;
     {
       FLAG_F=1;    //置标志位;

       if(!KEY)
         FREQ-=10;

       else
         FREQ--;

       if(FREQ<1)
         FREQ=50000;
      }

      else if(!KEY_W_UP)    //脉宽上调键按下;
      {
        FLAG_W=1;    //置标志位;

        if(!KEY)
          PERCENT+=5;

        else
          PERCENT++;

        if(PERCENT>49)
          PERCENT=1;
       }

      else if(!KEY_W_DOWN)   //脉宽下调键按下;
      {
        FLAG_W=1;    //置标志位;

        if(!KEY)
          PERCENT-=5;

        else
          PERCENT--;

        if(PERCENT<1)
           PERCENT=49;
       }

       else ;

       }

   void timer0() interrupt 1      //决定频率
    {
      TH0=TIMER0_H;
      TL0=TIMER0_L;
      TR1=1;           //开定时器1;
      OUTPUT=1;
    }

    void timer1() interrupt 3     //决定脉宽
    {
      TH1=TIMER1_H;
      TL1=TIMER1_L;
      TR1=0;          //关定时器1,一定要这一步,因为定时器1的定时短于定时器0;
      OUTPUT=0;
    }

    void init()
    {
      TMOD=0x11;      //定时器0和定时器1都工作在方式1,16位计数器;
      calculate_F();  //初始为1KHZ,占空比为50%;
      calculate_W();
      TH0=TIMER0_H;
      TL0=TIMER0_L;
      TH1=TIMER1_H;
      TL1=TIMER1_L;
      ET0=1;
      ET1=1;
      EA=1;
      TR0=1;
      TR1=1;
    }

main()
{
     init();

     while(1)
     {
        key_scan();

        if(FLAG_F)     //改变频率时要注意要进行脉宽的重新设置;
        {
          calculate_F();
          calculate_W();
          FLAG_F=0;
        }

        if(FLAG_W)     // 脉宽改变,频率不改变;
        {
          calculate_W();
          FLAG_W=0;
        }

      }
}

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
13条回答
傻大个牌纯碱
1楼-- · 2019-07-15 16:39
本帖最后由 傻大个牌纯碱 于 2016-11-2 22:23 编辑

偶然间看到这段代码,我解释下面这段代码:========================================================

void calculate_F()
{
     temp = ALL - F_osc/12.0/FREQ;
// 理解这段程序的关键点,在于理解时钟周期和机器周期;
// 从程序上看,这个单片机的时钟周期(即外部晶振周期)是1/(12 MHz)=1/F_osc ,而机器周期是12个时钟周期,即12/F_osc。
// 所以,如果要得到频率=FREQ的方波,必须赋予计数器一个初始值temp,然后计数器进行自加(ALL-temp)次,就有如下等式:
// (ALL-temp)*(12/F_osc)=1/FREQ,做等式变换,把temp做变量就可以得到:
//  temp = ALL - F_osc/12.0/FREQ
     TIMER0_H = (uint)temp/256;
     TIMER0_L = (uint)temp%256;
}

void calculate_W()
{
     float TEMP;
     TEMP = (1 - PERCENT/100.0)*ALL + temp*PERCENT/100.0;
// 如果你理解了上面那段,这段我相信你也理解了;
     TIMER1_H = (uint)TEMP/256;
     TIMER1_L = (uint)TEMP%256;
}

=====================================================================
不谢!

最佳答案

评分

参与人数 1积分 +5 收起 理由 511701105 + 5 为你点赞!!!

查看全部评分

李春明
2楼-- · 2019-07-15 17:07
有具体的资料和线路吗 求分享一下
511701105
3楼-- · 2019-07-15 20:57
王栋春 发表于 2016-10-28 20:35
有具体的资料和线路吗 求分享一下

这就是存粹的用51单片机产生PWM波代码,我觉得代码很经典,就分享出来与大家一起讨论了。
朋友我遇到了一些问题,请您赐教
李春明
4楼-- · 2019-07-15 23:53
511701105 发表于 2016-10-28 21:46
这就是存粹的用51单片机产生PWM波代码,我觉得代码很经典,就分享出来与大家一起讨论了。
朋友我遇到了一些问题,请您赐教

我对单片不了解  这个正好我最近要用到  希望楼主能够将全套资料分享一下 谢谢
blust
5楼-- · 2019-07-16 04:50
 精彩回答 2  元偷偷看……
511701105
6楼-- · 2019-07-16 07:12
blust 发表于 2016-10-29 08:22
有什么问题啊?你理解的已经很清楚了啊

这条代码(红字)不太明白,请您赐教!
//关定时器1,一定要这一步,因为定时器1的定时短于定时器0;

一周热门 更多>