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灯灭
}
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============
(原文件名: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.
一周热门 更多>