第三周学习笔记

第三周

大模板

创建工程模板
  • 新建 UserDriver 文件夹

  • User 中创建工程,选择 AT89C52

  • 在“魔法棒”中:Output 勾选 Hex;C51 添加 Driver 路径

  • 在“品”字图标中修改工程名、用户组名,并添加底层组

  • User 中创建 main.c

添加底层文件
  • Driver 中新建 Led.cLed.hKey.cKey.h 并加入工程
/* 按键层 */
unsigned char Key_Read()
{
    unsigned char temp = 0;
    P3_0 = 0; P3_1 = 1; P3_2 = 1; P3_3 = 1;
    if (P3_4 == 0) temp = 1;
    if (P3_5 == 0) temp = 2;
    if (P3_6 == 0) temp = 3;
    if (P3_7 == 0) temp = 4;
    P3_0 = 1; P3_1 = 0; P3_2 = 1; P3_3 = 1;
    if (P3_4 == 0) temp = 5;
    if (P3_5 == 0) temp = 6;
    if (P3_6 == 0) temp = 7;
    if (P3_7 == 0) temp = 8;
    P3_0 = 1; P3_1 = 1; P3_2 = 0; P3_3 = 1;
    if (P3_4 == 0) temp = 9;
    if (P3_5 == 0) temp = 10;
    if (P3_6 == 0) temp = 11;
    if (P3_7 == 0) temp = 12;
    P3_0 = 1; P3_1 = 1; P3_2 = 1; P3_3 = 0;
    if (P3_4 == 0) temp = 13;
    if (P3_5 == 0) temp = 14;
    if (P3_6 == 0) temp = 15;
    if (P3_7 == 0) temp = 16;
    return temp;
}
​
/* 数码管底层 */
unsigned char Seg_Dula[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};
unsigned char Seg_Wela[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
​
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;
}
主程序结构
  • 头文件声明区
/* 头文件声明 */
#include <REGX52.H>
#include "key.h"
#include "Seg.h"
  • 变量声明区
/* 变量声明 */
unsigned char Key_Slow_Down;        // 按键减速
unsigned int  Seg_Slow_Down;        // 数码管减速
unsigned char Key_Val, Key_Up, Key_Down, Key_Old; // 按键扫描相关
unsigned char Seg_Pos;              // 数码管扫描位
unsigned char Seg_Buf[6] = {1,2,3,4,5,6}; // 显示缓冲区
  • 按键处理函数
    按键减速用于消抖并降低 CPU 占用。
void Key_Proc()
{
    if (Key_Slow_Down) return;
    Key_Slow_Down = 1;
    Key_Val = Key_Read();
    Key_Down = Key_Val & (Key_Val ^ Key_Old); // 检测下降沿
    Key_Up = ~Key_Val & (Key_Val ^ Key_Old);  // 检测上升沿
    Key_Old = Key_Val;
​
    switch (Key_Down)
    {
​
    }
}
  • 信息处理函数
    数码管减速用于控制刷新频率,实现闪烁效果。
void Seg_Proc()
{
    if (Seg_Slow_Down) return;
    Seg_Slow_Down = 1;
}
  • 其他显示函数
void Led_Proc()
{
​
}
  • 定时器0初始化函数

STC 定时器计算器:12MHz、1ms、16位不自动重装载、12T;开启中断 ET0 = 1EA = 1

void Timer0_Init(void)      // 1ms @ 12.000MHz
{
    TMOD &= 0xF0;
    TMOD |= 0x01;
    TL0 = 0x18;
    TH0 = 0xFC;
    TF0 = 0;
    TR0 = 1;
    ET0 = 1;
    EA = 1;
}
  • 定时器0中断服务函数

先写入定时初值:

TL0 = 0x18;
TH0 = 0xFC;

完整中断:

void Timer0Sever() interrupt 1
{
    TL0 = 0x18;
    TH0 = 0xFC;
    if (++Key_Slow_Down == 10) Key_Slow_Down = 0;
    if (++Seg_Slow_Down == 500) Seg_Slow_Down = 0;
    if (++Seg_Pos == 6) Seg_Pos = 0;
    Seg_Disp(Seg_Pos, Seg_Buf[Seg_Pos]);
}
  • 主函数
void main()
{
    Timer0_Init();
    while (1)
    {
        Key_Proc();
        Seg_Proc();
        Led_Proc();
    }
}

模板完成后需上电测试;后续编码也应频繁测试,根据现象修改。

倒计时模拟题

题目阅读顺序
  • 先看硬件框图,确定用到的硬件

  • 浏览全题,了解要实现的功能

  • 逐步查看数码管显示界面、按键及其条件

用大模板编写程序

  • 从数码管入手,声明 Seg_Mode,编写显示界面

  • 实现倒计时并上电测试;时间为 0 时要“卡住”

  • 写“开始”按键,声明 System_Flag

  • 写“切换”按键:Seg_Mode 取反

  • 写设置界面:用数组保存设置参数

  • 写“设置”按键:通过数组索引显示设置参数,切换界面时保存参数

  • 写“复位”按键

  • 写 LED 和蜂鸣器;蜂鸣器对应 P2_3,低电平有效

  • 写闪烁程序

