ciky 发表于 2010-1-7 04:01:23

AT89S51模拟PWM(含源码)

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   

{

      staticuchar   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灯灭         

}

XA144F 发表于 2010-1-7 08:44:33

还可以使用74逻辑芯片加一个拨码开关来做一个PWM发生电路。

powerg7 发表于 2010-1-7 10:11:10

mark

smtgg 发表于 2010-1-7 10:29:01

mark

ciky 发表于 2010-1-7 14:16:12

回复【1楼】XA144F
还可以使用74逻辑芯片加一个拨码开关来做一个PWM发生电路。
-----------------------------------------------------------------------

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

amazing030 发表于 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亮度还是没问题的

ciky 发表于 2010-1-8 00:23:50

回复【5楼】amazing030
-----------------------------------------------------------------------

./emotion/em059.gif
一会偶试试,
先谢了。。。

zheshiwo 发表于 2010-1-8 20:25:46

记号。

ciky 发表于 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亮度还是没问题的
-----------------------------------------------------------------------

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

xiaolei0428 发表于 2010-1-9 18:53:18

待会我也试试

amazing030 发表于 2010-1-9 20:28:04

那程序就是PWM最基本原理,你得加个循环效果才好点,就是对同一个PWM值多跑几回,相当耗资源的说,跑个灯还行

zheshiwo 发表于 2010-1-12 19:33:52

在学pwm调灯,记号。

beer 发表于 2010-1-12 21:59:33

mark

barrie_xie 发表于 2010-1-13 03:13:58

mark

sw821025 发表于 2010-1-13 11:54:28

签名

lv998127 发表于 2010-1-13 19:03:05

mark

cxjnet 发表于 2010-1-22 02:48:01

Mark 一下,真是好东西

zhangchengxx 发表于 2010-7-15 12:41:55

谢谢楼主

Janroy 发表于 2010-9-23 16:12:21

mark

gxy508 发表于 2010-9-23 21:39:18

mark

0331631 发表于 2010-9-23 22:20:36

MARK回头找个板子回来试试看

xdpj 发表于 2010-9-28 00:15:09

我也试试

millwood0 发表于 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, inus - 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.

millwood0 发表于 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, inus - 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 发表于 2010-9-28 07:34:20

here is the waveform.


http://cache.amobbs.com/bbs_upload782111/files_33/ourdev_586110EU4JRE.PNG
(原文件名: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.

ljun 发表于 2010-9-28 07:53:15

记号

heyunqingfeng 发表于 2010-9-28 10:37:02

MARK it ! MARK millwood0 !

xifengxia 发表于 2010-9-29 00:04:48

you are very good

millwood0 发表于 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, inus - 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 发表于 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.


http://cache.amobbs.com/bbs_upload782111/files_33/ourdev_586473QG4L8E.PNG
(原文件名: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 发表于 2010-10-22 14:43:29

谢谢学习中

yulri 发表于 2010-10-26 16:26:22

标记了

qinhya 发表于 2010-10-26 17:00:27

标记了

yxy1900 发表于 2010-10-27 15:59:14

马克

summer13765 发表于 2010-11-3 22:19:32

好啊,这两天正在用51做脉冲发生器,用来给步进电机驱动器生成脉冲。

zhanghaodianzi 发表于 2010-11-17 18:01:14

可以模仿AVR单片机PWM的实现方法来做,频率和位数都可调的PWM波。用俩定时器在终端程序里进行比较,不占主程序时间。

cuikai12345 发表于 2010-12-11 23:29:14

mark

cszwm 发表于 2010-12-13 21:47:45

mark

AG17 发表于 2010-12-14 00:09:58

mark

tanguoyong 发表于 2010-12-14 09:26:32

记号

electron_love 发表于 2010-12-14 13:09:57

就你这个写程序的方式 估计c文件比别人的大不少

lixupeng 发表于 2010-12-29 10:46:27

mark

elf0228 发表于 2010-12-30 12:08:14

我现在也在做关于PWM的学习。主要目的是为了控制我的模型电调。呵呵,

zcgy 发表于 2010-12-31 10:52:25

5楼的办法简单直观

xiaochaicn 发表于 2011-1-1 22:07:39

mark

wuzhihui 发表于 2011-1-2 18:22:05

学习学习。

hhdslb 发表于 2011-1-2 18:35:55

mark

hhdslb 发表于 2011-1-2 21:52:44

MARK millwood0 !

hepday 发表于 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......
-----------------------------------------------------------------------

老外?

Jack_Lou 发表于 2011-2-21 14:21:38

我这正折腾呢,谢谢

hoho34 发表于 2011-2-21 15:39:01

mark

hubeilcsun3 发表于 2011-2-21 18:35:00

mark

kunpeng032 发表于 2011-2-21 20:05:00

mark

dong889 发表于 2011-2-22 00:26:37

理解了其实PWM并不难

ERDTxiduoduo 发表于 2011-2-22 02:01:11

i mark it

nbc327 发表于 2011-3-26 00:01:56

学习一下!

bzhou830 发表于 2011-5-22 22:48:05

mark

215661599 发表于 2011-5-25 12:17:13

shzdxx 发表于 2011-5-25 13:44:45

回复【35楼】zhanghaodianzi
可以模仿avr单片机pwm的实现方法来做,频率和位数都可调的pwm波。用俩定时器在终端程序里进行比较,不占主程序时间。
-----------------------------------------------------------------------
请问
这个具体怎么实现呢?

ths3091 发表于 2011-5-26 01:36:53

遥想兄弟当年用delay1ms()做PWM的日子。。。

jeazen 发表于 2011-5-26 10:02:09

mark,studying...

oaixuw 发表于 2011-5-26 10:46:36

mark

keen9327 发表于 2011-5-26 15:44:07

不错,我就等买个舵机回来转转

ouyj_0210 发表于 2011-5-26 17:10:36

mark

luobin_AVR 发表于 2011-7-4 23:19:41

挺好

qq345718287 发表于 2011-7-13 13:37:43

定时器中断输出PWM信号很费资源么?

nicholaszao 发表于 2012-2-13 20:37:53

mark

liu876453 发表于 2012-2-13 23:01:39

有些还是不懂。。。

hpdell 发表于 2012-3-5 09:26:43

好,研究研究

myem007 发表于 2012-5-19 12:58:08

e文很吃力

cd4000 发表于 2016-5-25 16:53:59

学习一下!

hejunhua 发表于 2016-5-25 23:08:20

这芯片现在还有人用吗

tangly2017 发表于 2017-11-9 10:56:34

路过,学习学习
页: [1]
查看完整版本: AT89S51模拟PWM(含源码)