一、点亮数码管
首先看一下案例源码:
#include <reg52.h>
sbit dula= P2^6; //声明U1锁存器的锁存端
sbit wela= P2^7; //声明U2锁存器的锁存端
int main()
{
wela= 1; //打开U2锁存端(控制灯亮的锁存器)
P0= 0xfc; //送入位选信号(亮灯的位置,十六进制从右到左,显示的是从左到右)
wela= 0;
dula= 1; //打开U1锁存端(控制灯显示的锁存器)
P0= 0x07; //送入段选信号(灯亮的数字,十六进制转二进制依次对应hgfe dcba段,h段为0其余为1则全亮)
dula= 0; //关闭U1锁存端
while(1); //程序停止
}
(1)全局变量区域的sbit表示声明锁存器的锁存端,简单来说就是需要用这两个锁存端来控制段选和位选信号的保持。段选用来控制数码管上的数字,位选用来控制数码管上的位置,因此这两个索存端就是用来控制在数码管的哪个(或哪几个)位置上显示什么数。
(2)主函数中就用上了在全局声明的锁存端,wela则是控制位选(哪个位置),为1时则打开,为0时则关闭,并保持设定好了的位选。dula段选控制同理。
(3)不管是段选还是位选,都是用P0来传入信号。
二、八位数码管同时从0到F
首先提供一下从0到F的段选信号:
//0到F段选信号
uchar code table[]={
0x3f, 0x06, 0x5b, 0x4f,
0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c,
0x39, 0x5e, 0x79, 0x71
};
(1)我们的本次要显示的是所有的数码管,这里是八位数码管,wela= 1; P0= 0x00; //位选信号,全亮 wela= 0;
(2)在点亮全部数码管的基础上,将这一系列的段选信号依次遍历,传入到P0中,再加上延迟函数,就能够实现动态变化了。
/*让8个数码管同时点亮,
依次显示0到F,时间间隔为0.5秒*/
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
//锁存器与全局变量
sbit dula= P2^6;
sbit wela= P2^7;
uchar num;
//0到F段选信号
uchar code table[]={
0x3f, 0x06, 0x5b, 0x4f,
0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c,
0x39, 0x5e, 0x79, 0x71
};
void delay(uint); //函数声明
void main()
{
wela= 1;
P0= 0x00; //位选信号,全亮
wela= 0;
while(1)
{
for(num=0; num<16; num++) //16个数循环显示
{
dula= 1;
P0= table[num]; //段选信号,依次取数组中的值
dula= 0;
delay(500); //延时0.5秒
}
}
}
void delay(uint x) //延时x毫秒的函数
{
uint i,j;
for(i=x; i>0; i--)
{
for(j=110; j>0; j--) ;
}
}
三、显示学号(指定数字)
稍微想一下就明白了,只要我们将依次将各个学号数字所对应的段选信号通过P0传入,位选信号我们都不用变,就可以在数码管上显示所对应的数字了。
/*显示学号*/
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
void delayms(uint x); //延时函数声明
//U1和U2锁存器的锁存端
sbit dula = P2^6;
sbit wela = P2^7;
//学号的段选信号
uchar code table[]={
0x5b, 0x3f, 0x06, 0x6f,
0x66, 0x5b, 0x7d, 0x3f
};
//存放位选信号
uchar D[]= {
0xfe, 0xfd, 0xfb, 0xf7,
0xef, 0xdf, 0xbf, 0x7f
};
void main()
{
uint i;
while(1)
{
for(i=0; i<sizeof(table); i++)
{
dula= 1;
P0= table[i]; //送入段选信号
dula= 0;
P0= 0xff; //送位选数据前关闭数码管的显示,防止位选锁存器混乱
wela= 1;
P0= D[i]; //送入位选信号,只亮一位,但由于视觉残留效应,会觉得是同时亮着的
wela= 0;
delayms(2); //(必须有延时)延时2毫秒,对于人眼的视觉暂留根本看出来在跳
}
}
}
//延时x毫秒的函数
void delayms(uint x)
{
uint i,j;
for(i=x; i>0; i--)
{
for(j=110; j>0; j--) ;
}
}
四、中断机制的引入
首先理解一下中断是什么,举个例子:我本来在上课,突然有电话打来,我去接了个电话,然后继续回来上课。在这个例子中,我去接电话就是在执行一个中断请求,执行完该中断之后,我会继续回到课堂上,继续断点位置的讲课,而不是从头开始讲课,这也就是中断的好处了。
1、中断允许控制位
EA:中断允许总开关控制位。(1:所有中断请求被允许;0:所有中断请求被屏蔽)
- ES:串行口中断允许控制位。(1:允许串口中断;0:禁止串口中断)
- ET1:定时器/计数器T1的溢出中断允许控制位。(1:允许T1溢出中断;0:禁止T1溢出中断)
- EX1:外部中断1中断允许位。(1:允许外部中断1中断;0:禁止外部中断1中断)
- ET0:定时器/计数器T0的溢出中断允许控制位。(1:允许T1溢出中断;0:禁止T1溢出中断)
- EX0:外部中断0中断允许位。(1:允许外部中断1中断;0:禁止外部中断1中断)
注意:除了EA总中断控制,以上中断类型中EX0即外部中断0的等级是最高的,就是0;从下往上依次递减,ES串行口中断等级为4,即最低的。
2、中断请求方式的选择
- IT0(TCON.0),外部中断0触发方式控制位。
当IT0=0时,为低电平触发方式。
当IT0=1时,为边沿触发方式(下降沿有效)。 - IE0(TCON.1),外部中断0中断请求标志位。
- IT1(TCON.2),外部中断1触发方式控制位。
- IE1(TCON.3),外部中断1中断请求标志位。
- TF0(TCON.5),定时/计数器T0溢出中断请求标志位。
- TF1(TCON.7),定时/计数器T1溢出中断请求标志位。
3、中断服务的必要操作
(1)中断初始化
EA=1;//打开总中断开关
EX0=1;//开外部中断0
IT0=0/1;//设置外部中断的触发方式
//便是以上中断方式TOCN的选择
(2)写中断服务函数
void int0 () interrupt 0 //外部中断0,即最高级别的中断,所以为0
{
//do anything that you want
}
3、外部中断0简单案例
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
void delayms(uint z);
void display();
//锁存器
sbit wela= P2^7; //位选
sbit dula= P2^6; //段选
//0到9的段选信号
uchar code table[]={
0x3F, 0x06, 0x5B, 0x4F, 0x66,
0x6D, 0x7D, 0x07, 0x7F, 0x6F
};
void main()
{
//主函数变量
uint i;
//初始化中断方式
IT0= 0; //低电平触发中断0
//中断基础设置
EA= 1; //开总中断
EX0= 1; //允许外部中断0
PX0= 1; //外部中断0为高优先级
while(1)
{
for(i=0; i<=6; i++)
{
wela= 1;
P0= 0xbF;
wela= 0;
dula= 1;
P0= table[i];
dula= 0;
delayms(10000);
}
}
}
void display() interrupt 0
{
uint i;
uchar temp= P0;
for(i=0; i<10; i++)
{
wela= 1;
P0= 0x7F;
wela= 0;
dula= 1;
P0= table[i];
dula= 0;
delayms(10000);
}
P0= temp;
}
void delayms(uint z)
{
uint i, j;
for(i=z; i>0; i--)
for(j=0; j<110; j--) ;
}
五、利用中断实现动态时钟
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
void time_init();
void delay(uint x);
void display(uchar addr, uchar sign);
void write_addr(uchar addr);
void write_data(uint sign);
void timer();
void dis_gang();
sbit dula = P2^6;
sbit wela = P2^7;
code uchar table[]={0x3F, 0x06, 0x5B, 0x4F, 0x66,
0x6D, 0x7D, 0x07, 0x7F, 0x6F}; //0-9段选
uint sign[6]={0, 0, 0, 0, 0, 0}; //用来显示数据的位置,不包括横杠
uint second=0;
uint minute=0;
uint hour=0;
uint n=0; //用来计数判断的中间变量
void time_init()
{
TMOD = 0x02;
ET0 = 1; //可用定时器0
TR0 = 1; //运行定时器0
TH0 = 156;
TL0 = 156;
EA = 1;
}
void display(uchar addr, uint j) //传入段选下标和位选信号
{
//送段选数据,点亮数码管
dula= 1;
P0= table[j];
dula= 0;
P0= 0xff; //关闭数码管,防止打开位选锁存器时原来的段选数据通过位选造成混乱
//送位选数据
wela= 1;
P0= addr;
wela= 0;
delay(1); //等待会触发中断,执行后一位的计数
dis_gang();
}
void dis_gang() //在数字交界显示横杠
{
wela = 1;
P0 = 0xDB; //第3位和第6位
wela = 0;
dula = 1;
P0 = 0x40; //一个杠
dula = 0;
delay(1);
}
void delay(uint x)
{
uint i, j;
for(i=x; i>0; i--)
for(j=110; j>0; j--) ;
}
void main()
{
time_init();
while(1)
{
display(0x7F, sign[0]);
display(0xBF, sign[1]);
display(0xEF, sign[2]);
display(0xF7, sign[3]);
display(0xFD, sign[4]);
display(0xFE, sign[5]);
}
}
void timer() interrupt 1
{
n++;
if(n==10000)
{
n=0;
second++;
if(second==60)
{
second=0;
minute++;
if(minute==60)
{
minute=0;
hour++;
if(hour==24)
{
hour=0;
}
}
}
sign[0]= second%10; //获得秒个位的下标
sign[1]= second/10; //获得秒十位
sign[2]= minute%10;
sign[3]= minute/10;
sign[4]= hour%10;
sign[5]= hour/10;
}
}