第三讲:数码管的显示原理、数码管的静态显示、中断原理和定时器应用(郭天祥)

说明:此文章仅是我学习过程中的一些记录,如有侵权,请联系我删除,文章中难免有遗漏错误之处,欢迎指出。

目录

一、显示器介绍

二、LED显示器

1.LED显示器介绍

2.LED数码显示方式及电路

3.点亮第一个数码管

4.数码管从0显示到F

三、中断

1.中断的概念

2.80C51中断系统的结构 

3.中断优先级的三条原则:

4.编写一个中断源是外部中断的程序

1.首先是开CPU开中断(允许),即打开总中断,肯定要的一步,EA = 1;

2.开你所选择的中断源的中断允许位,例如现在我选择的中断源是外部中断0,则要写EX0 = 1;

3.设置触发方式(外部中断才会用到)

4.编写中断函数

四、定时器

1.定时/计数器的结构

2.定时/计数器的工作原理

3.定时/计数器的控制

①工作方式寄存器TMOD

②控制寄存器TCON

4.定时/计数器的工作方式

5.编写一个定时器应用

1.设置定时器及工作方式

2.对计数初值赋值

3.开中断

4.开始定时


一、显示器介绍

单片机系统中常用的显示器有:

        发光二极管LED(Light Emitting Diode)显示器、液晶LCD(Liquid Crystal Display)显示器、CRT显示器等。LEDLCD显示器有两种显示结构:段显示(7段、米字型等)和点阵显示(5×88×8点阵等)。

二、LED显示器

1.LED显示器介绍

LED显示器分为两种,分别是共阴极和共阳极。

                                                共阴极                                                      共阳极

使用LED显示器时,要注意区分这两种不同的接法。

为了显示数字或字符,必须对数字或字符进行编码。例如为了显示1,需要让b,c管点亮,以共阴极为例,需要b,c所亮的引脚输出高电平,其他的引脚输出低电平。

七段数码管加上一个小数点,共计8段。因此为LED显示器提供的编码正好是一个字节。本次实验板用共阴LED显示器,根据电路连接图显示16进制数的编码已列在下表。   

2.LED数码显示方式及电路

LED显示器工作方式有两种:静态动态。本文讲的是静态显示方法,动态的在下一篇讲解

静态显示的特点是每个数码管的段选必须接一个8位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。这种方法的优点是占用CPU时间少,显示便于监测和控制。缺点是硬件电路比较复杂,成本较高。

先附上实验板上关于数码管的电路

 分别是单片机的连接引脚图,以及与其相连的两个74HC573锁存器,下面是数码管的电路部分

可以看到每个LED数码管所有引脚几乎相同,唯有第8位不同,这是数码管的位选端

已知本实验板所用的是共阴极数码管,因此想要LED数码管亮,需要数码管的输入高电平,观察电路板可以知道,数码管的输入来自74HC573锁存器,而74HC573锁存器的输入来自单片机的P0口。因此我们所要做的,就是操作单片机的P0口

这里介绍一下位选段选,以及与之有关的电路引脚。(以共阴极为例)

位选选择哪个数码管,即只要该数码管的位选引脚为低电平即可。因为该引脚即数码管的公共端,因为共阴极的公共端是地,所以该引脚需为地,而其他的引脚根据要显示的内容为高电平即可,即下面所说的段选。

段选数码管显示哪个数字,根据数码管的电路原理图,对应的引脚的输入为高电平,即可显示对应的数字。

3.点亮第一个数码管

现在来具体介绍如何使该电路板上的数码管亮。

可以看到P0口同时连到了2个锁存器,一个用于段选(显示哪些数字),一个用于位选(哪个数码管显示),为了不产生干扰,必须应用锁存器的功能。还是看真值表。

这里对这个真值表做一些解释,读者们即可轻松理解。这里不需要使用到高阻态Z的功能,默认是低电平,事实上上电后也默认是低电平。

可以看到当LE电平且D也是电平时,Q电平,而D电平时,则Q电平,即当LE是高电平时,锁存器是直通的即D为什么值,Q为什么值,我们需要这个功能来改变Q的值。

而我们同时也注意到,当LE电平时,不管D是什么值,Q默认是Q0,即Q保持原来的值,即锁存我们同样需要这个功能来保持住Q的值。

现在,我们来点亮第一个LED数码管,并且使其显示的数字为1。

其实总共就两步,先位选,再段选

我们观察到位选的LE引脚连接的是单片机的P2^7脚,段选的则为P2^6脚,由于不能直接修改IO口的值,我们应该定义两个位变量来对应这两个脚。

sbit wela = P2^7;

sbit dula = P2^6;

然后是利用锁存器的功能,进行位选。

