搜索
bottom↓
回复: 72

AT89S51模拟PWM(含源码)

[复制链接]

出0入0汤圆

发表于 2010-1-7 04:01:23 | 显示全部楼层 |阅读模式
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灯灭         

}

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2010-1-7 08:44:33 | 显示全部楼层
还可以使用74逻辑芯片加一个拨码开关来做一个PWM发生电路。

出0入0汤圆

发表于 2010-1-7 10:11:10 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-7 10:29:01 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2010-1-7 14:16:12 | 显示全部楼层
回复【1楼】XA144F
还可以使用74逻辑芯片加一个拨码开关来做一个PWM发生电路。
-----------------------------------------------------------------------

初学单片机,还请前辈们多多指教。。。

出0入0汤圆

发表于 2010-1-7 20:48:48 | 显示全部楼层
sbit LED = P1^0;

void PWM(uchar dat)
{
    uchar i;
    for(i = 0; i < 255; i++)
    {
        LED = (dat >= i) ? 1 : 0;
    }
}
嘿嘿,这也能当PWM,调个LED亮度还是没问题的

出0入0汤圆

 楼主| 发表于 2010-1-8 00:23:50 | 显示全部楼层
回复【5楼】amazing030
-----------------------------------------------------------------------


一会偶试试,
先谢了。。。

出0入0汤圆

发表于 2010-1-8 20:25:46 | 显示全部楼层
记号。

出0入0汤圆

 楼主| 发表于 2010-1-9 02:04:47 | 显示全部楼层
回复【5楼】amazing030
sbit LED = P1^0;
void PWM(uchar dat)
{
    uchar i;
    for(i = 0; i < 255; i++)
    {
        LED = (dat >= i) ? 1 : 0;
    }
}
嘿嘿,这也能当PWM,调个LED亮度还是没问题的
-----------------------------------------------------------------------

做了个简单测试,
虽说不准,
这种方法可行,很简捷。。。

出0入0汤圆

发表于 2010-1-9 18:53:18 | 显示全部楼层
待会我也试试

出0入0汤圆

发表于 2010-1-9 20:28:04 | 显示全部楼层
那程序就是PWM最基本原理,你得加个循环效果才好点,就是对同一个PWM值多跑几回,相当耗资源的说,跑个灯还行

出0入0汤圆

发表于 2010-1-12 19:33:52 | 显示全部楼层
在学pwm调灯,记号。

出0入0汤圆

发表于 2010-1-12 21:59:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-13 03:13:58 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-13 11:54:28 | 显示全部楼层
签名

出0入0汤圆

发表于 2010-1-13 19:03:05 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-22 02:48:01 | 显示全部楼层
Mark 一下,真是好东西

出0入0汤圆

发表于 2010-7-15 12:41:55 | 显示全部楼层
谢谢楼主

出0入0汤圆

发表于 2010-9-23 16:12:21 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-9-23 21:39:18 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-9-23 22:20:36 | 显示全部楼层
MARK  回头找个板子回来试试看

出0入0汤圆

发表于 2010-9-28 00:15:09 | 显示全部楼层
我也试试

出0入0汤圆

发表于 2010-9-28 06:25:21 | 显示全部楼层
your code, simplified and made more portable.

=========================================
#include <reg51.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        15                //pwm stepping, in  us - should leave enough time to fully execute the isr

unsigned char PWM_DC = 0x7f;        //desired pwm duty cycle, from 0x00 - 0xff

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

