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条回答
ljun
1楼-- · 2020-01-18 08:13
记号
heyunqingfeng
2楼-- · 2020-01-18 10:22
MARK it ! MARK millwood0 !
xifengxia
3楼-- · 2020-01-18 15:12
 精彩回答 2  元偷偷看……
millwood0
4楼-- · 2020-01-18 16:55
here is the same code, running on a 8Mhz AVR (clock divider to be 1:8, so each timer tick is 1us).

=========code============
#include <ioavr.h>
#include <intrinsics.h>
#include "gpio.h"

//hardware configuration
#define PWM_PORT        PORTB
#define PWM_DDR                 DDRB
#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////////////////////
#pragma vector = TIMER0_OVF_vect
__interrupt void timer0(void) {  
        static unsigned char pwm_index=0;   //t?????????????????  
       
        TCNT0+= -PWM_STEPPING;        //update tmr0 timer
        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) {
        TCCR0A = 0x00;                                                //normal port operation
        TCCR0B = 0x01;                                                //no scalling
        TCNT0 = -PWM_STEPPING;                                //initiate the tmr0 counter
        TIMSK0 = 0x01;                                                //enable tmr0 interrupt
        __enable_interrupt();        //?????
}

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

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============

as you can see, the code is remarkably similar to the 8051 code, with the exception of some unique AVR stuff (like the avr tmr0 doesn't have the automatic reload function).
millwood0
5楼-- · 2020-01-18 22:31
since our tmr offset / stepping is 150 (ticks) per interrupt, and our pwm period is 100 interrupts, we expect the pwm period to be 150 * 100 = 15ms.


that's confirmed via simulation as well.



(原文件名:USB1286 software PWM.PNG)

that's the beauty of C - portability.

you can port the same code to a PIC, or any other mcus and it would remain remarkably the same.
youmeng
6楼-- · 2020-01-19 00:17
谢谢学习中

一周热门 更多>