首先令wela = 1,即锁存器的LE = 1,根据真值表可以知道,此时D为什么值,Q就为什么值,因此我们对单片机的P0口进行赋值(因为P0口与D口直接相连),选择第1个数码管,即该数码管的位选引脚为低电平,即WE1 = 0,以十六进制进行赋值,即P0 = 0xfe,此时由于锁存器的直通功能,WE1是低电平,现在我们要令wela = 1,来保持住这个值。

然后就是段选了,

同样的步骤,dula = 1

接着是对P0口进行赋值,参考编码表或直接看数码管原理图可以知道想要显示1,需要输出P0 = 0x06

最后就是,dula = 0,编译运行上传到单片机开发板上,即可显示出结果。

具体代码如下:

#include <reg52.h>
#define uint unsigned int
sbit wela = P2^7;
sbit dula = P2^6;

void main()
{
	wela = 1;
	P0 = 0xfe;
	wela = 0;
	dula = 1;
	P0 = 0x06;
	dula = 0;
	while(1); //让程序停在这里,让数码管一直亮着
}

补充:对于该型号的单片机,P0口没有内接上拉电阻,需要在电路中外接一个上拉电阻,即电路原理图中的P1,P1是一个排阻,具体电路可以上网搜索,也很简单。为什么需要一个上拉电阻呢,首先是点亮二极管的电路总要一个电阻吧,参见第一讲的文章,然后最主要是,单片机IO口的输出电流太小,不到1mA,并不够点亮LED灯,LED灯的工作电流是3~10mA,因此接上一个上拉电阻。具体的原理我也不太清楚, 这个应该也可以搜到很多文章介绍。

4.数码管从0显示到F

现在我们来尝试让前4个数码管间隔1s显示0~F

创建一个数组,来存放0到F的编码值,这里注意一点,我们用code来定义这个数组,使该数组成为一个编码表的格式。

注:单片机的存储空间是有限的,据郭天祥老师介绍,该型号的单片机的数据存储器(或者叫:随机存储器)只有128个字节(这个应该可以在芯片手册上了解到),数据存储器是何物呢,就是你程序运行中创建的变量都会存放到数据存储器中,因此我们在编程中常常要注意选择合适的数据类型,避免空间不够。所以我们创建一个存放16个8位的数组时会浪费很多空间(虽然在这里还是够的),而当我们在定义数组的时候添加上code,则会让数组的数据存储在程序存储器中,即最终存放在

hex文件中,51单片机支持的最大空间为4k,52单片机是8k,可以看到是后面的数字乘上4k,这个空间是足够的。

据此我们就可以编写程序了。

代码如下:

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[] = {       //注意添加code
0x3f  , 0x06 , 0x5b , 0x4f , 0x66 , 0x6d ,
0x7d , 0x07 , 0x7f  , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 }; //编码表,存放使数码管显示0~F的编码值
void delay(uint z);
uchar num;
sbit wela = P2^7;//位选
sbit dula = P2^6;//段选
void main()
{
	wela = 1;
	P0 = 0xf0;//选择前4个数码管
	wela = 0;
	while(1)
	{
		for(num = 0;num < 16;num++)
		{
			dula = 1;
			P0 = table[num];
			dula = 0;
			delay(1000);//让数码管亮一会儿
		}
	}
}

void delay(uint z)  //延时函数,z的取值为这个函数的延时ms数,如delay(200);大约延时200ms.
{					//delay(500);大约延时500ms.
	uint x,y;
	for(x=z;x>0;x--)
		for(y=110;y>0;y--);	
}

三、中断

中断系统是微处理器的重点,一定要熟练掌握。

1.中断的概念

        CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);

        CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);

        待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断

 

中断技术的好处:

1.解决了快速主机与慢速I/O设备的数据传送问题;

2.分时操作。CPU可以分时为多个I/O设备服务,提高了计算机的利用率;

3.实时响应。CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;

4.可靠性高。CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。

2.80C51中断系统的结构 

80C51的中断系统有5个中断源80526个)2个优先级,可实现二级中断嵌套

有中断请求标志位、触发方式控制位、中断允许位,优先级设定位,分别由不同的寄存器控制,我猜测每个型号的单片机可能略有不同,这些对应控制的寄存器可以在芯片手册上找到,我们不必去记住这些,我们要会的是如何根据这些内容去写一个属于自己的中断函数。 

3.中断优先级的三条原则:

CPU同时接收到几个中断时,首先响应优先级别最高的中断请求

正在进行的中断过程不能被新的同级或低优先级的中断请求所中断

正在进行的低优先级中断服务,能被高优先级中断请求所中断

4.编写一个中断源是外部中断的程序


中断响应的条件:

①中断源有中断请求;

②此中断源的中断允许位为1

