资源描述
课程设计(论文)任务书
信息工程 学 院 计算机科学与技术 专 业 2014-1 班
一、课程设计(论文)题目 简易家电定时控制装置
二、课程设计(论文)工作自 2017 年 6 月 19 日起至 2017 年 6 月 29 日止。
三、课程设计(论文) 地点: 5#402
四、课程设计(论文)内容要求:
1.本课程设计的目的
(1)使学生掌握系统各功能模块的基本工作原理;
(2)培养学生基本掌握电路设计的基本思路和方法;
(3)使学生掌握接收系统调试;
(4)培养学生分析、解决问题的能力;
(5)提高学生的科技论文写作能力。
2.课程设计的任务及要求
1)基本要求:
(1)对家电的开启进行预定时控制,能显示并设置预定时间;
(2)对家电的工作时间进行定时控制,能显示并通过键盘设置工作时间的长短;
(3)工作时间到了后,并能声音报警;
(4)设定时间时,黄色发光二极管点亮,启动定时后,红色发光二极管点亮。
2)创新要求:
在基本要求达到后,可进行创新设计,如改善电路性能;对系统进行仿真分析。
3)课程设计论文编写要求
(1)要按照书稿的规格打印誊写毕业论文
(2)论文包括目录、绪论、正文、小结、参考文献、谢辞、附录等
(3)毕业论文装订按学校的统一要求完成
4)答辩与评分标准:
(1)完成原理分析:20分;
(2)完成设计过程(含翻译):40分;
(3)完成调试:20分;
(4)回答问题:20分。
5)参考文献:
(1)邓胡滨 .《单片机原理及应用技术——基于Keil C和Proteus仿真》 人民邮电出版社
6)课程设计进度安排
内容 天数 地点
构思及收集资料 2 图书馆
组装与调试 5 实验室
撰写论文 3 图书馆、实验室
学生签名:
2017 年 6 月 19 日
课程设计(论文)评审意见
(1)完成原理分析(20分):优( )、良( )、中( )、一般( )、差( );
(2)设计分析和翻译能力 (40分):优( )、良( )、中( )、一般( )、差( );
(3)完成调试 (20分):优( )、良( )、中( )、一般( )、差( );
(4)回答问题 (20分):优( )、良( )、中( )、一般( )、差( );
(5)格式规范性及考勤是否降等级:是( )、否( )
评阅人: 职称: 副教授
2017 年 6 月 29 日
目录
绪论 1
一、 设计任务及要求 2
1.1 设计目的 2
1.2 基本功能要求 2
1.3拓展功能设计 2
1.4 功能详细设计 3
二、硬件电路设计及描述 3
2.1 AT89C52单片机 3
2.2 DS1302数码管显示时钟 4
2.3 矩阵按键电路 5
2.4 LCD液晶显示屏电路 6
2.5 LED数码管动态显示 7
2.6 74HC138译码器 9
2.7 报警电路 10
三、程序设计思想及流程 11
四、电路的安装及调试 12
4.1 安装步骤 12
4.2 电路的调试 12
五、 总结 12
六、参考文献 13
七、附录 13
7.1 实物图 13
7.2仿真图 13
7.3 部分程序清单 14
绪论
单片微计算机自20世纪70年代问世以来,已对人类社会产生了巨大的影响。尤其是美国Intel公司生产的MCS-51系列单片机,由于其具有集成度高、处理功能强、可靠性高、系统结构简单、价格低廉、易于使用等优点,在世界范围内已经得到广泛的普及和应用。此外,世界各大公司以MCS-51单片机基本内核为核心的各种扩展型、增强型的新型的新型单片机不断推出,所以在今后若干年内,MCS-51系列以及世界其它各大公司生产的与其兼容的各种增强型、扩展型的单片机,仍是我国单片机应用领域的主流型。目前在工业控制、智能仪器仪表、办公自动化、家用电器登诸多领域,到处都可看见单片机的踪影,单片机技术开发和应用水平已成为一个国家工业发展水平的标志之一。
此装置硬件使用AT89C52单片机为控制核心,DS1302作为时钟芯片并同时在掉电时储存资料,LCD1602液晶显示屏以及LED数码管作为显示与用户交互,使用5V继电器控制外接电器电源的开启或关闭。软件采用模块化设计、驱动、控制、引脚、变量定义等采用多文件编译,即方便自己设计修改也方便他人理解。
这款家电定时器实现了时钟显示,预定时间设置、定时操作便捷设置、报警功能、系统状态提示功能。
关键词:单片机;定时控制;AT89S52;
第 0 页
一、 设计任务及要求
1.1 设计目的
根据本学年所学的单片机及微机接口的理论知识设计装置的电路结构和相应软件程序设计。提高学生实践能力,培养学生发现问题、解决问题的能力,锻炼学生理论联系实际、综合应用的能力。
1.2 基本功能要求
(1)对家电的开启进行预定时控制,能显示并设置预定时间;
(2)对家电的工作时间进行定时控制,能显示并通过键盘设置工作时间的长短;
(3)工作时间到了后,并能声音报警;
(4)设定时间时,黄色发光二极管点亮,启动定时后,红色发光二极管点亮。
1.3拓展功能设计
(1)使用LCD液晶显示屏显示定时时间,方便使用者的实际操作使用。
(2)采用矩阵键盘进行时间设置。以使得装置的时间设置操作更加的人性化。
(3)使用8个LED灯提示装置所处于的状态。当LED滚动显示时表示家电处于闲置状态,当流水灯全部显示时表示家电处于工作状态。
1.4 功能详细设计
装置的当前时间显示是用6个数码管实现的,显示格式为“时时:分分:秒秒”。当前日期以及定时时间是在LCD液晶显示屏上显示。
家电工作前程序控制LED灯流动点亮,家电工作后程序控制LED灯全部点亮。
矩阵键盘的控制功能为:
(1) K1、K2、K3、K5、K6、K7、K9、K10、K11、K13 分别对应数字1—9以及数字0。用于设置日期时间以及定时
时间的数值输入。
(2) K4、K8、K12、K16对应光标移动上、左、下、右方
向。
(3) K14、K15分别对应ESC键和回车键。
二、硬件电路设计及描述
2.1 AT89C52单片机
AT89C52是一个低电压,高性能CMOS 8位单片机,片内含8k bytes的可反复擦写的Flash只读程序存储器和256 bytes的随机存取数据存储器(RAM),器件采用ATMEL公司的高密度、非易失性存储技术生产,兼容标准MCS-51指令系统,片内置通用8位中央处理器和Flash存储单元,AT89C52单片机在电子行业中有着广泛的应用。
AT89C52有40个引脚,32个外部双向输入/输出(I/O)端口,同时内含2个外中断口,3个16位可编程定时计数器,2个全双工串行通信口,2 个读写口线,AT89C52可以按照常规方法进行编程,也可以在线编程。其将通用的微处理器和Flash存储器结合在一起,特别是可反复擦写的 Flash存储器可有效地降低开发成本。
图2-1
2.2 DS1302数码管显示时钟
1. DS1302简介
DS1302 是美国DALLAS公司推出的一款涓流充电时钟芯片。DS1302时钟芯片被广泛应用于电话、传真、便携式仪器等产品领域,它的主要性能指标如下。
(1) DS1302是一个实时时钟芯片,可以提供秒、分、小时、日期、月、年等信息,并且还有软件自动调整的能力,可以配置AM/PM来决定采用24小时格式还是12小时格式。
(2) 拥有31字节数据存储RAM。
(3) 串行I/O通信方式,相对并行来说比较节省IO口的使用。
(4) DS1302的工作电压比较宽,在2.0—5.5V的范围内部都可以正常工作。
(5) DS1302这种时钟芯片功耗一般都很低,它在工作电压2.0V的时候工作电流小于300nA。
(6) DS1302共有8个引脚,有两种封装形式。
(7) 当工作电压是5V的时候,兼容标准的TTL电平标准。
(8) DS1302有两个电源输入,一个是主电源,另一个是备用电源。可以使用电池或者大型电容。以使在系统掉电的情况下,时钟还会继续走。
2. DS1302结构
DS1302的引脚排列,其中Vcc2为主电源,VCC1为后备电源。在主电源关闭的情况下,也能保持时钟的连续运行。DS1302由Vcc1或Vcc2两者中的较大者供电。当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc1时,DS1302由Vcc1供电。X1和X2是振荡源,外接32.768kHz晶振。RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据传送的方法。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc>2.0V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。I/O为串行数据输入输出端(双向),后面有详细说明。SCLK为时钟输入端。 下图为DS1302的引脚功能图:
图2-2 DS1302引脚图
2.3 矩阵按键电路
矩阵按键电路如图2-3所示。
图2-3按键电路
矩阵键盘又称为行列式键盘,它是用4条I/O线作为行线,4条I/O线作为列线组成的键盘。在行线和列线的每一个交叉点上,设置一个按键。这样键盘中按键的个数是4×4个。这种行列式键盘结构能够有效地提高单片机系统中I/O口的利用率。工作时先从P1口的高四位输出低电平,低四位输出高电平,从P1口的低四位读取键盘状态。再从P1口的低四位输出低电平,高四位输出高电平,从P1口的高四位读取键盘状态。将两次读取结果组合起来就可以得到当前按键的特征编码。使用上述方法我们得到16个键的特征编码。
2.4 LCD液晶显示屏电路
(1)、LCD1602简介
1602液晶显示屏可以显示2行,每行16个字符的液晶。它的工作电压是4.5—5.5V,对于这点在设计电路的时候,直接按照5V系统设计,但是保证5V系统最低不能低于4.5V。在5V工作电压下测量它的工作电流是2mA。这个2mA仅仅是指液晶,而它的黄绿背光都是LED做的,所以功耗不会太小。
(2) 、LCD1602管脚功能
1602采用标准的16脚接口,其中:
第1脚:VSS为电源地
第2脚:VCC接5V电源正极
第3脚:V0为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高(对比度过高时会 产生“鬼影”,使用时可以通过一个10K的电位器调整对比度)。
第4脚:RS为寄存器选择,高电平1时选择数据寄存器、低电平0时选择指令寄存器。
第5脚:RW为读写信号线,高电平(1)时进行读操作,低电平(0)时进行写操作。
第6脚:E(或EN)端为使能(enable)端。
第7~14脚:D0~D7为8位双向数据端。
第15~16脚:空脚或背灯电源。15脚背光正极,16脚背光负极。
图2-5中断触发电路
2.5 LED数码管动态显示
(1)LED结构原理
LED数码管(LED Segment Displays)是由多个发光二极管封装在一起组成“8”字型的器件,引线已在接完成,只需引出它们的各个笔划,公共电极。随着信息时代的突飞猛进,越来越多的照明及工程应用需要要求更高的显示方案,同样,作为单片机的驱动显示也离不开LED数码管驱动显示方案。LED数码管显示主要分为静态显示和动态显示。
对于人机交互式单片机系统来说,不仅需要响应用户输入,同时也需要将一些测控信息输出显示。这些显示信息可以提供实时的数据或图形结果,以便于掌握系统 的状态并进行分析处理。目前,在单片机中最常用的是LED数码管显示。其成本低廉、使用简便,可以显示数字或几个特定的字符。
(2)动态显示原理
动态显示是指每隔一段时间循环点亮每个LED数码管,每次只有一个LED被点亮。根据人眼的视觉暂留效应,当循环点亮的速度很快的时候,可以认为各个LED是稳定显示的。动态显示的硬件连接比较简单,这里使用了8个LED数码管,将所有LED的8段引脚并联在一起,连接到8位的I/O数据总线上。而各个LED的共阳极引 脚或共阴极引脚分别由另一组I/O线控制,从图中可以看出,使用两个8位的I/O端口便可以动态显示8位LED数码管。其中一个并口作为LED数码管的控制引脚,另一个并口作为公共的数据总线。程序中采用扫描显示的方式,即在同一时刻,只使用一个LED显示数据。通过为共阴极LED(或共阳极LED)的公共引脚赋低电平(或高电平),从而选择 某个LED显示。如此循环,使每个LED显示该LED应显示的数据,并进行适当的延时,形成视觉暂留效果。这样便可以达到动态显示的目的。
图2-6 LED电路图
2.6 74HC138译码器
74HC138译码器作用是把3种输入状态翻译为8种输出状态。74HC138一共是6个输入引脚,但是其中4,5,6这三个引脚是使能引脚。这三个引脚如果不符合规定的输入要求Y0到Y7不管输入的1,2,3引脚是什么电平状态,总是高电平。
图2-5 74HC138译码器
其真值表为:
2.7 报警电路
报警电路如图2-6所示。
利用了三极管作为开关管驱动蜂鸣器发声。
图2-6报警电路
三、程序设计思想及流程
开始
开中断
配置T0定时
初始化LED,时钟模块,LCD
检测按键是否按下
是
执行相应按键函数
否
是否到定时时间
是
响铃、LED全亮
否
图3-1流程图
四、电路的安装及调试
4.1 安装步骤
1.检查元件的好坏
按电路图买好元件后首先检查买回元件的好坏,按各元件的检测方法分别进行检测,一定要仔细认真。而且要认真核对原理图是否一致,在检查好后才可上件、焊件,防止出现错误焊件后不便改正。
2.放置、焊接各元件
按原理图的位置放置各元件,在放置过程中要先放置、焊接较低的元件,后焊较高的和要求较高的元件。特别是容易损坏的元件要后焊,在焊集成芯片时连续焊接时间不要超过10s,注意芯片的安装方向。
4.2 电路的调试
首先烧入显示程序,看显示正不正常。在调试程序时,发现有的指令用的不正确,导致电路功能不能完全实现,另外软件程序中的延时有的过长、有的过短等等。
五、 总结
这一学年所学的单片机及微机接口知识在这次的课程设计中得到了充分的体验,在完成这个毕业设计的过程中,也遇到了不少问题,但在老师和同学的帮助下,问题很快就得到了解决。在此过程中,我也学到了不少的新知识,自己的动手能力里得到了大大的提高,分析问题和总结问题也积累了不少的经验,在此我衷心的感谢老师。感谢老师对我完成这个课题的支持与帮助,在设计过程中给了我宝贵的建议和意见,同时也感谢我的同组同学,为我提供了很多的资料和帮助。
六、参考文献
(1)邓胡滨 .《单片机原理及应用技术——基于Keil C和Proteus仿真》 人民邮电出版社
七、附录
7.1 实物图
7.2仿真图
7.3 部分程序清单
#ifndef _CONFIG_H
#define _CONFIG_H
/* 通用头文件 */
#include <reg52.h>
#include <intrins.h>
/* 数据类型定义 */
typedef signed char int8; // 8位有符号整型数
typedef signed int int16; //16位有符号整型数
typedef signed long int32; //32位有符号整型数
typedef unsigned char uint8; // 8位无符号整型数
typedef unsigned int uint16; //16位无符号整型数
typedef unsigned long uint32; //32位无符号整型数
/* 全局运行参数定义 */
#define SYS_MCLK (11059200/12) //系统主时钟频率,即振荡器频率÷12
/* IO引脚分配定义 */
sbit KEY_IN_1 = P2^4; //矩阵按键的扫描输入引脚1
sbit KEY_IN_2 = P2^5; //矩阵按键的扫描输入引脚2
sbit KEY_IN_3 = P2^6; //矩阵按键的扫描输入引脚3
sbit KEY_IN_4 = P2^7; //矩阵按键的扫描输入引脚4
sbit KEY_OUT_1 = P2^3; //矩阵按键的扫描输出引脚1
sbit KEY_OUT_2 = P2^2; //矩阵按键的扫描输出引脚2
sbit KEY_OUT_3 = P2^1; //矩阵按键的扫描输出引脚3
sbit KEY_OUT_4 = P2^0; //矩阵按键的扫描输出引脚4
sbit ADDR0 = P1^0; //LED位选译码地址引脚0
sbit ADDR1 = P1^1; //LED位选译码地址引脚1
sbit ADDR2 = P1^2; //LED位选译码地址引脚2
sbit ADDR3 = P1^3; //LED位选译码地址引脚3
sbit ENLED = P1^4; //LED显示部件的总使能引脚
#define LCD1602_DB P0 //1602液晶数据端口
sbit LCD1602_RS = P1^0; //1602液晶指令/数据选择引脚
sbit LCD1602_RW = P1^1; //1602液晶读写引脚
sbit LCD1602_E = P1^5; //1602液晶使能引脚
sbit DS1302_CE = P1^7; //DS1302片选引脚
sbit DS1302_CK = P3^5; //DS1302通信时钟引脚
sbit DS1302_IO = P3^4; //DS1302通信数据引脚
sbit I2C_SCL = P3^7; //I2C总线时钟引脚
sbit I2C_SDA = P3^6; //I2C总线数据引脚
sbit BUZZER = P1^6; //蜂鸣器控制引脚
#endif
#ifndef _DS1302_H
#define _DS1302_H
struct sTime { //日期时间结构
uint16 year; //年
uint8 mon; //月
uint8 day; //日
uint8 hour; //时
uint8 min; //分
uint8 sec; //秒
uint8 week; //星期
};
#ifndef _DS1302_C
#endif
void InitDS1302();
void GetRealTime(struct sTime *time);
void SetRealTime(struct sTime *time);
#endif
#ifndef _KEY_BOARD_H
#define _KEY_BOARD_H
#ifndef _KEY_BOARD_C
#endif
void KeyScan();
void KeyDriver();
#endif
#ifndef _LCD1602_H
#define _LCD1602_H
#ifndef _LCD1602_C
#endif
void InitLcd1602();
void LcdClearScreen();
void LcdOpenCursor();
void LcdCloseCursor();
void LcdSetCursor(uint8 x, uint8 y);
void LcdShowStr(uint8 x, uint8 y, uint8 *str);
void LcdShowChar(uint8 x, uint8 y, uint8 chr);
#endif
#define _MAIN_C
#include "config.h"
#include "Lcd1602.h"
#include "LedBuzzer.h"
#include "keyboard.h"
#include "DS1302.h"
#include "Time.h"
#include "main.h"
bit flag2s = 0; //2s定时标志位
bit flag200ms = 0; //200ms定时标志
uint8 T0RH = 0; //T0重载值的高字节
uint8 T0RL = 0; //T0重载值的低字节
static int *alarm = 0; //闹钟标志位
enum eStaSystem staSystem = E_NORMAL; //系统运行状态
void main()
{
EA = 1; //开总中断
ConfigTimer0(1); //配置T0定时1ms
InitLed(); //初始化LED模块
InitDS1302(); //初始化实时时钟模块
InitLcd1602(); //初始化液晶模
while (!flag2s); //上电后延时2秒
flag2s = 0;
RefreshTime(); //刷新当前时间
RefreshAlarm(); //闹钟设定值显示
while (1) //进入主循环
{
KeyDriver(); //执行按键驱动
if (flag200ms) //每隔200ms执行以下分支
{
flag200ms = 0;
FlowingLight(alarm); //流水灯效果实现
RefreshTime(); //刷新当前时间
AlarmMonitor(alarm); //监控闹钟
if (staSystem == E_NORMAL) //正常运行时刷新日期显示
{
RefreshDate(0);
}
}
}
}
/* 配置并启动T0,ms-T0定时时间 */
void ConfigTimer0(uint16 ms)//定时1ms
{
uint32 tmp;
tmp = (SYS_MCLK*ms)/1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 33; //补偿中断响应延时造成的误差
T0RH = (uint8)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (uint8)tmp;
TMOD &= 0xF0; //清零T0的控制位11110000
TMOD |= 0x01; //配置T0为模式1 xxxxxxx1
TH0 = T0RH; //加载T0重载值
TL0 = T0RL;
ET0 = 1; //使能T0中断
TR0 = 1; //启动T0
}
/* T0中断服务函数,实现系统定时和按键扫描 */
void InterruptTimer0() interrupt 1 //每次1ms
{
static uint8 tmr2s = 0;
static uint8 tmr200ms = 0;
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
tmr200ms++; //定时200ms
if (tmr200ms >= 200) //200ms
{
tmr200ms = 0;
flag200ms = 1;
tmr2s++; //定时2s
if (tmr2s >= 10) //200ms*10=2S
{
tmr2s = 0;
flag2s = 1;
}
}
KeyScan(); //执行按键扫描
}
#define _DS1302_C
#include "config.h"
#include "DS1302.h"
/* 发送一个字节到DS1302通信总线上 */
void DS1302ByteWrite(uint8 dat)
{
uint8 mask;
for (mask=0x01; mask!=0; mask<<=1) //低位在前,逐位移出
{
if ((mask&dat) != 0) //首先输出该位数据
DS1302_IO = 1;
else
DS1302_IO = 0;
DS1302_CK = 1; //然后拉高时钟
DS1302_CK = 0; //再拉低时钟,完成一个位的操作
}
DS1302_IO = 1; //最后确保释放IO引脚
}
/* 由DS1302通信总线上读取一个字节 */
uint8 DS1302ByteRead()
{
uint8 mask;
uint8 dat = 0;
for (mask=0x01; mask!=0; mask<<=1) //低位在前,逐位读取
{
if (DS1302_IO != 0) //首先读取此时的IO引脚,并设置dat中的对应位
{
dat |= mask;
}
DS1302_CK = 1; //然后拉高时钟
DS1302_CK = 0; //再拉低时钟,完成一个位的操作
}
return dat; //最后返回读到的字节数据
}
/* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */
void DS1302SingleWrite(uint8 reg, uint8 dat)
{
DS1302_CE = 1; //使能片选信号
DS1302ByteWrite((reg<<1)|0x80); //发送写寄存器指令
DS1302ByteWrite(dat); //写入字节数据
DS1302_CE = 0; //除能片选信号
}
/* 用单次读操作从某一寄存器读取一个字节,reg-寄存器地址,返回值-读到的字节 */
uint8 DS1302SingleRead(uint8 reg)
{
uint8 dat;
DS1302_CE = 1; //使能片选信号
DS1302ByteWrite((reg<<1)|0x81); //发送读寄存器指令
dat = DS1302ByteRead(); //读取字节数据
DS1302_CE = 0; //除能片选信号
return dat;
}
/* DS1302初始化,如发生掉电则重新设置初始时间 */
void InitDS1302()
{
uint8 dat;
struct sTime code InitTime[] = { //默认初始值:2014-01-01 12:30:00 星期3
0x2014,0x01,0x01, 0x12,0x30,0x00, 0x03
};
DS1302_CE = 0; //初始化DS1302通信引脚
DS1302_CK = 0;
dat = DS1302SingleRead(0); //读取秒寄存器
if ((dat & 0x80) != 0) //由秒寄存器最高位CH的值判断DS1302是否已停止
{
DS1302SingleWrite(7, 0x00); //撤销写保护以允许写入数据
SetRealTime(&InitTime); //设置DS1302为默认的初始时间
}
}
#define _KEY_BOARD_C
#include "config.h"
#include "keyboard.h"
#include "Time.h"
const uint8 code KeyCodeMap[4][4] = { //矩阵按键到标准键码的映射表
{ '1', '2', '3', 0x26 }, //数字键1、数字键2、数字键3、向上键
{ '4', '5', '6', 0x25 }, //数字键4、数字键5、数字键6、向左键
{ '7', '8', '9', 0x28 }, //数字键7、数字键8、数字键9、向下键
{ '0', 0x1B, 0x0D, 0x27 } //数字键0、ESC键、 回车键、 向右键
};
uint8 pdata KeySta[4][4] = { //全部矩阵按键的当前状态
{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
};
/* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 */
void KeyDriver()
{
uint8 i, j;
static uint8 pdata backup[4][4] = { //按键值备份,保存前一次的值
{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
};
for (i=0; i<4; i++) //循环检测4*4的矩阵按键
{
for (j=0; j<4; j++)
{
if (backup[i][j] != KeySta[i][j]) //检测按键动作
{
if (backup[i][j] != 0) //按键按下时执行动作
{
KeyAction(KeyCodeMap[i][j]); //调用按键动作函数
}
backup[i][j] = KeySta[i][j]; //刷新前一次的备份值
}
}
}
}
/* 按键扫描函数,需在定时中断中调用,推荐调用间隔1ms */
void KeyScan()
{
uint8 i;
static uint8 keyout = 0; //矩阵按键扫描输出索引
static uint8 keybuf[4][4] = { //矩阵按键扫描缓冲区
{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}
};
//将一行的4个按键值移入缓冲区
keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
//消抖后更新按键状态
for (i=0; i<4; i++) //每行4个按键,所以循环4次
{
if ((keybuf[keyout][i] & 0x0F) == 0x00)
{ //连续4次扫描
展开阅读全文