//*****使用STC15F104E制作的计时器*****
//1. 上电为关闭状态。
//2. 长按按键上电可转换模式,默认模式1:10分-150分 步进10分钟设置,共15档设置。模式2:1小时-15小时 步进1小时设置,共15档设置。
//3. 按一下按键可以递增计时时间,1、2、3...一直可以累加到15档,再按则关闭(计时时间为0)。
//4. 使用4个LED分别按8421码指示总计时时间,最多15档。
//5. 计时期间,继电器吸合,计时结束后继电器释放,蜂鸣器响起。
//6. 计时期间相应的LED每秒闪一次。
//7. 可以断电记忆未完成的时间,来电接着继续计时。考虑到STC单片机EEPROM寿命问题,故10分钟保存一次计时状态。
//8. 因STC15F104E上电后P3.3是低电平,故P3.3接按键,以避免副作用。
//9. 使用内部振荡11.0592M
#include
#include
typedef unsigned char UINT8;
typedef unsigned int UINT16;
//***************************************************************************
//定义Flash操作等待时间及允许IAP/ISP/EEPROM 操作的常数
#define ENABLE_ISP 0x83 //系统工作时钟<12MHz 时,对IAP_CONTR 寄存器设置此值
sbit relay= P3^5;
sbit key1 = P3^3; //因STC15F104E加电P3.3为低电平,为避免副作用,作为按键输入。
sbit led1 = P3^4;
sbit led2 = P3^2;
sbit led3 = P3^1;
sbit led4 = P3^0;
UINT8 sec, min,count; //时间计数
UINT8 buzz_time = 0; //蜂鸣器响铃时间设置
UINT8 Delay_counting = 0;//按键消抖
bit light, save;
bit min_hour = 0 ; //分和时计数选择 模式选择:0=时钟计数 1=分钟计数
bit system_time = 0; //系统时间标记
bit run_stop = 0; //时间计数与停止 0=停止计数 1=允许计数
bit buzz_run = 0 ; //1 = 启动蜂鸣器 0= 关闭蜂鸣器
#define MAX_HOURS 15 //时设置最大值
//***************************************************************************
void IAP_Disable()
{
//关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
//一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
IAP_CONTR = 0; //关闭IAP 功能
IAP_CMD = 0; //清命令寄存器,使命令寄存器无命令,此句可不用
IAP_TRIG = 0; //清命令触发寄存器,使命令触发寄存器无触发,此句可不用
IAP_ADDRH = 0;
IAP_ADDRL = 0;
}
//***************************************************************************
//eeprom读一字节
//入口: addr = 要读取字节的地址
//返回: 读出的字节
//***************************************************************************
UINT8 Byte_Read(UINT16 addr)
{
IAP_DATA = 0x00;
IAP_CONTR = ENABLE_ISP; //打开IAP 功能, 设置Flash 操作等待时间
IAP_CMD = 0x01; //IAP/ISP/EEPROM 字节读命令
IAP_ADDRH = addr >> 8; //设置目标单元地址的高8 位地址
IAP_ADDRL = addr; //设置目标单元地址的低8 位地址
IAP_TRIG = 0x5A; //先送5Ah,再送A5h 到ISP/IAP 触发寄存器,每次都需如此
IAP_TRIG = 0xA5; //送完A5h后,ISP/IAP 命令立即被触发起动
_nop_(); _nop_(); _nop_();
//IAP_Disable(); //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
//一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
return (IAP_DATA); //返回读到的数据
}
//***************************************************************************
//eeprom写一字节
//入口: addr = 要写字节的地址
// ch = 要写入的字节数据
//***************************************************************************
void Byte_Write(UINT16 addr, UINT8 ch)
{
IAP_CONTR = ENABLE_ISP; //打开 IAP 功能, 设置Flash 操作等待时间
IAP_CMD = 0x02; //IAP/ISP/EEPROM 字节编程命令
IAP_ADDRH = addr >> 8; //设置目标单元地址的高8 位地址
IAP_ADDRL = addr; //设置目标单元地址的低8 位地址
IAP_DATA = ch; //要编程的数据先送进IAP_DATA 寄存器
IAP_TRIG = 0x5A; //先送5Ah,再送A5h 到ISP/IAP 触发寄存器,每次都需如此
IAP_TRIG = 0xA5; //送完A5h后,ISP/IAP 命令立即被触发起动
_nop_(); _nop_(); _nop_();
//IAP_Disable(); //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
//一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
}
//***************************************************************************
//eeprom扇区擦除
//入口: sect_addr = 扇区地址
//***************************************************************************
void Sector_Erase(UINT16 sect_addr)
{
IAP_CONTR = ENABLE_ISP; //打开IAP 功能, 设置Flash 操作等待时间
IAP_CMD = 0x03; //IAP/ISP/EEPROM 扇区擦除命令
IAP_ADDRH = (sect_addr & 0xFE00) >> 8; //设置目标单元地址的高8 位地址
IAP_ADDRL = sect_addr; //设置目标单元地址的低8 位地址
IAP_TRIG = 0x5A; //先送 5Ah,再送A5h 到ISP/IAP 触发寄存器,每次都需如此
IAP_TRIG = 0xA5; //送完A5h 后,ISP/IAP 命令立即被触发起动
_nop_(); _nop_(); _nop_();
//IAP_Disable(); //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
//一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
}
//***************************************************************************
//**Run and stop**
//***************************************************************************
void BeginRun()
{
relay = 0;
led1 = led2 = led3 = led4 = 1;
run_stop = 1;
sec = min = 0;
}
//***************************************************************************
void StopRun()
{
relay = 1;
run_stop = 0;
led1 = led2 = led3 = led4 = 1;
sec = min = 0;
count = 0;
}
//***************************************************************************
//**定时器**
//***************************************************************************
void Init() //50毫秒@11.0592MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x4C; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //启动定时器0计时
ET0 = 1;
EA = 1;
}
//***************************************************************************
//定时器0中断
//***************************************************************************
void Timer0() interrupt 1
{
system_time = 1 ; //系统时间标记
}
//***************************************************************************
//**模式:按时钟计数**
//***************************************************************************
void time_hour()
{
static UINT8 tim0, tim1, tim2;
if(1==system_time && 1==run_stop)
{
system_time = 0;
if(++tim0 >= 10) //500ms
{
tim0 = 0;
light = !light;
if(++tim1 >= 2) //1s
{
tim1 = 0;
if(++sec >= 60) //1m
{
if(++tim2 >= 10) //10m
{
tim2 = 0;
save = 1;
}
sec = 0;
if(++min >= 60) //1h
{
min = 0;
if(count>=1){count--;if(0==count){buzz_run = 1;}}
else
{
StopRun();
Sector_Erase(0); //计时结束后清除保存的信息,以防下次加电继续计时。
}
}
}
}
}
}
}
//***************************************************************************
//**模式:按分钟计数**
//***************************************************************************
void time_min()
{
static UINT8 tim0, tim1, tim2;
if(1==system_time && 1==run_stop)
{
system_time = 0;
if(++tim0 >= 5) //500ms
{
tim0 = 0;
light = !light;
if(++tim1 >= 4) //1s
{
tim1 = 0;
if(++sec >= 60) //1min
{
sec = 0;
if(++tim2 >= 10) //10min
{
tim2 = 0;
save = 1;
if(count>=1){count--;if(0==count){buzz_run = 1 ;}}
else
{
StopRun(); //停止计数
Sector_Erase(0); //计时结束后清除保存的信息,以防下次加电继续计时。
}
}
}
}
}
}
}
//***************************************************************************
//**蜂鸣器输出**
//***************************************************************************
void buzz_out()
{
static UINT8 number;
if(1==buzz_run){buzz_time = 50;buzz_run = 0;}//如果时间到........
if(1 == system_time && 0== run_stop && buzz_time>=1)
{
system_time = 0; //系统时间时标
if(++number >= 5) //响铃间隔
{
number = 0 ;
if(--buzz_time>=1)
{
key1 = ~key1;
}
else {key1 = 1 ;buzz_time = 0;}
}
}
}
//***************************************************************************
//**10us延时**用于按键消抖
//***************************************************************************
void Delay(void)
{
UINT8 i,j,k;
for(i=6;i>0;i--)
for(j=1;j>0;j--)
for(k=1;k>0;k--);
}
//***************************************************************************
//**按键设置时间**
//***************************************************************************
void ReadKey()
{
if(buzz_time<=0) //按键引脚与蜂鸣器输出共用,蜂鸣器响时按键不参与检测
{
if(!key1)
{
if(Delay_counting < 40) //按键消抖
{
Delay(); //延时10us
Delay_counting++;
}
if(!key1 && Delay_counting == 40)
{
count++ ;
if(count > MAX_HOURS) count=0;
if(count == 0)
{
StopRun();
}
else
{
BeginRun();
}
Delay_counting = 210 ;
}
}
else if(key1)Delay_counting = 0;//松手清0,待下一次延时消抖
}
}
//***************************************************************************
//**检查时间**
//***************************************************************************
void check()
{
if(count == 0 || count > MAX_HOURS)
{
count = 0;
run_stop = 0;
}
else
{
relay = 0;
run_stop = 1;
}
}
//***************************************************************************
//**8 4 2 1 LED显示时间**
//***************************************************************************
void display()
{
if(count == 0) //运行检查
{
StopRun();
}
else
{
led4 = count & 8 ? light : 1; //显示时间
led3 = count & 4 ? light : 1;
led2 = count & 2 ? light : 1;
led1 = count & 1 ? light : 1;
}
}
//***************************************************************************
//**读EPPROM**
//***************************************************************************
void read_data()
{
sec = Byte_Read(0); //读秒
min = Byte_Read(1); //读分
min_hour = Byte_Read(2);//读模式
count = Byte_Read(3); //读时间设置
}
//***************************************************************************
//**写EPPROM**
//***************************************************************************
void Write_epprom()
{
if(save & TR0) //保存到eeprom中
{
save = 0;
Sector_Erase(0);//擦除EEPROM
Byte_Write(0, sec);//写秒
Byte_Write(1, min);//写分
Byte_Write(2, min_hour);//写模式
Byte_Write(3, count);//写设置时间
}
}
//***************************************************************************
//**程序入口**
//***************************************************************************
void main()
{
P3 = 0xFF; //初始化端口状态
light = 0;
save = 0; //10分钟保护一次时间数据
Init(); //初始化定时器
read_data();//读epprom
check(); //检查时间是否到
buzz_time = 0;//蜂鸣器响铃时间清0
//模式选择(按住按键上电)
if(!key1){min_hour=~min_hour;buzz_time = 5;Sector_Erase(0);while(!key1){if(0==min_hour){led1 = 0;}else led4=0;};}
//01 LED代表10 - 150 分钟模式 08 LED代表1 - 15 小时模式
while(min_hour == 1)//**模式:时间按时钟来计数**
{
ReadKey(); //读取按键
time_hour(); //时计数
buzz_out(); //蜂鸣器输出
Write_epprom(); //写epprom
display(); //8 4 2 1 LED显示时间
}
while(min_hour == 0)//**模式:时间按分钟来计数**
{
ReadKey(); //读取按键
time_min(); //分钟计数
buzz_out(); //蜂鸣器输出
Write_epprom(); //写epprom
display(); //8 4 2 1 LED显示时间
}
}