③CPU开中断(即EA=1)。

以上三条同时满足时,CPU才有可能响应中断。


根据以上条件,即可编写一个带有中断函数的程序。(以外部中断0为例,其他的中断源有其他的步骤需要补充,见其他文章其他部分)

步骤如下:(所有的寄存器可以在芯片手册找到)

1.首先是开CPU开中断(允许),即打开总中断,肯定要的一步,EA = 1

2.开你所选择的中断源的中断允许位,例如现在我选择的中断源是外部中断0,则要写EX0 = 1

3.设置触发方式外部中断才会用到)

共有两种,对于外部中断0来说,一种是电平触发方式,选择此方式,则当P3^2口电平时,触发中断,执行中断函数,而选为下降沿触发方式,则当P3^2口经历一个下降沿时触发中断。这里我们令IT0 = 0(对寄存器某一位进行操作),选择电平触发方式。也可以写TCON = 0x00。(对寄存器直接操作)

4.编写中断函数

注意:中断函数不需要声明

中断函数的有一个固定的模板,如下

void    xxxxx()  interrupt    x

{

        函数内容;

}

其中xxxxx是函数的名字,可以自己主观设置,也有一些约定俗成的,例如外部中断0就是exter0,定时器0就是timer0

x则是中断序号,不同的中断源有不同的序号,例如外部中断0的序号是0,具体如下表

中断源 序号
外部中断0  0
定时/计数器0 1
外部中断1 2
定时/计数器1 3
串行口 4

最终我们编写出一个程序,6个数码管间隔1s显示0~F,并且添加了中断功能,外部中断0,中断时(即当P3^2口输入低电平时),触发中断,使发光二极管D1亮起

具体代码如下:

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[] = {
0x3f  , 0x06 , 0x5b , 0x4f , 0x66 , 0x6d ,
0x7d , 0x07 , 0x7f  , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 };
void delay(uint z);
uchar num;
sbit wela = P2^7;
sbit dula = P2^6;
sbit D1 = P1^0;    
void main()
{
	EA = 1;  //第一步,开总中断
	EX0 = 1; //第二步,开外部中断0
	IT0 = 1; //第三步,设置触发方式
	wela = 1;
	P0 = 0xc0;
	wela = 0;
	while(1)
	{
		for(num = 0;num < 16;num++)
		{
			D1 = 1;    //中断函数结束时,关闭LED灯
			dula = 1;
			P0 = table[num];
			dula = 0;
			delay(500);
		}
	}
}

void delay(uint z)  //延时函数,z的取值为这个函数的延时ms数,如delay(200);大约延时200ms.
{					//delay(500);大约延时500ms.
	uint x,y;
	for(x=z;x>0;x--)
		for(y=110;y>0;y--);	
}

void exter0() interrupt 0      //中断函数
{
	D1 = 0;                    //函数的内容:点亮D1
}

四、定时器

实现定时功能,比较方便的方法是利用单片机内部的定时/计数器,也是我们这里要学的内容。还有下面这三种方法:

一种是软件定时,就是我们之前讲到的,自己创建一个delay函数,软件定时不占用硬件资源,即不需要占用单片机的IO口,但是它占用了CPU时间,降低了CPU的利用率。另一种是采用时基电路定时,即外接必要的元器件,构成硬件定时电路,但是硬件连接好以后,定时值与定时范围不能由软件进行控制和修改,即不可编程。还有一种是采用可编程芯片定时,可以用软件来确定和修改定时值和定时范围,这种芯片定时强,使用灵活,当单片机的定时/计数器不够用时,可以考虑进行扩展,在一些相对复杂的项目会用到,但是在本次课程学习中不会用到。

1.定时/计数器的结构

定时/计数器的实质是加1计数器(16位,方式1),由高8位低8位两个寄存器组成。另外,有TMOD寄存器,是定时/计数器的工作方式寄存器,确定工作方式和功能;还有TCON控制寄存器,控制T0、T1的启动和停止及设置溢出标志。

2.定时/计数器的工作原理

加1计数器输入的计数脉冲有两个来源:

一是由系统的时钟振荡器输出脉冲经12分频后送来,即每12个振荡周期(1个机器周期),计数值加1,这就是定时器的功能,需要重点掌握;

二是T0或T1引脚输入的外部脉冲源,来一个脉冲计数器加1,这是计数器的功能,这里不详细介绍。

当计数器全为1时,再来一个计数脉冲后,计数器回零,且计数器的溢出会使TCON寄存器中的TF0或TF1置1(看选用哪个定时器),同时向CPU发出中断请求(开定时/计数器中断的前提下)。

可见,溢出时计数器的值减去计数初值才是加1计数器的计数值。

这里包括下面均只介绍定时器模式。

