第三周
大模板
创建工程模板
-
新建
User、Driver文件夹 -
在
User中创建工程,选择 AT89C52 -
在“魔法棒”中:Output 勾选 Hex;C51 添加
Driver路径 -
在“品”字图标中修改工程名、用户组名,并添加底层组
-
在
User中创建main.c
添加底层文件
- 在
Driver中新建Led.c、Led.h、Key.c、Key.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 = 1、EA = 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中间变量,在定时器中异或实现闪烁,结构更简洁。