编程实现单个数码管封装的显示

一道关于数码管显示的微机编程题_百度知道
一道关于数码管显示的微机编程题
编程实现8255读取PB口的数字。8255的端口地址为40H—43H。2。用8255的PB7—PB0做一个键盘接口的输出端口。要求,接收0—9数字字符8255的数码管数字显示实训目的,读取PB口的输入;是0—9数字,以及8255的方式0的应用程序设计,以及译码电路的连接,读写控制线MEMR(有上划线)和MEMW(有上划线),设计该8255译码电路。8255的循环控制流程:要有一个图和程序代码:用PA7—PA0控制一个七段LED数码管,做“0”—“9”的数字显示,非0—9数字丢弃不要,让PA口的LED数码管显示的控制程序,数据总线D7—D0。实训内容;从PA口输出,让数码管显示、
给出8255与微机系统,以及PA口与LED数码管,PB口与键盘接口的连接电路,转换成对应的“0”—“9”的显示码。给出该8255与地址总线A15—A0,再重新读取:掌握8255定行IO接口工作原理
我有更好的答案
可以问一下高哥
其他类似问题
为您推荐:
数码管显示的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁单个数码管显示程序怎么编写_百度知道
单个数码管显示程序怎么编写
8点半之前需要,谢谢!
提问者采纳
单个的太简单了;#define uint unsigned int#define uchar unsigned char
case 0&#47,0x7d;
P1=table[num]: num=14,0x7c;sbit duan=P2^6;
case 0xd7;
case 0xb7: num=6: num=1;
if(temp: num=0;
P3=0xf7: num=10,0x07,;
case 0uchar code table[]={0x3f; break,0x71}: num=3,0x6d; wei=0x0: num=9;
H=0,H,temp,0x5e: num=4;
temp=P3;P0=table[num];
case 0 P1=0x00;
case 0x7e,0x5b,0x06;
H=0: num=12: num=8,0x7f;
case 0x7b,0x6f!=0xf7)
switch(temp)
case 0xe7;
temp=P3!=0xfd)
switch(temp)
case 0xed,你留言给我我帮你在写一个当个点亮的程序给你;sbit wei=P2^7;
case 0x77: num=15,0x79: num=2;
我这是个按键点亮数码管的,0x4f。如果这个对你没帮助: num=7;
case 0xdd,0x39,0x66;
P3=0 P1=0; duan=0; break。没试过,0x77!=0xfb)
switch(temp)
case 0xeb!=0xfe)
switch(temp)
case 0xee: num=13;
duan=0;reg52; P1=0xc0;
case 0x7d: num=11;
duan=1; while(1) {
temp=P3.h&
if(temp: num=5;void main(){ wei=1#include&
提问者评价
非常非常感谢啊!上不了网所以这么晚才采纳,抱歉!!
其他类似问题
为您推荐:
您可能关注的推广
数码管显示的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁单片机C语言程序设计:单只数码管循环显示 0~9_解决方案_元器件交易网
关注我们:
单片机C语言程序设计:单只数码管循环显示 0~9
/*& 名称:单只数码管循环显示 0~9
说明:主程序中的循环语句反复将 0~9 的段码送至 P0 口,使数字 0~9 循环显示
#include&reg51.h&
#include&intrins.h&
#define uchar unsigned char
#define uint unsigned int
uchar code DSY_CODE[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};
void DelayMS(uint x)
while(x--) for(t=0;t&120;t++);
void main()
uchar i=0;
P0=~DSY_CODE[i];
i=(i+1)%10;
DelayMS(300);
扫描左侧的二维码
科技圈最新动态一手掌握每日砸蛋,中奖率100%经现场120医生抢救无效,4人均已确认死亡。
英国皇家军队阅兵纪念女王90岁生日,一名士兵晕倒。
声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
  是系统重点中的重点,因为有了中断,单片机就具备了快速协调多模块工作的能力,可以完成复杂的任务。本章将首先带领大家学习一些必要的基础知识,然后讲解的原理,并最终借助于来完成实用的数码管显示程序。大家对本章节内容要多多研究,要完全掌握并能熟练运用。  1.1 C语言的数组 1.1.1 数组的基本概念   第四章已经学过变量的基本类型,比如char、int等等。这种类型描述的都是单个具有特定意义的数据,当我们要处理拥有同类意义但是却包含很多个数据的时候,就可以用到数组了,比如我们上节课那个数码管的真值表,就是用一个数组来表达的。  从概念上讲,数组是具有相同数据类型的有序数据的组合,一般来讲,数组定义后满足以下三个条件。  1、具有相同的数据类型;  2、具有相同的名字;  3、在存储器中是被连续存放的。  比如我们上节课定义的那个数码管真值表,如果我们把关键字code去掉,数组元素将被保存在RAM中,在程序中可读可写,同时我们也可以在中括号里边标明这个数组所包含的元素个数,比如:  unsigned char LedChar[16] = {  0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,  0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E  };   在这个数组中的每个值都称之为数组的一个元素,这些元素都具备相同的数据类型就是unsigned char型,他们有一个共同的名字LedChar,不管放到RAM中还是FLASH中,他们都是存放在一块连续的存储空间里的。  有一点要特别注意,这个数组一共有16(中括号里面的数值)个元素,但是数组的单个元素的表达方式――下标是从0开始,因此实际上上边这个数组的首个元素LedChar[0]的值是0xC0,而LedChar[15]的值是0x8E,下标从0到15一共是16个元素。  LedChar这个数组只有一个下标,我们称之为一维数组,还有两个下标和多个下标的,我们称之为二维数组和多维数组。比如unsigned char a[2][3];表示这是一个2行3列的二维数组。在大多数情况下我们使用的是一维数组,对于初学来说,我们先来研究一维数组,多维数组等遇到了再来了解。  1.1.2 数组的声明   一维数组的声明格式如下:  数据类型 数组名 [数组长度];  1、数组的数据类型声明的是该数组的每个元素的类型,即一个数组中的元素具有相同的数据类型。  2、数组名的声明要符合C语言固定的标识符的声明要求,只能由字母、数字、下划线这三种符号组成,且第一个字符只能是字母或者下划线。  3、方括号中的数组长度是一个常量或常量表达式,并且必须是正整数。  1.1.3 数组的初始化   数组在进行声明的同时可以进行初始化操作,格式如下:  数据类型 数组名 [数组长度] = {初值列表};  还是以上节课我们用的数码管的真值表为例来讲解注意事项。  unsigned char LedChar[16] = {  0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,  0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E  };   1、初值列表里的数据之间要用逗号隔开;  2、初值列表里的初值的数量必须等于或小于数组长度,当小于数组长度时,数组的后边没有赋初值的元素由系统自动赋值为0。  3、若给数组的所有元素都赋初值,那么可以省略数组的长度,上节课的例子中我们实际上已经省略了数组的长度。  4、系统为数组分配连续的存储单元的时候,数组元素的相对次序由下标来决定,就是说LedChar[0]、LedChar[1]……LedChar[15]是按照顺序紧挨着依次排下来的。  1.1.4 数组的使用和赋值   在C语言程序中,是不能一次使用整个数组的,只能使用数组的单个元素。一个数组元素相当于一个变量,使用数组元素的时候与使用相同数据类型的变量的方法是一样的。比如LedChar这个数组,如果没加code关键字,那么它可读可写,我们可以写成a = LedChar[0]这样来把数组的一个元素的值送个a这个变量,也可以写成LedChar[0] = a这样把a这个变量的值送给数组中的一个元素,以下三点要注意:  1、引用数组的时候,那个方括号里的数字代表的是数组元素的下标,而数组初始化的时候方括号里的数字代表的是这个数组中元素的总数。  2、数组元素的方括号里的下标可以是整型常数,整型变量或者表达式,而数组初始化的时候方括号里的数字必须是常数不能是变量。  3、数组整体赋值只能在初始化的时候进行,程序执行代码中只能对单个元素赋值。  1.2 if语句   到目前为止,我们对if语句应该已经不陌生了,前边程序已用过多次了,这里我们系统的介绍一下,方便后边的深入学习。if语句有两个关键字:if和else,把这两个关键字翻译一下就是:“如果”和“否则”。if语句一共有三种格式,我们分别来看。  1、if语句的默认形式:  if (条件表达式)  {  语句1;  }  其执行过程是,if(即如果)条件表达式的值为“真”,则执行语句1;如果条件表达式的值为“假”,则不执行语句1。真和假的概念不再赘述,参考第五章。  这里要提醒大家一点,C语言一个分号表示一条语句的结束,因此如果if后边只有一条执行语句的时候,可以省略大括号,但是如果有多条执行语句的话,必须加上大括号。  那么现在,我们上节课的语句就很好理解了:  if (sec &= 16)  {  sec = 0;  }  当sec的值大于或等于16的时候,括号里的值才是“真”,那么就执行sec=0这一句,当sec的值小于16时,那么括号里就为“假”,就不执行这一句。  2、if...else语句  有些情况下,我们除了要在括号里条件满足时执行相应的语句外,在不满足该条件的时候,也要执行一些另外的语句,这时候就用到了if...else语句,它的基本语法形式是:  if (条件表达式)  {  语句1;  }  else  {  语句2;  }  比如上节课的最后一段程序我们也可以写成:  P0 = LedChar[sec];  if (sec &= 15)  {  sec = 0;  }  else  {  Sec++;  }  这个程序大家可以修改下载到单片机里验证一下,程序逻辑大家自己动脑筋分析,注意条件表达式内16到15的变化,想一下为什么,我就不多解释了。  3、if....else if语句  if...esle语句是一个二选一的语句,或者执行if分支后的语句,或者执行else分支后的语句。还有一种多选一的用法就是if...else if语句。他的基本语法格式是:  if (条件表达式1) {语句1;}  else if (条件表达式2) {语句2;}  else if (条件表达式3) {语句3;}  ... ...  else {语句n;}  他的执行过程是:依次判断条件表达式的值,当出现某个值为“真”时,则执行相对应的语句,然后跳出整个if的语句块,执行“语句n”后面的程序;如果所有的表达式都为“假”,则执行else分支的“语句n”后,再执行“语句n”后边的程序。  if语句在C语言编程中使用频率很高,用法也不复杂,所以必须要熟练掌握。  1.3 switch语句   用if....else语句在处理多分支的时候,分支太多就会显得不方便,且容易出现if和else配对出现错误的情况,在C语言中提供了另外一种多分支选择的语句――switch语句,它的基本语法格式如下:  switch (表达式)  {  case 常量表达式1: 语句1;  case 常量表达式2: 语句2;  ......  case 常量表达式n: 语句n;  default: 语句n+1;  }  它的执行过程是:首先计算“表达式”的值,然后从第一个case开始,与“常量表达式x”进行比较,如果与当前常量表达式的值不相等,那么就不执行冒号后边的语句x,一旦发现和某个常量表达式的值相等了,那么它会执行之后所有的语句,如果直到最后一个“常量表达式n”都没有找到相等的值,那么就执行default后的“语句n+1”。请特别注意一点,当找到一个相等的case分支后,会执行该分支以及之后所有分支的语句,很明显这不是我们想要的结果。  在C语言中,有一条break语句,作用是跳出当前的循环语句,包括for循环和while循环,同时,它还能用来结束switch语句块。switch的分支语句一共有n+1种,而我们通常希望的都是选择其中的一个分支来执行,执行完后就结束整个switch语句,而继续执行switch后面的语句,此时就可以通过在每个分支后加上break语句来实现了。如下:  switch (表达式)  {  case 常量表达式1: 语句1;  case 常量表达式2: 语句2;  ......  case 常量表达式n: 语句n;  default: 语句n+1;  }  加了这个break语句后,一旦“常量表达式x”与“表达式”的值相等了,那么就执行“语句x”,执行完毕后,由于有了break则直接跳出switch语句,继续执行switch语句后面的程序了,这样就可以避免执行不必要的语句。了解了这个switch语句后,我们马上会在本章程序中使用巩固它。  1.4 数码管的动态显示 1.4.1 动态显示的基本原理   我们在上一章学习数码管静态显示的时候说到,74HC138只能在同一时刻导通一个,而我们的数码管是靠了6个三极管来控制,那我们如何来让数码管同时显示呢?这就用到了动态显示的概念。  多个数码管显示数字的时候,我们实际上是轮流点亮数码管(一个时刻内只有一个数码管是亮的),利用人眼的视觉暂留现象(也叫余辉效应),就可以做到看起来是所有数码管都同时亮了,这就是动态显示,也叫做动态扫描。  例如:有2个数码管,我们要显示“12”这个数字,先让高位的位选三极管导通,然后控制段选让其显示“1”,延时一定时间后再让低位的位选三极管导通,然后控制段选让其显示“2”。把这个流程以一定的速度循环运行就可以让数码管显示出“12”,由于交替速度非常快,人眼识别到的就是“12”这两位数字同时亮了。  那么一个数码管需要点亮多长时间呢?也就是说要多长时间完成一次全部数码管的扫描呢(很明显:整体扫描时间=单个数码管点亮时间*数码管个数)?答案是:10ms以内。当电视机和显示器还处在CRT(电子显像管)时代的时候,有一句很流行的广告语――“100Hz无闪烁”,没错,只要刷新率大于100Hz,即刷新时间小于10ms,就可以做到无闪烁,这也就是我们的动态扫描的硬性指标。那么你也许会问,有最小值的限制吗?理论上没有,但实际上做到更快的刷新却没有任何进步的意义了,因为已经无闪烁了,再快也还是无闪烁,只是徒然增加CPU的负荷而已(因为1秒内要执行更多次的扫描程序)。所以,通常我们设计程序的时候,都是取一个接近10ms,又比较规整的值就行了。我们开发板上有6个数码管,那么我们现在就来着手写一个数码管动态扫描的程序,实现兼验证上面讲的动态显示原理。  我们的目标还是实现秒表功能,只不过这次有6个位了,最大可以计到999999秒。那么现在要实现的这个程序相对于前几章的例程来说就要复杂的多了,既要处理秒表计数,又要处理动态扫描。在编写这类稍复杂的程序时,建议初学者们先用程序流程图来把程序的整个流程理清,在动手写程序之前先把整个程序的结构框架搭好,把每一个环节要实现的功能先细化出来,然后再用程序代码一步一步的去实现出来。这样就可以避免无处下笔的迷茫感了。如图6-1就是本例的程序流程图,大家先根据流程图把程序的执行经过在大脑里走一遍,然后再看接下来的程序代码,体会一下流程图的作用,看是不是能帮助你更顺畅的理清程序流程。    图6-1 数码管动态显示秒表程序流程图  #include   sbit ADDR0 = P1^0;  sbit ADDR1 = P1^1;  sbit ADDR2 = P1^2;  sbit ADDR3 = P1^3;  sbit EN= 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, 0xFF  };  void main()  {  unsigned char i = 0; //动态扫描的索引  unsigned int cnt = 0; //记录T0中断次数  unsigned long sec = 0; //记录经过的秒数  ENLED = 0; //使能U3,选择控制数码管  ADDR3 = 1; //因为需要动态改变ADDR0-2的值,所以不需要再初始化了  TMOD = 0x01; //设置T0为模式1  TH0 = 0xFC; //为T0赋初值0xFC67,定时1ms  TL0 = 0x67;  TR0 = 1; //启动T0  while (1)  {  if (TF0 == 1) //判断T0是否溢出  {  TF0 = 0; //T0溢出后,清零中断标志  TH0 = 0xFC; //并重新赋初值  TL0 = 0x67;  cnt++; //计数值自加1  if (cnt &= 1000) //判断T0溢出是否达到1000次  {  cnt = 0; //达到1000次后计数值清零  sec++; //秒计数自加1  //以下代码将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/];  }  //以下代码完成数码管动态扫描刷新  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]; }  }  }  }  这段程序,大家自己抄到Keil中,然后边抄边结合程序流程图来理解,最终下载到实验板上看一下运行结果。其中下边的if...else语句就是每1ms快速的刷新一个数码管,这样6个数码管整体刷新一遍的时间就是6ms,视觉感官上就是6个数码管同时亮起来了。  在C语言中,“/”等同于数学里的除法运算,而“%”等同于我们小学学的求余数运算,这个前边已有介绍。如果是123456这个数字,我们要正常显示在数码管上,个位显示,就是直接对10取余数,这个“6”就出来了,十位数字就是先除以10,然后再对10取余数,以此类推,就把6个数字全部显示出来了。  对于多选一的动态刷新数码管的方式,我们如果用switch会有更好的效果,大家来看一下我们用switch语句完成的情况。  #include   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, 0xFF  };  void main()  {  unsigned char i = 0; //动态扫描的索引  unsigned int cnt = 0; //记录T0中断次数  unsigned long sec = 0; //记录经过的秒数  ENLED = 0; //使能U3,选择控制数码管  ADDR3 = 1; //因为需要动态改变ADDR0-2的值,所以不需要再初始化了  TMOD = 0x01; //设置T0为模式1  TH0 = 0xFC; //为T0赋初值0xFC67,定时1ms  TL0 = 0x67;  TR0 = 1; //启动T0  while (1)  {  if (TF0 == 1) //判断T0是否溢出  {  TF0 = 0; //T0溢出后,清零中断标志  TH0 = 0xFC; //并重新赋初值  TL0 = 0x67;  cnt++; //计数值自加1  if (cnt &= 1000) //判断T0溢出是否达到1000次  {  cnt = 0; //达到1000次后计数值清零  sec++; //秒计数自加1  //以下代码将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/];  }  //以下代码完成数码管动态扫描刷新  switch (i)  {  case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0];  case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1];  case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2];  case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3];  case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4];  case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5];  default:  }  }  }  }  程序完成的功能是一模一样的,但大家看一下,switch语句是不是比if...else语句显得要整齐清爽呢。  1.4.2 数码管显示消隐   不知道同学们是否发现了,我们的这两个数码管动态显示程序的运行效果似乎并不是那么完美,第一个小问题,大家仔细看,数码管的不应该亮的段,似乎有微微的发亮,这种现象叫做“鬼影”,这个“鬼影”严重影响了我们的视觉效果,我们该如何解决呢?  同学们在今后可能会遇到各种各样的实际问题,可能很多都是我们没有讲过的,遇到问题怎么办呢?大家要相信,你作为初学者,遇到的问题肯定不是第一个遇到的,肯定有前辈已经遇到过相同的或类似的问题,他们一般都会在网上发表各种帖子,各种讨论,所以大家遇到问题,首先就应该形成一个到网上搜索的条件反射,这个问题大家可以到网上搜:“数码管消隐”或者“数码管鬼影解决”,多找相关关键词搜索试试,会搜索也是一种能力。  大家在网上搜了一下会发现,解决这类问题的方法有两个,其中之一是延时,延时之后我们肉眼就可能看不到这个“鬼影”了。但是延时是一个非常拙劣的手段,且不说延时多久能让我们看不到“鬼影”,延时后,我们的数码管亮度会普遍降低。我们解决问题呢,不能只知其然,还要知其所以然,那么我们首先就来弄明白为什么会出现“鬼影”。  “鬼影”的出现,主要是在数码管位选和段选产生的瞬态造成的。举个简单例子,我们在数码管动态显示的那部分程序中,实际上每一个数码管点亮的持续时间是1ms的时间,1ms后进行下个数码管的切换。在进行数码管切换的时候,比如我们从case 5要切换到case 0的时候,case 5的位选用的是ADDR0=1; ADDR1=0; ADDR2=1;假如此刻case 5也就是最高位数码管对应的值是0,我们要切换成的case 0的数码管位选是ADDR0=0; ADDR1=0; ADDR2=0;而对应的数码管的值假如是1。又因为C语言程序是一句一句顺序往下执行的,每一条语句的执行都会占用一定的时间,即使这个时间非常非常短暂。但是当我们把“ADDR0=1”改变成“ADDR0=0”的时候,这个瞬间存在了一个中间状态ADDR0=0; ADDR1=0; ADDR2=1;在这个瞬间上,我们就给case 4对应的数码管DS5瞬间赋值了0。当我们全部写完了ADDR0=0; ADDR1=0; ADDR2=0;后,这个时候,我们的P0还没有正式赋值,而P0此刻却保持了前一次的值,也就是在这个瞬间,我们又给case 0对应的数码管DS1赋值了一个0。直到我们把case 0后边的语句全部完成后,我们的刷新才正式完成。而在这个刷新过程中,有2个瞬间我们给错误的数码管赋了值,虽然很弱(因为亮的时间很短),但是我们还是能够发现。  那么搞明白了原理后,解决起来就不是困难的事情了,我们只要避开这个瞬间错误就可以了。不产生瞬间错误的方法是,在进行位选切换期间,避免一切数码管的赋值即可。方法有两个,一个方法是刷新之前关闭所有的段,改变好了位选后,再打开段即可;第二个方法是关闭数码管的位,赋值过程都做好后,再重新打开即可。这个不是很难,答案我都公布一下。  关闭段:在switch(i)这句程序之前,加一句P0=0xFF;这样就把数码管所有的段都关闭了,当把“ADDR”的值全部搞定后,再给P0赋对应的值即可。  关闭位:在switch(i)这句程序之前,加上一句ENLED=1;等到把ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0];这几条刷新程序全部写完后,再加上一句ENLED=0;然后再进行break操作即可。  这个地方逻辑思路上稍微有点复杂,大家一定要理解深刻,深刻理解,彻底弄明白,把这个瞬间的问题弄明白了,后边很多牵扯到此类情况的问题,我们都可以一并搞定。  上边的数码管程序还有第二个问题,大家仔细看,我们的数码管上的数字每一秒变化一次,变化的时候,不参加变化的数码管可能出现一次,这个抖动没有什么专业的名字,我们就称之为数码管抖动吧。这种数码管抖动是什么原因造成的呢?为何在数据改变的时候才抖动呢?  来分析一下我们的程序,程序在定时到1秒的时候,执行了“秒数+1并转换为数码管显示字符”这个操作,一个32位整型数的除法运算,实际上是比较耗费时间的,至于这一段程序究竟耗费了多少时间,大家可以通过第四章讲的调试方法来看看这段程序运行用了多少时间。由于每次定时到1秒的时候,程序都多运行了这么一段,导致了某个数码管的点亮时间比其他情况下要长一些,总时间就变成了1ms+本段程序运行时间,于此同时,其它的数码管就熄灭了5ms+本段程序运行时间,如果这段程序运行时间非常短,那么可以忽略不计,但很明显,现在这段程序运行时间已经比较长了,以致于严重影响到视觉效果了,所以我们要采取另外一种思路去解决这个问题。  1.5 单片机中断系统 1.5.1 中断的产生背景   请设想这样一个场景:此刻我正在厨房用煤气烧一壶水,而烧开一壶水刚好需要10分钟,我是一个主体,烧水是一个目的,而且我只能时时刻刻在这里烧水,因为一旦水开了,溢出来浇灭煤气的话,有可能引发一场灾难。但就在这个时候呢,我又听到了电视里传来《天龙八部》的主题歌,马上就要开演了,我真想夺门而出,去看我最喜欢的电视剧。然而,听到这个水壶发出的“咕嘟”的声音,我清楚:除非等水烧开了,否则我是无法享受我喜欢的电视剧的。  这里边主体只有一个我,而我要做的有两件事情,一个是看电视,一个是烧水,而电视和烧水是两个独立的客体,它们是同时进行的。其中烧水需要10分钟,但不需要了解烧水的过程,只需要得到水烧开的这样一个结果就行了,提下水壶和关闭煤气只需要几秒的时间而已。所以我们采取的办法就是:烧水的时候,定上一个闹钟,定时10分钟,然后我就可以安心看电视了。当10分钟时间到了,闹钟响了,此刻水也烧开了,我就过去把煤气灭掉,然后继续回来看电视就可以了。  这个场景和单片机有什么关系呢?  在单片机的程序处理过程中也有很多类似的场景,当单片机正在专心致志的做一件事情(看电视)的时候,总会有一件或者多件紧迫或者不紧迫的事情发生,需要我们去关注,有一些需要我们停下手头的工作去马上去处理(比如水开了),只有处理完了,才能回头继续完成刚才的工作(看电视)。这种情况下单片机的中断系统就该发挥它的强大作用了,合理巧妙的利用中断,不仅可以使我们获得处理突发状况的能力,而且可以使单片机能够“同时”完成多项任务。  1.5.2 的应用   在第五章我们学过了定时器,而实际上定时器一般用法都是采取中断方式来做的,我是故意在第五章用查询法,就是使用if(TF0==1)这样的语句先用定时器,目的是明确告诉同学们,定时器和中断不是一回事,定时器是单片机模块的一个资源,确确实实存在的一个模块,而中断,是单片机的一种运行机制。尤其是初学者们,很多人会误以为定时器和中断是一个东西,只有定时器才会触发中断,但实际上很多事件都会触发中断的,除了“烧水”,还有“有人按门铃”,“来电话了”等等。  标准中控制中断的寄存器有两个,一个是中断使能寄存器,另一个是中断优先级寄存器,这里先介绍中断使能寄存器,如表6-1和表6-2所示。随着一些增强型51单片机的问世,可能会有增加的寄存器,大家理解了我们这里所讲的,其它的通过自己研读数据手册就可以理解明白并且用起来了。
  本帖子中包含更多资源   您需要 登录 才可以下载或查看,没有帐号?立即注册  x
  我要点赞 0   收藏1 举报  这个家伙好懒,什么都没留下回复编辑
    金沙滩工作室
  发表于
08:47:22   2#
  表6-1 IE――中断使能寄存器的位分配(地址0xA8、可位寻址)
  复位值
  表6-2 IE――中断使能寄存器的位描述
  总中断使能位,相当于总开关
  中断使能
  串口中断使能
  定时器1中断使能
  外部中断1使能
  定时器0中断使能
  外部中断0使能
  中断使能寄存器IE的位0~5控制了6个中断使能,而第6位没有用到,第7位是总开关。总开关就相当于我们家里或者学生宿舍里的那个总闸门,而0~5位这6个位相当于每个分开关。那么也就是说,我们只要用到中断,就要写EA = 1这一句,打开中断总开关,然后用到哪个分中断,再打开相对应的控制位就可以了。  我们现在就把前面的数码管动态显示的程序改用中断再实现出来,同时数码管显示抖动和“鬼影”也一并处理掉了。程序运行的流程跟图6-1所示的流程图是基本一致的,但因为加入了中断,所以整个流程被分成了两部分,秒计数和转换为数码管显示字符的部分还留在主循环内,而动态扫描部分则移到了中断函数内,并加入了消隐的处理。下面来看程序:  #include   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, 0xFF  };  unsigned char i = 0; //动态扫描的索引  unsigned int cnt = 0; //记录T0中断次数  void main()  {  unsigned long sec = 0; //记录经过的秒数  EA = 1; //使能总中断  ENLED = 0; //使能U3,选择控制数码管  ADDR3 = 1; //因为需要动态改变ADDR0-2的值,所以不需要再初始化了  TMOD = 0x01; //设置T0为模式1  TH0 = 0xFC; //为T0赋初值0xFC67,定时1ms  TL0 = 0x67;  ET0 = 1; //使能T0中断  TR0 = 1; //启动T0  while (1)  {  if (cnt &= 1000) //判断T0溢出是否达到1000次  {  cnt = 0; //达到1000次后计数值清零  sec++; //秒计数自加1  //以下代码将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/];  }  }  }  /* 定时器0中断服务函数 */  void InterruptTimer0() 1  {  TH0 = 0xFC; //重新加载初值  TL0 = 0x67;  cnt++; //中断次数计数值加1  //以下代码完成数码管动态扫描刷新  P0 = 0xFF; //显示消隐  switch (i)  {  case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0];  case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1];  case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2];  case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3];  case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4];  case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5];  default:  }  }  大家可以先把程序抄下来,编译下载到单片机里运行,看看实际效果。是否可以看到,近乎完美的显示效果经过我们的努力终于做成功了。下面我们还要再来解析一下这个程序。  在这个程序中,有两个函数,一个是主函数,一个是中断服务函数。主函数main()我们就不用说了,重点强调一下中断服务函数,它的书写格式是固定的,首先中断函数前边void表示函数返回空,即中断函数不返回任何值,函数名是InterruptTimer0(),这个函数名在符合函数命名规则的前提下可以随便取,我们取这个名字是为了方便区分和记忆,而后是interrupt这个关键字,一定不能错,这是中断特有的关键字,另外后边还有个数字1,这个数字1怎么来的呢?我们先来看表6-3。  表6-3 中断查询序列
  中断  函数编号
  中断名称
  中断  标志位
  中断  使能位
  中断  向量地址
  默认  优先级
  外部中断0
  0x0003
  1(最高)
  T0中断
  0x000B
  外部中断1
  0x0013
  T1中断
  0x001B
  UART中断
  0x0023
  T2中断
  TF2/EXF2
  0x002B
  这个表格同样不需要大家记住,需要的时候过来查就可以了。我们现在看第二行的T0中断,要使能这个中断那么就要把它的中断使能位ET0置1,当它的中断标志位TF0变为1时,就会触发T0中断了,那么这时就应该来执行中断函数了,单片机又怎样找到这个中断函数呢?靠的就是中断向量地址,所以interrupt后面中断函数编号的数字x就是根据中断向量得出的,它的计算方法是x*8+3=向量地址。当然表中都已经给算好放在第一栏了,我们可以直接查出来用就行了。到此为止,中断函数的命名规则我们就都搞清楚了。  中断函数写好后,每当满足中断条件而触发中断后,系统就会自动来调用中断函数。比如我们上面这个程序,平时一直在主程序while(1)的循环中执行,假如程序有100行,当执行到50行时,定时器溢出了,那么单片机就会立刻跑到中断函数中执行中断程序,中断程序执行完毕后再自动返回到刚才的第50行处继续执行下面的程序,这样就保证了动态显示间隔是固定的1ms,不会因为程序执行时间不一致的原因导致数码管显示的抖动了。  1.1.1 中断的优先级   中断优先级的内容,大家先通过我的介绍大概了解一下即可,后边实际应用的时候我们再详细理解。  在讲中断产生背景的时候,我们仅仅讲了看电视和烧水的例子,但是实际生活当中还有更复杂的,比如我正在看电视,这个时候来电话了,我要进入接电话的“中断”程序当中去,就在接电话的同时,听到了水开的声音,水开的“中断”也发生了,我们就必须要放下手上的电话,先把煤气关掉,然后再回来听电话,最后听完了电话再看电视,这里就产生了一个优先级的问题。  还有一种情况,我们在看电视的时候,这个时候听到水开的声音,水开的“中断”发生了,我们要进入关煤气的“中断”程序当中,而在关煤气的同时,电话声音响了,而这个时候,我们的处理方式是先把煤气关闭,再去接听电话,最后再看电视。  从这两个过程中,我们可以得到一个结论,就是最最紧急的事情,一旦发生后,我们不管当时处在哪个“程序”当中,我们必须先去处理最最紧急的事情,处理完毕后再去解决其它事情。在我们的单片机程序当中有时候也是这样的,有一般紧急的中断,有特别紧急的中断,这取决于具体的系统设计,这就涉及到中断优先级和中断嵌套的概念,在本章节我们先简单介绍一下相关寄存器,不做例程说明。  中断优先级有两种,一种是抢占优先级,一种是固有优先级,先介绍抢占优先级。来看表6-4和表6-5。  表6-4 IP――中断优先级寄存器的位分配(地址0xB8、可位寻址)
  复位值
  表6-5 IP――中断优先级寄存器的位描述
  定时器2中断优先级控制位
  串口中断优先级控制位
  定时器1中断优先级控制位
  外部中断1中断优先级控制位
  定时器0中断优先级控制位
  外部中断0中断优先级控制位
  IP这个寄存器的每一位,表示对应中断的抢占优先级,每一位的复位值都是0,当我们把某一位设置为1的时候,这一位的优先级就比其它位的优先级高了。比如我们设置了PT0位为1后,当单片机在主循环或者任何其它中断程序中执行时,一旦定时器T0发生中断,作为更高的优先级,程序马上就会跑到T0的中断程序中来执行。反过来,当单片机正在T0中断程序中执行时,如果有其它中断发生了,还是会继续执行T0中断程序,直到把T0中的中断程序执行完毕以后,才会去执行其它中断程序。  当进入低优先级中断中执行时,如又发生了高优先级的中断,则立刻进入高优先级中断执行,处理完高优先级级中断后,再返回处理低优先级中断,这个过程就叫做中断嵌套,也称为抢占。所以抢占优先级的概念就是,优先级高的中断可以打断优先级低的中断的执行,从而形成嵌套。当然反过来,优先级低的中断是不能打断优先级高的中断的。  那么既然有抢占优先级,自然就也有非抢占优先级了,也称为固有优先级。在表6-3中的最后一列给出的就是固有优先级,请注意,在中断优先级的编号中,一般都是数字越小优先级越高。从表中可以看到一共有1~6共6级的优先级,这里的优先级与抢占优先级的一个不同点就是,它不具有抢占的特性,也就是说即使在低优先级中断执行过程中又发生了高优先级的中断,那么这个高优先级的中断也只能等到低优先级中断执行完后才能得到响应。既然不能抢占,那么这个优先级有什么用呢?  答案是多个中断同时存在时的仲裁。比如说有多个中断同时发生了,当然实际上发生这种情况的概率很低,但另  原文链接:
欢迎举报抄袭、转载、暴力色情及含有欺诈和虚假信息的不良文章。
请先登录再操作
请先登录再操作
微信扫一扫分享至朋友圈
搜狐公众平台官方账号
生活时尚&搭配博主 /生活时尚自媒体 /时尚类书籍作者
搜狐网教育频道官方账号
全球最大华文占星网站-专业研究星座命理及测算服务机构
电子工程世界(EEWorld)是一家专为中国电子工程师和电...
12580文章数
主演:黄晓明/陈乔恩/乔任梁/谢君豪/吕佳容/戚迹
主演:陈晓/陈妍希/张馨予/杨明娜/毛晓彤/孙耀琦
主演:陈键锋/李依晓/张迪/郑亦桐/张明明/何彦霓
主演:尚格?云顿/乔?弗拉尼甘/Bianca Bree
主演:艾斯?库珀/ 查宁?塔图姆/ 乔纳?希尔
baby14岁写真曝光
李冰冰向成龙撒娇争宠
李湘遭闺蜜曝光旧爱
美女模特教老板走秀
曝搬砖男神奇葩择偶观
柳岩被迫成赚钱工具
大屁小P虐心恋
匆匆那年大结局
乔杉遭粉丝骚扰
男闺蜜的尴尬初夜
客服热线:86-10-
客服邮箱:

我要回帖

更多关于 数码管单个数字闪烁 的文章

 

随机推荐