/*
*內部時鐘源無法高精度校準,每20分鐘較標準時鐘快1秒,可通過代碼修正
*/
#include <iom48v.h>
#define BUTTON_REPEAT_TIME 19 //按鍵重復速度,N個數值(N+1)*4ms
void clockInit(void);
void IOInit(void);
void timerInit(void);
void TIM0_COMPA(void);
void TIMER1_OVF(void);
void ringTheBell(void);
void canaelTheBell(void);
const unsigned char tubeData[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90};//0-9
typedef struct{
unsigned char hours;
unsigned char minutes;
unsigned char isAlarmEnable;
}Clock;
struct{
unsigned char data[4];
unsigned char dot[4];
}s;
Clock baseClock={12,30,0},alarmClock={0};
unsigned char buttonRepeatTime=0;
unsigned char second=0;
unsigned timerInterruptTimes=0;
unsigned char setAlarm=0;
unsigned char alarming=0;
unsigned char currentTube=0;
unsigned short ringToneCursor=10;
unsigned char cursorDirection=1;
void main(void)
{
s.data[0]=baseClock.hours/10;
s.data[1]=baseClock.hours%10;
s.data[2]=baseClock.minutes/10;
s.data[3]=baseClock.minutes%10;
clockInit();
IOInit();
timerInit();
SREG|=0X80;
while(1);
}
void IOInit(void){
/*
*PB1為蜂鳴器,2-5為按鍵輸入,4設置鬧鐘和確認,5取消鬧鐘功能,2,3改變時和分
*PC0-3為數碼管位選
*PD為數碼管段選
*/
MCUCR&=0xEF;//清零PUD,使能上拉
DDRB=0XC2;//0-3輸入
PORTB|=0X3C;//2-5輸出高電平
DDRC|=0X0F;//0-3為輸出
PORTC|=0X0E;//0-3輸出高,數碼管關閉
DDRD=0XFF;//全部為輸出
PORTD=0XFF;//關閉數碼管
}
void timerInit(void)
{
/*
*定時器0用于定時刷新數碼管,62Hz左右;定時檢測按鍵
*定時器1用于輸出音頻
*/
TCCR0A=0X02;//CTC模式,匹配即清零計數
TIMSK0=0X02;//使能A匹配中斷
OCR0A=249;//時鐘產生250Hz的中斷
TCCR0B=0X02;//選擇時鐘為64分頻,計數器開始計數
}
#pragma interrupt_handler TIM0_COMPA:15
void TIM0_COMPA(void)
{
//處理按鍵事件
if(~(PINB|0XC3))
{
buttonRepeatTime++;
if(buttonRepeatTime>BUTTON_REPEAT_TIME)
{
buttonRepeatTime=0;
if(~(PINB|0XDF))
{//判斷取消鬧鈴按鍵是否按下,優先級最高
//顯示回當前時間
s.data[0]=baseClock.hours/10;
s.data[1]=baseClock.hours%10;
s.data[2]=baseClock.minutes/10;
s.data[3]=baseClock.minutes%10;
alarmClock.isAlarmEnable=0;//關閉鬧鐘
s.dot[3]=0;//熄滅鬧鐘指示點
setAlarm=0;//狀態切換
if(alarming)//關閉鬧鐘
{
alarming=0;
canaelTheBell();
}
}
else if(~(PINB|0XEF)) //判斷設置確定按鍵
{
if(setAlarm)
{
//保存設置
alarmClock.isAlarmEnable=1;
s.dot[3]=1;
//顯示回當前時間
s.data[0]=baseClock.hours/10;
s.data[1]=baseClock.hours%10;
s.data[2]=baseClock.minutes/10;
s.data[3]=baseClock.minutes%10;
}
else
{
//顯示上次保存的鬧鐘時間
s.data[0]=alarmClock.hours/10;
s.data[1]=alarmClock.hours%10;
s.data[2]=alarmClock.minutes/10;
s.data[3]=alarmClock.minutes%10;
s.dot[1]=0;//熄滅秒顯示點
}
setAlarm=!setAlarm;
}
else
{
//設置當前顯示的時間數值
if(setAlarm)
{
if(~(PINB|0XFB)) alarmClock.hours=alarmClock.hours<23?alarmClock.hours+1:0;
if(~(PINB|0XF7)) alarmClock.minutes=alarmClock.minutes<59?alarmClock.minutes+1:0;
s.data[0]=alarmClock.hours/10;
s.data[1]=alarmClock.hours%10;
s.data[2]=alarmClock.minutes/10;
s.data[3]=alarmClock.minutes%10;
}
else
{
//重置秒計數和定時器計數
timerInterruptTimes=0;
second=0;
s.dot[1]=0;
if(~(PINB|0XFB)) baseClock.hours=baseClock.hours<23?baseClock.hours+1:0;
if(~(PINB|0XF7)) baseClock.minutes=baseClock.minutes<59?baseClock.minutes+1:0;
s.data[0]=baseClock.hours/10;
s.data[1]=baseClock.hours%10;
s.data[2]=baseClock.minutes/10;
s.data[3]=baseClock.minutes%10;
}
}
}
}
else buttonRepeatTime=0;
//時間定時
if(timerInterruptTimes>=249) //第250個中斷
{
timerInterruptTimes=0;
//第二點閃爍,指示秒
if(!setAlarm) s.dot[1]=!s.dot[1];
if(second>=59)
{
//重新計算時間
second=0;
if(baseClock.minutes>=59)
{
baseClock.minutes=0;
baseClock.hours=baseClock.hours<23?baseClock.hours+1:0;
}
else baseClock.minutes++;
//刷新數碼管時間值
if(!setAlarm)
{
s.data[0]=baseClock.hours/10;
s.data[1]=baseClock.hours%10;
s.data[2]=baseClock.minutes/10;
s.data[3]=baseClock.minutes%10;
}
} else second++;
//判斷鬧鐘是否匹配
if(alarmClock.isAlarmEnable&&baseClock.hours==alarmClock.hours&&baseClock.minutes==alarmClock.minutes)
{
if(!alarming)
{
alarming=1;
ringTheBell();
}
}
else if(alarming)
{
alarming=0;
canaelTheBell();
}
}
timerInterruptTimes++;
//刷新數碼管
currentTube=currentTube<3?currentTube+1:0;
PORTD=0XFF;//熄滅數碼管
PORTC=0XFF;//關閉位控
PORTC&=~(1<<currentTube);//1左移currentTube位
PORTD=tubeData[s.data[currentTube]];
if(s.dot[currentTube]) PORTD&=0X7F;
}
#pragma interrupt_handler TIMER1_OVF:14
void TIMER1_OVF(void)
{
OCR1AH=ringToneCursor/256;
OCR1AL=ringToneCursor%256;
if(cursorDirection)
{
if(ringToneCursor<1790)
{
ringToneCursor++;
}
else cursorDirection=0;
}else
{
if(ringToneCursor>500)
{
ringToneCursor--;
}
else cursorDirection=1;
}
}
void ringTheBell(void)
{
OCR1AH=0;//修改占空比數值,有緩存器,會在BOTTOM修改
OCR1AL=10;
TIMSK1=0X01;//開啟溢出中斷
TCNT1H=0;//歸零計數器
TCNT1L=0;
ICR1H=0X07;//0X01//設置輸出頻率,稍大于11025
ICR1L=0X08;//0X69
TCCR1B=0X19;
TCCR1A=0X82;//快速PWM模式,在BOTTOM置位,匹配清零,時鐘無分頻
}
void canaelTheBell(void)
{
//關閉音頻輸出步驟
TCCR1B=0;//先關閉時鐘
TCCR1A=0X80;//修改模式為匹配時清零
TCCR1C=0;//強制輸出為低,關閉音頻
}
|