基于STC15L104E单片机模拟PWM调光小夜灯

基于STC15L104E单片机模拟PWM调光小夜灯
基于STC15L104E单片机模拟PWM调光小夜灯

基于STC15L104E 单片机模拟PWM 调光小夜灯

(2012-10-23 14:44:44)

转载

▼ 标签:

单片机

stc15

模拟pwm

led 调光

杂谈 分类: 硬件

鉴于宿舍晚上会被宿管灭灯,所以制作了一个小台灯在关灯后"窥探"用,但是舍友陈某反应光线太强影响其睡眠,所以决心做个可以调光的LED 小夜灯,窥探自己的隐私,让别人睡觉去吧...

搜索自己的原件库,发现上半年买的STC15L104E 一直没用,所以打算就用单片机来控制了,当然,如果用模拟电路,可能几个元件就可以完成了,但是,模拟对我来说更难设计,所以,,,好吧,废话多了下面贴电路:

其实也没什么,纯粹是IO 操作,两个开光是调光用,AMS117是降压用,AMP4953就相当于开关,后面的电感电容电路是防止出现闪烁的,额,如果有问题的话可以贴出来.

下面介绍模拟PWM 操作,毕竟不是硬件的PWM 而且要顾及程序长度,所以模拟出来的PWM 频率600HZ,不过用在LED 上应该足够了.

设PWM 基数为PWM_NUM,PWM 比较输出值为PWM_CMP.

主要思路就是利用单片机内部计时器按一定周期中断,然后在中断服务程序里令

PWM_NUM 加一,判断是否小于PWM_CMP 比较值,如果小于,则IO 输出高,否则输出低,这样,

我们就可以通过修改PWM_CMP的值来调节IO输出脉宽,达到模拟PWM的效果了.PWM周期=计时器中断时间/PWM_NUM;

好吧,我的文字表达能力不好,贴张图来说明可能比较好:

如图,假设PWM_CMP=6,PWM_NUM=16,也就是,PWM_NUM达到16后清零.每个计时器中断周期PWM_NUM自动加一,判断是否小于PWM_CMP,是则输出高电平,否则输出低电平,所以,如图所示,当PWM_NUM累加到6前,输出电平是一直是高的,知道PWM_NUM累加到6后,输出就拉低了,当PWM_NUM达到16后,清零,这时PWM_NUM又小于PWM_CMP,故PWM输出又为高了,周而复始,就达到模拟PWM的效果了,PWM周期就等于PWM_NUM重装周期.

当然值得注意的是,因为判断是在中断服务程序中执行,所以计时器周期不能太短.

好吧,贴整个完整的程序出来,它的功能我先说明一下.

按下key1变亮,按下key2变暗,长按则连续调节,两个键一起按下改变显示方式,分别有四个模式,单个分别亮,两个亮,两个都不亮.每次改变亮度和模式都写入单片机EEPROM,开机读取EEPROM,这样便能开机保持上次关机的状态,不用每次打开都要调节了.说到这我就要吐槽了,这STC15系列A版外部中断呐,连掉电唤醒都实现不了,那几个外部中断口都是摆设的.所以原本打算使用掉电模式作为关机状态的,但是唤醒不了(除了复位键),所以只好用外部硬件开关关机了....

废话不多说,贴程序,程序有标注,看不明白或有问题指教的贴出来,虚心接受.

#include "reg52.h"

#include "intrins.h"

#define ON 0 //LED是低电平有效

#define OFF 1

#define PWM_TIME 200 //计时器计数值

#define MODEL_ADDR 0x0000 //EEPEOM地址#define PWM_CMP_ADDR 0x0200

sfr AUXR =0x8e;

sfr P3M1 =0xb1;

sfr P3M0 =0xb2;

sfr IAP_DATA =0xc2;

sfr IAP_ADDRH=0xc3;

sfr IAP_ADDRL=0xc4;

sfr IAP_CMD =0xc5;

sfr IAP_TRIG =0xc6;

sfr IAP_CONTR=0xc7;

sbit KEY1=P3^0;

sbit KEY2=P3^1;

sbit LED1=P3^2;

sbit LED2=P3^3;

unsigned char key_down,key_cont,key_up;

//donw为上升沿,cont为长效,up为下降沿unsigned char pwm_cmp=1; //PWM比较值unsigned char pwm_num=0; //PWM计数值unsigned char led_model=0; //LED模式unsigned char pwm_delay=0; //延时暂存unsigned char cmp_temp;

//led_cmp的暂存,用于运算是否写入EEPROM

//计时器0初始化

void timer_init()

{

TMOD=0x00; //模式0,16位自动装载模式

EA=1;

ET0=1;

TR0=1;

AUXR|=0x80; //1T模式

TL0=65536-PWM_TIME;

TH0=(65536-PWM_TIME)>>8;

}

//EEPROM读写后防止误操作

void eeprom_dle()

{

IAP_CONTR=0;

IAP_CMD =0;

IAP_TRIG =0;

IAP_ADDRH=0;

IAP_ADDRL=0;

}

//擦除EEPROM扇区

void eeprom_erase(unsigned int addr)

