第五课开始讲的那个例题感觉非常经典,几乎综合了之前学过的所有东西。所以我着重复习了这个。并给程序加上了注释,以备查阅:
/*程序流程:
程序初始化时为6个数码管显示765432
--->通过“定时器1”来让这个数以1/10秒的速度递减至765398,
同时流水灯从上到下流动,使用“定时器0”控制其速度为500毫秒每次。
---->数码管递减完成后,停止,流水灯停止流动,开始闪烁。
3秒后(定时器0控制)---> 关闭流水灯
数码管显示HELLO
*/
#include
#include //流水灯要用到_crol_()
#define uint unsigned int
#define uchar unsigned char
uchar code tabledu[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,
0x76,0x79,0x38,0x3f,0x00};
uchar temp,t0,t1,flag,flag1;
uint num,speed,bai,shi,ge;
sbit duan=P2^6;
sbit wei=P2^7;
void init();
void display(uint,uint,uint,uint,uint,uint);
void delay(uint speed);
void main()
{
init(); //调用初始化子程序
while(1)
{
if(flag1!=1) //调用中断0中定义的flag1,如果标志位flag1不等于1,则继续执行数码管减数到398的子程序,如果flag1=1,则执行else{}中显示HELLO的显示子程序。
{
display(7,6,5,bai,shi,ge);//调用数码管显示子程序(前3位因为不变so直接赋常量)
}
else
{
display(16,17,18,18,19,20);
}
}
}
void init() //初始化子程序。
{
speed=1; //设定个延时的速度,即扫描显示速度。
num=432; //给num赋初值432
temp=0xfe; //给temp赋初值。
P1=temp; //temp值赋给P1口,即让第一个发光二极管亮
TMOD=0x11; //给中断0和中断1的TMOD寄存器赋值,确定其工作方式
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TH1=(65536-50000)/256;
TL1=(65536-50000)%256; //给中断0和中断1的高8为何低8位赋初值,定时为每50毫秒中断一次。
//此处的理解为:将50000微秒分配到高8位(最大65536)和低8位(最大256)中, 中断即可识别出我们要定时多久。
EA=1; //打开IE总中断
ET0=1;
ET1=1; //打开IE的T0和T1中断
TR0=1;
TR1=1; //启动中断T0和中断T1
}
void time0() interrupt 1 //中断0
{
TH0=(65536-50000)/256; //中断开始时,必须再次初始化高8位和低8位
TL0=(65536-50000)%256;
t0++; //定义变量t0自加,即t0为每次中断自加一次,为中断计数。
/*备注:中断中写程序最好不要写太多
一方面难查,另一方面程序执行需要时间,
每条单周期指令大概1微妙,超过中断时间
会影响中断的执行。更加注意不要写延时。*/
if(flag!=1) //调用中断1中的标志位flag,如果成立,继续执行if{}内语句(流水灯流动),否则执行else{}中语句(流动灯闪烁(调用前已在中断1中停止流动))。
{
if(t0==10) //如果时间流逝500毫秒,执行IF{}内语句。
{
t0=0; //清零
temp=_crol_(temp,1);//左移一位 ,即流水灯换下一个点亮。
P1=temp; //再将TEMP赋给P1口
}
}
else //else内代码为实现流水灯闪烁。
{
if(t0%4==0) //T0中断每进入一次为50毫秒,我们让其200毫秒闪烁一次。 所以给T0的条件就是:4的整数倍,表示为t0%4==0, 同理,如果是每50毫秒闪烁,就是t0%1==0,100毫秒:t0%2==0
P1=~P1; //给所有的P1口(所有流水灯)值取反。
//以上2条语句在代码内实际意义为每隔200毫秒对P1口的值取反一次。即实现了流水灯每400毫秒亮灭各一次,达到闪烁的目的。
if(t0==60) //如果t0=60,即闪烁了50毫秒*60=3秒
{
TR0=0;//关闭T0定时器
P1=0xff;//把P1口关闭。为什么0xff是关闭?
flag1=1;//定义一个标志位flag1,赋值为1,给中断1调用,用于实现HELLO的显示。
}
}
}
void timer1() interrupt 3 //中断1
{
TH1=(65536-50000)/256;
TL1=(65536-50000)%256;
t1++;
if(t1==2) //当t1增加至2时,时间流逝100毫秒,即1/10秒
{
t1=0; // t1清零
num--; // 对num自减
bai=num/100;
shi=num%100/10;
ge=num%10; //将num分解为bai,shi,ge,便于table数组单独调用)
if(num==398) //如果num自减至398
{
TR0=0; //关闭流水灯定时器T0
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TR0=1; //打开T0定时器。以上4条语句为将T0定时器还原并重新打开,准备写3秒钟计时。
t0=0; //t0清零
P1=0xff; //关闭P1口。即初始化流水灯,等待闪烁。重要,否则会有1个流水灯闪烁很大可能与其他7个不同步。
TR1=0; //关闭数码管定时器T1
flag=1; //当执行至此时,定义标志位flag并赋值,给中断0拿去调用。
} //为何视频中写在P1=0xff前,主函数调用flag仍然可以使P1=0xff起作用?
}
}
void display(uint aa,uint bb,uint cc,uint bai,uint shi,uint ge)
//数码管显示子程序。
{
duan=1; //存疑。为何必须先送段选?
P0=tabledu[aa]; //如果先送位选,数码管的数字会整体右移一位,最后一位跑到最前。
duan=0;
P0=0xff; //重要,给P0口送个无显示的空值。无此语句,段选口的送的值保持在锁存端,接着位选一打开又会送给位选,和位选送的值混在一起,导致数码管显示混乱不正常。
wei=1;
P0=0xfe;
wei=0;
delay(speed);
duan=1;
P0=tabledu[bb];
duan=0;
P0=0xff;
wei=1;
P0=0xfd;
wei=0;
delay(speed);
duan=1;
P0=tabledu[cc];
duan=0;
P0=0xff;
wei=1;
P0=0xfb;
wei=0;
delay(speed);
duan=1;
P0=tabledu[bai];
duan=0;
P0=0xff;
wei=1;
P0=0xf7;
wei=0;
delay(speed);
duan=1;
P0=tabledu[shi];
duan=0;
P0=0xff;
wei=1;
P0=0xef;
wei=0;
delay(speed);
duan=1;
P0=tabledu[ge];
duan=0;
P0=0xff;
wei=1;
P0=0xdf;
wei=0;
delay(speed);
}
void delay(uint speed) //延时子程序。
{
uint x,y;
for(x=speed;x>0;x--)
for(y=110;y>0;y--);
}