数字闹钟设计

闹钟整体编程

任务目标:如下图使用八位数码管实现一个数字闹钟,要求功能与成绩评定如下。

  1. 八位数码管显示时间+CL或者+AL。
  2. 系统启动后从00.00.00开始每秒加一,并修改显示的时间。
  3. 设计若干按键能修改当前时间。
  4. 设计若干按键能修改闹钟鸣响时间,在设定时间到时鸣响。
  5. 采用一首歌曲作为闹钟鸣响的提示声音。
  • 完成功能1,2,为及格。
  • 完成功能1,2,3,为中。
  • 完成功能1,2,3,为良。
  • 完成功能1,2,3,4,为优。
  • 完成功能1,2,3,4,5,为优。
  • 若考勤三次迟到,上述等级降1级。
  • 若旷课6节,则不及格。
  • 三人一组,组内程序一致,组间程序不一致,如雷同,则两组等级降1级。
  • 每组准备答辩,为10分钟,老师对组内程序提问,若某位同学回答不对,等级降1级。 闹钟原理图

1. 第一种编程思路

采用传统的中断程序结构来设计,回顾中断程序结构如下: 中断结构图
其中主程序由于在恒真循环中,所以反复运行,而中断子程序则是在中断信号来才会运行一次,图示给了两个中断信号和中断子程序。8051单片机有5个中断信号,但是在本项目可能使用的中断信号是定时器0中断,定时器1中断,外部中断0,外部中断1。 现在我们需要实现任务目标里面的5个功能,这些功能该如何安排?是安排在主程序中,还是安排在中断子程序中?中断子程序设计几个? 主程序功能的设计原则是需要反复运行(因其在循环中),根据这个标准来考虑。

  • 初步考虑,功能1是显示模块,而数码管的动态显示需要反复扫描,反复运行,所以放在主程序中。而功能2,3,4,5似乎都是某个信号来了(按键按下或者定时时间到),则运行某种功能,似乎功能2,3,4,5都放在中断子程序中比较合适。
  • 进一步考虑,功能3需要的按键就包括了按一下加一秒,按一下加一分,按一下加一小时,这样至少3个按键了,功能4同理也至少需要3个按键。而8051单片机才两个外部中断,只能接两个外部按键,所以,功能3,功能4,不能放在中断子程序中(8051外部中断不够,这个学期学的arm芯片则可满足要求)。功能2显然需要一个定时器,功能5的歌曲其实也要用定时器来控制节拍,所以刚好用掉8051全部的两个定时器T0,T1。
  • 综合考虑,我们把功能1,功能3,功能4,放在主程序中,功能2,功能5,放在中断子程序中。 所以,整体的编程结构图大致如下。
    整体框图

1.1 代码编写

C语言编程的经典方法是模块化方法,由上而下,由粗而精。所谓的模块就是子函数,代表一个独立的较为完整的功能,从上面的框图出发来践行模块化方法。我们先考虑功能1,2,3,4写出程序框架。

1.1.1 程序框架