void timer0(void) interrupt 1 using 2   {
        static unsigned char pwm_index=0;   //t?????????????????
       
        pwm_index++;    //?15????1

        if (pwm_index==0) {   //1.5???????
                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_OFF(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) {

        mcu_init();                                        //reset the mcu
        tmr0_init();                                //set up tmr0
   
        while(1) {      
        }         
}
========end code============================

notice that all action is done in the isr and you can do whatever you want in the while() loop.

you can also define PWM_OUT to include multiple pins to generate different output.

出0入0汤圆

发表于 2010-9-28 07:29:40 | 显示全部楼层
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============

出0入0汤圆

发表于 2010-9-28 07:34:20 | 显示全部楼层
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.

出0入0汤圆

发表于 2010-9-28 07:53:15 | 显示全部楼层
记号

出0入0汤圆

发表于 2010-9-28 10:37:02 | 显示全部楼层
MARK it ! MARK millwood0 !

出0入0汤圆

发表于 2010-9-29 00:04:48 | 显示全部楼层
you are very good

出0入0汤圆

发表于 2010-9-29 07:50:18 | 显示全部楼层
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).

出0入0汤圆

发表于 2010-9-29 07:52:53 | 显示全部楼层
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.

出0入0汤圆

发表于 2010-10-22 14:43:29 | 显示全部楼层
谢谢学习中

出0入0汤圆

发表于 2010-10-26 16:26:22 | 显示全部楼层
标记了

出0入0汤圆

发表于 2010-10-26 17:00:27 | 显示全部楼层
标记了

出0入0汤圆

发表于 2010-10-27 15:59:14 | 显示全部楼层
马克

出0入0汤圆

发表于 2010-11-3 22:19:32 | 显示全部楼层
好啊,这两天正在用51做脉冲发生器,用来给步进电机驱动器生成脉冲。

出0入0汤圆

发表于 2010-11-17 18:01:14 | 显示全部楼层
可以模仿AVR单片机PWM的实现方法来做,频率和位数都可调的PWM波。用俩定时器在终端程序里进行比较,不占主程序时间。

出0入0汤圆

发表于 2010-12-11 23:29:14 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-13 21:47:45 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-14 00:09:58 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-14 09:26:32 | 显示全部楼层
记号

出0入0汤圆

发表于 2010-12-14 13:09:57 | 显示全部楼层
就你这个写程序的方式 估计c文件比别人的大不少

出0入0汤圆

发表于 2010-12-29 10:46:27 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-12-30 12:08:14 | 显示全部楼层
我现在也在做关于PWM的学习。主要目的是为了控制我的模型电调。呵呵,

出0入0汤圆

发表于 2010-12-31 10:52:25 | 显示全部楼层
5楼的办法简单直观

出0入0汤圆

发表于 2011-1-1 22:07:39 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-1-2 18:22:05 | 显示全部楼层
学习学习。

出0入0汤圆

发表于 2011-1-2 18:35:55 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-1-2 21:52:44 | 显示全部楼层
MARK millwood0 !

出0入0汤圆

发表于 2011-1-3 20:51:13 | 显示全部楼层
回复【29楼】millwood0
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)
<center><a class=tt16 onclick="fnquickimagequote(this,'files_33/ourdev_586473qg4l8e.png','原文件名:usb1286 software pwm.png')" href......
-----------------------------------------------------------------------

老外?

出0入0汤圆

发表于 2011-2-21 14:21:38 | 显示全部楼层
我这正折腾呢,谢谢

出0入0汤圆

发表于 2011-2-21 15:39:01 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-21 18:35:00 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-21 20:05:00 | 显示全部楼层
mark

出100入0汤圆

发表于 2011-2-22 00:26:37 | 显示全部楼层
理解了其实PWM并不难

出0入0汤圆

发表于 2011-2-22 02:01:11 | 显示全部楼层
i mark it

出0入0汤圆

发表于 2011-3-26 00:01:56 | 显示全部楼层
学习一下!

出0入0汤圆

发表于 2011-5-22 22:48:05 | 显示全部楼层
mark
头像被屏蔽

出0入0汤圆

发表于 2011-5-25 12:17:13 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2011-5-25 13:44:45 | 显示全部楼层
回复【35楼】zhanghaodianzi
可以模仿avr单片机pwm的实现方法来做,频率和位数都可调的pwm波。用俩定时器在终端程序里进行比较,不占主程序时间。
-----------------------------------------------------------------------
请问
这个具体怎么实现呢?

出0入0汤圆

发表于 2011-5-26 01:36:53 | 显示全部楼层
遥想兄弟当年用delay1ms()做PWM的日子。。。

出0入0汤圆

发表于 2011-5-26 10:02:09 | 显示全部楼层
mark,studying...

出0入0汤圆

发表于 2011-5-26 10:46:36 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-5-26 15:44:07 | 显示全部楼层
不错,我就等买个舵机回来转转

出0入0汤圆

发表于 2011-5-26 17:10:36 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-7-4 23:19:41 | 显示全部楼层
挺好

出0入0汤圆

发表于 2011-7-13 13:37:43 | 显示全部楼层
定时器中断输出PWM信号很费资源么?

出0入0汤圆

发表于 2012-2-13 20:37:53 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-2-13 23:01:39 | 显示全部楼层
有些还是不懂。。。

出0入0汤圆

发表于 2012-3-5 09:26:43 | 显示全部楼层
好,研究研究

出0入0汤圆

发表于 2012-5-19 12:58:08 | 显示全部楼层
e文很吃力

出0入0汤圆

发表于 2016-5-25 16:53:59 | 显示全部楼层
学习一下!

出0入0汤圆

发表于 2016-5-25 23:08:20 来自手机 | 显示全部楼层
这芯片现在还有人用吗

出0入0汤圆

发表于 2017-11-9 10:56:34 | 显示全部楼层
路过,学习学习
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 23:43

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表