微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 基于STC15L104E单片机模拟PWM调光小夜灯

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

时间:11-25 来源:互联网 点击:
鉴于宿舍晚上会被宿管灭灯,所以制作了一个小台灯在关灯后"窥探"用,但是舍友陈某反应光线太强影响其睡眠,所以决心做个可以调光的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;

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top