一、点亮数码管
首先看一下案例源码:
#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 delayms(uint z); //延时z毫秒
void dis_shi(); //显示十位的中断服务函数
void dis_ge(); //显示个位的中断服务函数
void display(uchar wei, uint zz); //显示函数,wei是位选信号,zz是个位或者十位
void dis_gang(uchar gang);
//初始化全局变量
uint num, num1=0, num2=0, shi=0, ge=0; //num1,shi分别为十位的数大小、十位的下标
uint shi2=0, ge2=0, num3=0;
uint shi3=0, ge3=0, num4=0;
//锁存器
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()
{
TMOD= 0x11; //设置定时器0和定时器1为工作方式,即为设置为(0001 0001)
TH0= 156; //装初值(高八位)
TL0= 156; //低八位
TH1= 156;
TL1= 156;
EA= 1; //开总中断
ET0= 1; //开定时器0中断
ET1= 1; //开定时器1中断
TR0= 1; //启动定时器0
TR1= 1; //启动定时器1
while(1)
{
display(0xfd, shi3);
display(0xfe, ge3);
display(0xf7, shi2);
display(0xef, ge2);
display(0xbf, shi);
display(0x7f, ge);
}
}
void display(uchar wei, uint shi)
{
//送段选数据,点亮数码管
dula= 1;
P0= table[shi];
dula= 0;
P0= 0xff; //关闭数码管,防止打开位选锁存器时原来的段选数据通过位选造成混乱
//送位选数据
wela= 1;
P0= wei;
wela= 0;
delayms(1); //等待会触发中断,执行后一位的计数
dis_gang(0xfb);
dis_gang(0xdf);
}
void dis_gang(uchar gang)
{
dula= 1; //倒数第三位
P0= 0x40;
dula= 0;
P0= 0xff;
wela= 1;
P0= gang;
wela= 0;
delayms(1);
}
void dis_ge() interrupt 3
{
TH0= 156; //重装初值
TL0= 156;
num1++;
if(num1==4) //如果到了4次,说明200ms时间到
{
num1= 0; //则把num1清零,再重新计算4次
}
}
//先执行高级的定时器
void T1_shi() interrupt 1 //定时器1中断的序号是3
{
TH0= 156; //重装初值
TL0= 156;
num2++;
if(num2==20) //1秒到
{
num2= 0;
num++;
if(num==60) //1分钟到
{
num= 0; //总数到了60则清零
num3++;
if(num3==60) //1小时到
{
num3= 0;
num4++;
if(num4==24)
{
num4= 0;
}
}
}
//控制十位和个位显示的数字(因为段选信号的下标与应该显示的数字是对应的,从0开始)
shi3= num4/10;
ge3= num4%10;
shi2= num3/10;
ge2= num3%10;
shi= num/10;
ge= num%10;
}
}
void delayms(uint z)
{
uint i, j;
for(i=z; i>0; i--)
for(j=110; j>0; j--) ;
}