本帖最后由 wosiyabo 于 2017-3-1 16:30 编辑
//项目:单按键手电控制开关机和调灯光暗模式
//单片机型号:STC15F104W 供电5V 内部晶振选12.00MHz
#include "reg51.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
uint zkb;
sfr P3M0 = 0xB2; //0000,0000 端口3模式寄存器0
sfr P3M1 = 0xB1; //0000,0000 端口3模式寄存器1
sfr AUXR = 0x8E; //辅助寄存器
sfr IAP_DATA = 0xC2; //IAP数据寄存器
sfr IAP_ADDRH = 0xC3; //IAP地址寄存器高字节
sfr IAP_ADDRL = 0xC4; //IAP地址寄存器低字节
sfr IAP_CMD = 0xC5; //IAP命令寄存器
sfr IAP_TRIG = 0xC6; //IAP命令触发寄存器
sfr IAP_CONTR = 0xC7; //IAP控制寄存器
#define CMD_IDLE 0 //空闲模式
#define CMD_READ 1 //IAP字节读命令
#define CMD_PROGRAM 2 //IAP字节编程命令
#define CMD_ERASE 3 //IAP扇区擦除命令
#define ENABLE_IAP 0x82 //if SYSCLK<20MHz
sbit P32 = P3^2; //按键
sbit P33 = P3^3; //输出电源控制
sbit P34 = P3^4; //PWM输出 781Hz 10/255 分辩率
void Delayms(uint ms) //1mS@12.000MHz
{
unsigned char i, j;
while(ms--)
{i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
void IapIdle()//关闭IAP
{
IAP_CONTR = 0; //关闭IAP功能
IAP_CMD = 0; //清除命令寄存器
IAP_TRIG = 0; //清除触发寄存器
IAP_ADDRH = 0x80; //将地址设置到非IAP区域
IAP_ADDRL = 0;
}
uchar IapReadByte(uint addr)//从ISP/IAP/EEPROM区域读取一字节
{
uchar dat; //数据缓冲区
IAP_CONTR = ENABLE_IAP; //使能IAP
IAP_CMD = CMD_READ; //设置IAP命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
dat = IAP_DATA; //读ISP/IAP/EEPROM数据
IapIdle(); //关闭IAP功能
return dat; //返回
}
void IapProgramByte(uint addr, uchar dat)//写一字节数据到ISP/IAP/EEPROM区域
{
IAP_CONTR = ENABLE_IAP; //使能IAP
IAP_CMD = CMD_PROGRAM; //设置IAP命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_DATA = dat; //写ISP/IAP/EEPROM数据
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
IapIdle();
}
void IapEraseSector(uint addr)//扇区擦除
{
IAP_CONTR = ENABLE_IAP; //使能IAP
IAP_CMD = CMD_ERASE; //设置IAP命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
IapIdle();
}
void Timer0Init(void) //5微秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xC4; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void main()
{
bit b;
uchar num,key_num=0;
P3M0 = 0x18;//P3M0置1时为强上拉输出 【7:0】 0001 1000 P34和P33为高
P3M1 = 0x00;
zkb=IapReadByte(0x0001);
P33=1;
Timer0Init();
ET0 = 1; //使能定时器0中断
EA = 1;
P32=1;
while(!P32)
{
Delayms(100);// 防止开机长按进入调光模式
}
if(zkb==255)b=1;//记录上一次是否最大,如果是最大,那么长按进入调暗
while (1)
{
if(!P32)
{
Delayms(10); // 开关防抖动
if(!P32)
{
while(!P32 && b==0) //以b=0作为判断上一次是调暗
{
key_num++;
Delayms(10);
if(key_num>60)//实际测试要2秒才进入调光模式
{
key_num=30;
zkb+=10; //因为试过加1次和加5次都不明显,加10次比较好辩认有没有工作
if(zkb>254)//限制最大值
{
zkb=0;
Delayms(20); //调到最大时闪一下提示
zkb=255;
Delayms(20);
}
}
}
while(!P32 && b==1) //以b=1作为判断上一次是调光
{
key_num++;
Delayms(10);
if(key_num>60)
{
key_num=30;
zkb-=10;
if(zkb<10)//限制最小值
{
zkb=0;
Delayms(20); //调到最小时闪一下提示
zkb=10;
Delayms(20);
}
}
}
b=~b; //反转做标记,记录调光或者是调暗
if(key_num<30) //短按小于1秒时进入保存数据和关机
{
num = zkb; ////这里是数据交换放在num内,因为写EEPROM时zkb的值会瞬间变为FF一下子
zkb=0; //防止关机闪强光
IapEraseSector(0x0001); // 清除EEPROM
IapProgramByte(0x0001, num); // 写入EEPROM
P33=0; //关机
}
key_num=0;//清计时,下一次如果是短按则关机
}
}
}
}
void tm0_isr() interrupt 1 using 1
{
static uchar i; /*中断次数计数器变量*/
TL0 = 0xC4; //设置定时初值
TH0 = 0xFF; //设置定时初值
i++;
if(i<=zkb)P34=1; //判断zkb的值选择是否输出高电平
else P34=0; //否则输出低电平
}
|