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灯灭
} 还可以使用74逻辑芯片加一个拨码开关来做一个PWM发生电路。 mark mark 回复【1楼】XA144F
还可以使用74逻辑芯片加一个拨码开关来做一个PWM发生电路。
-----------------------------------------------------------------------
初学单片机,还请前辈们多多指教。。。 sbit LED = P1^0;
void PWM(uchar dat)
{
uchar i;
for(i = 0; i < 255; i++)
{
LED = (dat >= i) ? 1 : 0;
}
}
嘿嘿,这也能当PWM,调个LED亮度还是没问题的 回复【5楼】amazing030
-----------------------------------------------------------------------
./emotion/em059.gif
一会偶试试,
先谢了。。。 记号。 回复【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亮度还是没问题的
-----------------------------------------------------------------------
做了个简单测试,
虽说不准,
这种方法可行,很简捷。。。 待会我也试试 那程序就是PWM最基本原理,你得加个循环效果才好点,就是对同一个PWM值多跑几回,相当耗资源的说,跑个灯还行 在学pwm调灯,记号。 mark mark 签名 mark Mark 一下,真是好东西 谢谢楼主 mark mark MARK回头找个板子回来试试看 我也试试 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. 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============ 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. 记号 MARK it ! MARK millwood0 ! you are very good 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). 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. 谢谢学习中 标记了 标记了 马克 好啊,这两天正在用51做脉冲发生器,用来给步进电机驱动器生成脉冲。 可以模仿AVR单片机PWM的实现方法来做,频率和位数都可调的PWM波。用俩定时器在终端程序里进行比较,不占主程序时间。 mark mark mark 记号 就你这个写程序的方式 估计c文件比别人的大不少 mark 我现在也在做关于PWM的学习。主要目的是为了控制我的模型电调。呵呵, 5楼的办法简单直观 mark 学习学习。 mark MARK millwood0 ! 回复【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......
-----------------------------------------------------------------------
老外? 我这正折腾呢,谢谢 mark mark mark 理解了其实PWM并不难 i mark it 学习一下! mark 回复【35楼】zhanghaodianzi
可以模仿avr单片机pwm的实现方法来做,频率和位数都可调的pwm波。用俩定时器在终端程序里进行比较,不占主程序时间。
-----------------------------------------------------------------------
请问
这个具体怎么实现呢? 遥想兄弟当年用delay1ms()做PWM的日子。。。 mark,studying... mark 不错,我就等买个舵机回来转转 mark 挺好 定时器中断输出PWM信号很费资源么? mark 有些还是不懂。。。 好,研究研究 e文很吃力 学习一下! 这芯片现在还有人用吗 路过,学习学习
页:
[1]