//*****使用STC15F104E制作的計(jì)時(shí)器*****
//1. 上電為關(guān)閉狀態(tài)。
//2. 長(zhǎng)按按鍵上電可轉(zhuǎn)換模式,默認(rèn)模式1:10分-150分 步進(jìn)10分鐘設(shè)置,共15檔設(shè)置。模式2:1小時(shí)-15小時(shí) 步進(jìn)1小時(shí)設(shè)置,共15檔設(shè)置。
//3. 按一下按鍵可以遞增計(jì)時(shí)時(shí)間,1、2、3...一直可以累加到15檔,再按則關(guān)閉(計(jì)時(shí)時(shí)間為0)。
//4. 使用4個(gè)LED分別按8421碼指示總計(jì)時(shí)時(shí)間,最多15檔。
//5. 計(jì)時(shí)期間,繼電器吸合,計(jì)時(shí)結(jié)束后繼電器釋放,蜂鳴器響起。
//6. 計(jì)時(shí)期間相應(yīng)的LED每秒閃一次。
//7. 可以斷電記憶未完成的時(shí)間,來電接著繼續(xù)計(jì)時(shí)??紤]到STC單片機(jī)EEPROM壽命問題,故10分鐘保存一次計(jì)時(shí)狀態(tài)。
//8. 因STC15F104E上電后P3.3是低電平,故P3.3接按鍵,以避免副作用。
//9. 使用內(nèi)部振蕩11.0592M
#include
#include
typedef unsigned char UINT8;
typedef unsigned int UINT16;
//***************************************************************************
//定義Flash操作等待時(shí)間及允許IAP/ISP/EEPROM 操作的常數(shù)
#define ENABLE_ISP 0x83 //系統(tǒng)工作時(shí)鐘<12MHz 時(shí),對(duì)IAP_CONTR 寄存器設(shè)置此值
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; //時(shí)間計(jì)數(shù)
UINT8 buzz_time = 0; //蜂鳴器響鈴時(shí)間設(shè)置
UINT8 Delay_counting = 0;//按鍵消抖
bit light, save;
bit min_hour = 0 ; //分和時(shí)計(jì)數(shù)選擇 模式選擇:0=時(shí)鐘計(jì)數(shù) 1=分鐘計(jì)數(shù)
bit system_time = 0; //系統(tǒng)時(shí)間標(biāo)記
bit run_stop = 0; //時(shí)間計(jì)數(shù)與停止 0=停止計(jì)數(shù) 1=允許計(jì)數(shù)
bit buzz_run = 0 ; //1 = 啟動(dòng)蜂鳴器 0= 關(guān)閉蜂鳴器
#define MAX_HOURS 15 //時(shí)設(shè)置最大值
//***************************************************************************
void IAP_Disable()
{
//關(guān)閉IAP 功能, 清相關(guān)的特殊功能寄存器,使CPU 處于安全狀態(tài),
//一次連續(xù)的IAP 操作完成之后建議關(guān)閉IAP 功能,不需要每次都關(guān)
IAP_CONTR = 0; //關(guān)閉IAP 功能
IAP_CMD = 0; //清命令寄存器,使命令寄存器無命令,此句可不用
IAP_TRIG = 0; //清命令觸發(fā)寄存器,使命令觸發(fā)寄存器無觸發(fā),此句可不用
IAP_ADDRH = 0;
IAP_ADDRL = 0;
}
//***************************************************************************
//eeprom讀一字節(jié)
//入口: addr = 要讀取字節(jié)的地址
//返回: 讀出的字節(jié)
//***************************************************************************
UINT8 Byte_Read(UINT16 addr)
{
IAP_DATA = 0x00;
IAP_CONTR = ENABLE_ISP; //打開IAP 功能, 設(shè)置Flash 操作等待時(shí)間
IAP_CMD = 0x01; //IAP/ISP/EEPROM 字節(jié)讀命令
IAP_ADDRH = addr >> 8; //設(shè)置目標(biāo)單元地址的高8 位地址
IAP_ADDRL = addr; //設(shè)置目標(biāo)單元地址的低8 位地址
IAP_TRIG = 0x5A; //先送5Ah,再送A5h 到ISP/IAP 觸發(fā)寄存器,每次都需如此
IAP_TRIG = 0xA5; //送完A5h后,ISP/IAP 命令立即被觸發(fā)起動(dòng)
_nop_(); _nop_(); _nop_();
//IAP_Disable(); //關(guān)閉IAP 功能, 清相關(guān)的特殊功能寄存器,使CPU 處于安全狀態(tài),
//一次連續(xù)的IAP 操作完成之后建議關(guān)閉IAP 功能,不需要每次都關(guān)
return (IAP_DATA); //返回讀到的數(shù)據(jù)
}
//***************************************************************************
//eeprom寫一字節(jié)
//入口: addr = 要寫字節(jié)的地址
// ch = 要寫入的字節(jié)數(shù)據(jù)
//***************************************************************************
void Byte_Write(UINT16 addr, UINT8 ch)
{
IAP_CONTR = ENABLE_ISP; //打開 IAP 功能, 設(shè)置Flash 操作等待時(shí)間
IAP_CMD = 0x02; //IAP/ISP/EEPROM 字節(jié)編程命令
IAP_ADDRH = addr >> 8; //設(shè)置目標(biāo)單元地址的高8 位地址
IAP_ADDRL = addr; //設(shè)置目標(biāo)單元地址的低8 位地址
IAP_DATA = ch; //要編程的數(shù)據(jù)先送進(jìn)IAP_DATA 寄存器
IAP_TRIG = 0x5A; //先送5Ah,再送A5h 到ISP/IAP 觸發(fā)寄存器,每次都需如此
IAP_TRIG = 0xA5; //送完A5h后,ISP/IAP 命令立即被觸發(fā)起動(dòng)
_nop_(); _nop_(); _nop_();
//IAP_Disable(); //關(guān)閉IAP 功能, 清相關(guān)的特殊功能寄存器,使CPU 處于安全狀態(tài),
//一次連續(xù)的IAP 操作完成之后建議關(guān)閉IAP 功能,不需要每次都關(guān)
}
//***************************************************************************
//eeprom扇區(qū)擦除
//入口: sect_addr = 扇區(qū)地址
//***************************************************************************
void Sector_Erase(UINT16 sect_addr)
{
IAP_CONTR = ENABLE_ISP; //打開IAP 功能, 設(shè)置Flash 操作等待時(shí)間
IAP_CMD = 0x03; //IAP/ISP/EEPROM 扇區(qū)擦除命令
IAP_ADDRH = (sect_addr & 0xFE00) >> 8; //設(shè)置目標(biāo)單元地址的高8 位地址
IAP_ADDRL = sect_addr; //設(shè)置目標(biāo)單元地址的低8 位地址
IAP_TRIG = 0x5A; //先送 5Ah,再送A5h 到ISP/IAP 觸發(fā)寄存器,每次都需如此
IAP_TRIG = 0xA5; //送完A5h 后,ISP/IAP 命令立即被觸發(fā)起動(dòng)
_nop_(); _nop_(); _nop_();
//IAP_Disable(); //關(guān)閉IAP 功能, 清相關(guān)的特殊功能寄存器,使CPU 處于安全狀態(tài),
//一次連續(xù)的IAP 操作完成之后建議關(guān)閉IAP 功能,不需要每次都關(guān)
}
//***************************************************************************
//**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;
}
//***************************************************************************
//**定時(shí)器**
//***************************************************************************
void Init() //50毫秒@11.0592MHz
{
AUXR &= 0x7F; //定時(shí)器時(shí)鐘12T模式
TMOD &= 0xF0; //設(shè)置定時(shí)器模式
TL0 = 0x00; //設(shè)置定時(shí)初值
TH0 = 0x4C; //設(shè)置定時(shí)初值
TF0 = 0; //清除TF0標(biāo)志
TR0 = 1; //啟動(dòng)定時(shí)器0計(jì)時(shí)
ET0 = 1;
EA = 1;
}
//***************************************************************************
//定時(shí)器0中斷
//***************************************************************************
void Timer0() interrupt 1
{
system_time = 1 ; //系統(tǒng)時(shí)間標(biāo)記
}
//***************************************************************************
//**模式:按時(shí)鐘計(jì)數(shù)**
//***************************************************************************
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); //計(jì)時(shí)結(jié)束后清除保存的信息,以防下次加電繼續(xù)計(jì)時(shí)。
}
}
}
}
}
}
}
//***************************************************************************
//**模式:按分鐘計(jì)數(shù)**
//***************************************************************************
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(); //停止計(jì)數(shù)
Sector_Erase(0); //計(jì)時(shí)結(jié)束后清除保存的信息,以防下次加電繼續(xù)計(jì)時(shí)。
}
}
}
}
}
}
}
//***************************************************************************
//**蜂鳴器輸出**
//***************************************************************************
void buzz_out()
{
static UINT8 number;
if(1==buzz_run){buzz_time = 50;buzz_run = 0;}//如果時(shí)間到........
if(1 == system_time && 0== run_stop && buzz_time>=1)
{
system_time = 0; //系統(tǒng)時(shí)間時(shí)標(biāo)
if(++number >= 5) //響鈴間隔
{
number = 0 ;
if(--buzz_time>=1)
{
key1 = ~key1;
}
else {key1 = 1 ;buzz_time = 0;}
}
}
}
//***************************************************************************
//**10us延時(shí)**用于按鍵消抖
//***************************************************************************
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--);
}
//***************************************************************************
//**按鍵設(shè)置時(shí)間**
//***************************************************************************
void ReadKey()
{
if(buzz_time<=0) //按鍵引腳與蜂鳴器輸出共用,蜂鳴器響時(shí)按鍵不參與檢測(cè)
{
if(!key1)
{
if(Delay_counting < 40) //按鍵消抖
{
Delay(); //延時(shí)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,待下一次延時(shí)消抖
}
}
//***************************************************************************
//**檢查時(shí)間**
//***************************************************************************
void check()
{
if(count == 0 || count > MAX_HOURS)
{
count = 0;
run_stop = 0;
}
else
{
relay = 0;
run_stop = 1;
}
}
//***************************************************************************
//**8 4 2 1 LED顯示時(shí)間**
//***************************************************************************
void display()
{
if(count == 0) //運(yùn)行檢查
{
StopRun();
}
else
{
led4 = count & 8 ? light : 1; //顯示時(shí)間
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); //讀時(shí)間設(shè)置
}
//***************************************************************************
//**寫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);//寫設(shè)置時(shí)間
}
}
//***************************************************************************
//**程序入口**
//***************************************************************************
void main()
{
P3 = 0xFF; //初始化端口狀態(tài)
light = 0;
save = 0; //10分鐘保護(hù)一次時(shí)間數(shù)據(jù)
Init(); //初始化定時(shí)器
read_data();//讀epprom
check(); //檢查時(shí)間是否到
buzz_time = 0;//蜂鳴器響鈴時(shí)間清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 小時(shí)模式
while(min_hour == 1)//**模式:時(shí)間按時(shí)鐘來計(jì)數(shù)**
{
ReadKey(); //讀取按鍵
time_hour(); //時(shí)計(jì)數(shù)
buzz_out(); //蜂鳴器輸出
Write_epprom(); //寫epprom
display(); //8 4 2 1 LED顯示時(shí)間
}
while(min_hour == 0)//**模式:時(shí)間按分鐘來計(jì)數(shù)**
{
ReadKey(); //讀取按鍵
time_min(); //分鐘計(jì)數(shù)
buzz_out(); //蜂鳴器輸出
Write_epprom(); //寫epprom
display(); //8 4 2 1 LED顯示時(shí)間
}
}