基于定时器的倒计时程序设计

一、代码模块化编写笔记

1. 模块化核心思想

将程序按功能拆分为独立的模块,每个模块负责特定功能,通过接口(函数声明)交互,降低代码耦合度,提高可维护性和复用性。

2. 模块划分原则

  • 按功能划分:如示例中拆分为 “按键模块”(key.c/key.h)和 “数码管显示模块”(seg.c/seg.h),分别负责按键读取和数码管驱动。

  • 单一职责:每个模块只做一件事(如 key 模块仅处理按键读取,不涉及显示逻辑)。

3. 模块文件组成(以 key 模块为例)

  • 头文件(.h):声明模块对外暴露的接口(函数、全局变量),供其他模块调用。

    示例(key.h):

    #include "REGX52.h"
    unsigned char Key_Read(); // 声明按键读取函数,供外部调用
    
  • 源文件(.c):实现头文件中声明的接口,包含模块内部逻辑(隐藏实现细节)。

    示例(key.c):

    #include "key.h"
    // 实现按键读取逻辑,具体引脚判断仅在模块内部可见
    unsigned char Key_Read(){
        unsigned char temp = 0;
        if(P2_4 == 0) temp = 1;
        if(P2_5 == 0) temp = 2;
        // ... 其他按键判断
        return temp;
    }
    

4. 模块间交互规则

  • 其他模块通过#include "模块名.h"使用该模块的功能(如 main.c 通过#include "key.h"调用Key_Read())。

  • 模块内部变量 / 函数(仅模块内使用)应声明为static,避免外部访问(示例中未体现,可优化)。

  • 全局变量需在头文件中声明(加extern),源文件中定义(示例中 main.c 的全局变量为整个程序共享,未拆分到模块)。

5. 优势

  • 可维护性:修改某功能只需改对应模块(如改按键引脚只需改 key.c)。

  • 可复用性:模块可直接移植到其他项目(如 seg 模块可复用在其他需要数码管显示的程序)。

  • 逻辑清晰:主程序(main.c)只需关注业务流程,无需关心硬件细节。

二、main.c 大模板笔记

示例 main.c 是典型的 51 单片机程序框架,核心是 “定时器中断 + 主循环” 结构,适用于需要定时刷新(如按键消抖、数码管动态扫描)的场景。

1. 结构组成

(1)头文件与全局变量定义

  • 包含所需模块的头文件(#include "key.h"#include "seg.h")。

  • 定义全局变量:存储系统状态(如按键值Key_Val、数码管显示缓存Seg_Buf)、定时计数器(如Key_Slow用于按键消抖计时)。

    // 全局变量示例
    unsigned char Key_Val, Key_Down, Key_Old; // 按键状态变量
    unsigned char Seg_Slow; // 数码管刷新定时器(500ms)
    unsigned char Seg_Buf[]={10,10,10,10,10,10}; // 数码管显示缓存
    

(2)功能处理函数

按功能拆分的业务逻辑函数,在主循环中轮询调用:

  • Key_proc():处理按键逻辑(读取按键、判断按键按下事件)。

  • Seg_proc():处理数码管显示逻辑(更新显示缓存)。

  • Led_proc():预留 LED 处理函数(可扩展 LED 控制逻辑)。

(3)定时器初始化与中断服务

  • 定时器初始化:配置定时器(如Timer0Init()配置 1ms 定时,用于定时刷新)。

  • 中断服务程序:定时触发(如 1ms 一次),处理周期性任务:

    • 计时(Key_SlowSeg_Slow计数,实现延时)。

    • 硬件刷新(如Seg_Disp()动态扫描数码管,避免闪烁)。

    void Timer0server() interrupt 1{
        TL0 = 0x18; TH0 = 0xFC; // 重装载定时值(1ms)
        if(++Key_Slow == 10) Key_Slow = 0; // 10ms按键消抖计时
        if(++Seg_Slow == 500) Seg_Slow =0; // 500ms数码管刷新计时
        Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]); // 动态扫描数码管
    }
    

(4)主函数(main)

  • 初始化硬件(如Timer0Init()初始化定时器)。

  • 死循环中轮询调用功能处理函数,实现业务逻辑:

    void main(){
        Timer0Init(); // 初始化定时器
        while(1){
            Key_proc(); // 按键处理
            Seg_proc(); // 显示处理
            Led_proc(); // LED处理
        }
    }
    

2. 模板优势

  • 实时性:定时器中断保证周期性任务(如数码管扫描)准时执行。

  • 逻辑分离:主循环处理业务逻辑,中断处理硬件定时任务,分工明确。

  • 可扩展性:新增功能只需添加对应处理函数(如Buzzer_proc()蜂鸣器处理),并在主循环调用。

3. 适用场景

  • 需定时刷新的嵌入式系统(如数码管显示、按键消抖、传感器采样)。

  • 多任务简单处理(通过主循环轮询实现 “伪多任务”)