中断系统非常重要,学过STM32的我深刻体会到,在STM32没有中断的代码都不是完整的,在51单片机中同样重要
文章目录
一、数码管
1、认识数码管
数码管其实就是8个LED小灯组合起来的,是由a,b,c,d,e,f,g,dp八段组合而成,八个LED小灯可以通过三八译码器来进行控制来显示数字和字符。它还多出两个com口这是数码管的公共段主要作用是来分流的,减小单条电路所受的电流(串联分压,并联分流),其原理图如下显示
在KST-51上面有六个数码管,且是共阳数码管(共阳数码管就是8个LED小灯的阳极连在一起,共阴数码管就是阴极连在一起)
2、如何使用数码管
我们要结合三八译码器来学习如何使用数码管
第一步就是学习开启数码管,根据上图可以看出,数码管的激活是靠它上方的三极管控制的,而这六个三极管分别游LEDS0~LEDS5控制,由图可见这些三极管全是PNP型三极管,只有基级为低电平时三极管才能导通,数码管才能被开启
数码管显示字符A
#include<reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
void main()
{
ENLED = 0;
ADDR3 = 1;
ADDR2 = 0;
ADDR1 = 0;
ADDR0 = 0; //开启第一个数码管
P0 = 0x88; //显示字母A
while(1);
}
3、数码管真值表
我们先看真值表,不知道你是否疑惑这个表是怎么来的,来让我分析第一个0开始
要想让数码管显示0,我们就要让abcdef亮,即开启abcdef这六个LED灯,这里就不得不提我们在第一课中学习的51单片机的245应用电路了,我们可以看到数码管的a,b,c,d,e,f,g,dp对应的口分别为DB0,DB1,DB2,DB3,DB4,DB5,DB6,DB7。这八个口来决定数码管LED小灯的熄灭情况,所以我们可以控制这八个IO口来进行数码管控制,好巧不巧这八个口都归P0口管理当我们对P0输入0xC0时,对应的二进制就是11000000(对应DB是倒着来读的,前面是DB7,后面是DB0),自然除了g,dp口是高电平(1),其他都是低电平(0),自然1也是同理(0xF9)转换成二进制就是11111001,说明b,c是低电平在数码管上显示的就是1),其他都是一样
数码管静态显示
#include <reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[]={
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
}; //数码管真值表通过数组进行封装
void main()
{
unsigned char cnt = 0;
unsigned char sec = 0;
ENLED = 0;
ADDR3 = 1;
ADDR2 = 0;
ADDR1 = 0;
ADDR0 = 0; //开启第一个数码管
TMOD = 0x01; //定时器模式1
TH0 = 0xB8;
TL0 = 0x00; //定时20ms
TR0 = 1; 开启定时器0
while(1)
{
if(TF0 == 1) //判断定时器是否溢出,溢出标志就是TF0 == 1
{
TF0 = 0; //清0
TH0 = 0xB8;
TL0 = 0x00;
cnt++;
if(cnt >= 50) //定时1s
{
cnt = 0;
P0 = LedChar[sec]; //将真值表的值在P0刷新
sec++;
if(sec >=16) //当秒数大于0x0F(15)时
{
sec = 0; //归0
}
}
}
}
}
4、数码管动态显示
74HC138 只能在同一时刻导通一个三极管,而我们的数码管是靠了 6 个三极管来控制,那我们在怎么操作才能一次开启六个数码管呢?
其实多个数码管显示数字的时候,我们实际上是轮流点亮数码管(一个时刻内只有一个数码 管是亮的),利用人眼的视觉暂留现象(也叫余辉效应),就可以做到看起来是所有数码管都 同时亮了,这就是动态显示,也叫做动态扫描。
实际只要刷新率超过100HZ(即刷新时间小于 10ms)就没有闪烁了
数码管动态刷新
#include <reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[]={
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
}; //数码管真值表
unsigned char LedBuff[6]={
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
}; //数码管显示缓冲区
void main()
{
unsigned int cnt = 0;
unsigned long sec = 0;
unsigned char i = 0;
ENLED = 0;
ADDR3 = 1;
TMOD = 0x01; //定时器模式1
TH0 = 0xFC;
TL0 = 0x67; //定时1ms
TR0 = 1;
while(1)
{
if(TF0 ==1)
{
TF0 = 0;
TH0 = 0xFC;
TL0 = 0x67;
cnt++;
if(cnt >=1000) //定时1s
{
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];
}
//以下代码实现数码管动态刷新
if(i == 0)
{
ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];
}
else if(i == 1)
{
ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=LedBuff[1];
}
else if(i == 2)
{
ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=LedBuff[2];
}
else if(i == 3)
{
ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=LedBuff[3];
}
else if(i == 4)
{
ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=LedBuff[4];
}
else if(i == 5)
{
ADDR2=1;ADDR1=0;ADDR0=1;i=0;P0=LedBuff[5];
}
}
}
}
让我分析一下这个难理解的几个点
1.提取数字,根据定时器每1s后cnt清0且sec++,sec从0开始加,分析第一句LedBuff[0] = LedChar[sec%10];LedBuff[0]=sec对10取余,按照c语言逻辑,LedBuff[0]=sec对10取余还是等于LedChar[sec](sec<=10),动态刷新数码管真值表中的0~9
当sec>10后执行LedBuff[1] = LedChar[sec/10%10];sec/10%10等于其个位数,其他同理
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];
}
2.动态刷新i ==0时,if的内容是ADDR2=0;ADDR1=0;ADDR0=0这就是是三八编码器的控制此时开启的是LEDS0,之后i++,i==1,将上个代码的LedBuff[0]代入P0,LedBuff[0]中的数随1s加1
i==1,内容为ADDR2=0;ADDR1=1;ADDR0=0开启LEDS1(第二个数码管);i++;P0=LedBuff[2],后面依次循环,知道执行到i==5执行完 ADDR2=1;ADDR1=0;ADDR0=1;i=0;P0=LedBuff[5]后i=0回到开始,所以每一次都会把下面所有代码遍历,在代码证存在缓冲区保证没执行到时其他数码管,保持全是0xFF
if(i == 0)
{
ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];
}
else if(i == 1)
{
ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=LedBuff[1];
}
else if(i == 2)
{
ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=LedBuff[2];
}
else if(i == 3)
{
ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=LedBuff[3];
}
else if(i == 4)
{
ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=LedBuff[4];
}
else if(i == 5)
{
ADDR2=1;ADDR1=0;ADDR0=1;i=0;P0=LedBuff[5];
}
}
}
二、中断
在51单片机中,中断就CPU正在处理A程序,出现了B程序,请求CPU马上去处理(中断发生);CPU暂时停止当前的工作(中断响应),转去执行B程序;待CPU处理B程序完成后,再回到原来的A程序中(断点)继续执行,这样的行为我们称为中断。
在51单片机中,有5个基本的中断源,即外部中断0(INT0)、外部中断1(INT1)、定时/计数器0中断(T0)、定时/计数器1(T1)中断以及串口中断(UART),这5个是我们经常见到的5个中断。
标准 51 单片机中控制中断的寄存器有两个,一个是中断使能寄存器,另一个是中断优先级寄存器。
1、中断的寄存器
1.IE中断使能寄存器
EA是中断的总开关
当写上EA = 1时,单片机就开启了中断
IE的位0~5的控制6个中断使能,根据查询序列的内容进行认识,不必记住,需要使用随时可查。
在这里使能ET0 (定时器0中断使能),即ET0 = 1,
中断的学习是很模糊的,还是搭配程序才能更好认识
数码管显示程序(中断)
#include <reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[]={ //数码管显示字符的表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6]={ //数码管显示缓冲
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
};
unsigned char cnt =0;
unsigned char i = 0; //记录T0中断次数
unsigned char flag1s = 0; //1s定时器标志位
void main()
{
unsigned int cnt = 0;
unsigned long sec = 0;
unsigned char i = 0;
ENLED = 0;
ADDR3 = 1;
TMOD = 0x01; //开启定时器模式1
TH0 = 0xFC;
TL0 = 0x67; //1ms定时
TR0 = 1;
while(1)
{
if(flag1s ==1)
{
flag1s =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];
}
}
}
void IniterruptTime0() interrupt 1 //定时器0中断服务函数
{
TH0 = 0xFC;
TL0 = ox67;
cnt++;
if(cnt>=1000) //中断1000次即1s
{
cnt = 0; //清0重新计数1s
flag1s = 1; //设置1s定时器标志位
}
P0 = 0xFF;
switch(i)
{
case 0: ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];break;
case 1: ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=LedBuff[1];break;
case 2: ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=LedBuff[2];break;
case 3: ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=LedBuff[3];break;
case 4: ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=LedBuff[4];break;
case 5: ADDR2=1;ADDR1=0;ADDR0=1;i=0;P0=LedBuff[5];break;
default:break; // 动态刷新数码管
}
}
2.IP中断优先级寄存器
简单了解即可,目前没太多使用。
中断优先级包括抢占优先级和固有优先级
将IP的某一位置1其优先级就高于其他优先级
总结
文章围绕 51 单片机数码管与中断展开讲解。在数码管部分,先介绍其由 8 个 LED 小灯组成,有共阳、共阴之分,结合三八译码器使用,阐述了开启数码管的方法、真值表原理,给出了显示字符 A、静态显示、动态显示的代码及原理分析,动态显示利用视觉暂留现象,通过轮流点亮数码管实现多管同时亮的效果。在中断部分,解释中断概念为 CPU 暂停当前程序处理其他程序后再返回继续执行,51 单片机有 5 个基本中断源,介绍了中断使能寄存器 IE 和中断优先级寄存器 IP,以数码管显示程序为例展示中断应用,通过定时器 0 中断服务函数实现定时刷新数码管显示 。
最后本文用于个人学习和帮助他人学习解惑,如有不对的地方欢迎给作者建议和学习交流,本文以《手把手教你学51单片机》作为课本教材,本文图片摘自其中,如有侵犯,私下联系,作者会及时更改,感谢。