学会查错,善用大模板。程序题70分,客观题30分(4道C语言、6道用户手册)。

倒计时程序

  • 从数码管入手,有两种显示界面时立即写 Mode 变量

  • 编写显示界面与倒计时

unsigned char Seg_Mode; // 0-显示界面 1-设置界面
​
Seg_Buf[0] = Seg_Mode + 1;
if (Seg_Mode == 0)      // 显示界面
{
    Seg_Buf[4] = Timer_Count / 10 % 10;
    Seg_Buf[5] = Timer_Count % 10;
}
  • S1启动按键:使用 System_Flag 标志位,启动逻辑写在倒计时程序中
if (System_Flag == 1)        // 系统使能
{
    if (++Timer_1000Ms == 1000)
    {
        Timer_1000Ms = 0;
        Timer_Count--;
        if (Timer_Count == 255)
            Timer_Count = 0;
    }
}
  • S3切换按键:Seg_Mode 取反
case 3: // “切换”按键
    Seg_Mode ^= 1;
    break;
  • S4设置按键:采用数组思想,注意处理逻辑易错点
case 4: // “设置”按键
    if (Seg_Mode == 1)
    {
        if (++Timer_Data_Index == 3)
            Timer_Data_Index = 0;
    }
    break;
  • S2复位按键:Timer_Count = Timer_Data[Timer_Data_Index];
case 2: // “复位”按键
    Timer_Count = Timer_Data[Timer_Data_Index];
    break;
  • 设置参数闪烁:1s 周期,500ms 亮、500ms 灭
if (++Timer_500ms == 500)
{
    Timer_500ms = 0;
    Seg_Flag ^= 1;
}
​
if (Timer_Count == 0)
{
    P1 = 0x00; // LED 全亮
    P2_3 = 0;  // 蜂鸣器响
}
else
{
    P1 = 0xff; // LED 全灭
    P2_3 = 1;  // 蜂鸣器停
}

时钟程序

  • 参数+按键:累加条件为 (Clock_Set_Data_Index ? 60 : 24)
if (++Clock_Set_Data[Clock_Set_Data_Index] == (Clock_Set_Data_Index ? 60 : 24))
    Clock_Set_Data[Clock_Set_Data_Index] = 0;
  • 时钟程序
if (Timer_1000ms == 1000)
{
    Timer_1000ms = 0;
    Clock_Dat[2]++;
    if (Clock_Dat[2] == 60)
    {
        Clock_Dat[2] = 0;
        Clock_Dat[1]++;
        if (Clock_Dat[1] == 60)
        {
            Clock_Dat[1] = 0;
            Clock_Dat[0]++;
            if (Clock_Dat[0] == 24)
                Clock_Dat[0] = 0;
        }
    }
}
  • LED 控制逻辑

我的版本:

bit Seg_Flag;            // 数码管闪烁标志
bit Alarm_Enable_Flag;   // 闹钟使能 0-使能
bit Led_Flag;            // LED 标志
bit Alarm_Time_Flag;     // 上/下午判断
​
if (Alarm_Enable_Flag == 0 &&
    Clock_Disp[0] == Alarm_Set_Data[0] &&
    Clock_Disp[1] == Alarm_Set_Data[1] &&
    Clock_Disp[2] == Alarm_Set_Data[2])
    Led_Flag = 1;
​
if (Led_Flag)
{
    if (Clock_Disp[0] < 12)              // 上午:前4个闪烁
        P1 = Seg_Flag ? 0xf0 : 0xff;
    else                                 // 下午:后4个闪烁
        P1 = Seg_Flag ? 0x0f : 0xff;
    P2_3 = 0;
}
else
{
    P1 = 0xff;
    P2_3 = 1;
}
​
if (++Timer_500Ms == 500)
{
    Timer_500Ms = 0;
    Seg_Flag ^= 1;
}

范例程序:

unsigned char Alarm_Flag;   // 闹钟工作标志位 0-工作 1-不工作
bit Led_Enable_Flag;        // LED、蜂鸣器使能标志 0-不使能 1-使能
unsigned char Led;          // LED 工作状态
​
void Led_Proc()
​
{
    if (Alarm_Flag == 0)
    {
        if (Clock_Dat[0] == Alarm[0] &&
            Clock_Dat[1] == Alarm[1] &&
            Clock_Dat[2] == Alarm[2])
            Led_Enable_Flag = 1;
        if (Led_Enable_Flag == 1)
        {
            P2_3 = 0;
            P1 = Led;
        }
        else
        {
            P2_3 = 1;
            P1 = 0xff;
        }
    }
    else
    {
        P2_3 = 1;
        P1 = 0xff;
    }
}
​
if (Timer_500ms == 500)
{
    Timer_500ms = 0;
    Clock_Set_Flag ^= 1;
    if (Clock_Dat[0] >= 12)
        Led ^= 0xf0; // 前四个灯亮
    else
        Led ^= 0x0f; // 后四个灯亮
}

范例程序通过定义 Led 中间变量,在定时器中异或实现闪烁,结构更简洁。