AT89S51模拟PWM(含源码)

2020-01-15 19:19发布

AT89S51本身没有PWM接口,程序采用软件模拟PWM,P1口控制LED亮度,还可驱动小舵机。实验效果点击观看:51单片机模拟PWM效果


测试成功原代码:

/****************************************************

* 声明:     此制作为CIKY单片机学习过程,欢迎爱好者

*           一起学习和探讨,共同进步。

* Title:      51单片机模拟PWM简单例程

* Description: 51单片机模拟PWM输出控制灯的10个亮度级 ,还可驱动小舵机

* @author   CIKY

* Date:      Jan. 06, 2010

****************************************************/



#include <reg51.h>



#define uInt unsigned int

#define uchar unsigned char



uchar PWM_T = 0;   //占空比控制变量



//////////////////主程序入口//////////////////////

void main(void)      

{

      bit flag = 1; //控制灯渐亮渐熄方式

      uInt n;



      TMOD=0x02;   //定时器0,工作模式2,8位定时模式

      TH0=241;     //写入预置初值241到定时器0,使15微秒溢出一次(11.0592MHz)

      TL0=241;     //写入预置值

      TR0=1;       //启动定时器

      ET0=1;       //允许定时器0中断

      

      EA=1;        //允许总中断



      P1=0xff; //初始化P1



      while(1)     

      {      

             for(n=0;n<300;n++);  //延时,将响应定时器中断,灯会自动加/减一个档次的亮度



             if(flag==1)       //灯渐亮

                    PWM_T++;

             else          //灯渐熄

                    PWM_T--;

                     

             if(PWM_T>=10) //设置灯亮度级别为10

                    flag=0;



             if(PWM_T==0)   //限定最低亮度级别为0

                    flag = 1;

      }         



}



///////////////////定时器0中断模拟PWM////////////////////

timer0() interrupt 1 using 2   

{

      static  uchar   t ;   //t用来保存当前时间在一秒中的比例位置



      t++;    //每15微秒增加1

      

      if(t==10)   //1.5毫秒的时钟周期

      {

             t=0;  //使t=0,开始新的PWM周期

             P1=0x00;  //使LED灯亮              

      }

      

      if(PWM_T==t)  //按照当前占空比切换输出为高电平

             P1=0xff;        //使LED灯灭         

}
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
71条回答
gxy508
1楼-- · 2020-01-17 19:43
mark
0331631
2楼-- · 2020-01-17 22:10
MARK  回头找个板子回来试试看
xdpj
3楼-- · 2020-01-17 23:57
我也试试
millwood0
4楼-- · 2020-01-18 05:00
 精彩回答 2  元偷偷看……
millwood0
5楼-- · 2020-01-18 06:04
the beauty of the code shown above is that all the work is done in the interrupt so no reliance on the main() loop to generate the signal. if you don't change PWM_DC, the mcu will just continue to generate the output.

the pwm period in the above code is presumed to be 256 x PWM_STEPPING (because pwm_dc is an unsigned char type). I have been asked to allow a user-define pwm period, and to show an example of variable pwm output. so here it is the 2nd revision to the code above.

============code========
#include <regx51.h>
#include "gpio.h"

//hardware configuration
#define PWM_PORT        P2
#define PWM_DDR                 P2
#define PWM_OUT                 (1<<0)        //pwm output on p2.0
//end hardware configuration

#define PWM_ON(bits)        IO_CLR(PWM_PORT, bits)        //turn on bits
#define PWM_OFF(bits)        IO_SET(PWM_PORT, bits)        //turn off bits
#define PWM_STEPPING        150                 //pwm stepping, in  us - should leave enough time to fully execute the isr, 0x00 - 0xff
#define PWM_PERIOD                100                //pwm period, 0x00 - 0xff
unsigned char PWM_DC = 4;        //desired pwm duty cycle, from 0x00 - PWM_PERIOD

///////////////////???0????PWM////////////////////

void timer0(void) interrupt 1 using 2   {  
        static unsigned char pwm_index=0;   //t?????????????????  
       
        pwm_index++;    //?15????1
       
        if (pwm_index==PWM_PERIOD) {   //1.5???????  
                pwm_index=0;                //reset pwm_index
                PWM_ON(PWM_OUT);        //turn on pwm_out               
        }
        if (pwm_index==PWM_DC)  //???????????????  
                PWM_OFF(PWM_OUT);        //turn off pwm
}  

void mcu_init(void) {
        PWM_ON(PWM_OUT);         //drive pwm_out off
        IO_OUT(PWM_DDR, PWM_OUT);        //pwm_out as output
}

void tmr0_init(void) {
        TMOD = (TMOD & 0xf0) | 0x02;   //???0,????2,8?????  
        TH0=-PWM_STEPPING;     //??????241????0,?15??????(11.0592MHz)  
        TL0=TH0;     //?????  
        TR0=1;       //?????  
        ET0=1;       //?????0??
        EA=1;        //?????
}

//////////////////?????//////////////////////

void main(void) {  

        volatile unsigned short i;

        mcu_init();         //reset the mcu
        tmr0_init();         //set up tmr0
             
        while(1) {      
                for (i=0; i<0x02ff; i++)
                        continue;                                //waste some time
                PWM_DC++;                                        //update pwm duty cycle
                if (PWM_DC==PWM_PERIOD) PWM_DC=0;
                //(PWM_DC == PWM_PERIOD)? PWM_DC=0: PWM_DC++;
        }         
}  
============end code============
millwood0
6楼-- · 2020-01-18 06:47
here is the waveform.



(原文件名:C51 PWM.PNG)

as PWM_ON() is defined as a logic low, and PWM_OFF() as a logic high, the code starts with DC=0% and ends with DC=100%. if you connect an led + resistor from the output pin (p2.0) to Vcc, you will see the light goes brighter.

you can change the pin by redefining PWM_PORT, PWM_DDR and PWM_OUT.

and the code can be easily ported to any other processor, as long as you reconfigure the timer and interrupt portion of it.

一周热门 更多>