第2周第4讲

蓝桥杯国赛大模板搭建笔记

一、前言

  • 适用人群:有编程基础,目标冲击蓝桥杯省赛、国赛的同学
  • 核心价值:模块化封装工程利于后续嵌入式开发学习
  • 校赛要求:掌握三大模板即可,无需强制掌握模块化封装

二、工程搭建步骤(共 12 步)

1. 创建工程文件夹(核心结构)

  • 新建根文件夹「国赛大模板」
  • 根目录下新增 2 个文件夹(缺一不可):
    • User:存放工程文件、main.c
    • Driver:存放底层驱动文件(如 Key、Seg 相关)

2. 使用 Keil5 新建工程

  • 打开 Keil5,选择「Create New Project」
  • 保存路径:国赛大模板\User
  • 工程名称:国赛大模板(保存类型默认 Project Files)

3. 配置工程「品」字工具栏

  • 点击 Keil5 中「Manage Project Items」(工程项管理)
  • 在「Groups」栏点击「Add Files」,新增「Driver」组(用于存放底层驱动)
  • 确认当前目标为「国赛大模板」,点击「OK」

4. 魔术棒(Options for Target)配置(2 个关键配置)

配置 1:生成 Hex 文件

  • 点击 Keil5 工具栏「魔术棒」图标
  • 切换到「Output」选项卡
  • 勾选「Create HEX File」(必须勾选,否则无法生成烧录文件)
  • 点击「OK」

配置 2:添加头文件引用路径

  • 魔术棒中切换到「C51」选项卡
  • 点击「Include Paths」右侧的「…」(文件夹图标)
  • 浏览并选中「国赛大模板 \Driver」文件夹,点击「确定」
  • 点击「OK」保存配置

5. 新建 main.c 并编写主体框架

  • 在「User」组内右键→「Add New Item」→选择「C Source File」
  • 文件名:main.c,保存路径:User文件夹
  • 编写基础框架代码:
/*头文件声明区*/
#include <REGX52.H>

/*Main*/
void main()
{
    while(1)
    {
        // 后续添加业务逻辑
    }
}

6. 新建 Key 底层文件(Driver 文件夹)

  • 打开 Keil5→「File」→「New」,新建 2 个空白文件
  • 分别保存为:
    • 文件名 1:Key.c,保存路径:Driver文件夹
    • 文件名 2:Key.h,保存路径:Driver文件夹
  • 保存类型均选择「All Files」

7. 编写 Key 底层代码

Key.c(按键底层实现)

#include <Key.h>  // 底层.c文件必须引用自身.h文件

// 按键读取函数(检测P3_4~P3_7按键)
unsigned char Key_Read()
{
    unsigned char temp = 0;
    if(P3_4 == 0) temp = 1;
    if(P3_5 == 0) temp = 2;
    if(P3_6 == 0) temp = 3;
    if(P3_7 == 0) temp = 4;
    return temp;
}

Key.h(按键函数声明)

#include <REGX52.H>  // 用到单片机寄存器,需引用51头文件

// 函数声明(结尾必须加分号)
unsigned char Key_Read();

8. 新建 Seg 底层文件(Driver 文件夹)

  • 操作步骤同第 6 步,新建 2 个文件:
    • Seg.c(数码管底层实现)
    • Seg.h(数码管函数声明)
  • 保存路径:Driver文件夹

9. 编写 Seg 底层代码

Seg.c(数码管显示实现)

#include <Seg.h>  // 引用自身头文件