从上述的定时计数方式可以知道,计数频率为晶振频率的1/12,且

计数值N*机器周期Tcy=定时时间t,公式如下:

N = \frac{t}{Tcy}

其中,机器周期 = 振荡周期*12

例:假设实验板上的晶振频率是12MHz(虽然实际上是11.0592MHz,但是这里为了计算方便)

因为晶振频率是12MHz,所以振荡周期就是晶振频率的倒数,即1/12μs机器周期就是1μs,即1次计数1μs

因此若想定时时间t = 20ms,则

计数值N = 20ms/1μs = 20000。

3.定时/计数器的控制

(寄存器的使用不需要特别记住,用到再查芯片手册即可,这里主要是对其中的内容做一些介绍)

①工作方式寄存器TMOD

低四位用于T0,高四位用于T1 

②控制寄存器TCON

TCON的低四位用于控制外部中断,这里就不介绍了

高4位用于控制定时/计数器的启动和中断申请。

4.定时/计数器的工作方式

有方式0、1、2、3共4种,这里只讲第二种,即方式1
方式1的计数位数是16位,由TL0作为低8位TH0作为高8位,组成了16位加1计数器 。

16位,所以计数范围从0~65535,最大计数个数是65536个,即2^16

所以计数初值与计数值的关系如下:

                                ​​​​​​​        ​​​​​​​        ​​​​​​​        X = 2^{16} - N     

其中X是计数初值,N是计数值。

5.编写一个定时器应用

同中断系统一样,我们需要学会的是如何去使用定时器功能,而不是去记忆寄存器。

首先需要介绍下,计数初值如何赋值到计数位上。

一种办法是直接算出十六进制表示,例如,我要设置计数初值为20000,利用计算器可以得到:

20000的十六进制表示是4E20,因此我要将4E赋值给TH0,20赋值给TL0

TH0 = 0x4E;

TL0 = 0x20;

 另一种方法是在程序中去计算,即

TH0 = (65536 - N)/256;

TL0 =  (65536 - N)%256;

其中计数值N根据自己的需要进行赋值。

现在我们来编写一个程序实现1s定时,使第1、3、5个数码管间隔1s显示0~F

步骤如下:

1.设置定时器及工作方式

这里我们设置定时器0为工作方式1,对TMOD进行赋值,再回顾一遍该寄存器的内容

可以看到,GATE = 0即可,也要等于0,工作方式是方式1,所以M1M0 = 01,根据寄存器的内部格式,进行赋值,令TMOD = 0x01; 

2.对计数初值赋值

这里我们要注意到,由于计数位只有16位,所以对于晶振频率为12MHz的板子来说,最大的计数值只有65536,即65ms左右就进入一次中断了,因此我们可以利用20*50ms = 1s来实现定时1s,即进入20次中断,每次定时只有50ms,通过一个临时变量来记录中断次数,每当达到20次中断时,再执行我们想要的操作。

3.开中断

开总中断以及中断源的中断(即定时器0的中断)

4.开始定时

通过控制寄存器TCON,来开始定时。

具体代码如下:

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[] = {
0x3f  , 0x06 , 0x5b , 0x4f , 0x66 , 0x6d ,
0x7d , 0x07 , 0x7f  , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 };
void delay(uint z);
uchar num,tt; //临时变量用来计数
sbit wela = P2^7;
sbit dula = P2^6;
sbit D1 = P1^0;
void main()
{
	num = 0;
	tt = 0;
	TMOD = 0x01;//设置定时器0为工作方式1
	TH0 = (65536-50000)/256;  //计数值为50000,即50ms
	TL0	= (65536-50000)%256;
	EA = 1;			//开总中断
	ET0 = 1;		//开定时器0中断
	TR0 = 1;		//启动定时器0
	wela = 1;
	P0 = 0xea;
	wela = 0;
	dula = 1;
	P0 = 0x3f;
	dula = 0;
	while(1)
	{
		if(tt == 20)  //每当经过20次中断,即1s
		{
			tt = 0;   //重新记录中断次数
			num++;    //数码管的显示
			if(num == 16)//防止显示内容异常
				num = 0;
			dula = 1;
			P0 = table[num];
			dula = 0;

		}
	}
}


void timer0() interrupt 1  //定时器0的中断序号为1
{
	TH0 = (65536-50000)/256; //进入中断后,重新赋值,重新计数
	TL0	= (65536-50000)%256; 
	tt++;                    //对中断次数计数

}

 

 

 

版权声明:本文为CSDN博主「林佳展」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_48021291/article/details/122628908

说明:此文章仅是我学习过程中的一些记录,如有侵权,请联系我删除,文章中难免有遗漏错误之处,欢迎指出。

目录

一、显示器介绍

二、LED显示器

