一、设计目的
1、学习并掌握定时器/计数器的工作原理;
2、学习并掌握定时器/计数器控制程序设计方法;
3、学习并掌握矩阵键盘工作原理与程序设计方法;
4、学习并掌握显示程序设计;
二、实验设备
1、计算机;
2、单片机开发板;
3、Keil C51编程软件;
4、Proteus仿真软件;
三、设计内容
1、功能
必须使用定时器完成以下功能。
功能1:4位数码管显示“分钟.秒”
功能2:K1设置键、K2加1键、K2减1键
(1)正常情况:显示“分钟.秒”
(2)按下K1:第一次按下调整分钟状态、第二次按下调整秒状态、第三次按下正常显示状态
(3)按下K2:分钟或秒加1
(4)按下K3:分钟或秒减1
选做功能:闹钟功能(请先完成功能1和功能2并提交后,选做)
(1)当到达设定好的时间,蜂鸣器奏乐、LED闪烁报警;
(2)按下K4:停止报警,正常显示;
(3)闹钟时间可调;
(4)按下K1:第一次按下调整分钟状态、第二次按下调整秒状态、第三次按下调整闹钟分钟状态、第四次按下调整闹钟秒状态、第五次按下正常显示状态
(5)按下K2:按一下分钟或秒加1
(6)按下K3:按一下分钟或秒减1
2、硬件电路原理图
3、设计程序(5号、宋体、单倍行距、分两栏,要求程序设计规范化,添加必要的说明和注释)
3.1必做题程序
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
/*****************************************
* 实 验 名 :4*4矩阵按键实验
* 实验说明 :采用循环方式实现:数码管显示电子表:分钟、秒,用3个按键实现调整
* 按键1:设置、确定
* 按键2:加1
* 按键3:减1
*****************************************/
#include <reg52.h>
#include <intrins.h>
#define FOSC 11059200L //晶振设置,默认使用11.0592M Hz
//#define FOSC 12000000L //晶振设置,使用12M Hz
//#define FOSC 24000000L //晶振设置,使用24M Hz
#define TIME0_MS 50 //设定定时时间50ms
//IO接口定义
#define LED_PORT P0 //数码管的段码显示控制接口
#define KEY_PORT P1 //键盘控制接口
sbit wela_1 = P2^4; //数码管个位显示控制口
sbit wela_2 = P2^5; //数码管十位显示控制口
sbit wela_3 = P2^6; //数码管百位显示控制口
sbit wela_4 = P2^7; //数码管千位显示控制口
//全局变量定义
unsigned char count; //定时计数次数
unsigned char key_value;//键值
unsigned char qian,bai,shi,ge; //数码管显示的4位数字
unsigned char state=0; //状态变量,反映当前状态:0-正常显示,1-调分钟,2-调秒
unsigned char miao,fen; //分钟、秒
//LED显示字模 0-F 共阳模式
unsigned code table[]= {0Xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
/*****************************************
* 函 数 名 :Delayms
* 函数功能 :实现 ms级的延时
* 输 入 :ms
* 输 出 :无
*****************************************/
void Delayms(unsigned int ms)
{
unsigned int i,j;
for(i=0;i<ms;i++)
#if FOSC == 11059200L
for(j=0;j<114;j++);
#elif FOSC ==12000000L
for(j=0;j<123;j++);
#elif FOSC ==24000000L
for(j=0;j<249;j++);
#else
for(j=0;j<114;j++);
#endif
}
/*****************************************
* 函 数 名 :Timer0Init
* 函数功能 :定时器0初始化
* 输 入 :无
* 输 出 :无
*****************************************/
void Timer0Init()
{
TMOD=0x01; //设置定时器0工作方式为1
TH0=(65536-FOSC/12/1000*TIME0_MS)/256; //设置定时器初值高8位
TL0=(65536-FOSC/12/1000*TIME0_MS)%256; //设置定时器初值低8位
ET0=1; //开启定时器0中断
TR0=1; //开启定时器0
EA=1; //开总中断
}
/*****************************************
* 函 数 名 :LEDdisplay
* 函数功能 :4位数码管显示函数,循环显示各个位上的数据
* 输 入 :num要显示的数据
* 输 出 :无
*************************************/
void LEDdisplay()
{
wela_1 = 1; //关闭所有数码管
wela_2 = 1;
wela_3 = 1;
wela_4 = 1;
wela_4=0; //显示千位,千位数码管的位控制为0
LED_PORT=table[qian]; //取table数组中的段码送段码显示控制接口
Delayms(1); //延时几毫秒
LED_PORT = 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_4=1; //熄灭千位
wela_3=0; //显示百位,百数码管的位控制为0
LED_PORT=table[bai]& 0x7F ; //取table数组中的段码送段码显示控制接口 0x7f实现显示小数点
Delayms(1); //延时几毫秒
LED_PORT = 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_3=1; //熄灭百位
wela_2=0; //显示十位,十位数码管的位控制为0
LED_PORT=table[shi]; //取table数组中的段码送段码显示控制接口
Delayms(1); //延时几毫秒
LED_PORT = 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_2=1; //熄灭十位
wela_1=0; //显示个位,个位数码管的位控制为0
LED_PORT=table[ge]; //取table数组中的段码送段码显示控制接口
Delayms(1); //延时几毫秒
LED_PORT = 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_1=1; //熄灭个位
}
/*****************************************
* 函 数 名 :KeyScan
* 函数功能 :4*4键盘扫描
* 输 入 :无
* 输 出 :num 获取的键值,若没有键值则返回 0xff
*************************************/
unsigned char KeyScan()
{
unsigned chartemp,num;
num = 0xff;
//************************************
KEY_PORT=0xfe; //P3口:1111 1110 行0为低电平(0)
temp=KEY_PORT; //读取P3口,送temp
temp=temp&0xf0; //temp高四位不变(保存列值),低四位清零
while(temp!=0xf0) //temp高四位不等于1111,说明列线中有0,说明有键按下
{
Delayms(5); //延时消抖
temp=KEY_PORT; //再次读取P3口,送temp
temp=temp&0xf0; //temp高四位不变,低四位清零
while(temp!=0xf0) //temp高四位不等于1111,说明列线中有0,说明确实有键按下
{
temp=KEY_PORT; //读取P3口,送temp
switch(temp)
{
case0xee:num=1; //高四位:1110(列0=0),低四位:1110(行0=0),说明行0与列0交叉位置按键按下
break;
case0xde:num=2; //高四位:1101(列1=0),低四位:1110(行0=0),说明行0与列1交叉位置按键按下
break;
case0xbe:num=3; //高四位:1011(列2=0),低四位:1110(行0=0),说明行0与列2交叉位置按键按下
break;
case0x7e:num=4; //高四位:0111(列3=0),低四位:1110(行0=0),说明行0与列3交叉位置按键按下
break;
}
while(temp!=0xf0) //temp高四位不等于1111,说明列线中有0,说明有键按下没松开(释放)
{
temp=KEY_PORT; //读取P3口,送temp
temp=temp&0xf0; //temp高四位不变(保存列值),低四位清零
}
}
}
//************************************
KEY_PORT=0xfd; //P3口:1111 1101 行1为低电平(0)
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case0xed:num=5; //高四位:1110(列0=0),低四位:1101(行1=0),说明行1与列0交叉位置按键按下
break;
case0xdd:num=6; //高四位:1101(列1=0),低四位:1101(行1=0),说明行1与列1交叉位置按键按下
break;
case0xbd:num=7; //高四位:1011(列2=0),低四位:1101(行1=0),说明行1与列2交叉位置按键按下
break;
case0x7d:num=8; //高四位:0111(列3=0),低四位:1101(行1=0),说明行1与列3交叉位置按键按下
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
//***********************************
KEY_PORT=0xfb; //P3口:1111 1011 行2为低电平(0)
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case0xeb:num=9; //高四位:1110(列0=0),低四位:1011(行2=0),说明行2与列0交叉位置按键按下
break;
case0xdb:num=10; //高四位:1101(列1=0),低四位:1011(行2=0),说明行2与列1交叉位置按键按下
break;
case0xbb:num=11; //高四位:1011(列2=0),低四位:1011(行2=0),说明行2与列2交叉位置按键按下
break;
case0x7b:num=12; //高四位:0111(列3=0),低四位:1011(行2=0),说明行2与列3交叉位置按键按下
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
//************************************
KEY_PORT=0xf7; //P3口:1111 0111 行3为低电平(0)
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case0xe7:num=13; //高四位:1110(列0=0),低四位:0111(行3=0),说明行3与列0交叉位置按键按下
break;
case 0xd7:num=14; //高四位:1101(列1=0),低四位:0111(行3=0),说明行3与列1交叉位置按键按下
break;
case0xb7:num=15; //高四位:1011(列2=0),低四位:0111(行3=0),说明行3与列2交叉位置按键按下
break;
case0x77:num=16; //高四位:0111(列3=0),低四位:0111(行3=0),说明行3与列3交叉位置按键按下
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
//************************************
return num;
}
/*****************************************
* 函 数 名 :Key1
*************************************/
void Key3() //key3减1键
{
if(state==1) //状态为1,调分钟状态
if(fen==0) //分钟值为0,则减1为59
fen=59;
else //分钟值不为0,则减1
fen--; //因此,分钟的变化规律是0,59,...,1,0,59,...
if(state==2) //状态为2,调秒状态
if(miao==0) //秒值为0,则减1为59
miao=59;
else //秒值不为0,则减1
miao--; //因此,秒的变化规律是0,59,...,1,0,59,...
}
/*****************************************
* 函 数 名 :KeyHandle
* 函数功能 :键处理程序
* 输 入 :无
* 输 出 :无
*************************************/
void KeyHandle()
{
switch(key_value) //判断键值,哪个按键按下?
{
case1:Key1();break; //key1按下,调用key1()函数
case2:Key2();break; //key2按下,调用key2()函数
case3:Key3();break; //key3按下,调用key3()函数
default:break;
}
}
/*****************************************
* 函 数 名 :main
* 函数功能 :主函数
* 输 入 :无
* 输 出 :无
*************************************/
void main()
{
Timer0Init();
count = 0;
fen = 59;
miao = 58;
while(1)
{
if(count == 20) //达到1s=50ms*20次
{
count=0; //计数值回零
miao++; //秒加1
if(miao == 60) //秒为60
{
miao = 0; //秒回零
fen ++; //分加一
if(fen ==60) // 分为60
fen =0; //分回零
}
}
key_value = KeyScan();
KeyHandle();
while(state!=0)
{
ET0=0; //关闭定时器0中断
key_value=KeyScan();//调用键扫描函数获得键值
KeyHandle(); //调用键处理函数运行各个键处理函数
qian=fen/10; //取分钟的十位
bai=fen%10; //取分钟的个位
shi=miao/10; //取秒的十位
ge=miao%10; //取秒的个位
LEDdisplay(); //显示当前时间
}
//处理LED显示
qian=fen%100/10;
bai=fen%10;
shi=miao%100/10;
ge=miao%10;
LEDdisplay();
}
}
/*****************************************
* 函 数 名 :Timer0Int
* 函数功能 :定时器0中断函数 , 每隔TIME0_MS ms进入
* 输 入 :无
* 输 出 :无
*************************************/
void Timer0Int() interrupt 1
{
TH0=(65536-FOSC/12/1000*TIME0_MS)/256;//重新装入初值高8位
TL0=(65536-FOSC/12/1000*TIME0_MS)%256;//重新装入初值低8位
count++; //计数值加1
}
3.2选做题程序
/****************************************************************************************
* 实 验 名:4*4矩阵按键实验
* 实验说明:采用循环方式实现:数码管显示电子表:分钟、秒,用3个按键实现调整
* 按键1:设置、确定
* 按键2:加1
* 按键3:减1
****************************************************************************************/
#include<reg52.h>
#include<intrins.h>
#defineFOSC 11059200L //晶振设置,默认使用11.0592M Hz
//#defineFOSC 12000000L //晶振设置,使用12M Hz
//#defineFOSC 24000000L //晶振设置,使用24M Hz
#defineTIME0_MS 50 //设定定时时间50ms
#defineT10MS (65536-FOSC/12/100) //10ms定时:T10MS为定时器初值(16位)
//IO接口定义
#defineLED_PORT P0 //数码管的段码显示控制接口
#defineKEY_PORT P1 //键盘控制接口
sbitwela_1 = P2^4; //数码管个位显示控制口
sbitwela_2 = P2^5; //数码管十位显示控制口
sbitwela_3 = P2^6; //数码管百位显示控制口
sbitwela_4 = P2^7; //数码管千位显示控制口
sbitspeak_IO =P3^6 ; //蜂鸣器控制引脚
sbitLED1 = P3^5; //LED1控制引脚
//全局变量定义
unsignedchar count; //定时器计数次数
unsignedchar count_2; //计数器变量
unsignedchar key_value;//键值
unsignedchar qian,bai,shi,ge; //数码管显示的4位数字
unsignedchar alarm_qian,alarm_bai,alarm_shi,alarm_ge; //数码管显示的4位数字
unsignedchar state=0; //状态变量,反映当前状态:0-正常显示,1-调分钟,2-调秒
unsignedchar miao,fen; //分钟、秒
unsignedchar alarm_miao,alarm_fen; //分钟、秒
//LED显示字模 0-F 共阳模式
unsignedcode table[]={0Xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
unsignedchar code SONG[] ={
//祝你平安(217字节)
0x26,0x20,0x20,0x20,0x20,0x20,0x26,0x10,0x20,0x10,0x20,0x80,
0x26,0x20,0x30,0x20,0x30,0x20,0x39,0x10,0x30,0x10,0x30,0x80,
0x26,0x20,0x20,0x20,0x20,0x20,0x1c,0x20,0x20,0x80,0x2b,0x20,
0x26,0x20,0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x80,0x26,0x20,
0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,0x26,0x60,0x40,0x10,
0x39,0x10,0x26,0x20,0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,
0x26,0x80,0x26,0x20,0x2b,0x10,0x2b,0x10,0x2b,0x20,0x30,0x10,
0x39,0x10,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x20,
0x20,0x10,0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,0x18,0x20,
0x18,0x20,0x26,0x20,0x20,0x20,0x20,0x40,0x26,0x20,0x2b,0x20,
0x30,0x20,0x30,0x20,0x1c,0x20,0x20,0x20,0x20,0x80,0x1c,0x20,
0x1c,0x20,0x1c,0x20,0x30,0x20,0x30,0x60,0x39,0x10,0x30,0x10,
0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x10,0x26,0x10,0x26,0x10,
0x2b,0x10,0x2b,0x80,0x18,0x20,0x18,0x20,0x26,0x20,0x20,0x20,
0x20,0x60,0x26,0x10,0x2b,0x20,0x30,0x20,0x30,0x20,0x1c,0x20,
0x20,0x20,0x20,0x80,0x26,0x20,0x30,0x10,0x30,0x10,0x30,0x20,
0x39,0x20,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x10,
0x40,0x10,0x20,0x10,0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,
0x00,
//路边的野花不要采(164字节)
0x20,0x30,0x1C,0x10,0x18,0x20,0x15,0x20,0x1C,0x20,0x20,0x20,
0x26,0x40,0x20,0x20,0x2B,0x20,0x26,0x20,0x20,0x20,0x30,0x80,
0xFF,0x20,0x20,0x1C,0x10,0x18,0x10,0x20,0x20,0x26,0x20,0x2B,
0x20,0x30,0x20,0x2B,0x40,0x20,0x20,0x1C,0x10,0x18,0x10,0x20,
0x20,0x26,0x20,0x2B,0x20,0x30,0x20,0x2B,0x40,0x20,0x30,0x1C,
0x10,0x18,0x20,0x15,0x20,0x1C,0x20,0x20,0x20,0x26,0x40,0x20,
0x20,0x2B,0x20,0x26,0x20,0x20,0x20,0x30,0x80,0x20,0x30,0x1C,
0x10,0x20,0x10,0x1C,0x10,0x20,0x20,0x26,0x20,0x2B,0x20,0x30,
0x20,0x2B,0x40,0x20,0x15,0x1F,0x05,0x20,0x10,0x1C,0x10,0x20,
0x20,0x26,0x20,0x2B,0x20,0x30,0x20,0x2B,0x40,0x20,0x30,0x1C,
0x10,0x18,0x20,0x15,0x20,0x1C,0x20,0x20,0x20,0x26,0x40,0x20,
0x20,0x2B,0x20,0x26,0x20,0x20,0x20,0x30,0x30,0x20,0x30,0x1C,
0x10,0x18,0x40,0x1C,0x20,0x20,0x20,0x26,0x40,0x13,0x60,0x18,
0x20,0x15,0x40,0x13,0x40,0x18,0x80,
0x00,
};
/*******************************************************************************
* 函 数 名:Delayms
* 函数功能:实现 ms级的延时
* 输 入 :ms
* 输 出 :无
*******************************************************************************/
voidDelayms(unsigned int ms)
{
unsignedint i,j;
for(i=0;i<ms;i++)
#ifFOSC == 11059200L
for(j=0;j<114;j++);
#elifFOSC == 12000000L
for(j=0;j<123;j++);
#elifFOSC == 24000000L
for(j=0;j<249;j++);
#else
for(j=0;j<114;j++);
#endif
}
/*******************************************************************************
* 函 数 名:Delayus
* 函数功能:us级延时
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidDelayus(unsigned int us)
{
unsignedint i,j;
for(i =0;i < us;i++ )
{
for(j =0;j<3;j++ );
}
}
/*******************************************************************************
* 函 数 名:Timer0Init
* 函数功能:定时器0初始化
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidTimer0Init()
{
TMOD=0x01;//设置定时器0工作方式为1
TH0=(65536-FOSC/12/1000*TIME0_MS)/256; //设置定时器初值高8位
TL0=(65536-FOSC/12/1000*TIME0_MS)%256; //设置定时器初值低8位
ET0=1;//开启定时器0中断
TR0=1;//开启定时器0
EA=1; //开总中断
}
/*******************************************************************************
* 函 数 名:Time1_Init
* 函数功能:定时器1初始化
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidTime1Init()
{
TMOD|= 0x10; //工作模式选择:定时器T1工作在方式1:16位定时器模式
ET1 = 1; //定时器T1中断允许
EA = 1; //总中断允许
TH1 = T10MS>>8; //装初值高8位,延时10ms
TL1 = T10MS; //装初值低8位,延时10ms
}
/*******************************************************************************
* 函 数 名:LEDdisplay
* 函数功能:4位数码管显示函数,循环显示各个位上的数据
* 输 入 :num要显示的数据
* 输 出 :无
*******************************************************************************/
voidLEDdisplay()
{
wela_1= 1; //关闭所有数码管
wela_2= 1;
wela_3= 1;
wela_4= 1;
wela_4=0; //显示千位,千位数码管的位控制为0
LED_PORT=table[qian]; //取table数组中的段码送段码显示控制接口
Delayms(1); //延时几毫秒
LED_PORT= 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_4=1; //熄灭千位
wela_3=0; //显示百位,百数码管的位控制为0
LED_PORT=table[bai]& 0x7F ; //取table数组中的段码送段码显示控制接口 0x7f实现显示小数点
Delayms(1); //延时几毫秒
LED_PORT= 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_3=1; //熄灭百位
wela_2=0; //显示十位,十位数码管的位控制为0
LED_PORT=table[shi]; //取table数组中的段码送段码显示控制接口
Delayms(1); //延时几毫秒
LED_PORT= 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_2=1; //熄灭十位
wela_1=0; //显示个位,个位数码管的位控制为0
LED_PORT=table[ge]; //取table数组中的段码送段码显示控制接口
Delayms(1); //延时几毫秒
LED_PORT= 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_1=1; //熄灭个位
}
/*******************************************************************************
* 函 数 名:LEDdisplay_alarm
* 函数功能:4位数码管显示函数,循环显示各个位上的数据
* 输 入 :num要显示的数据
* 输 出 :无
*******************************************************************************/
voidLEDdisplay_alarm()
{
wela_1= 1; //关闭所有数码管
wela_2= 1;
wela_3= 1;
wela_4= 1;
wela_4=0; //显示千位,千位数码管的位控制为0
LED_PORT=table[alarm_qian]; //取table数组中的段码送段码显示控制接口
Delayms(1); //延时几毫秒
LED_PORT= 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_4=1; //熄灭千位
wela_3=0; //显示百位,百数码管的位控制为0
LED_PORT=table[alarm_bai]& 0x7F ; //取table数组中的段码送段码显示控制接口 0x7f实现显示小数点
Delayms(1); //延时几毫秒
LED_PORT= 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_3=1; //熄灭百位
wela_2=0; //显示十位,十位数码管的位控制为0
LED_PORT=table[alarm_shi]; //取table数组中的段码送段码显示控制接口
Delayms(1); //延时几毫秒
LED_PORT= 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_2=1; //熄灭十位
wela_1=0; //显示个位,个位数码管的位控制为0
LED_PORT=table[alarm_ge]; //取table数组中的段码送段码显示控制接口
Delayms(1); //延时几毫秒
LED_PORT= 0xff; //熄灭数码管,因共阳极所以0xff送段码显示控制接口
wela_1=1; //熄灭个位
}
/*******************************************************************************
* 函 数 名:KeyScan
* 函数功能:4*4键盘扫描
* 输 入 :无
* 输 出 :num 获取的键值,若没有键值则返回 0xff
*******************************************************************************/
unsignedchar KeyScan()
{
unsignedchar temp,num;
num= 0xff;
//**************************************************************************
KEY_PORT=0xfe; //P3口:1111 1110 行0为低电平(0)
temp=KEY_PORT; //读取P3口,送temp
temp=temp&0xf0; //temp高四位不变(保存列值),低四位清零
while(temp!=0xf0) //temp高四位不等于1111,说明列线中有0,说明有键按下
{
Delayms(5); //延时消抖
temp=KEY_PORT; //再次读取P3口,送temp
temp=temp&0xf0; //temp高四位不变,低四位清零
while(temp!=0xf0) //temp高四位不等于1111,说明列线中有0,说明确实有键按下
{
temp=KEY_PORT; //读取P3口,送temp
switch(temp)
{
case0xee:num=1; //高四位:1110(列0=0),低四位:1110(行0=0),说明行0与列0交叉位置按键按下
break;
case0xde:num=2; //高四位:1101(列1=0),低四位:1110(行0=0),说明行0与列1交叉位置按键按下
break;
case0xbe:num=3; //高四位:1011(列2=0),低四位:1110(行0=0),说明行0与列2交叉位置按键按下
break;
case0x7e:num=4; //高四位:0111(列3=0),低四位:1110(行0=0),说明行0与列3交叉位置按键按下
break;
}
while(temp!=0xf0) //temp高四位不等于1111,说明列线中有0,说明有键按下没松开(释放)
{
temp=KEY_PORT; //读取P3口,送temp
temp=temp&0xf0; //temp高四位不变(保存列值),低四位清零
}
}
}
//**************************************************************************
KEY_PORT=0xfd; //P3口:1111 1101 行1为低电平(0)
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case0xed:num=5; //高四位:1110(列0=0),低四位:1101(行1=0),说明行1与列0交叉位置按键按下
break;
case0xdd:num=6; //高四位:1101(列1=0),低四位:1101(行1=0),说明行1与列1交叉位置按键按下
break;
case0xbd:num=7; //高四位:1011(列2=0),低四位:1101(行1=0),说明行1与列2交叉位置按键按下
break;
case0x7d:num=8; //高四位:0111(列3=0),低四位:1101(行1=0),说明行1与列3交叉位置按键按下
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
//**************************************************************************
KEY_PORT=0xfb; //P3口:1111 1011 行2为低电平(0)
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case0xeb:num=9; //高四位:1110(列0=0),低四位:1011(行2=0),说明行2与列0交叉位置按键按下
break;
case0xdb:num=10; //高四位:1101(列1=0),低四位:1011(行2=0),说明行2与列1交叉位置按键按下
break;
case0xbb:num=11; //高四位:1011(列2=0),低四位:1011(行2=0),说明行2与列2交叉位置按键按下
break;
case0x7b:num=12; //高四位:0111(列3=0),低四位:1011(行2=0),说明行2与列3交叉位置按键按下
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
//**************************************************************************
KEY_PORT=0xf7; //P3口:1111 0111 行3为低电平(0)
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case0xe7:num=13; //高四位:1110(列0=0),低四位:0111(行3=0),说明行3与列0交叉位置按键按下
break;
case0xd7:num=14; //高四位:1101(列1=0),低四位:0111(行3=0),说明行3与列1交叉位置按键按下
break;
case0xb7:num=15; //高四位:1011(列2=0),低四位:0111(行3=0),说明行3与列2交叉位置按键按下
break;
case0x77:num=16; //高四位:0111(列3=0),低四位:0111(行3=0),说明行3与列3交叉位置按键按下
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
//**************************************************************************
returnnum;
}
/*******************************************************************************
* 函 数 名:Key1
* 函数功能:1号键处理程序
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidKey1() //key1改变状态
{
state++; //状态值加1
if(state==5)//若状态加到3,
{
state=0; //则回0
ET0= 1;
}
//因此,按下key1状态state变化规律为:0,1,2,0,1,2,...
//状态state=0:正常显示
//状态state=1:调分钟
//状态state=2:调秒
}
/*******************************************************************************
* 函 数 名:Key2
* 函数功能:2号键处理程序
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidKey2() //key2加1键
{
if(state==1) //状态为1,调分钟状态
if(fen==59) //分钟值为59,则加1为0
fen=0;
else //分钟值不为59,则加1
fen++; //因此,分钟的变化规律是0,1,...,59,0,1,...
if(state==2) //状态为2,调秒状态
if(miao==59)//秒值为59,则加1为0
miao=0;
else //秒值不为59,则加1
miao++; //因此,秒的变化规律是0,1,...,59,0,1,...
if(state==3) //状态为1,调闹钟分钟状态
if(alarm_fen==59) //分钟值为59,则加1为0
alarm_fen=0;
else //分钟值不为59,则加1
alarm_fen++; //因此,闹钟分钟的变化规律是0,1,...,59,0,1,...
if(state==4) //状态为2,调闹钟秒状态
if(alarm_miao==59)//秒值为59,则加1为0
alarm_miao=0;
else //秒值不为59,则加1
alarm_miao++; //因此,秒的变化规律是0,1,...,59,0,1,...
}
/*******************************************************************************
* 函 数 名:Key3
* 函数功能:3号键处理程序
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidKey3() //key3减1键
{
if(state==1) //状态为1,调分钟状态
if(fen==0) //分钟值为0,则减1为59
fen=59;
else //分钟值不为0,则减1
fen--; //因此,分钟的变化规律是0,59,...,1,0,59,...
if(state==2) //状态为2,调秒状态
if(miao==0) //秒值为0,则减1为59
miao=59;
else //秒值不为0,则减1
miao--; //因此,秒的变化规律是0,59,...,1,0,59,...
if(state==3) //状态为1,调分钟状态
if(alarm_fen==0) //分钟值为0,则减1为59
alarm_fen=59;
else //分钟值不为0,则减1
alarm_fen--; //因此,分钟的变化规律是0,59,...,1,0,59,...
if(state==4) //状态为2,调秒状态
if(alarm_miao==0) //秒值为0,则减1为59
alarm_miao=59;
else //秒值不为0,则减1
alarm_miao--; //因此,秒的变化规律是0,59,...,1,0,59,...
}
/*******************************************************************************
* 函 数 名:KeyHandle
* 函数功能:键处理程序
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidKeyHandle()
{
switch(key_value) //判断键值,哪个按键按下?
{
case1:Key1();break; //key1按下,调用key1()函数
case2:Key2();break; //key2按下,调用key2()函数
case3:Key3();break; //key3按下,调用key3()函数
default:break;
}
}
/*******************************************************************************
* 函 数 名:PlaySong
* 函数功能:歌曲播放子程序,i为播放哪一段曲目
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidPlaySong(unsigned char i)
{
unsignedchar Temp1,Temp2;
unsignedint Addr;
count_2= 0; //中断计数器清0
Addr= i * 217;
ET0= 0; //关闭定时器0中断
while(key_value!= 4)
{
Temp1= SONG[Addr++]; //读取音符的频率值
if (Temp1 == 0xFF ) //休止符0xFF
{
TR1= 0; //停止定时器T1
Delayus(100); //延时100us
}
elseif ( Temp1 == 0x00 ) //歌曲结束符0x00
{
return;
}
else
{
Temp2= SONG[Addr++];//读取一个音的时长
TR1= 1; //启动定时器T1
while(key_value!= 4)
{
key_value= KeyScan();
if (key_value == 4)
ET0= 1; //打开定时器0中断
speak_IO= ~speak_IO;
LED1= ~LED1;
Delayus(Temp1);
if (Temp2 == count_2 )//一个音结束
{
count_2= 0;
break;
}
}
}
}
}
/*******************************************************************************
* 函 数 名:main
* 函数功能:主函数
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidmain()
{
Timer0Init();
Time1Init(); //定时器1中断初始化
count= 0;
fen= 59;
miao= 58;
alarm_fen= 0;
alarm_miao= 2;
while(1)
{
if(count== 20) //达到1s=50ms*20次
{
count=0; //计数值回零
miao++; //秒加1
if(miao== 60) //秒为60
{
miao= 0; //秒回零
fen++; //分加一
if(fen== 60) // 分为60
fen= 0; //分回零
}
}
key_value=KeyScan();//调用键扫描函数获得键值
KeyHandle(); //调用键处理函数运行各个键处理函数
//闹钟
if ((alarm_miao == miao) && (alarm_fen == fen) )
{
PlaySong(0); //播放第一首歌
miao++;
}
while(state!=0)
{
ET0= 0; //关闭定时器0中断
key_value=KeyScan();//调用键扫描函数获得键值
KeyHandle(); //调用键处理函数运行各个键处理函数
qian=fen/10; //取分钟的十位
bai=fen%10; //取分钟的个位
shi=miao/10; //取秒的十位
ge=miao%10; //取秒的个位
alarm_qian=alarm_fen/10; //取闹钟的分钟的十位
alarm_bai=alarm_fen%10; //取闹钟的分钟的个位
alarm_shi=alarm_miao/10; //取闹钟的秒的十位
alarm_ge=alarm_miao%10; //取闹钟的秒的个位
if((state == 1)||(state == 2) )
LEDdisplay(); //显示当前时间
elseif( (state == 3)||(state == 4) )
LEDdisplay_alarm();//显示当前闹钟时间
else
;
}
//处理LED显示
qian=fen%100/10;
bai=fen%10;
shi=miao%100/10;
ge=miao%10;
LEDdisplay();
}
}
/*******************************************************************************
* 函 数 名:Timer0Int
* 函数功能:定时器0中断函数 , 每隔TIME0_MS ms进入
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidTimer0Int() interrupt 1
{
TH0=(65536-FOSC/12/1000*TIME0_MS)/256;//重新装入初值高8位
TL0=(65536-FOSC/12/1000*TIME0_MS)%256;//重新装入初值低8位
count++; //计数值加1
}
/*******************************************************************************
* 函 数 名:Time1Int
* 函数功能:定时器1中断子函数,count为10ms计数器变量
* 输 入 :无
* 输 出 :无
*******************************************************************************/
voidTime1Int() interrupt 3 //定时器1中断子函数
{
TH1 = T10MS>>8; //16位数右移8位得到高8位,TH1装初值高8位,定时10ms
TL1 = T10MS; //TL1装初值低8位,定时10ms
count_2++; //计数器变量加1
}
四、本任务中的几个功能如何实现?(说明设计思路)
设计思路:
例程“矩阵按键调整时间实验”符合选做功能框架、不同点为计时采用的是依靠程序运行时间,不准确,例程“动态数码管与定时器中断实验”采用定时器计时,则考虑用前者为基础,将后者的定时器计时代码整合。即可完成必做功能。选做功能以必做功能为依托,增加闹钟功能,闹钟的播放乐曲依托蜂鸣器实验例程完成,闹钟实现采用当设定时间的分、秒与单片机当前计时一致时(if语句)、开始报警。
编程过程:
必做功能,
1. 在例程基础上增加分钟计时,当秒为60时,秒回零,分钟加一,分钟为60时,回零。
2. LED显示函数中,百位显示小数点:将table中的码段&0x7f,显示小数点;实现功能。
3. 在按下key1后,应当关闭中断,停止当前计时,待state为3时(恢复为计时功能)重新开中断。
选做功能:
1. 增加Playsong函数,对Playsong中加入按键扫描,将函数中while(1)改为while(key_value!= 4),用作实现按下key4停止功能。
2. 修改三个按键处理函数,对key1,增加state的显示状态,用于设置调整闹钟时间的模式,对key2和key3,增加响应state下的调整功能。
全部资料51hei下载地址:
选做-定时器应用设计.rar
(6.55 MB, 下载次数: 16)
|