找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1442|回复: 0
打印 上一主题 下一主题
收起左侧

48单片机闹钟设计 内部时钟源无法高精度校准,每20分钟较标准时钟快1秒

[复制链接]
跳转到指定楼层
楼主
ID:489155 发表于 2019-3-12 13:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
/*
*内部时钟源无法高精度校准,每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;//强制输出为低,关闭音频
}



评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表