1.LED显示器介绍

2.LED数码显示方式及电路

3.点亮第一个数码管

4.数码管从0显示到F

三、中断

1.中断的概念

2.80C51中断系统的结构 

3.中断优先级的三条原则:

4.编写一个中断源是外部中断的程序

1.首先是开CPU开中断(允许),即打开总中断,肯定要的一步,EA = 1;

2.开你所选择的中断源的中断允许位,例如现在我选择的中断源是外部中断0,则要写EX0 = 1;

3.设置触发方式(外部中断才会用到)

4.编写中断函数

四、定时器

1.定时/计数器的结构

2.定时/计数器的工作原理

3.定时/计数器的控制

①工作方式寄存器TMOD

②控制寄存器TCON

4.定时/计数器的工作方式

5.编写一个定时器应用

1.设置定时器及工作方式

2.对计数初值赋值

3.开中断

4.开始定时


一、显示器介绍

单片机系统中常用的显示器有:

        发光二极管LED(Light Emitting Diode)显示器、液晶LCD(Liquid Crystal Display)显示器、CRT显示器等。LEDLCD显示器有两种显示结构:段显示(7段、米字型等)和点阵显示(5×88×8点阵等)。

二、LED显示器

1.LED显示器介绍

LED显示器分为两种,分别是共阴极和共阳极。

                                                共阴极                                                      共阳极

使用LED显示器时,要注意区分这两种不同的接法。

为了显示数字或字符,必须对数字或字符进行编码。例如为了显示1,需要让b,c管点亮,以共阴极为例,需要b,c所亮的引脚输出高电平,其他的引脚输出低电平。

七段数码管加上一个小数点,共计8段。因此为LED显示器提供的编码正好是一个字节。本次实验板用共阴LED显示器,根据电路连接图显示16进制数的编码已列在下表。   

2.LED数码显示方式及电路

LED显示器工作方式有两种:静态动态。本文讲的是静态显示方法,动态的在下一篇讲解

静态显示的特点是每个数码管的段选必须接一个8位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。这种方法的优点是占用CPU时间少,显示便于监测和控制。缺点是硬件电路比较复杂,成本较高。

先附上实验板上关于数码管的电路

 分别是单片机的连接引脚图,以及与其相连的两个74HC573锁存器,下面是数码管的电路部分

可以看到每个LED数码管所有引脚几乎相同,唯有第8位不同,这是数码管的位选端

已知本实验板所用的是共阴极数码管,因此想要LED数码管亮,需要数码管的输入高电平,观察电路板可以知道,数码管的输入来自74HC573锁存器,而74HC573锁存器的输入来自单片机的P0口。因此我们所要做的,就是操作单片机的P0口

这里介绍一下位选段选,以及与之有关的电路引脚。(以共阴极为例)

位选选择哪个数码管,即只要该数码管的位选引脚为低电平即可。因为该引脚即数码管的公共端,因为共阴极的公共端是地,所以该引脚需为地,而其他的引脚根据要显示的内容为高电平即可,即下面所说的段选。

段选数码管显示哪个数字,根据数码管的电路原理图,对应的引脚的输入为高电平,即可显示对应的数字。

3.点亮第一个数码管

现在来具体介绍如何使该电路板上的数码管亮。

可以看到P0口同时连到了2个锁存器,一个用于段选(显示哪些数字),一个用于位选(哪个数码管显示),为了不产生干扰,必须应用锁存器的功能。还是看真值表。

这里对这个真值表做一些解释,读者们即可轻松理解。这里不需要使用到高阻态Z的功能,默认是低电平,事实上上电后也默认是低电平。

可以看到当LE电平且D也是电平时,Q电平,而D电平时,则Q电平,即当LE是高电平时,锁存器是直通的即D为什么值,Q为什么值,我们需要这个功能来改变Q的值。

而我们同时也注意到,当LE电平时,不管D是什么值,Q默认是Q0,即Q保持原来的值,即锁存我们同样需要这个功能来保持住Q的值。

现在,我们来点亮第一个LED数码管,并且使其显示的数字为1。

其实总共就两步,先位选,再段选

我们观察到位选的LE引脚连接的是单片机的P2^7脚,段选的则为P2^6脚,由于不能直接修改IO口的值,我们应该定义两个位变量来对应这两个脚。

sbit wela = P2^7;

sbit dula = P2^6;

然后是利用锁存器的功能,进行位选。

首先令wela = 1,即锁存器的LE = 1,根据真值表可以知道,此时D为什么值,Q就为什么值,因此我们对单片机的P0口进行赋值(因为P0口与D口直接相连),选择第1个数码管,即该数码管的位选引脚为低电平,即WE1 = 0,以十六进制进行赋值,即P0 = 0xfe,此时由于锁存器的直通功能,WE1是低电平,现在我们要令wela = 1,来保持住这个值。

