[原创]51单片机输出可调PWM信号
ldc2011/08/22电子技术 IP:澳大利亚
上个月帮忙设计了一个线切割机的高频信号源,完成后感觉可以稍加改造后应用到数控电源的设计上,于是写了这篇贴子供大家参考研究。这个项目要求使用51单片机输出脉宽可调的脉冲信号,高低电平脉宽分别可调,脉宽值有5us、8us、10us、16us、32us、48us若干个档位。难点在于如何使用51的定时器产生满足精度的高低电平。51单片机使用24MHz的晶振,它的时钟周期就是0.5us,最极端的情况是要每隔10个时钟周期产生一次定时器中断。若只使用一个定时器,则在定时器中断函数中要判断当前是处于高电平还是低电平,还要重新设置定时器初装值,这些操作会使用较多的CPU时间,甚至会超过10个时钟周期,结果没有办法得到相应宽度的脉冲。简化定时器中断函数中的操作是唯一能解决这个问题的办法,最终我使用的方案是使用两个定时器分别控制高电平与低电平的时间。定时器0只负责将输出脚的电平拉高,定时器1只负责将输出脚的电平拉低,这样定时器中断函数都可以在1个时钟周期内完成操作。两个定时器都采用自动重装的方式工作,周期均为脉冲的周期,令定时器1后于定时器0一定时间工作,令这个时间差正好等于高电平的脉宽,两个定时器轮流工作,正好可以达到要求。(见图)
934_bea813139984020c235d6f0fff4b3.png


附设计原理图和源码
934_b2b813139985587722990038c3f2d.png



/*
* File:                main.h
* Author:                luodichen@XXXXXXX
* Date:                2011-07-23
*
* MCU:                    AT89C52            
*/

#include <reg52.h>
#include <intrins.h>
#include <string.h>

#define SCANSPEED(X) ((X)*2000)

#define KEYSCAN_DELAY_TIME         50
#define SIGNALWIDE_TYPE_NUM        5

#define OPERAT_MINUS         -1
#define OPERAT_PLUS            1
#define SET_LOWLEVAL        0
#define SET_HIGHLEVAL        1        

#define NUM_SHOW P0

#define FRESH_NUM_TIME            20000

typedef unsigned char BYTE;

sbit sbOutput = P1^7;

sbit sbP2_6 = P2^6;
sbit sbP2_7 = P2^7;

sbit sbControl = P1^6;

void TimerInit();
void SystemInit();
void NumScan(BYTE *buf);
void delay(unsigned long t);
void ShowWelcome();
void BeginToWork();
void SetSignalSource(BYTE time_l, BYTE time_h);
void UpdateBitsOut();
void KeysScan();
void ShowData();
void SetBitsOut(char operat);
void SetSignalOut(char opeart, BYTE type);




[/i]
+1  学术分    虎哥    2011/08/22 赞扬您的作品。为什么用51方案,是系统可靠性有要求吗
来自:电子信息 / 电子技术
23
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
ldc 作者
13年6个月前 IP:未同步
317800
捕获2.png
高电平8us,低电平10us,挺准确的~
+1
科创币
jrcsh
2011-08-22
不错不错
+10
科创币
delete
2011-08-22
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
bighead
13年6个月前 IP:未同步
317836
额。。。软件PWM无爱。。。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
yuehan319
13年6个月前 IP:未同步
317842
曾经做过个只用一个定时器做的PWM,[s:307]。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
zx-16533
13年6个月前 IP:未同步
317879
lz用的是什么仿真软件?
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
fgtzjl
13年6个月前 IP:未同步
317887
mark 一下  好好学习下
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
ldc作者
13年6个月前 IP:未同步
317898
回 4楼(kokming999) 的帖子
Proteus[10字节10字节]
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
jrcsh
13年6个月前 IP:未同步
317899
引用第3楼yuehan319于2011-08-22 18:11发表的  :
曾经做过个只用一个定时器做的PWM,[s:307]。


100us 以上就行 象10us内的哪些就不能保证准确性了  呵呵
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
10班陈大葱22号
13年6个月前 IP:未同步
317908
9元一片stm32101c8t6就没那么累了...
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
虎哥
13年6个月前 IP:未同步
317917
方法巧妙,符合技术宅追求极限的作风。。
话说原著名炸药专家LDC同志两年没来发帖,今日回来视察,感觉可好?
还有GQL在哪里,N年没见了,你们两位我一贯是混淆的,下面这位是谁?
IMG_0931.jpg
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
ldc作者
13年6个月前 IP:未同步
317955
回 9楼(虎哥) 的帖子
虎哥还记得俺俺表示无限感动啊[s:241]
图里的是GQL,目前考研奋斗中...
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
justkaka
13年6个月前 IP:未同步
317969
请教一下PWM信号做逆变器,是否靠线圈升压?
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
jrcsh
13年6个月前 IP:未同步
317977
引用第11楼justkaka于2011-08-23 10:51发表的  :
请教一下PWM信号做逆变器,是否靠线圈升压?