#include "reg51.h"
#define OSC 12000000 //单片机晶振频率
#define FREQ 800     //蜂鸣器鸣叫的频率
char current_second = 0, current_minute = 0, current_hour = 0;
char alarm_second = 0, alarm_minute = 0, alarm_hour = 23;
sbit key0 = P1 ^ 0;
sbit key1 = P1 ^ 1;
sbit key2 = P1 ^ 2;
sbit key3 = P1 ^ 3;
sbit key4 = P1 ^ 4;
sbit buzzer = P1 ^ 5;
bit clock_mode = 1; //为1表示显示当前时间,为0表示显示闹钟设定时间。
void LedDisplay();  //函数声明,显示当前模式下应该显示的数值。
char GetKey();      //函数声明,该函数得到按键的值,返回-1表示按键没按下。
void Timer0Init();  //函数声明,定时器0初始化函数.
void BuzzerInit();  //函数声明,蜂鸣器初始化,用到定时器1。
void main()
{
    Timer0Init(); //定时器0初始化
    BuzzerInit();
    while (1)
    {
        LedDisplay(); //显示当前位
        switch (GetKey())
        {
        case 0:
            ;    //按键0 启动暂停切换
            break;
        case 1:
            if (clock_mode)
            {
                ;//按键1,时钟模式下秒加一
            }
            else
            {
                ;//按键1,闹钟模式下秒加一
            }
            break;
        case 2:
            if (clock_mode)
            {
                ;//按键2,时钟模式分加一
            }
            else
            {
                ;//按键2,闹钟模式分加一
            }
            break;
        case 3:
            if (clock_mode)
            {
                ;//按键3,时钟模式下分加一
            }
            else
            {
                ;//按键3,闹钟模式分加一
            }
            break;
        case 4:
            ; //时钟闹钟模式切换
            break;
        default:; //无按键按下后要做的事
            break;
        }
        if (current_hour == alarm_hour && current_minute == alarm_minute)
        {
            ; //蜂鸣器发声
        }
        else
        {
            ; //蜂鸣器停止发声
        }
    }
}
void Timer0Init()
{
    TMOD = (TMOD & 0xf0) | 0x01;                    //设定时器0为模式1且不修改T1的设置。
    TH0 = (int)(65536 - ((OSC / 12) * 0.01)) / 256; //定时10000us,即0.01秒
    TL0 = (int)(65536 - ((OSC / 12) * 0.01)) % 256;
    EA = 1;
    ET0 = 1;
    TR0 = 1;
}
void BuzzerInit()
{
    ;    //T1定时器初始化
}
void LedDisplay()
{
    static unsigned char current_bit = 0; //当前要显示的位
    unsigned char bit_code[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};
    unsigned char seg_code[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66,
                                0x6d, 0x7d, 0x07, 0x7f, 0x6f};
    unsigned char seg_code_point[] = {0xbf, 0x86, 0xdb, 0xcf, 0xe6,
                                      0xed, 0xfd, 0x87, 0xff, 0xef};
    P2 = 0x00;                  //消重影
    P3 = bit_code[current_bit]; //送当前位的位码
    switch (current_bit)
    {
    case 0:  //送第0位的段码
        P2 = clock_mode ? seg_code[current_hour / 10] : seg_code[alarm_hour / 10]; 
        break;
    case 1:
        ; //送第1位的段码
        break;
    case 2:
        ;//送第2位的段码
        break;
    case 3:
        ;//送第3位的段码
        break;
    case 4:
        ;//送第4位的段码
        break;
    case 5:
       ;//送第5位的段码
        break;
    case 6:
        ;//送第5位的段码
        break;
    case 7:
       ;//送第7位的段码
        break;
    default:
        break;
    }
    current_bit++;
    if (current_bit == 8)
        current_bit = 0;
}
//如果返回0,1,2,3,4分别表示0,1,2,3,4号键按下,返回-1表示无键按下。
char GetKey()
{
    static unsigned char current_key0 = 1;
    static unsigned char current_key1 = 1;
    static unsigned char current_key2 = 1;
    static unsigned char current_key3 = 1;
    static unsigned char current_key4 = 1;
    static unsigned char former_key0 = 1;
    static unsigned char former_key1 = 1;
    static unsigned char former_key2 = 1;
    static unsigned char former_key3 = 1;
    static unsigned char former_key4 = 1;

    former_key0 = current_key0;
    current_key0 = key0;
    former_key1 = current_key1;
    current_key1 = key1;
    former_key2 = current_key2;
    current_key2 = key2;
    former_key3 = current_key3;
    current_key3 = key3;
    former_key4 = current_key4;
    current_key4 = key4;

    if (former_key0 == 0 && current_key0 == 1)
    {
        return 0;
    }
    if (former_key1 == 0 && current_key1 == 1)
    {
        return 1;
    }
    if (former_key2 == 0 && current_key2 == 1)
    {
        return 2;
    }
    if (former_key3 == 0 && current_key3 == 1)
    {
        return 3;
    }
    if (former_key4 == 0 && current_key4 == 1)
    {
        return 4;
    }
    return -1;
}
void Ms100() interrupt 1
{
    /* 
    0.01秒到了,进入此中断子程序,进入100次后,表示1秒到,然后秒加一,秒到了60秒,分加一,分到了60分,
    小时加一,24小时到了,小时清零。
    */
   ;

}
void BuzzerISR() interrupt 3
{
   ;//重新加载T1定时器初值
   ;//蜂鸣器端口取反,发出方波
}

关于作者

添加评论

icesky

Get in touch

Quickly communicate covalent niche markets for maintainable sources. Collaboratively harness resource sucking experiences whereas cost effective meta-services.