然后就是段选了,

同样的步骤,dula = 1

接着是对P0口进行赋值,参考编码表或直接看数码管原理图可以知道想要显示1,需要输出P0 = 0x06

最后就是,dula = 0,编译运行上传到单片机开发板上,即可显示出结果。

具体代码如下:

#include <reg52.h>
#define uint unsigned int
sbit wela = P2^7;
sbit dula = P2^6;

void main()
{
	wela = 1;
	P0 = 0xfe;
	wela = 0;
	dula = 1;
	P0 = 0x06;
	dula = 0;
	while(1); //让程序停在这里,让数码管一直亮着
}

补充:对于该型号的单片机,P0口没有内接上拉电阻,需要在电路中外接一个上拉电阻,即电路原理图中的P1,P1是一个排阻,具体电路可以上网搜索,也很简单。为什么需要一个上拉电阻呢,首先是点亮二极管的电路总要一个电阻吧,参见第一讲的文章,然后最主要是,单片机IO口的输出电流太小,不到1mA,并不够点亮LED灯,LED灯的工作电流是3~10mA,因此接上一个上拉电阻。具体的原理我也不太清楚, 这个应该也可以搜到很多文章介绍。

4.数码管从0显示到F

现在我们来尝试让前4个数码管间隔1s显示0~F

创建一个数组,来存放0到F的编码值,这里注意一点,我们用code来定义这个数组,使该数组成为一个编码表的格式。

注:单片机的存储空间是有限的,据郭天祥老师介绍,该型号的单片机的数据存储器(或者叫:随机存储器)只有128个字节(这个应该可以在芯片手册上了解到),数据存储器是何物呢,就是你程序运行中创建的变量都会存放到数据存储器中,因此我们在编程中常常要注意选择合适的数据类型,避免空间不够。所以我们创建一个存放16个8位的数组时会浪费很多空间(虽然在这里还是够的),而当我们在定义数组的时候添加上code,则会让数组的数据存储在程序存储器中,即最终存放在

hex文件中,51单片机支持的最大空间为4k,52单片机是8k,可以看到是后面的数字乘上4k,这个空间是足够的。

据此我们就可以编写程序了。

代码如下:

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[] = {       //注意添加code
0x3f  , 0x06 , 0x5b , 0x4f , 0x66 , 0x6d ,
0x7d , 0x07 , 0x7f  , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 }; //编码表,存放使数码管显示0~F的编码值
void delay(uint z);
uchar num;
sbit wela = P2^7;//位选
sbit dula = P2^6;//段选
void main()
{
	wela = 1;
	P0 = 0xf0;//选择前4个数码管
	wela = 0;
	while(1)
	{
		for(num = 0;num < 16;num++)
		{
			dula = 1;
			P0 = table[num];
			dula = 0;
			delay(1000);//让数码管亮一会儿
		}
	}
}

void delay(uint z)  //延时函数,z的取值为这个函数的延时ms数,如delay(200);大约延时200ms.
{					//delay(500);大约延时500ms.
	uint x,y;
	for(x=z;x>0;x--)
		for(y=110;y>0;y--);	
}

三、中断

中断系统是微处理器的重点,一定要熟练掌握。

1.中断的概念

        CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);

        CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);

        待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断

 

中断技术的好处:

1.解决了快速主机与慢速I/O设备的数据传送问题;

2.分时操作。CPU可以分时为多个I/O设备服务,提高了计算机的利用率;

3.实时响应。CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;

4.可靠性高。CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。

2.80C51中断系统的结构 

80C51的中断系统有5个中断源80526个)2个优先级,可实现二级中断嵌套

有中断请求标志位、触发方式控制位、中断允许位,优先级设定位,分别由不同的寄存器控制,我猜测每个型号的单片机可能略有不同,这些对应控制的寄存器可以在芯片手册上找到,我们不必去记住这些,我们要会的是如何根据这些内容去写一个属于自己的中断函数。 

3.中断优先级的三条原则:

CPU同时接收到几个中断时,首先响应优先级别最高的中断请求

正在进行的中断过程不能被新的同级或低优先级的中断请求所中断

正在进行的低优先级中断服务,能被高优先级中断请求所中断

4.编写一个中断源是外部中断的程序


中断响应的条件:

①中断源有中断请求;

②此中断源的中断允许位为1

③CPU开中断(即EA=1)。

以上三条同时满足时,CPU才有可能响应中断。


根据以上条件,即可编写一个带有中断函数的程序。(以外部中断0为例,其他的中断源有其他的步骤需要补充,见其他文章其他部分)

步骤如下:(所有的寄存器可以在芯片手册找到)