不用电感升压用什么升压了
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
zx-16533
13年6个月前 IP:未同步
318065
回 7楼(jrcsh) 的帖子
谢谢!!!!!!!
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
cjboy2008
13年6个月前 IP:未同步
318103
引用第12楼jrcsh于2011-08-23 11:19发表的  :


不用电感升压用什么升压了


电荷泵。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
jrcsh
13年6个月前 IP:未同步
318400
/*
本程序要用到哪些功能了

定时器1 用于 发生频率的时间
定时器2用于 串行通信
中断0 用于key1 的进入

P1要进行IO翻转,传说中使用强推的方式会快点,你信不信,反正我没进行实测不知,反正开了强推也不挨事,开就是了.


接下来~~~找以前写的程序~~~~挖代码过来拼程序


20110817
中断0没有启用,普通IO查询可以满足.
完善按键处理程序
定时器0送入数据处理

下一计划完成手动设置及串口显示状态可工作后在加入通过串口进行数据设置

*/



#include <REG51.H>
//#include <intrins.h>
//#include <absacc.h>

#define uint unsigned int
#define uchar unsigned char



sfr P1M1   = 0x91 ;
sfr P1M0   = 0x92 ;
    
sbit d2=P2^1;
sbit d1=P2^0;
sbit key1=P3^2;
sbit key2=P3^6;
sbit key3=P3^7;

uint mmm,ccc,ddd,eee,fff,ggg,hhh,iii,lll,ppp;
uchar xxx,yyy,zzz,aaa,bbb,qq123;


uchar code commm[]={'0','1','2','3','4','5','6','7','8','9'};
/*


void delayms(uint z)  //ms
{                    
    uint x,y;
    for(x=z;x>0;x--)
        for(y=100;y>0;y--);    
}

*/

void send_char(unsigned char ddat)
// 传送一个字符
{
    SBUF = ddat;
    while(!TI);                // 等特数据传送
    TI = 0;                    // 清除数据传送标志
}


//timer0/counter0 interrupt
void timer0(void) interrupt 1
{
P1=~P1;
TL0=aaa;
TH0=bbb;
TR0=1; //管你有没关了开了在说

}

//int0 interrupt

