PWM是什么?有什么用?PWM能玩出什么花样?

电子说

1.2w人已加入

描述

一、PWM是什么?有什么用?

PWM指的是脉冲宽度调制技术,通过对脉冲宽度的调节可以达到通信(如控制舵机)、模拟“模拟输出”(如调节灯的亮度),前者在以后再结合舵机来讲,本文侧重讲后者。

脉冲宽度调制

首先,我们来了解几个概念:

1、PWM频率、PWM周期

脉冲宽度调制

这是一个约为50Hz的PWM输出波形

脉冲宽度调制

这个PWM的周期约为20ms

PWM频率指1秒的时间里PWM运行的次数;

PWM周期指一次完整的PWM输出所使用的时间。

2、占空比

脉冲宽度调制

从上往下,占空比分别为25%、50%、75%

占空比指在一个周期内接通的时间占这一周期的比例。

明白这些后,恭喜你已经基本掌握PWM的原理了!

我们知道单片机的IO口只有0和1两种输出状态,只能控制LED的亮与灭,如果我们想要得到下面这样的输出效果,思考一下,结合PWM我们可以怎么做?

脉冲宽度调制

你可能已经想到了,IO口保持高电平(1)时LED最亮,此时电压为5V(以5V电压工作的单片机为例),如果在里面插入低电平,输出10101010...不就相当于输出2.5V了吗?

不严谨地说,这样使用PWM确实能达到“模拟输出”的效果,但如果真的需要模拟输出,单单这样是不够的(所以前面标了引号),在此不进行细说。

二、怎样设计PWM程序?

我们先来构造这么一个框架:

1、确定一个单位时间t,每个t内固定地输出0或1;

2、过了n个t完成一个PWM周期;

3、使用程序控制一个周期内输出1的数量为m,输出0的数量为(n-m)。

有了上面的框架,设计程序就不难了:

我们可以使用定时器,每隔一定的时间进入一次中断,并记录进入中断的次数x,直到完成一次PWM周期,将x归零;

设我们所需要的PWM输出占空比为y,当x<=y时输出高电平,x>y时输出低电平。

这样,我们的程序基本就设计出来了,是不是很简单?(〃'▽'〃)

在正式编写程序前,我们还需要考虑一些小问题:

因为51单片机的运行频率不高,PWM的频率也不能设计得太高,过于频繁地进入中断也会影响程序的正常运行。

在下面的例程中,我所设置的定时器中断的间隔为0.1ms,每20ms完成一次PWM周期。

在这一小节的最后,我们整理一下思路,可以得到下面的流程图:

脉冲宽度调制

三、写个程序试试看!

按上面的流程图,我们就可以写一个控制LED亮度的程序了:

#include < reg52.h >


#define PWM_T 200                      //产生中断的时间,因为是24MHz,200即100微秒(0.1毫秒)
#define LED P1


int PWM_count0 = 0;                    //进入中断的次数
int PWM0 = 100;                        //控制PWM的占空比,下同
int PWM1 = 170;
int PWM2 = 188;
int PWM3 = 198;


sbit LED0 = P1^0;                      //LED引脚定义,下同
sbit LED1 = P1^1;
sbit LED2 = P1^2;
sbit LED3 = P1^3;


void PWM_Start()                      //PWM初始化函数,打开了定时器0
{
  EA = 1;
  ET0 = 1;
  TMOD = 0x09;
  TR0 = 1;


  TH0 = (65536-PWM_T)/256;
  TL0 = (65536-PWM_T)%256;
}


void main()
{
  PWM_Start();                        //PWM开始运行


  while(1)
  {
    if(PWM_count0 <= PWM0)            //调节LED0的亮度
    {
      LED0 = 1;
    }
    else
    {
      LED0 = 0;
    }


    if(PWM_count0 <= PWM1)            //调节LED1的亮度
    {
      LED1 = 1;
    }
    else
    {
      LED1 = 0;
    }


    if(PWM_count0 <= PWM2)            //调节LED2的亮度
    {
      LED2 = 1;
    }
    else
    {
      LED2 = 0;
    }


    if(PWM_count0 <= PWM3)            //调节LED3的亮度
    {
      LED3 = 1;
    }
    else
    {
      LED3 = 0;
    }
  }
}


void Timer0() interrupt 1
{
  TH0 = (65536-PWM_T)/256;
  TL0 = (65536-PWM_T)%256;


  PWM_count0++;
  if(PWM_count0 == 200)                //完成了一个PWM周期,计数变量清零
  {
    PWM_count0 = 0;
  }
}

把上面的程序编译后下载到开发板上:

脉冲宽度调制

小提示:人眼对亮度的感觉不是线性变化的,因此LED0与LED1虽然占空比相差较大,但肉眼感觉亮度不相上下,感兴趣的可以去研究一下。

用逻辑分析仪收集一下IO口的输出信息:

脉冲宽度调制

黄框里的为一个PWM周期

上面的程序还有一些需要注意的地方:

1、记得加while循环,因为PWM输出是持续的,没有循环就只会进行一个周期;

2、晶振频率建议设置为24MHz,12MHz也可以,相应地定时器中断时间也要更改。

我们可以将上面的程序进一步优化,如果我们把if语句写成子函数,通过参数控制占空比,返回值控制0和1的输出,程序会简化很多:

#include < reg52.h >


#define PWM_T 200                      //产生中断的时间,因为是24MHz,200即100微秒(0.1毫秒)
#define LED P1


int PWM_count0 = 0;                    //进入中断的次数
int PWM0 = 100; //去掉7~10行                       //控制PWM的占空比,下同
int PWM1 = 170;
int PWM2 = 188;
int PWM3 = 198;


sbit LED0 = P1^0;                      //LED引脚定义,下同
sbit LED1 = P1^1;
sbit LED2 = P1^2;
sbit LED3 = P1^3;


void PWM_Start()                      //PWM初始化函数,打开了定时器0
{
  EA = 1;
  ET0 = 1;
  TMOD = 0x09;
  TR0 = 1;


  TH0 = (65536-PWM_T)/256;
  TL0 = (65536-PWM_T)%256;
}


int PWM(int PWM_value)                //控制PWM输出的子函数
{
  if(PWM_count0 <= PWM_value)
  {
    return 1;
  }
  else
  {
    return 0;
  }
}


void main()
{
  PWM_Start();                        //PWM开始运行


  while(1)
  {
    LED0 = PWM(100);                  //调节LED0的亮度
    LED1 = PWM(170);                  //调节LED1的亮度
    LED2 = PWM(188);                  //调节LED2的亮度
    LED3 = PWM(198);                  //调节LED3的亮度
  }
}


void Timer0() interrupt 1
{
  TH0 = (65536-PWM_T)/256;
  TL0 = (65536-PWM_T)%256;


  PWM_count0++;
  if(PWM_count0 == 200)                //完成了一个PWM周期,计数变量清零
  {
    PWM_count0 = 0;
  }
}

可以看到,写成子函数后调用PWM输出方便了不少。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分