资源描述
(word完整版)基于51单片机状态机矩阵键盘扫描数码管动态显示的时钟系统
/******************************************************************************
程序功能:基于状态机的线性反转扫描方法实现按键扫描 数码管动态显示
开发环境:KeiL4
硬件环境:STC12C5A60S2,11.0592
接线说明:单片机P1口接底板JP29,具体接法为:P10—L1,P11—L2,P12—L3,P13-L4,P14-L5,P15-L6,P16-L7,P17-L8
单片机P20~P21接底板JP26,具体接法:P20—DS,P21—SHCP,P22-STCP
跳线说明:J70
实验现象:数码管动态显示时钟时间 同时显示当前被按下的键值 可通过矩阵键盘对时钟进行开关已经参数调试
******************************************************************************/
#include <REG52。h>
typedef unsigned char uint8;
typedef unsigned int uint16;
#define KEY_PORT P1 //定义4x4键盘使用的单片机端口
sbit SEG_DS = P2^0; //74HC595芯片的数据引脚
sbit SEG_SHCP = P2^1; //74HC595芯片的控制引脚,上升沿移入数据
sbit SEG_STCP = P2^2; //74HC595芯片的控制引脚,上升沿更新数据
//*****************************************************************************
//全局变量
//*****************************************************************************
char hour,min,sec; // 秒 分 时
uint8 Clock_flag; //时钟开关标志位
uint8 ct_flag; //按键长按标志位 //*****************************************************************************
//反转法矩阵键盘的各个按键的计算值
unsigned char tabLe[]={
0xee,//0
0xed,//1
0xeb,//2
0xe7,//3
0xde,//4
0xdd,//5
0xdb,//6
0xd7,//7
0xbe,//8
0xbd,//9
0xbb,//10
0xb7,//11
0x7e,//12
0x7d,//13
0x7b,//14
0x77 //15
};
//共阳数码管的编码,并将数据定义在CODE区
unsigned char code Seg_Data[]={
0xc0,/*0*/
0xF9,/*1*/
0xA4,/*2*/
0xB0,/*3*/
0x99,/*4*/
0x92,/*5*/
0x82,/*6*/
0xF8,/*7*/
0x80,/*8*/
0x90,/*9*/
0x88,/*A*/
0x83,/*b*/
0xC6,/*C*/
0xA1,/*d*/
0x86,/*E*/
0x8E,/*F*/
};
//数码管位选编码,控制显示8位中的第几位
unsigned char code Seg_Addr[]={
0x01,
0x02,
0x04,
0x08,
0x10,
0x20,
0x40,
0x80,
0xFF,//ALL ON
0x00 //OFF
};
//*****************************************************************************
//函数声明
//*****************************************************************************
void SEG_Send595OneByte(unsigned char ucData); //向74HC595写入一个8位的数据
//指定数码管显示定数字
void DispLayOneCharOnAddr(unsigned char Data,unsigned char Addr);
unsigned char Key_Scan(void); //基于状态机的按键扫描
void Parameter_Setting(uint8 key_value_get); //按键参数设置
void Timer0Configuration(); //定时器初始化
void CLOCK(void); //时钟运行函数
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//定时器0中断服务函数
//*****************************************************************************
void Timer0() interrupt 1
{
uint8 key_get,j;
static uint8 i=0,key_value;
static uint16 count=0;
TH0 = (65536-922)/256;
TL0 = (65536-922)%256;
/***********************用户代码************************************/
count++;
if(962==count) //一秒运行一次时钟
{
if(Clock_flag)
CLOCK(); //时钟走一次
count=0; //定时计数清零
}
switch(i)
{
case 0:i++;DispLayOneCharOnAddr(hour/10,0) ; //数码管动态显示 第一位
break;
case 1:i++;DispLayOneCharOnAddr(hour%10,1) ; //数码管动态显示 第二位
break;
case 2:i++;DispLayOneCharOnAddr(min/10,2) ; //数码管动态显示 第三位
break;
case 3:i++;DispLayOneCharOnAddr(min%10,3) ; //数码管动态显示 第四位
break;
case 4:i++;DispLayOneCharOnAddr(sec/10,4) ; //数码管动态显示 第五位
break;
case 5:i++;DispLayOneCharOnAddr(sec%10,5) ; //数码管动态显示 第六位
break;
case 6:i++;DispLayOneCharOnAddr(key_value,6); //数码管动态显示 第七位
break;
case 7:i++; //数码管动态显示 第八位
break;
case 8: i=0;
key_get=Key_Scan(); //状态机按键扫描 每9ms进行一次扫描
if(key_get) //按键没按下 返回零
{ for(j=0;j<16;j++)
{
if(key_get==tabLe[j]) //通过查表得出按键的号
{key_value=j;break;}
}
Parameter_Setting(key_value); //按键参数调试函数输入按键号
}
break;
default:break;
}
}
//*****************************************************************************
//主函数
//*****************************************************************************
void main(void)
{
EA=0; //关总中断
Timer0Configuration(); //定时器0初始化 1ms中断
EA=1; //开总中断
while(1) //循环等待中断到来
{
}
}
//***************************************************************************** //*****************************************************************************
//向HC595发送一个字节
void SEG_Send595OneByte(unsigned char ucData)
{
unsigned char i;
for(i = 0;i < 8;i++) //8位数据依次写入,先写最低位
{
SEG_DS = (ucData & 0x80); //先读入高位 x&0x80;
SEG_SHCP = 0;
SEG_SHCP = 1;
SEG_SHCP = 0; //SHCP引脚的上升沿移入数据
ucData <〈=1; //数据左移
}
}
/*******************************************************
函数功能:在指定位置显示一个数据
参数说明:Data是要显示的数据,Addr是在第几位显示.
Addr取值范围是0~9。
Addr=0~7时,选择的是显示在第几位数码管上;
Addr=8 时,同时选中8位数码管,即打开所有数码管
Addr=9 时,关闭8位数码管
8位数码管,左数依次为第0位,第1位。..第7位。
*******************************************************/
void DispLayOneCharOnAddr(unsigned char Data,unsigned char Addr)
{
SEG_Send595OneByte(Seg_Addr[Addr]); //显示在哪一个数码管上
SEG_Send595OneByte( Seg_Data[Data]); //显示的数据
SEG_STCP = 0;
SEG_STCP = 1; //STCP引脚的上升沿更新数据
SEG_STCP = 0;
}
/******************************************************************************
函数名称:Key_Scan
函数功能:返回按键编码
入口参数:无
返回值:按键编码
备注:无
*******************************************************************************
unsigned char Key_Scan(void)
{
static unsigned char st=0,i,key; //静态变量 st状态位 i 长按计数 key键值
uint8 L,H; //定义局部变量,用L得出低4位的值,用h得出高4位的值
switch(st)
{
//=========================================================================
//状态0 按键查询状态
//=========================================================================
case 0:
KEY_PORT=0x0f; //给P1赋值00001111
L=KEY_PORT&0x0f;
if(L!=0x0f) //按键按下
{ st=1; } //进入下一状态按键确认状态
return 0;
break;
//=========================================================================
//状态1 按键确认状态
//=========================================================================
case 1:
KEY_PORT=0x0f; //给P1赋值00001111
L=KEY_PORT&0x0f;
if(L!=0x0f) //按键按下
{ KEY_PORT=0xf0; //给P1赋值11110000
H=KEY_PORT&0xf0;
key=L+H; //读取键值 高4位的值与低4位的值相加
st=2; //下一状态按键释放状态
}
else {st=0;} //按键没被按下 返回按键查询状态 消抖
return 0;
break;
//=========================================================================
//状态2 按键释放状态
//=========================================================================
case 2:
KEY_PORT=0x0f; //给P1赋值00001111
L=KEY_PORT&0x0f;
if(L==0x0f) //按键松手
{
st= 0; //返回按键查询状态
i=0; //长按计数值清零
ct_flag=0; //长按标志位清零
return key; //返回键值
}
else //按键没松手
{
st=2; //继续检测按键松手
if(ct_flag){return key;} //如果按键被长按就不断返回键值
else
{ //按键松手检测
i++;
if(i==100){ ct_flag=1;i=0;return key;} //按键长按计时时间到咯 置长按标志位 计数值清零 返回键值
return 0;
}
}
break;
default: return 0;break;
}
}
/******************************************************************************
函数名称:Parameter_Setting
函数功能:通过矩阵键盘实现系统的参数设置
入口参数:按键编号
返回值: 无
备注: 无
******************************************************************************/
void Parameter_Setting(uint8 key_value_get)
{ switch(key_value_get)
{ case 0: Clock_flag=1; //开启时钟
break;
case 1: hour++;if(hour〉23)hour=0; //时加一 限幅
break;
case 2: min++;if(min〉59)min=0; //分加一 限幅
break;
case 3: sec++;if(sec>59)sec=0; //秒加一 限幅
break;
case 4: Clock_flag=0; //关闭时钟
break;
case 5: hour—-;if(hour<0)hour=23; //时减一 限幅
break;
case 6: min -—;if(min〈0)min=59; //分减一 限幅
break;
case 7: sec—-; if(sec〈0)sec=59; //秒减一 限幅
break;
case 8:
break;
case 9:
break;
case 10:
break;
case 11:
break;
case 12:
break;
case 13:
break;
case 14:
break;
case 15:
break;
default:break;
}
}
/******************************************************************************
函数名称:Timer0Configuration
函数功能:定时器0一毫秒定时
入口参数:无
返回值: 无
备注: 无
******************************************************************************/
void Timer0Configuration()
{
TMOD |= 0x01; //计数器0选择工作方式1
TH0 = (65536-922)/256; //计数初值
TL0 = (65536—922)%256;
ET0 =1; // 1 打开定时器0中断 0 关闭定时器0中断
TR0 = 1; //启动定时器0
}
/******************************************************************************
函数名称:CLOCK
函数功能:时钟运行
入口参数:无
返回值: 无
备注: 无
******************************************************************************/
void CLOCK(void)
{ sec++;
if(sec==60)
{ sec=0;
min++;
if(min==60)
{ min=0;
hour++;
if(hour==24)
{
hour=0;
}
}
}
}
//**************************End of File***************************************
展开阅读全文