void main()
{

TMOD=0x21;
TH0=1;
TL0=1;

SCON=0x50;
TH1=0xf3;
TL1=0xf3;
TR1=1;

TR0=1;
ET0=1;
EA=1;

P1M1   = 0x00 ;   //P1口设置为强推
P1M0   = 0xff ;    //晚些在设置
aaa=128;
bbb=128;
ccc=2;
ddd=0;
fff=6553;
eee=100;
ccc=0;//调试
P1=0xff;
d1=1;
d2=1;
while(1)
{

//=== key1=====
if(key1==0)
{
ccc=1;
ddd++;
TR0=0; //关闭定时器0
TR1=1;//打开串口通信波特发生
d1=0;
while(key1==0)
{
;
}
mmm=1;
d1=1;
}


//=== key1=====
//===========
if(ccc==1)
{
d2=0;
if(ddd==2)
{
d2=1;
ddd=0; //进入次数记录
ccc=2; // 退出CCC

aaa=(65535-eee)%256;
bbb=(65535-eee)/256;

/*
TL0=aaa;
TH0=bbb;
TR0=1; //打开定时器0
TR1=0;//关闭串口通信波特发生
*/

//bbb=TH0=(65535-50000)/256;
//aaa= TL0=(65535-50000)%256;
mmm=1;
}//ddd=2 //二次进入处理
//===========短按、单按响应======
if(key2==0)
{
if(fff<20)
{
eee++;
fff=fff+100;
mmm=1;
}
}

if(key3==0)
{
if(fff<20)
{
eee--;
fff=fff+100;
mmm=1;
}
}

//===========短按、单按响应======
fff--;
if(fff<10)
{
fff=10;
}
hhh--;
if(hhh<fff)
{
hhh=6553;
if(key2==0)
{
eee++;
fff=fff+100;
mmm=1;
}
if(key3==0)
{
eee--;
fff=fff+100;

mmm=1;
}
if(fff>6300)
{
fff=6300;
}

}// hhh<fff


//=============数据输出
if(mmm==1)
{
mmm=65530;

ppp=1000000/(65535-eee);
aaa=(65535-eee)%256;
bbb=(65535-eee)/256;
send_char('[s:9]');
send_char('1');
send_char('=');
send_char(commm[ppp/10000%10]);
send_char(commm[ppp/1000%10]);
send_char(commm[ppp/100%10]);
send_char(commm[ppp/10%10]);
send_char(commm[ppp%10]);
send_char('H');
send_char('z');

send_char(' ');
send_char('H');
send_char('=');
send_char(commm[bbb/100%10]);
send_char(commm[bbb/10%10]);
send_char(commm[bbb%10]);

send_char(' ');
send_char('L');
send_char('=');
send_char(commm[aaa/100%10]);
send_char(commm[aaa/10%10]);
send_char(commm[aaa%10]);




qq123++;
if(qq123>7)
{
qq123=1;
send_char(' ');
send_char(' ');
send_char('<');
send_char('>');
}

SBUF=0xd;//28;    
while(!TI);    
TI=0;
SBUF=0xa;//28;    
while(!TI);    
TI=0;
send_char(' ');
SBUF=0xd;//28;    
while(!TI);    
TI=0;
SBUF=0xa;//28;    
while(!TI);    
TI=0;
}

//mmm--;
//=============数据输出
}// ccc=1
//===================




前几天就开始搞的 一真玩一下放一下, 写完核心在写设置
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
我说要有光
13年6个月前 IP:未同步
318435
回 15楼(jrcsh) 的帖子
不如换成52   用T2来做串口通讯
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
jrcsh
13年6个月前 IP:未同步
318478
Re:回 15楼(jrcsh) 的帖子
引用第16楼我说要有光于2011-08-25 19:39发表的 回 15楼(jrcsh) 的帖子 :
不如换成52   用T2来做串口通讯


STC10F08XE



/*
本程序要用到哪些功能了

定时器1 用于 发生频率的时间
定时器2用于 串行通信
中断0 用于key1 的进入

P1要进行IO翻转,传说中使用强推的方式会快点,你信不信,反正我没进行实测不知,反正开了强推也不挨事,开就是了.


接下来~~~找以前写的程序~~~~挖代码过来拼程序


20110817
中断0没有启用,普通IO查询可以满足.
完善按键处理程序
定时器0送入数据处理

下一计划完成手动设置及串口显示状态可工作后在加入通过串口进行数据设置



20110825
下午发呆...又操了操...
加速上数工作了
数据写到定时器有BUG 排除之
*/



#include <REG51.H>
//#include <intrins.h>
//#include <absacc.h>

#define uint unsigned int
#define uchar unsigned char



sfr P1M1   = 0x91 ;
sfr P1M0   = 0x92 ;
    
sbit d2=P2^1;
sbit d1=P2^0;
sbit key1=P3^2;
sbit key2=P3^6;
sbit key3=P3^7;
sbit key4=P3^5;

uint mmm,ccc,ddd,eee,fff,ggg,hhh,iii,lll,ppp;
uchar xxx,yyy,zzz,aaa,bbb,qq123;


uchar code commm[]={'0','1','2','3','4','5','6','7','8','9'};
/*


void delayms(uint z)  //ms
{                    
    uint x,y;
    for(x=z;x>0;x--)
        for(y=100;y>0;y--);    
}

*/

void send_char(unsigned char ddat)
// 传送一个字符
{
    SBUF = ddat;
    while(!TI);                // 等特数据传送
    TI = 0;                    // 清除数据传送标志
}


//timer0/counter0 interrupt
void timer0(void) interrupt 1
{
P1=~P1;
TL0=aaa;
TH0=bbb;
TR0=1; //管你有没关了开了在说

}

//int0 interrupt

void main()
{

TMOD=0x21;
TH0=255;
TL0=255;
TR0=1;
ET0=1;
SCON=0x50;
TH1=0xf3;
TL1=0xf3;
TR1=1;

EA=1;

P1M1   = 0x00 ;   //P1口设置为强推
P1M0   = 0xff ;    //晚些在设置
aaa=128;
bbb=128;
ccc=2;
ddd=0;
fff=6553;
eee=1;
ppp=30000;
ccc=0;//调试
P1=0xff;
d1=1;
d2=1;
while(1)
{

//=== key1=====
if(key1==0)
{
ccc=1;
ddd++;
//TR0=0; //关闭定时器0
//TR1=1;//打开串口通信波特发生
d1=0;
while(key1==0)
{
;
}
mmm=1;
d1=1;
}


//=== key1=====
//===========
if(ccc==1)
{
d2=0;
if(ddd==2)
{
d2=1;
ddd=0; //进入次数记录
ccc=2; // 退出CCC

aaa=(65535-eee)%256;
bbb=(65535-eee)/256;

TL0=aaa;
TH0=bbb;
//TR0=1; //打开定时器0
//TR1=0;//关闭串口通信波特发生


//bbb=TH0=(65535-50000)/256;
//aaa= TL0=(65535-50000)%256;
mmm=1;
}//ddd=2 //二次进入处理
//===========短按、单按响应======



if(fff==1)
{
eee=1;
}
if(fff==2)
{
eee=10;
}
if(fff==3)
{
eee=100;
}
if(fff==4)
{
eee=1000;
}
if(fff==5)
{
eee=10000;
}


if(key4==0)
{
fff++;
mmm=1;
while(key4==0)
{
;
}
}
if(fff>5)
{
fff=1;
}
//==============


if(key2==0)
{
ppp=ppp+eee;
mmm=1;
while(key2==0)
{
;
}
}

if(key3==0)
{
ppp=ppp-eee;
mmm=1;
while(key3==0)
{
;
}
}
if(ppp>65535)
{
ppp=1;
}
if(ppp<1)
{
ppp=65535;
}
//=============数据输出
if(mmm==1)
{
mmm=65530;
eee=1000000/ppp;
bbb=(65535-eee)/256;
aaa=(65535-eee)%256;
TL0=aaa;
TH0=bbb;
send_char('[s:9]');
send_char('1');
send_char('=');
send_char(commm[ppp/100000%10]);
send_char(commm[ppp/10000%10]);
send_char(commm[ppp/1000%10]);
send_char(' ');
send_char(commm[ppp/100%10]);
send_char(commm[ppp/10%10]);
send_char(commm[ppp%10]);
send_char('H');
send_char('z');
send_char(' ');

send_char('E');
send_char('=');
send_char(commm[eee/10000%10]);
send_char(commm[eee/1000%10]);
send_char(' ');
send_char(commm[eee/100%10]);
send_char(commm[eee/10%10]);
send_char(commm[eee%10]);
send_char('u');
send_char('S');
send_char(' ');


send_char(' ');
send_char('H');
send_char('=');
send_char(commm[bbb/100%10]);
send_char(commm[bbb/10%10]);
send_char(commm[bbb%10]);

send_char(' ');
send_char('L');
send_char('=');
send_char(commm[aaa/100%10]);
send_char(commm[aaa/10%10]);
send_char(commm[aaa%10]);
send_char(' ');
send_char(' ');
send_char(commm[fff]);


qq123++;
if(qq123>7)
{
qq123=1;
send_char(' ');
send_char(' ');
send_char('<');
send_char('>');
}

SBUF=0xd;//28;    
while(!TI);    
TI=0;
SBUF=0xa;//28;    
while(!TI);    
TI=0;
send_char(' ');
SBUF=0xd;//28;    
while(!TI);    
TI=0;
SBUF=0xa;//28;    
while(!TI);    
TI=0;
}

//mmm--;
//=============数据输出
}// ccc=1
//===================





    } //w1
}//ma



  可以工作了, 可是用示波器和频率记~~~来校对~~~ 设定的和输出的出入偏差很大  当玩具都不行
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
jrcsh
13年6个月前 IP:未同步
318493
输出范围 15Hz~32/65 KHz

测试录象

误差修下了一些小了好多更是还是比较不理想 ,不过已经可以作为一个玩具了
点击此处查看视频
+50
科创币
ldc
2011-08-26
输出范围挺宽的~
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
神之觉醒
13年6个月前 IP:未同步
318601
代码看得眼晕。。。  看LZ是个高手。。。单片机中断比较难搞定。。。定时器比较精确。。。 单用程序搞时钟输出不知能达到这个精度不。。。[s:275] 本人是新手。。。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
ldc作者
13年6个月前 IP:未同步
318621
回 19楼(神之觉醒) 的帖子
使用定时器的好处是可以充分利用CPU资源。不用定时器的话勉强可以达到要求的精度,但是必须不使用中断,键盘扫描、显示等其它功能函数也必须仔细地设计。因为不能合理有效地利用CPU资源,输出脉宽是不可能达到几微秒这个数量级的。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
zhangjl
11年3个月前 IP:未同步
653306
楼主,请问用STC89S52单片机怎样实现可调PWM,频率上线要求达到30K
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
金坷居士
11年3个月前 IP:未同步
654220
这东西感觉 精度还是硬伤
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
上级专业
同级专业
ldc
学者 机友 笔友
文章
75
回复
768
学术分
34
2005/10/01注册,14天4时前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:未同步
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}