微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > MCU和单片机设计讨论 > 一篇关于PWM波频率与占空可调的好代码

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

时间:10-02 整理:3721RD 点击:
前日看到一篇很好的代码。有好代码就要拿出来与坛友分享嘛
程序可以利用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;
        }
      }
}

偶然间看到这段代码,我解释下面这段代码:========================================================
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;
}
=====================================================================
不谢!

有具体的资料和线路吗 求分享一下

这就是存粹的用51单片机产生PWM波代码,我觉得代码很经典,就分享出来与大家一起讨论了。
朋友我遇到了一些问题,请您赐教

我对单片不了解  这个正好我最近要用到  希望小编能够将全套资料分享一下 谢谢

有什么问题啊?你理解的已经很清楚了啊

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

上面的推导看懂了,下面的推导还真没想明白,是利用占空比的定义进行推导的吗?指示指示,学习一下,谢谢
TEMP = (1 - PERCENT/100.0)*ALL + temp*PERCENT/100.0;

使用stm32系列的单片机做起来就简单多了

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

//不明白有两个原因
1. 没理解晶振频率与机器周期的关系
2. 没理解占空比(高电平占整个周期的时间)计算方法

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;
//计算占空比就是计算一个周期中的高电平时间(占空比*周期)(PERCENT/100)1/FREQ
// 这个单片机的时钟周期(即外部晶振周期)是1/(12 MHz)=1/F_osc ,而机器周期是12个时钟  周期,即12/F_osc。
// 所以,如果要得到频率=FREQ,占空比为PERCENT/100方波,必须赋予计数器一个初始值TEMP,然后计数器进行自加(ALL-TEMP)次,就有如下等式:
// (ALL-TEMP)*(12/F_osc)=(PERCENT/100)1/FREQ,做等式变换(把temp带入替换)可得
//TEMP = (1 - PERCENT/100.0)*ALL + temp*PERCENT/100.0;(完全可以不用temp替换,以上等式即可)
     TIMER1_H = (uint)TEMP/256;
     TIMER1_L = (uint)TEMP%256;
}

clever Boy or Girl!

在这借鉴 一些经验

硬件开销有点大,不过单个定时器做的话做很难做到50k

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top