本文共 5582 字,大约阅读时间需要 18 分钟。
满意答案
改改你的想法,这种 汇编思想 编C语言程序要不得。
/***数显电子时钟——火柴天堂作品-20120603***/
/***源程序默认硬件环境:52单片机,12MHz晶振,四位共阳数码管,P0段选,P20-P23低电平位选,P20最高位,P23最低位,P24~P25独立按键,P1口8位低电平驱动LED***/
/***功能描述:4位数显时钟:HH.MM,小数点闪烁表示秒,P24设置键,P25调时键,不调秒,P1口流水灯(间隔1s)***/
#include"reg52.h"//包含52头文件
#define TRUE1//定义布尔量'1':真
#define FALSE0//定义布尔量'0':假
#define uchar unsigned char//定义 无符号字符型数据 简称
#define uint unsigned int//定义 无符号整型数据 简称
#define th00x3c
#define tl00xb0//50ms at 12MHz(定时器工作模式1 状态)
#define th10xfc
#define tl10x18//1ms at 12MHz(定时器工作模式1 状态)
#define T500msAt50msCount10//定义 500ms 在 50ms 计时基准状态下的计数值为10
#define T1sAt50msCount20//定义 1s 在 50ms 计时基准状态下的计数值为20
#define SEG_Num4//数码管位数
#define SEG_DataP0//数码管段驱动接口
#define SEG_EnP2//数码管位驱动接口
#define SEG_AllOff(SEG_En|=0x0f)//关闭所有数码管(位驱动)
#define DisTimeAt1msCount4//单'位'数码管显示时间,数码管刷新频率f=1/(N*t),其中 N为数码管位数,t 为单'位'数码管显示时间
#define SetStatus3//设置状态数量,三个,0 显示态,1调时态,2调分态
uchar SEG_B_List[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//共阳数码管代码表"0-9"
#define SEG_B_Point0x7f//共阳数码管小数点
sbit KeySet=P2^4;//定义 设置键接口
sbit KeyAdj=P2^5;//定义 调整键接口
#define KeySetValue1//定义 设置键键值为1
#define KeyAdjValue2//定义 调整键键值为2
#define LED_PortP1//定义 流水灯端口
#define LED_Num8//定义 流水灯数量
uchar Sec=0,Min=0,Hour=0,SetLoca=0;
uchar bdata Flag=0x12;//可位寻址标志位寄存器
sbit T1sTimesUpFlag=Flag^0;//1s时间标志位
sbit DisplayFlag=Flag^1;//显示标志位
sbit FlickFlag=Flag^2;//闪烁显示标志位:1显示,0不显示
sbit SettingFlag=Flag^3;//设置标志位
sbit MarqFlag=Flag^4;//流水标志位
void Timer0() interrupt 1//定时器0中断函数
{
static uchar t50ms;
TL0=tl0;
TH0=th0;
t50ms=++t50ms%T1sAt50msCount;//先对50ms计时变量加1,后对变量范围进行限制(0~19)(即对20取模)
if(!t50ms)//若计时变量归0,表示计时变量曾经到达20(1s),则
{
MarqFlag=TRUE;//流水标志位 置位
if(!SettingFlag) T1sTimesUpFlag=TRUE;//若在显示态,则对1s计时标志位 置位
}
if(!(t50ms%T500msAt50msCount))FlickFlag=~FlickFlag;//每500ms 秒(小数点)切换状态一次(即1s闪一次)
}
void Timer1() interrupt 3
{
static uchar t1ms;
TL1=tl1;
TH1=th1;
t1ms=++t1ms%DisTimeAt1msCount;//先计数值加1,后对计数范围进行限制0~(DisTimeAt1msCount-1)
if(!t1ms) DisplayFlag=TRUE;//若定时计数值归0,则表示计数值曾到达 单'位'显示时间(DisTimeAt1msCount),显示标志 置位
}
void TimerInit()
{
TMOD=0x11;//开启定时器0、定时器1,并都工作在模式1
TH0=th0;
TL0=tl0;
TR0=1;//启动T0定时器 计时
ET0=1;//允许定时器0中断
TH1=th1;
TL1=tl1;
TR1=1;//启动T1定时器 计时
ET1=1;//允许定时器1中断
EA=1;//开启系统中断功能
}
void TimesUpdata()//时间更新函数
{
if(T1sTimesUpFlag)//若 1s计时标志位 为 真,即 1s定时时间到
{
Sec=++Sec%60;//秒Sec在0-59范围内加1
if(!Sec)//若秒Sec 为0,则
{
Min=++Min%60;//分Min 在0-59范围内加1
if(!Min) Hour=++Hour%24;//若 分Min 为0,则 时Hour 在0-23范围内加1
}
T1sTimesUpFlag=FALSE;//清 1s计时标志位
}
}
float Pow_Self(float x,uint y)//自编简易 x 的 y 次方函数,y只能是 非负整数
{
float sum;
if(x==0 && y==0) return;//0 的 0 次方无意义
else if(x==0) sum=0;//可有可无,y!=0的情况已经包含x=0,不加不影响结果,但影响运算速度
else if(y==0) sum=1;//除上述情况外,任何数的 0 次方均为 1
else if(y==1) sum=x;//任何数的 1 次方 均为 本身
else if(y>1) sum=Pow_Self(x,--y)*x;//递归调用,降幂
return sum;//返回计算结果
}
void Display(uint dis_num)//显示函数,显示内容为 无符号整型数据 dis_num
{
static uchar dis_loca;//定义静态变量 显示位置
dis_loca=++dis_loca%SEG_Num;//先对 显示位置 加1,后对变量范围进行限制 0~(SEG_Num-1)
SEG_AllOff;//关闭所有数码管显示(位驱动)
if(!SettingFlag || FlickFlag ||(SetLoca==1 && dis_loca<2)||(SetLoca==2 && dis_loca>1))//显示态、闪烁显示态、调Hour时的分位 或 调Min时的时位,均正常显示
SEG_Data=SEG_B_List[(dis_num/(uint)(Pow_Self(10,dis_loca)))%10];//将显示内容(dis_num) 本次需显示的位(dis_loca)上的数值转成代码,并送到数据端口
if(dis_loca==2 &&(SettingFlag||FlickFlag)) SEG_Data &=SEG_B_Point;//闪烁显示 小数点,代替 秒Sec(共阳数码管)
SEG_En&=~(1<
}
void ClockDisplay()//时钟显示函数
{
if(DisplayFlag)//若 显示标志位 为 真
{
Display(Hour*100+Min);//调用 显示函数,显示内容为:高2位显示 分Min,低2位显示 秒Sec
DisplayFlag=FALSE;//清 显示标志
}
}
uchar KeyScan()//按键扫描函数
{
if(KeySet && KeyAdj) return 0;//无按键 返回0
if(!KeySet) return KeySetValue;//设置键 返回设置键键值
if(!KeyAdj) return KeyAdjValue;//调整键 返回调整键键值
return 0;//非以上按键,返回0
}
void KeyResp()//按键响应函数
{
static uchar KeyValue;//定义静态变量 存储按键值
static bit KeyDownFlag,KeyReadyFlag;//定义静态变量 按键按下标志位,按键准备(响应)标志位
uchar key_value=KeyScan();//调用 按键扫描函数,并将扫描结果放在 key_value中
if(key_value)//若扫描结果 有按键
{
KeyValue=key_value;//存储按键值
KeyDownFlag=TRUE;//按键按下标志位 置位
KeyReadyFlag=TRUE;//按键准备(响应)标志位 置位
}
else KeyDownFlag=FALSE;//若扫描结果 无按键,则 清 按键按下标志位
if(KeyReadyFlag && !KeyDownFlag)//若按键准备就绪,且无按键按下,则表示已松手情况
{
switch(KeyValue)//选择按键对应处理
{
case KeySetValue://设置键处理
SetLoca=++SetLoca%SetStatus;//切换状态,0显示态,1调时态,2调分态
if(SetLoca) SettingFlag=TRUE;//若非显示态,则设置标志位 置位
else SettingFlag=FALSE;//否则,清 设置标志位
break;//设置键处理完毕
case KeyAdjValue://调整键处理
switch(SetLoca)//选择状态对应处理对象
{
case 1:Hour=++Hour%24;break;//调时态,时Hour在0-23之间加1
case 2:Min=++Min%60;break;//调分态,分Min在0-59之间加1
default://其他情况
SetLoca=0;//切换回显示态,或忽略操作
SettingFlag=FALSE;//清 设置标志位
break;
}
break;//调整键处理完毕
default:break;//按键处理完毕
}
KeyValue=0;//清 按键值
KeyReadyFlag=FALSE;//清 按键准备(响应)标志位
}
}
void Marquee()//跑马灯函数
{
static uchar marq_loca=LED_Num-1;//静态变量,初始化为最后一盏LED,进入if后,直接到第一盏LED
if(MarqFlag)//若流水标志位为真,则
{
marq_loca=++marq_loca%LED_Num;//切换下一个LED
LED_Port=~(1<
MarqFlag=FALSE;//清 流水标志位
}
}
void SystemInit()
{
TimerInit();//调用 定时器初始化函数
KeySet=1;
KeyAdj=1;//初始化按键端口
}
void main()
{
SystemInit();//调用 系统初始化函数
while(1)//循环系统
{
KeyResp();//调用 按键响应函数
TimesUpdata();//调用 时间更新函数
ClockDisplay();//调用 时钟显示函数
Marquee();//调用 跑马灯函数
}
}追问: 能不能说下思路
追答:思路一大把,还有这个是 俺 12年的程序,让你学习思路的,你的一句“复制”,心塞啊!
先了解下,子函数需要 while 来干嘛呢??
流水灯:它需要 while 来 不停的“流”,它的作用是 在 流水灯跑到末尾的时候,能回到开头。
数码管:它需要 while 来 不停的“换位”,它的作用跟流水大同小异,也是在 数码管 显示最后一位的时候,能回到开头。
想要 删除 while ,必须找得能替代 它作用 的东西,从上面的作用看,while 只是让 函数 末尾跑回开头。如果你能 记录 它的位置,能知道它在末尾,就能让它又回到开头,也就不在需要while 了。于是,需要一个变量 来记录“位置”。(例程里面:dis_loca 是显示位置,marq_loca 是跑马位置,这就是它们取代while 的地方)
另外,要想它们 能正常替代 while的功能,必须排除 另一个东西 的干扰,这个干扰就是 延时函数(delay(uint xx))。
延时函数,顾名思义,就是用来 拖时间的。它对函数的作用是,保证某个时间要求(如跑马1s,显示4ms),但对于CPU来说却是天大的浪费,所以找到替代它的东西也是不可避免的。
延时的作用是保证时间要求,而定时器,却是专为定时而生。它就是替代延时函数的“最佳机选”,于是……
追问: 别复制啊
追问: 谢谢
00分享举报
转载地址:http://qbtnx.baihongyu.com/