/*
*内部时钟源无法高精度校准,每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;//强制输出为低,关闭音频
}
|