第三课,数码管显示原理和中断
了解80C51各个引脚的功能,如图,主要注意P0口都外接了10K的上拉电阻,将使1为输出稳定为高电平。
用数码管显示数字。
LED灯由八段灯管组成,为构成八字形的七段灯管a~g和小数点h.
LED显示器有两种接法,共阴极和共阳极。共阴极即阴极接0,由单片机决定阳极的0和1来决定哪些段亮,单片机输出1时亮;共阳极反之,阳极都接Vcc,阴极由单片机决定,单片机输出0时亮。
数码管的静态显示:每个数码管的段选必须接一个8位数据线来保持显示的字形码,当送入一次字形后,显示字形可以一直保持,直到送入新的字形。
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x5d,0x7d,0x07,
0x6f,0x77,0x7c,0x39,
0x5e,0x79,0x71,0x00}; // 使用数组,中括号中输入某个数值,返回对应第几个元素,这里数组中为从0到9,a到f,全灭对应P0口应该输出的十六进制数值
sbit wela=P2^7
sbit dula=P2^6
uint x,y,z,num;
void delay(uint);
void main()
{
wela=1;
P0=0xfc; //此时WEI1和WEI2为0(共阴极),即LED1和2都亮
wela=0;
for(num=0;num<16;num++)
{
dula=1;
P0=table[num]; //根据num选择数组中第几个元素
dula=0;
delay(100);
}
}
void delay(uint z)
{
for(x=z;x>0;x--)
for(y=100,y>0;y--);
}
这里不同数码管同时显示只能是相同的字形,如果要实现不同数码管显示不同字形,比如第一个1,第二个2,可以每次只控制一个数码管,delay时间极短时,由于视觉暂留,看起来就像是所有数码管同时显示,这就是动态扫描。
80C51中断系统结构(是魔鬼吗,学了好几遍)
是什么?
CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生),CPU暂停事件A去处理B(中断响应和服务),处理完B后返回原来事件A被暂停的地方,继续处理事件A的过程就是中断。
C51有五个中断源,即能请求中断的引脚
这张图很重要,这里把端口看做是开关,置1和置零表示接入或断开,或者双向选择。EA为总开关,控制所有的中断发生。左边表示5个中断源,从上至下分别为外部中断0,定时/计数器0,外部中断1,定时计数1,串行口。外部中断0通过IT0置1或0来控制中断信号是下降沿还是低电平触发。下降沿为电平由高跳变到低的瞬间触发中断,中断程序执行完后自动回到主程序;**低电平则需要人为使电平回到高电平后才能继续主程序,**否则一直处于中断。
TCON是各种中断控制,低四位中IE0和IE1是外部中断0和1的中断请求控制,IT0和IT1是触发方式控制;高四位中一般只需操作TF0和TF1,是定时计数中断请求控制。
一个栗子
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x5d,0x7d,0x07,
0x6f,0x77,0x7c,0x39,
0x5e,0x79,0x71,0x00};
sbit dula=P2^6;
sbit wela=P2^7;
sbit d1=P1^0;
uint x,y,z,num,tt;
void delay(uint);
void main()
{
EA=1;//打开总中断控制
EXO=0;//打开外部中断0允许
IT0=0;//设置外部中断触发为低电平
wela=1;
P0=0xc0; //位选置数,打开LED1和LED2
wela=0;
while(1)
{
if(tt==20)
{
tt=0;
num++;
if(num==16)
{num=0;}
dula=1;
P0=table[num];
dula=0;
delay(100);
};
}
}
void delay(uint z)
{
for(x=z;x>0;x--)
for(y=600;y>0;y--);
}
void exter0() interrupt 0 // 中断服务程序,0是优先级第一级,即外部中断0,优先级参见下图
{
d1=0;
}
INT0口接低电平会触发中断程序,其他条件都满足时运行进入中断服务程序。
定时/计数器
定时计数器是16位加1计数器,由高八位(THX)低八位(TLX)两个寄存器组成,原理可以看数电中各种计数器,总之就是每来一个脉冲信号计数器加1,直到满了进位同时清零。
TMOD控制定时计数器的工作方式,高八位为TI方式,低八位为T0方式(两者好像没啥区别),T!和T0都有以下四个位
GATE一般设置为0即可
C/T 置0时是定时模式,置1计数模式
M1M0 决定工作方式:
00:13位定时计数器
01:16位定时计数器
10:8位自动重复定时
11:T0分为两个独立的8位定时计数器,T1没有此模式
定时计数器的启动由TCON的TR0和TR1控制,置1时对应的定时计数器工作。
如何初始设置
相关说明
1、TMOD没有特殊位定义,所以得整体赋值
2、置入初值,由于16位寄存器全部计满有65536,故从0计数直到溢出的时间约为65毫秒(以晶振频率12兆,则晶振周期为1/12微秒,机器周期为1微秒,65536个机器周期为65毫秒)。如果要实现小于65毫秒的定时,需要置入初始值。例如50毫秒,则应该置入的数值为:
高八位THX:(65536-50000)/256(求模)
第八位TLX:(65536-50000)%256(取余)
3、IE是中断允许控制
EX0:外部中断0允许位
ET0:定时计数器0中断允许位
EX1:外部中断1允许位
ET1:定时计数器1中断允许位
ES:串行口
4、TR0或TR1在TCON中,可以直接用位操作
再来一个栗子
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x5d,0x7d,0x07,
0x6f,0x77,0x7c,0x39,
0x5e,0x79,0x71,0x00};
sbit dula=P2^6;
sbit wela=P2^7;
uint x,y,z,num,tt;
void delay(uint);
void main()
{
TMOD=0x01;//设置定时器0位工作方式
TH0=(65536-50000)/256;// 高八位
TL0=(65536-50000)%256;// 低八位
EA=1;//中断总开关
ET0=1;//定时计数器0中断允许
TR0=1;//启动定时计数器0
wela=1;
P0=0xc0;
wela=0;
while(1)
{
if(tt==20)
{
tt=0;
num++;
if(num==16)
{num=0;}
dula=1;
P0=table[num];
dula=0;
delay(100);
};
}
}
void delay(uint z)
{
for(x=z;x>0;x--)
for(y=600;y>0;y--);
}
void exter0() interrupt 1//1表示定时计数器0
{
TH0=(65536-50000)/256;
TL0=(65536-20000)%256;
tt++;
}
程序运行顺序,进入主函数后,启动定时器,开始计数,50毫秒后计满溢出请求中断,进入中断服务程序,此时重新置初值,又进行计数,重复进入中断服务程序,直至tt=20(即经过1s),进行if函数,tt被置零,数码管显示一个数字,重复此过程,实际上就是每隔1秒显示一下数字,用定时器相对于用程序来延时更加精确。注意这里主程序运行时,定时计数也是在运行的,只不过这里主程序中只有当tt满足条件是才有操作。
if函数中还使用了delay延时会造成一些影响,具体还要实操检验一下。