1.首先是开CPU开中断(允许),即打开总中断,肯定要的一步,EA = 1

2.开你所选择的中断源的中断允许位,例如现在我选择的中断源是外部中断0,则要写EX0 = 1

3.设置触发方式外部中断才会用到)

共有两种,对于外部中断0来说,一种是电平触发方式,选择此方式,则当P3^2口电平时,触发中断,执行中断函数,而选为下降沿触发方式,则当P3^2口经历一个下降沿时触发中断。这里我们令IT0 = 0(对寄存器某一位进行操作),选择电平触发方式。也可以写TCON = 0x00。(对寄存器直接操作)

4.编写中断函数

注意:中断函数不需要声明

中断函数的有一个固定的模板,如下

void    xxxxx()  interrupt    x

{

        函数内容;

}

其中xxxxx是函数的名字,可以自己主观设置,也有一些约定俗成的,例如外部中断0就是exter0,定时器0就是timer0

x则是中断序号,不同的中断源有不同的序号,例如外部中断0的序号是0,具体如下表

中断源 序号
外部中断0  0
定时/计数器0 1
外部中断1 2
定时/计数器1 3
串行口 4

最终我们编写出一个程序,6个数码管间隔1s显示0~F,并且添加了中断功能,外部中断0,中断时(即当P3^2口输入低电平时),触发中断,使发光二极管D1亮起

具体代码如下:

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[] = {
0x3f  , 0x06 , 0x5b , 0x4f , 0x66 , 0x6d ,
0x7d , 0x07 , 0x7f  , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 };
void delay(uint z);
uchar num;
sbit wela = P2^7;
sbit dula = P2^6;
sbit D1 = P1^0;    
void main()
{
	EA = 1;  //第一步,开总中断
	EX0 = 1; //第二步,开外部中断0
	IT0 = 1; //第三步,设置触发方式
	wela = 1;
	P0 = 0xc0;
	wela = 0;
	while(1)
	{
		for(num = 0;num < 16;num++)
		{
			D1 = 1;    //中断函数结束时,关闭LED灯
			dula = 1;
			P0 = table[num];
			dula = 0;
			delay(500);
		}
	}
}

void delay(uint z)  //延时函数,z的取值为这个函数的延时ms数,如delay(200);大约延时200ms.
{					//delay(500);大约延时500ms.
	uint x,y;
	for(x=z;x>0;x--)
		for(y=110;y>0;y--);	
}

void exter0() interrupt 0      //中断函数
{
	D1 = 0;                    //函数的内容:点亮D1
}

四、定时器

实现定时功能,比较方便的方法是利用单片机内部的定时/计数器,也是我们这里要学的内容。还有下面这三种方法:

一种是软件定时,就是我们之前讲到的,自己创建一个delay函数,软件定时不占用硬件资源,即不需要占用单片机的IO口,但是它占用了CPU时间,降低了CPU的利用率。另一种是采用时基电路定时,即外接必要的元器件,构成硬件定时电路,但是硬件连接好以后,定时值与定时范围不能由软件进行控制和修改,即不可编程。还有一种是采用可编程芯片定时,可以用软件来确定和修改定时值和定时范围,这种芯片定时强,使用灵活,当单片机的定时/计数器不够用时,可以考虑进行扩展,在一些相对复杂的项目会用到,但是在本次课程学习中不会用到。

1.定时/计数器的结构

定时/计数器的实质是加1计数器(16位,方式1),由高8位低8位两个寄存器组成。另外,有TMOD寄存器,是定时/计数器的工作方式寄存器,确定工作方式和功能;还有TCON控制寄存器,控制T0、T1的启动和停止及设置溢出标志。

2.定时/计数器的工作原理

加1计数器输入的计数脉冲有两个来源:

一是由系统的时钟振荡器输出脉冲经12分频后送来,即每12个振荡周期(1个机器周期),计数值加1,这就是定时器的功能,需要重点掌握;

二是T0或T1引脚输入的外部脉冲源,来一个脉冲计数器加1,这是计数器的功能,这里不详细介绍。

当计数器全为1时,再来一个计数脉冲后,计数器回零,且计数器的溢出会使TCON寄存器中的TF0或TF1置1(看选用哪个定时器),同时向CPU发出中断请求(开定时/计数器中断的前提下)。

可见,溢出时计数器的值减去计数初值才是加1计数器的计数值。

这里包括下面均只介绍定时器模式。

从上述的定时计数方式可以知道,计数频率为晶振频率的1/12,且

计数值N*机器周期Tcy=定时时间t,公式如下:

N = \frac{t}{Tcy}

其中,机器周期 = 振荡周期*12

例:假设实验板上的晶振频率是12MHz(虽然实际上是11.0592MHz,但是这里为了计算方便)

