目录
完整文本及仿真、程序下载链接:链接
前言
随着物质生活水平的提高,人们越来越重视生命的质量。据调查运动作为一种绿色消费在人民日常消费中所占得比例呈持续上升的趋势,全民健身运动热潮愈演愈烈,健身器材的市场需求也会不断扩大。羽毛球是一项室内、室外都可以进行的体育运动,在我国羽毛球运动是一项很普及的运动,更深受青少年的喜爱。其比赛规则经过多年的改进变得更公正合理,计分方式也变得多元化。近年来随着科技的发展,单片机的应用在不断深入,单片机的集成度高,通用性好,特别是它具有体积小,重量轻,能耗低,价格便宜,可靠性高,抗干扰能力强和使用方便等独特的优点,所以本课题采用单片机AT89C51作为本设计的核心元件。本设计由控制系统,显示模块,按键驱动模块3部分组成。主要编写了主程序,中断程序,显示程序功能模块程序等。实现羽毛球计分器功能。
第一章 设计任务及方案
1.1 设计任务
设计一个羽毛球电子计分器,能用按键控制其启动、计分、停止、清零, 并在LM1602液晶屏上显示且自动输出比赛结果。
1.2 总体设计分析
根据功能要求及设计成本,控制系统选用AT89C51芯片。采用按钮开关实现功能控制,按照设计的功能模块共选用8个按钮开关,分别实现启动、A队加分、A队减分、B队加分、B队减分、暂停、停止、清零功能。时钟电路由晶体振荡器、晶震控制芯片和电容组成,采用串行数据传输,为掉电保护电源提供可编程的充电功能,并且可以关闭充电功能。复位电路用来使电路恢复到起始状态的电路设备,确保微机系统中电路稳定可靠工作,是电路图中必不可少的一部分。选用LM1602液晶屏作为显示器件,用于记录两队的分数。
1.3 功能模块方案设计
表1-1 按键功能
按键 | 功能说明 | 显示区域 |
S3 | 比赛开始 | T00:00 A:B 00:00 |
S1 | A队加一分 | T00:30 A:B 01:00 |
S2 | B队加一分 | T01:00 A:B 01:01 |
S4 | 比赛暂停 | T02:36 A:B 03:02 |
S5 | A队减一分 | T02:38 A:B 02:02 |
S6 | B队减一分 | T02:45 A:B 02:01 |
S7 | 时间、比分清零 | T00:00 A:B 00:00 |
S8 | 比赛结束 | END |
1.4 方案确定
选定方案描述:
在像羽毛球这样的竞技比赛中,计分器占着很大的作用。如果我们就只在心里记着双方的比分,显然是不实际的,而且在现在的乒乓球比赛中采用的都是21分制。因此我们不能再用传统的计分方式了。本次课题采用单片机设计了一个羽毛球的计分器,该计分器操作简单,使用安全,方便,能满足广大羽毛球爱好者的需要。
图1-1 总设计框图
第二章、硬件设计
2.1 AT89C51 单片机芯片介绍
单片机就是一块硅片上集成了中央处理器(cpu)存储器和输入输出接口(并行I/O串行通信口)振荡电路,计数器等电路的一块集成电路,这样的一块集成电路具有一台计算机的基本功能,因而被称为单片微型计算机,简称单片机(MCU)。
图2-1 AT89C51 单片机
2.1.1 主要特性
•与MCS-51 兼容
•4K字节可编程FLASH存储器
•寿命:1000写/擦循环
•数据保留时间:10年
•全静态工作:0Hz-24MHz
•三级程序存储器锁定
•128×8位内部RAM
•32可编程I/O线
•两个16位定时器/计数器
•5个中断源
•可编程串行通道
•低功耗的闲置和掉电模式
•片内振荡器和时钟电路
2.1.2 管脚说明
VCC:供电电压。
GND:接地。
P0口:P0口为一个8位漏级开路双向I/O口,每脚可吸收8TTL门电流。当P0口的管脚第一次写1时,被定义为高阻输入。P0能够用于外部程序数据存储器,它可以被定义为数据/地址的低八位。在FIASH编程时,P0 口作为原码输入口,当FIASH进行校验时,P0输出原码,此时P0外部必须接上拉电阻。
P1口:P1口是一个内部提供上拉电阻的8位双向I/O口,P1口缓冲器能接收输出4TTL门电流。P1口管脚写入1后,被内部上拉为高,可用作输入,P1口被外部下拉为低电平时,将输出电流,这是由于内部上拉的缘故。在FLASH编程和校验时,P1口作为低八位地址接收。
P2口:P2口为一个内部上拉电阻的8位双向I/O口,P2口缓冲器可接收,输出4个TTL门电流,当P2口被写“1”时,其管脚被内部上拉电阻拉高,且作为输入。并因此作为输入时,P2口的管脚被外部拉低,将输出电流。这是由于内部上拉的缘故。P2口当用于外部程序存储器或16位地址外部数据存储器进行存取时,P2口输出地址的高八位。在给出地址“1”时,它利用内部上拉优势,当对外部八位地址数据存储器进行读写时,P2口输出其特殊功能寄存器的内容。P2口在FLASH编程和校验时接收高八位地址信号和控制信号。
P3口:P3口管脚是8个带内部上拉电阻的双向I/O口,可接收输出4个TTL门电流。当P3口写入“1”后,它们被内部上拉为高电平,并用作输入。作为输入,由于外部下拉为低电平,P3口将输出电流(ILL)这是由于上拉的缘故。
P3口也可作为AT89C51的一些特殊功能口,如下表所示:
口管脚 备选功能
P3.0 RXD(串行输入口)
P3.1 TXD(串行输出口)
P3.2 /INT0(外部中断0)
P3.3 /INT1(外部中断1)
P3.4 T0(计时器0外部输入)
P3.5 T1(计时器1外部输入)
P3.6 /WR(外部数据存储器写选通)
P3.7 /RD(外部数据存储器读选通)
P3口同时为闪烁编程和编程校验接收一些控制信号。
RST:复位输入。当振荡器复位器件时,要保持RST脚两个机器周期的高电平时间。
ALE/PROG:当访问外部存储器时,地址锁存允许的输出电平用于锁存地址的低位字节。在FLASH编程期间,此引脚用于输入编程脉冲。在平时,ALE端以不变的频率周期输出正脉冲信号,此频率为振荡器频率的1/6。因此它可用作对外部输出的脉冲或用于定时目的。然而要注意的是:每当用作外部数据存储器时,将跳过一个ALE脉冲。如想禁止ALE的输出可在SFR8EH地址上置0。此时, ALE只有在执行MOVX,MOVC指令是ALE才起作用。另外,该引脚被略微拉高。如果微处理器在外部执行状态ALE禁止,置位无效。
/PSEN:外部程序存储器的选通信号。在由外部程序存储器取指期间,每个机器周期两次/PSEN有效。但在访问外部数据存储器时,这两次有效的/PSEN信号将不出现。
/EA/VPP:当/EA保持低电平时,则在此期间外部程序存储器(0000H-FFFFH),不管是否有内部程序存储器。注意加密方式1时,/EA将内部锁定为RESET;当/EA端保持高电平时,此间内部程序存储器。在FLASH编程期间,此引脚也用于施加12V编程电源(VPP)。
XTAL1:反向振荡放大器的输入及内部时钟工作电路的输入。
XTAL2:来自反向振荡器的输出。
2.1.3 元件清单
表2-1 元件清单
序号 | 元器件代号 | 名称 | 型号及参数 |
1 | R×18 | 电阻 | 10K |
2 | C3 | 瓷片电容 | 10pF |
3 | XTAL1 | 晶振 | 11.0592M |
4 | C1-C2 | 瓷片电容 | 30PF |
5 | U1 | 芯片 | AT89C51 |
6 | LM1602 | 液晶屏 | 1 |
7 | S1-S8 | 按钮 | 8 |
2.2 电路介绍
2.2.1 电源部分
电源主要采用+5V电源,安全且节能环保,符合设计要求和环保要求。
2.2.2 按键部分
按键部分主要由A加分按钮、A误操作按钮、B加分按钮、B误操作按钮、开始计时按钮,暂停按钮,清零按钮,停止按钮八部分组成。其中S1为A加分按钮,S2为B加分按钮,S3为开始计时按钮,S4暂停按钮,S5为A误操作按钮,S6为B误操作按钮,S7为清零按钮,S8为停止按钮。
图2-2 按钮部分
2.2.3 显示部分
显示由LM1602液晶屏显示,显示功能区分为三个模块,分别为第一行显示比赛场次及谁获胜,第二行1-6位显示比赛时间,7-16行显示比分。时钟部分构成单片机的最小系统。
图2-3 LM1602液晶屏
2.2.4 时钟、复位电路
图2-4 时钟、复位电路
第三章、软件设计
3.1 程序流程图
系统的操作过程和工作过程在程序的设计过程中起着很重要的指导作用,因此在软件设计之前应首先分析羽毛球比赛记分器的工作原理。当比赛开始时,先判断是哪队得分,每次加分加1分。如果不小心加错分了,则按键减去多加的分。当比赛开始时,首先按下开始按钮S1显示开始工作,加分和误操作,暂停等按钮可工作。设计程序流程图如图3-1。
图3-1 程序流程图
3.2 模块设计
3.2.1 主函数
void main()
{
EA = 1; //开总中断
ConfigTimer0(1); //配置T0定时1ms
InitLcd1602(); //初始化液晶
while (1)
{
KeyDriver(); //调用按键功能
if(k==1)
LcdInit(); //显示字幕
}
}
3.2.2 显示函数
/* 等待液晶准备好 */
void LcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do {
LCD1602_E = 1;
sta = LCD1602_DB; //读取状态字
LCD1602_E = 0;
} while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
}
/* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_E = 1;
LCD1602_E = 0;
}
//显示字母及计时器
void LcdInit()
{
if(flag1s == 1)
{
sec++;
flag1s = 0;
}
if(sec>=60)
{ minute++;
sec = 0;
if(minute>59)
minute = 59;
}
LcdShowStr(0, 1, "T");
len = LongToString(str, minute);
LcdShowStr(1, 1, str);
LcdShowStr(3, 1, ":");
len = LongToString(str, sec);
LcdShowStr(4, 1, str);
LcdShowStr(7, 1, "A:B");
len = LongToString(str, a);
LcdShowStr(11, 1, str);
LcdShowStr(13, 1, ":");
len = LongToString(str, b);
LcdShowStr(14, 1, str);
}
/* 向LCD1602液晶写入一字节数据,dat-待写入数据值 */
void LcdWriteDat(unsigned char dat)
{
LcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_E = 1;
LCD1602_E = 0;
}
/* 设置显示RAM起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */
void LcdSetCursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if (y == 0) //由输入的屏幕坐标计算显示RAM的地址
addr = 0x00 + x; //第一行字符地址从0x00起始
else
addr = 0x40 + x; //第二行字符地址从0x40起始
LcdWriteCmd(addr | 0x80); //设置RAM地址
}
/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
LcdSetCursor(x, y); //设置起始地址
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str++);
}
}
/* 区域清除,清除从(x,y)坐标起始的len个字符位 */
void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len)
{
LcdSetCursor(x, y); //设置起始地址
while (len--) //连续写入空格
{
LcdWriteDat(' ');
}
}
/* 初始化1602液晶 */
void InitLcd1602()
{
LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口
// LcdWriteCmd(0x0C); //显示器开,光标关闭
LcdWriteCmd(0x06); //文字不动,地址自动+1
LcdWriteCmd(0x01); //清屏
}
3.2.3 延时函数
void delay()
{
unsigned int i,j;
for(i=0;i<9000;i++)
for(j=0;j<4;j++);
}
3.2.4 定时器函数
/* 配置并启动T0,ms-T0定时时间 */
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp; //临时变量
tmp = 11059200 / 12; //定时器计数频率
tmp = (tmp * ms) / 1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 12; //补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0的控制位
TMOD |= 0x01; //配置T0为模式1
TH0 = T0RH; //加载T0重载值
TL0 = T0RL;
ET0 = 1; //使能T0中断
}
3.2.5 中断函数
/* T0中断服务函数,配置定时器时间 */
void InterruptTimer0() interrupt 1
{
static unsigned count = 0;
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
count++;
if(count>=1000)
{
count = 0;
flag1s = 1;
}
}
3.2.6 字符转换函数
//将数字转换为字符
unsigned char LongToString(unsigned char *str, signed long dat)
{
signed char i = 0;
signed char len = 0;
unsigned char buf[3];
for(i=0;i<2;i++)
{
buf[i] = dat%10;
dat = dat/10;
}
len = i;
for(i=1;i>=0;i--)
{
*str++ = buf[i] + '0';
}
*str = '\0';
return len;
}
3.2.7 全局变量定义及函数声明
#include <reg52.h>
#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;
unsigned char T0RH = 0;
unsigned char T0RL = 0;
sbit KEY_IN_1 = P2^0;
sbit KEY_IN_2 = P2^1;
sbit KEY_IN_3 = P2^2;
sbit KEY_IN_4 = P2^3;
sbit KEY_IN_5 = P2^4;
sbit KEY_IN_6 = P2^5;
sbit KEY_IN_7 = P2^6;
sbit KEY_IN_8 = P2^7;
bit flag1s = 0; //定时1s标志
char a = 0; //A的分数
char b = 0; //B的分数
bit k=1; //开显示开关
unsigned char minute = 0; //定时器分钟
unsigned char sec = 0; //定时器秒
unsigned char len = 0; //字符串转换长度
unsigned char str[3]; //字符转换指针
void InitLcd1602();
void ConfigTimer0(unsigned int ms);
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
unsigned char LongToString(unsigned char *str, signed long dat);
void KeyDriver();
void LcdInit();
第四章、仿真与调试
4.1 调试的环境
在Keil4.0中运行编写的程序,将生成的 .hex文件导入protues7.0中以连接好的电路的单片机中。
4.2 按键及功能的调试
图4-1 电路原理图
按下按钮S3表示比赛开始,定时器开始计时,其他按钮允许工作。A队赢球时,按一下S1按钮,A队加一分。B队赢球时,按一下S1按钮,B队加一分。若出现误加分情况,按钮S5\S6分别对A\B队减一分。如比赛过程中出现紧急情况需暂停比赛时,计分器S4按钮可实现暂停功能,其定时器暂停工作,加减分按钮失效,保证比赛的公正性。比赛结束时按钮S8实现停止功能,数据保存。按钮S7实现清零功能,比赛结束,可对下场比赛进行计时。
图4-2 比赛结果仿真图
当A队获得第一场胜利时,屏幕上会有“1Awin”字样,B队获得第二场胜利,显示“1Awin2Bwin”字样。A队再次获胜后,计分器会自动进入停止状态,加分减分按钮失效,液晶屏显示此次比赛所用时间以及“1Awin2Bwin3Awin”,表示此次比赛A队为获胜方。
总 结
通过这两周的课程设计,我发现了自己的很多不足,自己知识的很多漏洞,看到了自己的实践经验还是比较缺乏,在这个过程中,我也曾经因为实践经验的缺乏失落过,也曾经仿真成功而热情高涨。生活就是这样,汗水预示着结果也见证着收获。劳动是人类生存生活永恒不变的话题。虽然这只是一次的极简单的课程制作,可是平心而论,也耗费了我们不少的心血。通过这次课程设计,我想说:为完成这次课程设计我确实很辛苦,但苦中仍有乐,我对单片机的产品开发有了系统的认识,从硬件的设计到软件的设计再到软硬件的联合工作。我懂得了以前没有接触到的知识,通过大量的查阅资料,让我对单片机有了清晰的认识。
软件设计方面不仅得考虑软件设计的合理性,更得从实用性。在软件设计时,查阅了大量软件工程方面的书籍,使得软件的时间复杂度有所降低,而且将软件的功能模块化,利用了模块化的编程思想,根据电路设计出了高效的软件。在软硬件联合工作方面实现了完美的链接,设计出了符合要求且高效率的程序。
以前种种艰辛这时就变成了最甜美的回忆!对我而言,知识上的收获重要,精神上的丰收更加可喜。让我知道了学无止境的道理。我们每一个人永远不能满足于现有的成就,人生就像在爬山,一座山峰的后面还有更高的山峰在等着你。挫折是一份财富,经历是一份拥有。这次课程设计必将成为我人生旅途上一个非常美好的回忆!
参考文献
[1] 林立,张俊亮.单片机原理及应用——基于Proteus和KeilC[M].北京电子工业出版社,2014
[2] 陈忠平.基于Proteus的51系列单片机设计与仿真[M].北京电子工业出版社,2015
[3] 陈海宴.51单片机原理及应用[M].北京航空航天大学出版社,2010.
[4] 刘守义等.单片机技术基础[M].西安电子科技大学出版社,2007.
[5] 钟富昭等.8051单片机典型模块设计与应用[M].人民邮电出版社,2007.
[6] 隋清江等.基于Proteus的LCDl602接口设计与仿真[J].微计算机信息,2010.7-1:17l-172.
[7] 郭志卓,陈振军.基于单片机控制的电子计分器设计[J].电脑知识与技术,2014,10(12):2861-2863.
[8] 雷道仲,罗政球,廖永忠.基于单片机的球赛计时计分器设计[J].湖南第一师范学报,2009,9(04):164-166.
[9] 王文军.基于C51单片机的多功能计时计分器设计[J].科学与财富,2016,(9):674-674,675. DOI:10.3969/j.issn.1671-2226.2016.09.631.
[10] 牛军,王萍.无线电子打分计分系统的设计与实现[J].南阳理工学院学报,2015,7(04):1-4.
[11] 王文军.基于C51单片机的多功能计时计分器设计[J].科学与财富,2016,(9):674-674,675. DOI:10.3969/j.issn.1671-2226.2016.09.631.
[12] 耿天寿.单片机控制的计时计分显示系统[C].//第三届全国电子技术应用大会.北京:山西大学,1993:193~196页.
[13] 伍学珍,岑斌.无线数传的计分计时系统[C].//中国通信集成电路技术与应用研讨会论文集.广西水利电力职业技术学院,2005:79-84.
[14] 赵学军,熊发明.球类比赛计时记分操作台的设计[C].//中国自动化学会中南六省(区)第20届学术年会.北京:2003:148-150.
[15] 陈雷,刘茂元.基于单片机计数计时仪的设计与制作[C].//2016年全国高等学校物理基础课程教育学术研讨会论文集.西藏大学,2016:73-77.
附 录
#include <reg52.h>
#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;
unsigned char T0RH = 0;
unsigned char T0RL = 0;
sbit KEY_IN_1 = P2^0;
sbit KEY_IN_2 = P2^1;
sbit KEY_IN_3 = P2^2;
sbit KEY_IN_4 = P2^3;
sbit KEY_IN_5 = P2^4;
sbit KEY_IN_6 = P2^5;
sbit KEY_IN_7 = P2^6;
sbit KEY_IN_8 = P2^7;
bit flag1s = 0; //定时1s标志
char a = 0; //A的分数
char b = 0; //B的分数
bit k=1; //开显示开关
unsigned char minute = 0; //定时器分钟
unsigned char sec = 0; //定时器秒
unsigned char len = 0; //字符串转换长度
unsigned char str[3]; //字符转换指针
void InitLcd1602();
void ConfigTimer0(unsigned int ms);
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
unsigned char LongToString(unsigned char *str, signed long dat);
void KeyDriver();
void LcdInit();
void main()
{
EA = 1; //开总中断
ConfigTimer0(1); //配置T0定时1ms
InitLcd1602(); //初始化液晶
while (1)
{
KeyDriver(); //调用按键功能
if(k==1)
LcdInit(); //显示字幕
}
}
void delay()
{
unsigned int i,j;
for(i=0;i<9000;i++)
for(j=0;j<4;j++);
}
/* 等待液晶准备好 */
void LcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do {
LCD1602_E = 1;
sta = LCD1602_DB; //读取状态字
LCD1602_E = 0;
} while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
}
/* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_E = 1;
LCD1602_E = 0;
}
/* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 */
void KeyDriver()
{
static bit star = 0; //开始比赛
unsigned char but = 0; //胜利标志
static unsigned char cnt = 0; //比赛次数
static unsigned char cnta = 0; //A胜利次数
static unsigned char cntb = 0; //B胜利次数
if(KEY_IN_3 == 0) { delay(); star = 1; TR0 = 1;k=1;}
if(star == 1)
{
LcdWriteCmd(0x0C); //显示器开,光标关闭
if(KEY_IN_1 ==0) { a++; delay();}
if(KEY_IN_2 ==0) { b++; delay();}
if(KEY_IN_4 ==0) { TR0 = 0; star=0; delay(); }
if(KEY_IN_5 ==0) { a--; if(a<0)a=0; delay();}
if(KEY_IN_6 ==0) { b--; if(b<0)b=0; delay(); }
}
if(KEY_IN_7 ==0) { a=0; b=0; sec=0; minute=0;
TR0=0;star=0;cnta=0; cntb=0; cnt=0; LcdShowStr(0, 0, " "); delay();}
if(KEY_IN_8 ==0){ a=0; b=0; sec=0; minute=0; LcdShowStr(0, 0, " ");
k=0;cnta=0;cntb=0;cnt=0;LcdWriteCmd(0x01); delay();}
if(a>=21 && (a-b)>=2 ) {cnt++; cnta++; but = 1; a = 0; b = 0; }
if(b>=21 && (b-a)>=2 ) {cnt++; cntb++; but = 2; a = 0; b = 0; }
if(cnt==2&&(cnta==2||cntb==2)) {TR0 = 0; star = 0;}
switch(cnt)
{
case 1: if(but==1) LcdShowStr(0, 0, "1Awin"); if(but==2) LcdShowStr(0, 0, "1Bwin");break;
case 2: if(but==1) LcdShowStr(5, 0, "2Awin"); if(but==2) LcdShowStr(5, 0, "2Bwin");break;
case 3: if(but==1) LcdShowStr(10, 0, "3Awin"); if(but==2) LcdShowStr(10, 0, "3Bwin"); TR0=0;star=0;break;
default: break;
}
}
//显示字母及计时器
void LcdInit()
{
if(flag1s == 1)
{
sec++;
flag1s = 0;
}
if(sec>=60)
{ minute++;
sec = 0;
if(minute>59)
minute = 59;
}
LcdShowStr(0, 1, "T");
len = LongToString(str, minute);
LcdShowStr(1, 1, str);
LcdShowStr(3, 1, ":");
len = LongToString(str, sec);
LcdShowStr(4, 1, str);
LcdShowStr(7, 1, "A:B");
len = LongToString(str, a);
LcdShowStr(11, 1, str);
LcdShowStr(13, 1, ":");
len = LongToString(str, b);
LcdShowStr(14, 1, str);
}
//将数字转换为字符
unsigned char LongToString(unsigned char *str, signed long dat)
{
signed char i = 0;
signed char len = 0;
unsigned char buf[3];
for(i=0;i<2;i++)
{
buf[i] = dat%10;
dat = dat/10;
}
len = i;
for(i=1;i>=0;i--)
{
*str++ = buf[i] + '0';
}
*str = '\0';
return len;
}
/* 向LCD1602液晶写入一字节数据,dat-待写入数据值 */
void LcdWriteDat(unsigned char dat)
{
LcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_E = 1;
LCD1602_E = 0;
}
/* 设置显示RAM起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */
void LcdSetCursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if (y == 0) //由输入的屏幕坐标计算显示RAM的地址
addr = 0x00 + x; //第一行字符地址从0x00起始
else
addr = 0x40 + x; //第二行字符地址从0x40起始
LcdWriteCmd(addr | 0x80); //设置RAM地址
}
/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
LcdSetCursor(x, y); //设置起始地址
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str++);
}
}
/* 区域清除,清除从(x,y)坐标起始的len个字符位 */
void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len)
{
LcdSetCursor(x, y); //设置起始地址
while (len--) //连续写入空格
{
LcdWriteDat(' ');
}
}
/* 初始化1602液晶 */
void InitLcd1602()
{
LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口
// LcdWriteCmd(0x0C); //显示器开,光标关闭
LcdWriteCmd(0x06); //文字不动,地址自动+1
LcdWriteCmd(0x01); //清屏
}
/* 配置并启动T0,ms-T0定时时间 */
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp; //临时变量
tmp = 11059200 / 12; //定时器计数频率
tmp = (tmp * ms) / 1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 12; //补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0的控制位
TMOD |= 0x01; //配置T0为模式1
TH0 = T0RH; //加载T0重载值
TL0 = T0RL;
ET0 = 1; //使能T0中断
}
/* T0中断服务函数,配置定时器时间 */
void InterruptTimer0() interrupt 1
{
static unsigned count = 0;
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
count++;
if(count>=1000)
{
count = 0;
flag1s = 1;
}
}