资源描述
中断是单片机系统的重点中的重点,因为有了中断,单片机6.1 C语言的数组6.1.1数组的差不多概念我们第四章学过变量的差不多类型,比如char、int等等。这种类型描述的数据 是比较有限的,当我们要处理特别大量数据的时候,就能够用到数组了,比如我 们上节课的那个数码管的真值表,我们就能够用一个数组来表达。从概念上讲,数组是具有相同数据类型的有序数据的组合,一般来讲,数组定义 后满足以下三个条件。(1) 具有相同的数据类型;(2) 具有相同的名字;(3) 在存储器中是被连续存放的。比如我们上节课定义的那个数码管真值表,假如我们把关键字code去掉, 数组元素将被保存在 RAM 中,在程序中可读可写,同时我们也能够在中括号里边 标明那个数组元素的个数,比如:unsigned char LedChar16 = 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8, 0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e; 在那个数组中的每个值都称之为数组的一个元素,这些元素都具备相同的数据类 型确实是unsigned char型,他们有一个共同的名字LedChar,不管放到RAM中 依旧FLASH中,他们基本上存放在一块连续的存储空间里的。有一点要特别注意,那个数组一共有 16中括号里面的数值个元素,然而数 组的元素的表达方式下标是从 0 开始,因此实际上上边那个数组的首个元素LedChar0的值是OxCO,而LedChar15的值是0x8e,下标从0到15 共是16 个元素。LedChar那个数组只有一个下标,我们称之为一维数组,还有两个下标或者多个 下标的,我们称之为多维数组。比如unsigned char a23;表示这是一个2 行3 列的二维数组。在大多数情况下我们使用的是一维数组,关于初学来说,我 们先来研究一维数组,多维数组遇到了再了解。6.1.2 数组的声明一维数组的声明格式如下:数据类型 数组名数组长度;(1) 数组的数据类型声明的是该数组的每个元素的类型,即一个数组中的元素具 有相同的数据类型。(2) 数组名的声明要符合C语言固定的标识符的声明要求,只能由字母、数字、 下划线这三种符号组成,且第一个字符只能是字母或者下划线。(3) 方括号中的数组长度是一个常量或常量表达式,同时必须是正整数。6.1.3 数组的初始化 数组在进行声明的同时能够进行初始化操作,格式如下: 数据类型 数组名数组长度 = 初值列表; 依旧以上节课我们用的数码管的真值表为例来讲解考前须知。unsigned char LedChar16 = 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8, 0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e;(1) 初值列表里的数据之间要用逗号隔开。(2) 初值列表里的初值的数量必须小于或者等于数组长度,当小于数组长度时, 数组的后边没有赋初值的元素由系统自动赋值 0。(3) 假设给数组的所有元素赋初值,能够省略数组的长度,上节课的例子中我们 实际上差不多省略了数组的长度。(4) 系统为数组分配连续的存储单元的时候,数组元素的相对次序由下标来决定, 确实是说LedCharO、LedCharl LedChar15是按照顺序排下来的。6.1.4 数组的使用和赋值在C语言程序中,是不能一次使用整个数组的,只能使用单个数组元素。一个数 组元素相当于一个变量,使用数组元素的时候与使用相同数据类型的变量的方法 一样。比如那个LedChar那个数组,假如没加code关键字,那么它可读可写, 我们能够写成a = LedChar0如此来把数组的一个元素的值送个a那个变量, 也能够写成LedChar0 = a如此把a那个变量的值送给数组的一个元素,以下 * I* 111: , . 、八三点要注意:(1) 引用数组的时候,那个方括号里的数字代表的是数组元素的下标,而数组初 始化的时候方括号里的数字代表的是那个数组元素的个数。(2) 数组元素的方括号里的下标能够是整型常数,整型变量或者表达式,而数组 初始化的时候方括号里的数字必须是常数不能是变量。(3) 数组整体赋值只能够在初始化的时候操作,功能程序只能对单个元素赋值。6.2 if 语句if 语句差不多不陌生了,前边程序我们事实上差不多用过了,那个地方我们系 统的介绍一下,方便后边的深入学习。if语句有两个关键字:if和else,把这 两个关键字翻译一下确实是:“假如”和“否那么”。 if 语句一共有三种格式, 我们分别来看。1.if语句的默认形式。if ( 条件表达式 )语句 1;其执行过程是,if(假如)条件表达式的值为“真”,那么执行语句1;假 如条件表达式的值为“假”,那么不执行语句1。真和假的概念不再赘述,参考 第五章。那个地方要提醒一句,C语言一个分号表示一句语句的结束,因此假如if 后边只有一条执行语句的时候,能够省略大括号,然而假如有多条执行语句的话, 必须加上大括号。我们上节课的语句就很好理解了 if(16 =j) j = 0;,假如 j 等于 16 的时候,括号里的值才是“真”,那么就执行j=0这一句,假如j不等于16, 那么里边就为“假”,就不执行这一句。2.if.else 语句有些情况下,我们除了判断一下if括号里的是否满足条件,执行相应的 语句,在不满足条件的时候,我们又要执行另外相应的语句,那个时候就用到了 if.else语句,它的差不多的语法形式是:if (条件表达式)语句 1;else语句 2; 比如上节课的后半段程序我们也能够写成:P0if(15 = j)/ 当显示到 F 后,归0 重新开始j = 0; else j+; 那个程序大伙能够改改下载进去试试,程序逻辑大伙自己动脑分析一下, 我就不解释了。3.ifelse if 语句if.esle 语句是一个二选一的语句,或者执行 if 条件下的语句,或者 执行 else 条件下的语句。还有一种多项选择一的用法确实是 if.else if 语句。 他的差不多语法格式是:if (条件表达式 1)语句 1;else if (条件表达式 2)语句 2; else if (条件表达式 3)语句 3; else语句 n;他的执行过程是:依次判断条件表达式的值,当出现某个值为“真”时,那么执 行相对应的语句,然后跳出整个if的语句块,执行“语句n”后边的程序;假 如所有的表达式都为“假”,那么执行“语句n”后,再执行“语句n”后边的 程序。if语句在C语言编程的过程中使用频率很高,用法也简单,因此必须要熟练掌 握。6.3 switch 语句用 ifelse 语句在处理多分支的时候,分支太多就会显得不方便,且容易出现if和else配对出现错误的情况,在C语言中提供了另外一种多分支选择的语 句switch语句,它的差不多语法格式如下:switch (表达式)case 常量表达式 1:执行语句 1;case 常量表达式 2:执行语句 2; case常量表达式n:执行语句n;default: 执行语句 n+1;它的执行过程是:首先计算“表达式”的值,然后从第一个case开始,与“常 量表达式x”进行比较,假如与当前常量表达式的值不相等,那么就不执行冒号 后边的程序,一旦发明和一个常量表达式的值相等了,那么他会执行之后所有的, 注意是所有的“执行语句”,显然这不是我们想要的结果。在C语言中,有一条break语句,作用是跳出当前循环语句,不管是for和while 循环,依旧switch循环,都能够用其搭配使用跳出循环。switch语句一共有n+1 种可能,而我们盼望要的是一条多项选择一的语句,只执行其中一条然后直截了当退出该循环,不再执行下边的任何语句,那个时候就需要用到 break 语句,比 如我们在 switch 表达式上加上 break 语句,如下:switch (表达式)case 常量表达式 1 :执行语句 1 ; break ;case 常量表达式 2 :执行语句 2 ; break ; case 常量表达式 n :执行语句 n ; break ;default: 语句 n+1;加了那个break语句后,一旦“常量表达式x”与“表达式”相等了,那就执行 “执行语句x,执行完毕后,由于有了break,直截了当跳出switch语句,执 行switch语句循环后边的程序了,如此就能够幸免执行不必要的语句。了解了 那个switch语句,我们将会在本章程序中使用巩固。6.4 数码管的动态显示 6.4.1 动态显示的差不多原理 我们在上一章学习数码管静态显示的时候说到, 74HC138 只能在同一时刻导通一 个三极管,而我们的数码管是靠了 6 个三极管来操纵,那我们如何来让数码管同 时显示呢?这就用到了我们这节课的动态显示。多个数码管显示数字的时候,我们实际上是轮流点亮数码管一个时刻内只有一 个数码管是亮的,利用人眼的视觉暂留现象也叫余辉效应,就能够做到看 起来是所有数码管都同时亮了,这确实是动态扫描显示的含义。例如:我们有2个数码管,我们要显示“12”那个数字,让高位的位选三极管导 通,然后给它赋值“1”,延时一定时间后让低位的位选三极管导通,然后给它 赋值“2”。把那个流程以一定的速度循环运行就能够让数码管显示出“12”, 由于交替速度特别快,人肉眼识别到的确实是“ 12”那个数字。那么一个数码管需要点亮多长时间呢?也确实是说要多长时间完成一次全部数 码管的扫描呢很明显:整体扫描时间=单个数码管点亮时间*数码管个数?答 案是:10ms以内。当电视机和显示器还处在CRT电子显像管时代时,有一句 很流行的广告语一一“100Hz无闪烁”,没错,只要刷新率大于100Hz,即刷新 时间小于10ms,就能够做到无闪烁,这也确实是我们的动态扫描的硬性指标。那么你也许会问,有最小值的限制吗?理论上没有,但实际上做到更快的刷新却 没有任何进步的意义了,因为差不多无闪烁了,再快也依旧无闪烁,只是徒然增 加CPU的负荷而已因为1秒内要执行更多次的扫描程序。因此,通常我们设 计程序的时候,基本上取一个接近10ms,又比较规整的值就行了。我们板子上 有 6 个数码管,我们下面用程序来验证一下数码管动态显示程序。#include /包含寄存器的库文件sbitADDR0 = P0;sbitADDR1 = P1;sbitADDR2 = P2;sbitADDR3 = P3;sbitENLED = P4;unsigned char code LedChar = /用数组来表示数码管真值表0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e,;void main()unsigned int counter = 0;unsigned char j = 0;unsigned long stopwatch = 0;unsigned char LedNumber6 = 0;ENLED = 0; ADDR3 = 1;P0 = 0XFF; /74HC138 和 P0 初始化部分 TMOD = 0x01; /设置定时器 0为模式 1TH0= 0xFC;TL0= 0x67; /定时值初值,定时 1msTR0 = 1;/ 打开定时器 0while(1) /判断定时器 0 是否溢出if(1 = TF0)TF0 = 0;TH0 = 0xFC;TL0 = 0x67; counter+; if(1000 = counter) 达到50 次counter = 0; stopwatch+; /秒表数值一秒加 1LedNumber0LedNumber1LedNumber2LedNumber3 管显示值计算LedNumber4LedNumber5 if (0=j) ADDR0=0; ADDR1=0;P0=LedCharLedNumber0; else if (1=j) ADDR0=1; ADDR1=0;P0=LedCharLedNumber1; else if (2=j) ADDR0=0; ADDR1=1;/一旦溢出后,重新赋值/判断定时器 0 溢出是否/= stopwatch%10;= stopwatch/10%10;= stopwatch/100%10;= stopwatch/1000%10;/ 数码= stopwatch/10000%10;= stopwatch/100000%10;ADDR2=0; j+;ADDR2=0; j+;ADDR2=0; j+;P0=LedCharLedNumber2; else if (3=j) ADDR0=1; ADDR1=1; ADDR2=0; j+;P0=LedCharLedNumber3; else if (4=j) ADDR0=0; ADDR1=0; ADDR2=1; j+;P0=LedCharLedNumber4; else if (5=j) ADDR0=1; ADDR1=0; ADDR2=1; j=0;P0=LedCharLedNumber5; / 数码管动态刷新部分这程序,大伙自己抄到Keil中,然后边抄边理解,最终下载到实验板上实验一 下效果。其中下边的if.else语句确实是每1ms快速的刷新一个数码管,如此 6个数码管整体刷新一遍的时间确实是6ms,视觉上确实是6个数码管无闪烁的 同时亮起来了。另外一个简单知识点那个地方也提一下,事实上属于小学三年级知识,然而许多 同学刚接触C语言,可能遇到了也会发懵。确实是在数码管显示值计算那个地方, 相信小学我们没学小数之前,除法运算里边有“被除数”、“除数”、“商”、“余数”这四个概念年。而在我们C语言中,“/”等同于数学里的除法运算, 而“%”等同于我们小学学的求余数运算。假如是123456那个数字,我们要正常 显示在数码管上,个位显示,确实是直截了当对10取余数,那个“6”就出来了, 十位数字确实是先除以10,然后再对10取余数,以此类推,就把6个数字全部 显示出来了。关于多项选择一的动态刷新数码管的方式,我们假如用 switch 会有更好的效果, 大伙来看一下我们用switch语句完成的情况。#include /包含寄存器的库文件sbitADDRO = P0;sbitADDR1 = Pl;sbitADDR2 = P2;sbitADDR3 = P3;sbitENLED = P4;unsigned char code LedChar = /用数组来表示数码管真值表0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e,;void main()unsigned int counter = 0;unsigned char j = 0;unsigned long stopwatch =0;unsigned char LedNumber6=0;ENLED = 0; ADDR3 = 1;P0 = 0XFF; /74HC138 和 P0 初始化部分TMOD =TH0TL0TR0 while(1) if(1 = TF0) TF0 = 0; TH0 = 0xFC; TL0 = 0x67; counter+; if(1000 = counter) 达到1000 次counter = 0; stopwatch+; /秒表数值一秒加 1LedNumber0 LedNumber1 LedNumber2 LedNumber3 管显示值计算LedNumber4 LedNumber5 switch(j) case 0: ADDR0=0;P0=LedCharLedNumber0;break;case 1: ADDR0=1;P0=LedCharLedNumber1;break;case 2: ADDR0=0;P0=LedCharLedNumber2;break;case 3: ADDR0=1;P0=LedCharLedNumber3;break;case 4: ADDR0=0;P0=LedCharLedNumber4;break;case 5: ADDR0=1;P0=LedCharLedNumber5;break; default: break;0x01; /设置定时器 0为模式 1 0xFC;0x67; /定时值初值,定时 1ms 1; /打开定时器 0/ 判断定时器 0 是否溢出/一旦溢出后,重新赋值/判断定时器 0 溢出是否/= stopwatch%10;= stopwatch/10%10;= stopwatch/100%10;= stopwatch/1000%10;/ 数码= stopwatch/10000%10;= stopwatch/100000%10;ADDR1=0; ADDR2=0; j+;ADDR1=0; ADDR2=0; j+;ADDR1=1; ADDR2=0; j+;ADDR1=1; ADDR2=0; j+;ADDR1=0; ADDR2=1; j+;ADDR1=0; ADDR2=1; j=0;/数码管动态刷新部分大伙是否能感受到switch语句比if.else语句显得要整齐的多?6.4.2 数码管消隐处理 不明白细心的同学能否发明,我们的两次数码管动态刷新显示的时候大概并不是 那么完美,第一个小问题,大伙认真看,数码管的不应该显示的段,大概有微微 的发亮,这种现象叫做“鬼影”,那个“鬼影”严峻妨碍了我们的视觉效果,我 们该如何解决呢?同 学们今后可能会遇到各种各样的问题,可能有许多我是没有讲过的问题,遇 到问题如何办呢?大伙要相信,你作为初学者,遇到的问题确信不是第一个遇到 的,确信 有前辈会遇到同类问题,他们一般会在网上发表各种帖子,各种讨论, 因此大伙遇到问题,首先解决方法就应该形成一个到网上搜索的条件反射,那个 问题大伙能够 到网上搜:“数码管消隐”或者“数码管鬼影解决”,多找相关 关键词搜索,会搜索也是一种能力。大伙在网 上搜了一下会发明,解决这类问题的普遍两个方法,其中之一是延时, 延时之后我们肉眼就可能看不到那个“鬼影”了。然而延时是一个特别拙劣的手 段,且不说延 时多久能让我们看不到“鬼影”,延时后,我们的数码管亮度会 普遍降低。我们解决问题呢,不能只知其然,不知其因此然,因此我们首先要弄 懂什么原因会出现“鬼 影”。“鬼影”的出现,要紧是因为我们数码管位选和段选产生的瞬态所造成的。举个 简单例子,我们在数码管动态刷新的那部分程序中,实际上每一个数码管点亮的 持续时间是1ms的时间,1ms后进行下个数码管的切换。在进行数码管切换的时 候,比如我们从case 5要切换到case 0的时候,case 5的位选用的是ADDR0=1;ADDR1=0; ADDR2=1;假如此刻case5也确实是最高位数码管对应的值是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次瞬间我们给了错误的数码管赋值,尽 管很弱因为亮的时间很短,然而我们依旧能够发明。那 弄懂了原理后,解决起来就不是困难的情况了,我们只要避开那个瞬态就能 够了。不产生瞬态的方法是,我们在进行刷新的赋值语句期间,幸免一切数码管 的赋值即 可。方法有两个,一个方法是刷新之前关闭所有的段,改变好了位选 后,再打开段即可;第二个方法是关闭数码管的位,赋值过程都做好后,再重新 打开即可。那个 不是很难,答案我都公布一下。关闭段:在swi tchj)这句程序之前,加一句P0=0XFF;如此就把数码管所有的 段都关闭了,当把“ADDR”的值全部搞定后,再给P0赋对应的值即可。关闭位:在switch(j)这句程序之前,加上一句ENLED=1 ;等到把“ADDR=0; ADDR1=0; ADDR2=0; P0=LedCharLedNumber0; 这几条刷新程序全部写完后,再加上一句 ENLED=0;然后再进行break操作即可。那个地方略微有点逻辑思路在里边,大伙一定要理解深刻,深刻理解,完全弄明 白,把那个瞬态弄明白,后边许多牵扯到此类情况的问题,我们都能够一并搞定。 上边的数码管程序还有第二个问题,大伙认真看,我们的数码管上的数字每一秒 变化一次,变化的时候,不参加变化的数码管可能出现一次抖动,那个抖动没有 什么专业的名字,我们就称之为数码管抖动吧。这种数码管抖动是什么缘故造成 的呢?为何在数据改变的时候才抖动呢? 我们来看我们的程序。我们的程序在定时到 1 秒的时候,执行了“数码管显示值 计算”那个过程,一个 32位的除法运算,实际上是比较耗费时间的,至于这一 段程序占用了多少时间,大伙能够通过第四章讲的Debug进入看看这段程序运行 一共占据了多少时间。由于达到1秒的时候,程序多运行了这么一段,导致了某 个数码管的点亮时间比其他情况下要长一些,时间是1ms+程序消耗时间,于此 同时,其它的数码管就熄灭了 5ms+程序消耗时间,假如那个程序消耗时间特别 短,那么能够忽略不计,但很明显,现在这段程序差不多比较长了,严峻妨碍我 们的视觉效果了,因此我们要采取另外一种思路去解决那个问题。6.5 中断的学习 6.5.1 中断的产生背景 比如此刻我正在厨房用煤气烧一壶水,烧开一壶水刚好需要10分钟。我是一个 主体,烧水是一个目的,而且我只能时 时刻刻在那个地方烧水,因为一旦水开 了,溢出来浇灭煤气的话,有可能引发一场灾难。而那个时候呢,我听到了电视 里传来天龙八部的主题歌,马上就要开演了, 我真想夺门而出,去看我最 喜爱的电视剧。然而,听到那个水壶发出的“咕嘟”的声音,我清晰:除非水开 了,否那么我是无法享受我喜爱的电视剧的。那个地方边主体只有我一个,而我要做的有两件情况,一个是看电视,一个是烧 水,而电视和烧水是两个独立的客体,他们是同时进行的。其中烧水需要10分 钟,但不需要了解烧水的过程的,只需要得到水烧开的如此一个结果就行了,提 下水壶和关闭煤气只需要几秒的时间而已。因此我们采取的方法确实是:烧水的 时候,定上一个闹钟,定时10分钟,然后我就能够安心看电视了。当10分钟时 间到了,闹钟响了,此刻水也烧开了,我就过去把煤气灭掉,然后接着回来看电 视就能够了。那个场景和单片机有什么关系呢? 在单片机的程序处理过程中也有许多类似的场景,当单片机正在用心致志的做一 件情况的时候(如看电视),总会有一件或者多件紧迫或者不紧迫的情况发生,需 要我们去关注,有一些需要我们停下手头的工作去马上完成(比如水开了),只有 处理完,才能回头接着完成刚才的工作(看电视)。假如在那个地方用上了单片机 的中断机制,不仅仅我拥有了处理意外情况的能力,而且假如我能够充分发挥那 个机制的妙用,就能够“同时”完成多个任务了。假如依旧一知半解关于中断更6.5.2 定时器中断应用方法 在第五章我们学过定时器,而实际上定时器一般用法基本上采取中断方式来做 的,我是有意在第五章用查询法,确实是使用 if(TR0 =0)这 样的语句先讲定 时器,目的是明确告诉同学们,定时器和中断不是一回事,定时器是单片机模块 的一个资源,确真的实存在的一个模块,而中断,是单片机的一种运 行机制。 尤其是初学者们,许多人会误以为定时器和中断是一个东西,只有定时器才会触 发中断,但实际上许多事件都会触发中断的,除了“烧水”,还有“有人按 门 铃”,“来标准51中与中断相关的寄存器,一共有2个,其中1个是中断使能 寄存器,另外 1 个是中断优先级寄存器,那个地方先介绍中断使能寄存器。随着一些增强型51 单片机的问世,可能会有增加的寄存器,大伙这些理解了那个地方所讲的,其他的通过自己研读数据手册全部能够理解明白同时使用起来。可位寻址;复位值:0x00;复位源:任何复位位76543210符号EA-ET2ESET1EX1ET0EX0表6-2 IE-中断使能寄存器的位描述位符号描述7EA总中断使能位,相当于总开关65ET2定时器 2 中断使能4ES串口中断使能3ET1定时器 1 溢出中断使能2EX1外部中断 1 使能1ET0定时器 0 中断使能0EX0外部中断 0 使能中断使能寄存器IE操纵了 6个中断使能,其中第6位临时不用,第七位是总开 关,相当于我们家里或者学生宿舍里的那个电源总闸门。而0到5位这6个相当 于每个分开关。那么也确实是说,我们只要用到中断,就要写 EA = 1 这一句, 打开中断总开关,然后用到哪个分中断,再打开相对应的位就能够了。我们现在就把第五章学的定时器的程序进行改写,使用中断实现出来,把数码管的抖动问题也同时一并处理掉。#include 件/包含寄存器的库文sbitADDR0 = P0;sbitADDR1 = Pl;sbitADDR2 = P2;sbitADDR3 = P3;sbitENLED = P4;unsigned char code LedChar = /用数组来表示数码管真值表0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e,;unsigned char LedNumber6 = 0; /定义全局变量unsigned char j = 0;unsigned int counter = 0;void main()unsigned long stopwatch =0;ENLED = 0; ADDR3 = 1; P0 = 0XFF; /74HC138 和 P0 初始化部分TMOD = 0x01;/设置定时器 0 为模式 1TH0=0xFC;TL0=0x67;/定时值初值,定时 1msTR0=1;/打开定时器 0/打开中中断/打开定时器 0 中断EA = 1; ET0 = 1; while(1) if(1000 = counter)/判断定时器 0 溢出是否达到1000次counter = 0; stopwatch+;LedNumber0 = stopwatch%10; LedNumber1 = stopwatch/10%10;LedNumber2 = stopwatch/100%10; LedNumber3 = stopwatch/1000%10;LedNumber4 = stopwatch/10000%10; LedNumber5 = stopwatch/100000%10;void InterruptTimer0() interrupt 1/中断函数的特别写法,数字1为中断入口号TH0 = 0xFC;/ 溢出后进入中断重新赋值TL0 = 0x67;counter+;/ 计数值 counter 加 1P0 = 0xFF;/ 消隐switch(j)case 0: ADDR0=0; ADDR1=0; ADDR2=0; j+; P0=LedCharLedNumber0; break;case 1: ADDR0=1; ADDR1=0; ADDR2=0; j+; P0=LedCharLedNumber1; break;case 2: ADDR0=0; ADDR1=1; ADDR2=0; j+; P0=LedCharLedNumber2; break;case 3: ADDR0=1; ADDR1=1; ADDR2=0; j+; P0=LedCharLedNumber3; break;case 4: ADDR0=0; ADDR1=0; ADDR2=1; j+; P0=LedCharLedNumber4; break;case 5: ADDR0=1; ADDR1=0; ADDR2=1; j=0;P0=LedCharLedNumber5; break;default: break;/ 动态刷新大伙能够先把那个程序了解明白,下载到单片机里边实验一下,看看实际效果。 是否能够看出来,近乎完美的显示效果通过我们的努力终于做成功了。那下面我 们还要来解析一下我们的那个程序。在我们那个程序中,有两个函数,一个是主函数,一个是中断函数。主函数 main() 我们就不用说了,重点强调一下中断函数,中断函数的格式是固定的,首先中断 函数前边void表示函数返回空,即中断函数不返回任何值,函数名字是InterruptTimerO(),那个函数名字只要符合函数命名规那么的前提下我们就能 够随便起,我如此起名字是为了方便区分和经历,而后是 interrupt 那个关键字 不能错,那个是中断特有的关键字,另外后边还有个数字1,那个数字1如何来的呢?我们先来看一个表格。表 6-3中断查询序列描述中断标志中断使能默认优先级外部中断 0IE00003HEX01(最高)T0 中断TF0000BHET02外部中断 1IE10013HEX13T1 中断TF1001BHET14UART 中断TI/RI0023HES5T2 中断TF2/EXF2002BHET26那个表格同样不需要大伙记住,需要的时候过来查就能够了。我们现在看第二行T0中断,它的中断标志是TF0,也确实是当TF0变成1的时候,就会触发中断。 而在interrupt后边的数字x的计算方法是x*8+3=向量地址,T0的向量地址是 000BH,那么我们能够求得x的值是1。如此那个中断函数名字我们就完全明白 了。中断函数和一般函数有个不一样的地方,一般函数一般是在程序中调用,而中断 函数因为有了中断入口,达到中断条件后,他会自动进入程序执行。比如咱那个 程序,平时一直在主程序while(1)的循环中运行,假如程序有100行,当运行 到了 50行的时候,定时器溢出了,那么CPU就会赶忙跑到中断函数中执行中断 程序,中断程序运行完毕后再自动返回到刚才的第50行处接着运行下面的程序, 如此就保证了动态刷新是固定的1ms时间,可不能因为程序运行时间不一致的缘 故导致数码管的抖动了。6.5.3 中断的优先级 中断优先级的内容,大伙先通过我的介绍大概了解一下即可,后边真正实际应用 的时候我们再详细理解。在讲中断产生背景的时候,我们仅仅讲了看电视和烧水的例子,然而实际生活话 的“中断”程序当中去,就在接电话的同时,听到了水开的声音,水开的“中断” 也发生了,我们要放下手上的电话,先把煤气关掉,然后再回来听电话,最后听 完了电话再看电视,那个地方就产生了一个优先级的问题。还有一种情况,我们在看电视的时候,那个时候听到水开的声音,水开的“中断” 发生了,我们要进入关煤气的“中断”程序当中,而在关煤气的同时,电话声音 响了,而那个时候,我们的处理方式是先把煤气关闭,再去接听电话,最后再看 电视。从这两个过程中,我们能够得到一个结论,确实是最最紧急的情况,一旦发生后, 我们不管当时处在哪个“程序”当中,我们必须先去解决最最紧急的情况,解决 完毕后再去解决其他情况。在我们的单片机程序当中有时候也是如此的,有一般 紧急的中断,有特别紧急的中断,这取决于具体的系统设计,这就牵扯到一个中 断优先级和中断嵌套的概念,在本章节我们先简单介绍一下相关寄存器,不做例 程说明。中断优先级有两种,一种是抢占优先级,一种是固有优先级,先介绍抢占优先级。表6-4IP-中断优先级寄存器的位分配(地址:B8H)可位寻址;复位值:0x00 ;复位源:任何复位位76543210符号-PT2PSPT1PX1PT0PX0表 6-5IP-中断优先级寄存器的位描述(地址: B8H)位符号描述7-保留6-保留5PT2定时器 2 中断优先级操纵位4PS串口中断优先级操纵位3PT1定时器 1 中断优先级操纵位2PX1外部中断 1 中断优先级操纵位1PT0定时器 0 中断优先级操纵位0PX0外部中断 0 中断优先级操纵位那个寄存器的每一位,表示对应的中断功能的优先级,每一位的复位值基本上 0, 当我们把某一位设置为 1 的时候,这一位的优先级就比其他位的优先级高。比如 我们设置了 PT0 位为 1 后,当程序运行在主循环里边,或者任何其他中断程序内 部的时候,一旦定时器0发生中断,作为更高级的优先级,程序马上就会跑到定 时器0 的中断程序中运行。同理,当程序此刻运行在定时器0中断中时,其他低 级的中断发生后,程序依旧会接着运行定时器0中断程序,直到把定时器0中的 中断程序运行完成后,再会去相应其他中断程序。我们在专业的术语中,当进入低级中断以后,发生高级中断,我们先进入高级中 断运行,处理完了高级中断后,返回处理低级中断,低级中断处理完了再返回主 函数,这种叫做中断嵌套。在抢占优先级配置过程中,优先级高的中断是能够抢 占优先级低的中断,形成中断嵌套的,所以,优先级低的是不能抢占优先级高的 中断的。第二种是固有优先级,大伙可能在看表 6-3 中断查询序列里就看到了有一个中断 优先级列表,在那个列表中,中断优先级是从高到低排列的。然而固有优先级和 抢占优先级不同,首先固有优先级可不能形成中断嵌套,也确实是只要当前程序 进入中断执行程序了,其他任何中断来了,都会先执行完了当前的中断再回头响 应的。那那个固有优先级的作用是什么呢?还有一种情况,确实是当中断同时发生,或 者是我们在开中断前,差不多有几个中断标志位置位了,也确实是说我们能够理 解为同时检测到几个中断产生了,那么我们会先相应表 6-3 中的优先级高的中 断,处理完后再来相应优先级低的中断。6.6 作业1、掌握 C 语言的数组的概念、定义和应用。2、掌握if语句和switch语句的用法及区别,编程的时候能够正确选择使用哪 个语句。3、完全理解中断的原理和应用方法,关闭教程自己独立把本章节程序编写完毕 同时下载到实验板上实践。4、大伙尝试修改程序,让我们的数码管只显示有效位,也确实是高位的 0 不显 示。5、大伙改动程序,写一个数码管从 999999倒计时程序,同时改用定时器 1 的中 断来完成,通过写那个程序,熟练掌握定时器和中断的应用。上一课:第五章定时器和数码管下一课:第七章点阵LED的学习文章中的代码及软件请到 51hei 单片机论坛下载附件,对本文如有不理解之处请 在论坛回帖我会一一解答的.论坛地址文章中的代码及软件请到 51hei 单片机论 坛下载附件,对本文如有不理解之处请在论坛回帖我会一一解答的.论坛地址到 论坛回帖里面.盼望大伙多多交流.作业提交到论坛回帖里面.
展开阅读全文