因为晶振频率是12MHz,所以振荡周期就是晶振频率的倒数,即1/12μs机器周期就是1μs,即1次计数1μs

因此若想定时时间t = 20ms,则

计数值N = 20ms/1μs = 20000。

3.定时/计数器的控制

(寄存器的使用不需要特别记住,用到再查芯片手册即可,这里主要是对其中的内容做一些介绍)

①工作方式寄存器TMOD

低四位用于T0,高四位用于T1 

②控制寄存器TCON

TCON的低四位用于控制外部中断,这里就不介绍了

高4位用于控制定时/计数器的启动和中断申请。

4.定时/计数器的工作方式

有方式0、1、2、3共4种,这里只讲第二种,即方式1
方式1的计数位数是16位,由TL0作为低8位TH0作为高8位,组成了16位加1计数器 。

16位,所以计数范围从0~65535,最大计数个数是65536个,即2^16

所以计数初值与计数值的关系如下:

                                ​​​​​​​        ​​​​​​​        ​​​​​​​        X = 2^{16} - N     

其中X是计数初值,N是计数值。

5.编写一个定时器应用

同中断系统一样,我们需要学会的是如何去使用定时器功能,而不是去记忆寄存器。

首先需要介绍下,计数初值如何赋值到计数位上。

一种办法是直接算出十六进制表示,例如,我要设置计数初值为20000,利用计算器可以得到:

20000的十六进制表示是4E20,因此我要将4E赋值给TH0,20赋值给TL0

TH0 = 0x4E;

TL0 = 0x20;

 另一种方法是在程序中去计算,即

TH0 = (65536 - N)/256;

TL0 =  (65536 - N)%256;

其中计数值N根据自己的需要进行赋值。

现在我们来编写一个程序实现1s定时,使第1、3、5个数码管间隔1s显示0~F

步骤如下:

1.设置定时器及工作方式

这里我们设置定时器0为工作方式1,对TMOD进行赋值,再回顾一遍该寄存器的内容

可以看到,GATE = 0即可,也要等于0,工作方式是方式1,所以M1M0 = 01,根据寄存器的内部格式,进行赋值,令TMOD = 0x01; 

2.对计数初值赋值

这里我们要注意到,由于计数位只有16位,所以对于晶振频率为12MHz的板子来说,最大的计数值只有65536,即65ms左右就进入一次中断了,因此我们可以利用20*50ms = 1s来实现定时1s,即进入20次中断,每次定时只有50ms,通过一个临时变量来记录中断次数,每当达到20次中断时,再执行我们想要的操作。

3.开中断

开总中断以及中断源的中断(即定时器0的中断)

4.开始定时

通过控制寄存器TCON,来开始定时。

具体代码如下:

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[] = {
0x3f  , 0x06 , 0x5b , 0x4f , 0x66 , 0x6d ,
0x7d , 0x07 , 0x7f  , 0x6f , 0x77 , 0x7c ,
0x39 , 0x5e , 0x79 , 0x71 };
void delay(uint z);
uchar num,tt; //临时变量用来计数
sbit wela = P2^7;
sbit dula = P2^6;
sbit D1 = P1^0;
void main()
{
	num = 0;
	tt = 0;
	TMOD = 0x01;//设置定时器0为工作方式1
	TH0 = (65536-50000)/256;  //计数值为50000,即50ms
	TL0	= (65536-50000)%256;
	EA = 1;			//开总中断
	ET0 = 1;		//开定时器0中断
	TR0 = 1;		//启动定时器0
	wela = 1;
	P0 = 0xea;
	wela = 0;
	dula = 1;
	P0 = 0x3f;
	dula = 0;
	while(1)
	{
		if(tt == 20)  //每当经过20次中断,即1s
		{
			tt = 0;   //重新记录中断次数
			num++;    //数码管的显示
			if(num == 16)//防止显示内容异常
				num = 0;
			dula = 1;
			P0 = table[num];
			dula = 0;

		}
	}
}


void timer0() interrupt 1  //定时器0的中断序号为1
{
	TH0 = (65536-50000)/256; //进入中断后,重新赋值,重新计数
	TL0	= (65536-50000)%256; 
	tt++;                    //对中断次数计数

}

 

 

 

版权声明:本文为CSDN博主「林佳展」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_48021291/article/details/122628908

生成海报
点赞 0

林佳展

我还没有学会写个人说明!

暂无评论

相关推荐

蓝桥杯国赛,省赛经验总结(单片机组)

大一到大二,我依次获得了蓝桥杯大赛的省二,省一,国三。这一路走来我也是不断地在总结经验,力求下次做得更好,可惜“下次”不多了。 接下来我将总结蓝桥杯单片机组的比赛经验&#xf