数码管的动态显示
动态显示的基本原理
多个数码管显示数字的时候,实际上是轮流点亮数码管(一个时刻内只有一个数码管是亮的),利用人眼的视觉暂留现象(也叫余晖效应),就可以做到看起来是所有数码管都同时亮了,这就是动态显示,也叫作动态扫描。
那么一个数码管需要点亮多长时间呢?也就是说要多长时间完成一次全部数码管的扫描呢(很明显:整体扫描时间=单个数码管点亮时间*数码管个数)?答案是10ms以内。当电视机和显示器还处在CRT(电子显像管)时代的时候,有一句很流行的广告语——“100Hz无闪烁”,没错,只要刷新率大于100Hz,即刷新时间小于10ms,就可以做到无闪烁,这也就是动态扫描的硬性指标。
#include <REG52.H>
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
/*
sbit ADDR0 = P2 ^ 2;
sbit ADDR1 = P2 ^ 3;
sbit ADDR2 = P2 ^ 4;
*/
sbit LSA = P2 ^ 2;
sbit LSB = P2 ^ 3;
sbit LSC = P2 ^ 4;
u8 code LedChar[] = {
0x3f,
0x06,
0x5b,
0x4f,
0x66,
0x6d,
0x7d,
0x07,
0x7f,
0x6f,
0x77,
0x7c,
0x39,
0x5e,
0x79,
0x71,
};
u8 LedBuff[8] = {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
u8 i=0,j= 0;
u16 cnt = 0;
u16 sec = 0;
void display()
{
u8 i;
for (i = 0; i < 8; i++) {
switch (i) {
case 0:
LSC = 0;
LSB = 0;
LSA = 0;
break; // 7
case 1:
LSC = 0;
LSB = 0;
LSA = 1;
break; // 6
case 2:
LSC = 0;
LSB = 1;
LSA = 0;
break; // 5
case 3:
LSC = 0;
LSB = 1;
LSA = 1;
break; // 4
case 4:
LSC = 1;
LSB = 0;
LSA = 0;
break; // 3
case 5:
LSC = 1;
LSB = 0;
LSA = 1;
break; // 2
case 6:
LSC = 1;
LSB = 1;
LSA = 0;
break; // 1
case 7:
LSC = 1;
LSB = 1;
LSA = 1;
break; // 0
}
P0 = LedBuff[i];
for(j=0;j<10;j++);
P0 = 0x00;
}
}
void main()
{
TMOD = 0x01;
TH0 = 0xfc;
TL0 = 0x67;
TR0 = 1;
while (1) {
if(TF0==1){
TF0 = 0;
TH0 = 0xfc;
TL0 = 0x67;
cnt++;
if (cnt == 1000) {
cnt = 0;
sec++;
LedBuff[0] = LedChar[sec % 10];
LedBuff[1] = LedChar[sec / 10 % 10];
LedBuff[2] = LedChar[sec / 100 % 10];
LedBuff[3] = LedChar[sec / 1000 % 10];
LedBuff[4] = LedChar[sec / 10000 % 10];
LedBuff[5] = LedChar[sec / 100000 % 10];
LedBuff[6] = LedChar[sec / 1000000 % 10];
LedBuff[7] = LedChar[sec / 10000000 % 10];
}
}
display();
}
}
数码管显示消隐
数码管的不应该亮的段,似乎有微微的发亮,这种现象叫作“鬼影”,这个“鬼影”严重影响了视觉效果。“鬼影”的出现,主要是在数码管位选和段选产生的瞬态造成的。
解决这类问题的方法有两个,其中之一是延时;
不产生瞬间错误的方法是,在进行位选切换期间,避免一切数码管的赋值即可。方法有两个,一个方法是刷新之前关闭所有的段,改变好了位选后,再打开段即可;第二个方法是关闭数码管的位,赋值过程都做好后,再重新打开即可。
关闭段:在switch(i)这句程序之前,加一句P0=0x00;这样就把数码管所有的段都关闭了,当把“ADDR”的值全部搞定后,再给P0赋对应的值即可。
关闭位:在switch(i)这句程序之前,加上一句ENLED=1;等到把ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0];这几条刷新程序全部写完后,再加上一句ENLED=0;然后再进行break操作即可。
单片机中断系统
定时器中断的应用
首先中断函数前边void表示函数返回空,即中断函数不返回任何值,函数名是InterruptTimer0(),这个函数名在符合函数命名规则的前提下可以随便取,取这个名字是为了方便区分和记忆,而后是interrupt这个关键字,一定不能错,这是中断特有的关键字,另外后边还有个数字1
现在看第2行的T0中断,要使能这个中断那么就要把它的中断使能位ET0置1,当它的中断标志位TF0变为1时,就会触发T0中断了,这时就应该来执行中断函数了,单片机又怎样找到这个中断函数呢?靠的就是中断向量地址,所以interrupt后面中断函数编号的数字x就是根据中断向量得出的,它的计算方法是x*8+3=向量地址。当然表中都已经给算好放在第一栏了,可以直接查出来用就行了。
IP这个寄存器的每一位,表示对应中断的抢占优先级,每一位的复位值都是0,当把某一位设置为1的时候,这一位的优先级就比其他位的优先级高了。
当进入低优先级中断中执行时,如又发生了高优先级的中断,则立刻进入高优先级中断执行,处理完高优先级中断后,再返回处理低优先级中断,这个过程就称为中断嵌套,也称为抢占。
既然有抢占优先级,自然就也有非抢占优先级了,也称为固有优先级。
在表6-3中的最后一列给出的就是固有优先级,请注意,在中断优先级的编号中,一般都是数字越小优先级越高。