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灯灭
}
=========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).
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.
一周热门 更多>