单片机第三周重制版

:blue_book: 蓝桥杯单片机“大模板”完全指南

:glowing_star: 一、 核心架构:分层思想

“大模板”不是一堆乱七八糟的代码,而是一个分工明确的工厂

层级 对应函数/模块 职责 (通俗解释)
底层驱动 Key.c, Seg.c “手和脚”:负责直接操作硬件IO口,比如读电平、点亮段码。
中断层 Timer0Server “心脏”:负责提供时间基准(1ms),并在后台偷偷刷新数码管,保证不闪烁。
逻辑层 Key_Proc, Seg_Proc “大脑”:只处理数据。比如按下键数字+1,把数字拆解填入显存。
主循环 main() “老板”:坐在办公室里,不停地轮询各个部门(函数)有没有工作要做。

:rocket: 二、 核心机制详解

1. “时间切片”机制 (解决 delay 卡死问题)

单片机三.md 的案例中,我们需要同时走时、扫描按键、刷新数码管。如果用 Delay(500),单片机就“睡死”了。

解决方案: 使用减速变量 (Slow_Down)。

  • 原理:定时器每 1ms 进来把变量 +1。主程序里检查变量是不是到了阈值(比如 10)。

  • 代码范式

C

// 在 Timer0Server 中 (每1ms执行)
if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
​
// 在 Key_Proc 中
void Key_Proc()
{
    if(Key_Slow_Down) return; // 时间没到10ms?直接走人,去干别的
    Key_Slow_Down = 1;        // 时间到了!重置锁,开始干活
    
    // ...这里写按键逻辑...
}

2. “显存”机制 (Seg_Buf)

你不需要在逻辑代码里去管数码管哪一位亮。你只需要修改数组

  • 定义unsigned char Seg_Buf[8]

  • 操作

    • 想让第 1 位显示 ‘5’:Seg_Buf[0] = 5;

    • 想让第 3 位熄灭:Seg_Buf[2] = 10; (假设 10 代表熄灭)

  • 自动刷新Timer0Server 会自动把这个数组里的数搬运到屏幕上。


:light_bulb: 三、 实战案例:电子钟与闹钟 (基于单片机三.md)

结合你上传的文档,我们看看大模板是如何处理一个复杂的“时钟+闹钟”项目的。

1. 全局变量设计

不要把所有变量混在一起,要分类管理:

变量类 变量名示例 作用
时间变量 Clock_Disp (包含时分秒) 记录当前真实时间,每秒更新。
闹钟变量 Alarm, Alarm_Set 记录闹钟设定值和开关状态。
显示模式 Seg_Disp_Mode 核心变量:0=显示时间,1=显示闹钟设置。
辅助计时 Timer_1000ms 用来计算 1 秒钟,让时间走动。

2. 逻辑层实现 (Seg_Proc)

显示函数只根据 Seg_Disp_Mode 来决定 Seg_Buf 里填什么,完全不管硬件

C

void Seg_Proc()
{
    if(Seg_Slow_Down) return;
    Seg_Slow_Down = 1;
​
    switch(Seg_Disp_Mode) 
    {
        case 0: // 模式0:显示实时时间
            Seg_Buf[0] = Clock_Disp.hour / 10;
            Seg_Buf[1] = Clock_Disp.hour % 10;
            Seg_Buf[2] = 11; // 显示横杠 -
            // ...填入分和秒
            break;
​
        case 1: // 模式1:显示闹钟设置
            Seg_Buf[0] = Alarm.hour / 10;
            Seg_Buf[1] = Alarm.hour % 10;
            // ...
            break;
    }
}

3. 输入层实现 (Key_Proc)

按键只负责修改数据和模式。

C

void Key_Proc()
{
    // ...消抖逻辑...
    
    switch(Key_Down)
    {
        case 1: // 切换显示模式
            Seg_Disp_Mode++;
            if(Seg_Disp_Mode > 1) Seg_Disp_Mode = 0;
            break;
            
        case 2: // 修改时间
            if(Seg_Disp_Mode == 0) Clock_Disp.hour++; // 改时间
            if(Seg_Disp_Mode == 1) Alarm.hour++;      // 改闹钟
            break;
    }
}

:hammer_and_wrench: 四、 进阶规范与避坑 (来自文档精华)

为了写出更稳定、不报错的代码,单片机三.md 提出了以下建议:

1. 关键字的使用

关键字 场景 作用
code unsigned char code Table[] 把表格存在 Flash (硬盘) 里,节省内存 (RAM)
static 局部变量 让变量在函数结束周后不丢失数值,下次进来还能接着用。
volatile 中断共享变量 告诉编译器:这个变量会在中断里变,不要自作聪明去优化它

2. 调试技巧

  • 警告等级:在 Keil 中把 Warning Level 调高,打开 “All Warnings”。

  • 常见错误

    • switchcase 忘记写 break(导致穿透执行)。

    • 数组未初始化(可能导致上电乱码)。


:memo: 五、 零基础复习口诀

  1. 中断 是心脏,每毫秒跳一次。

  2. Key_Proc 改变量,Seg_Proc 填数组。

  3. Seg_Buf 是屏幕,填啥它就显啥。

  4. Slow_Down 是闹钟,时间没到不干活。

  5. 变量 要分类,模式 (Mode) 决定显谁。