// 数码管段码表(0~9)
unsigned char Seg_Dula[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
// 数码管位选表(6个数码管)
unsigned char Seg_Wela[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf};

// 数码管显示函数(wela:位选,dula:段码)
void Seg_Disp(unsigned char wela, unsigned char dula)
{
     // 位选控制
    P0 = 0x00;  // 清空显示
    P2_6 = 1;
    P2_6 = 0;
    
     // 段码控制
    P0 = Seg_Wela[wela];
    P2_7 = 1;
    P2_7 = 0;
    
     // 锁存显示
    P0 = Seg_Dula[dula];
    P2_6 = 1;
    P2_6 = 0;
}

Seg.h(数码管函数声明)

#include <REGX52.H>  // 引用51头文件

// 函数声明(结尾加分号)
void Seg_Disp(unsigned char wela, unsigned char dula);

10. 添加底层文件到工程

  • 在 Keil5 左侧「Project」窗口,双击「Driver」组
  • 弹出「Add Files to Group Driver」窗口,浏览到Driver文件夹
  • 选中「Key.c」和「Seg.c」(按住 Ctrl 全选)
  • 点击「Add」→「Close」,完成添加

11. 测试工程框架正确性

  • 点击 Keil5 工具栏「Build」(编译)按钮
  • 查看底部「Build Output」:
    • 显示「0 错误,2 警告」:工程创建正确
    • 若有错误,检查文件路径、头文件引用、代码语法

12. 编写 main.c 完整国赛模板

/*头文件声明区*/
#include <REGX52.H>
#include <Key.h>
#include <Seg.h>

/*全局变量定义区*/
// 按键专用变量
unsigned char Key_Val, Key_Down, Key_Old;
unsigned char Key_Slow_Down;  // 按键减速防抖
// 数码管专用变量
unsigned char Seg_Buf[6] = {10, 10, 10, 10, 10, 10};  // 显示数据缓存(10代表熄灭)
unsigned char Seg_Pos;  // 扫描位置
unsigned int Seg_Slow_Down;  // 数码管减速扫描

/*键盘处理函数(捕捉下降沿+防抖)*/
void Key_Proc()
{
    if(Key_Slow_Down) return; // 启动防抖计时 
    Key_Slow_Down = 1; 

/* 理解:当Key_Slow_Down被清零后,下一次执行Key_Proc时,不会触发return,(这中间隔了10ms,足以消抖)就能顺利执行Key_Val */  
    
    Key_Val = Key_Read();  // 实时读取键值
    Key_Down = Key_Val & (Key_Old ^ Key_Val);  // 捕捉按键下降沿
    Key_Old = Key_Val;  // 更新旧键值
}

/*数码管处理函数*/
void Seg_Proc()
{
    if(Seg_Slow_Down) return;
    Seg_Slow_Down = 1;  // 启动扫描减速
    
    // 此处可添加显示逻辑(如Seg_Buf赋值)
}

/*LED处理函数(可扩展)*/
void Led_Proc()
{
    // 按需添加LED控制逻辑
}

/*定时器0初始化(1毫秒@12.000MHz)*/
void Timer0Init(void)
{
    TMOD &= 0xF0;  // 清除定时器0模式
    TMOD |= 0x01;  // 设置定时器0为模式1(16位自动重装)
    TL0 = 0x18;    // 定时初始值低位
    TH0 = 0xFC;    // 定时初始值高位
    TF0 = 0;       // 清除溢出标志
    TR0 = 1;       // 启动定时器0
    ET0 = 1;       // 使能定时器0中断
    EA = 1;        // 使能总中断
}

/*定时器0中断服务函数(处理减速计时+数码管扫描)*/
void Timer0Server() interrupt 1
{
    TL0 = 0x18;  // 重置定时初始值
    TH0 = 0xFC;
    
    // 按键防抖计时(10ms)
    if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
    // 数码管扫描计时(500ms)
    if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;
    
    // 数码管循环扫描(6个位置)
    Seg_Disp(Seg_Pos, Seg_Buf[Seg_Pos]);
    if(++Seg_Pos == 6) Seg_Pos = 0;
}

/*主函数*/
void main()
{
    Timer0Init();  // 初始化定时器0
    while(1)
    {
        Key_Proc();  // 按键处理
        Seg_Proc();  // 数码管处理
        Led_Proc();  // LED处理
    }
}

三、关键注意事项

  1. 文件夹结构:UserDriver缺一不可,路径不能包含中文
  2. 头文件引用:
    • 底层.c 文件必须引用自身.h 文件(如 Key.c→Key.h)
    • 底层.h 文件若用到寄存器,需引用REGX52.H
  3. 函数声明:.h 文件中函数声明结尾必须加分号
  4. 编译验证:0 错误 + 2 警告为正常(警告多为未使用变量,不影响功能)
  5. 熟练技巧:main.c 中的模板代码需多敲多练,形成肌肉记忆