{

IAP_CONTR=0x83;

IAP_CMD =0x03;

IAP_ADDRL=addr;

IAP_ADDRH=addr>>8;

IAP_TRIG =0x5a;

IAP_TRIG =0xa5;

_nop_();_nop_();_nop_();

//eeprom_dle();

}

//读取EEPROM,因为EEPROM只存两个变量,故读取直接改变变量void read_eeprom()

{

IAP_CONTR=0x83;

IAP_CMD =0x01;

IAP_ADDRL=MODEL_ADDR;

IAP_ADDRH=MODEL_ADDR>>8;

IAP_TRIG =0x5a;

IAP_TRIG =0xa5;

_nop_();_nop_();_nop_();

led_model=IAP_DATA;

IAP_CMD =0x01;

IAP_ADDRL=PWM_CMP_ADDR;

IAP_ADDRH=PWM_CMP_ADDR>>8;

IAP_TRIG =0x5a;

IAP_TRIG =0xa5;

_nop_();_nop_();_nop_();

pwm_cmp=IAP_DATA;

if(pwm_cmp>180)pwm_cmp=0x01;

eeprom_dle();

}

//写EEPROM

void write_eeprom(unsigned int add,unsigned char dat)

{

if(PCON&0x20)return; //如果电压过低,不操作

eeprom_erase(add);

IAP_CONTR =0x83;

IAP_CMD =0x02;

IAP_ADDRL =add;

IAP_ADDRH =add>>8;

IAP_DATA =dat;

IAP_TRIG =0x5a;

IAP_TRIG =0xa5;

_nop_();_nop_();_nop_();

eeprom_dle();

}

//模式切换

void model_change()

{

//因为模式切换无非是改变P3^2,P3^3状态而led_model是直接赋值给P3的(在T0中断函数里)

//所以相应的只改变led_model相应位即可

led_model+=4;

write_eeprom(MODEL_ADDR,led_model); //将模式写入EEPROM

while((key_cont&0x03)==0x03); //直到按键释放

}

void main()

{

P3M1=0x0f;

P3M0=0x0f; //P3为开漏模式

PCON=0x00; //清除低电压标志(必须)

LED2=LED1=OFF;

read_eeprom(); //读取状态

timer_init(); //计数器初始化

cmp_temp=pwm_cmp;

while(1)

{

cmp_temp=pwm_cmp; //储存pwm比较值

if((key_down&0x01)&&(pwm_cmp<180))

{

//如果KEY1按下,比较值加一,相应的是LED变亮

pwm_cmp++;

pwm_delay=0;

}

if((key_down&0x02)&&(pwm_cmp>1))

{

//如果KEY2按下,比较值减一,相应的是LED变暗

pwm_cmp--;

pwm_delay=0;

}

if(pwm_delay>30) //延时,延时时间为PWM周期*30

{

//如果按键长按,则每次延时周期自加减,实现长按快速调光

if((key_cont&0x01)&&(pwm_cmp<180))

pwm_cmp++;

if((key_cont&0x02)&&(pwm_cmp>1))

pwm_cmp--;

pwm_delay=0; //重新计时

//如果两个按键都是按下的,则改变模式

if((key_cont&0x03)==0x03)model_change();

}

//如果比较值改变了,而且只是上升沿和下降沿,则写入EEPROM

//因为EEPROM操作时间较长,所以如果每次改变都写入,则在长按状态下会出现闪烁现象//所以只在单次按下或按键长按释放时写入EEPROM

if((pwm_cmp^cmp_temp)&&(key_down|key_up))

{

//关停LED再做写入操作,如果写入操作前LED为亮的状态

//则在整个EEPROM操作时间里LED都为亮,故出现"爆闪"现象

EA=0;

P3|=0x0c;

write_eeprom(PWM_CMP_ADDR,pwm_cmp);

EA=1;

}

}

}

void timer0() interrupt 1

{

unsigned char read_data=(P3&0x03)^0x03; //按键扫描

key_down=read_data&(read_data^key_cont); //按键上升沿

key_up=key_cont&(read_data^key_cont); //按键下降沿

key_cont=read_data; //按键长效值

pwm_num++; //PWM周期数自加

if(pwm_num>pwm_cmp) //控制PWM输出

P3&=(0xf3|led_model); //开

else

P3|=0x0c; //关

if(pwm_num>200) //PWM周期

{

pwm_num=0;

pwm_delay++;

}

}

里要说说程序下载的问题,因为这片单片机是没有外部晶振的,所以下载时要确认单片机完全掉电,就连串口的GND端都要从电路板上拔出.下载软件我用的是STC-ISP V4.88 beta版本,下载时选好型号,然后修改最低波特率为2400,在我实际应用中,默认的4800是写不进去的,RC频率按自己要求修改,其他的默认就好了.单片机如果第一次写可能要将P3.2和P3.3接GND,如果勾选<下次冷启动P3.2/P3.3与下载无关>(默认),那以后就不用在乎它们的状态了.

好了,程序也贴了,也没什么可以供我诉说的了,实物太丑陋,就不贴了.

相关主题
相关文档
最新文档