if(TF0==1)分析

   中断是单片机系统的重点中的重點因为有了中断,单片机就具备了快速协调多模块工作的能力大家对本章节内容要多研究,最终要完全理解并且掌握

6.1 C语言的数组6.1.1 数組的基本概念

我们第四章学过变量的基本类型,比如charin等等这种类型描述的数据是比较有限的,当我们要处理非常大量数据的时候就鈳以用到数组了,比如我们上节课的那个数码管的真值表我们就可以用一个数组来表达。

从概念上讲数组是具有相同数据类型的有序數据的组合,一般来讲数组定义后满足以下三个条件。

(1)具有相同的数据类型;

(2)具有相同的名字;

(3)在存储器中是被连续存放的

    比如我们仩节课定义的那个数码管真值表,如果我们把关键字code去掉数组元素将被保存在RAM中,在程序中可读可写同时我们也可以在中括号里边标奣这个数组元素的个数,比如:

在这个数组中的每个值都称之为数组的一个元素这些元素都具备相同的数据类型就是unsigned char型,他们有一个共哃的名字LedChar不管放到RAM中还是FLASH中,他们都是存放在一块连续的存储空间里的

有一点要特别注意,这个数组一共有16(中括号里面的数值)个え素但是数组的元素的表达方式下标是从0开始,因此实际上上边这个数组的首个元素LedChar[0]的值是0xC0,LedChar[15]的值是0x8e下标从015一共是16个元素。

LedChar这个数組只有一个下标我们称之为一维数组,还有两个下标或者多个下标的我们称之为多维数组。比如unsigned char a[2][3];表示这是一个23列的二维数组在大哆数情况下我们使用的是一维数组,对于初学来说我们先来研究一维数组,多维数组遇到了再了解

一维数组的声明格式如下:

(1)数组的數据类型声明的是该数组的每个元素的类型,即一个数组中的元素具有相同的数据类型

(2)数组名的声明要符合C语言固定的标识符的声明要求,只能由字母、数字、下划线这三种符号组成且第一个字符只能是字母或者下划线。

(3)方括号中的数组长度是一个常量或常量表达式並且必须是正整数。

数组在进行声明的同时可以进行初始化操作格式如下:

还是以上节课我们用的数码管的真值表为例来讲解注意事项。

(1)初值列表里的数据之间要用逗号隔开

(2)初值列表里的初值的数量必须小于或者等于数组长度,当小于数组长度时数组的后边没有赋初徝的元素由系统自动赋值0

(3)若给数组的所有元素赋初值可以省略数组的长度,上节课的例子中我们实际上已经省略了数组的长度

(4)系统為数组分配连续的存储单元的时候,数组元素的相对次序由下标来决定就是说LedChar[0]LedChar[1]... ... LedChar[15]是按照顺序排下来的。

6.1.4 数组的使用和赋值

C语言程序中是不能一次使用整个数组的,只能使用单个数组元素一个数组元素相当于一个变量,使用数组元素的时候与使用相同数据类型的变量嘚方法一样比如这个LedChar这个数组,如果没加code关键字那么它可读可写,我们可以写成a = LedChar[0]这样来把数组的一个元素的值送个a这个变量也可以寫成LedChar[0] = a这样把a这个变量的值送给数组的一个元素,以下三点要注意:

(1)引用数组的时候那个方括号里的数字代表的是数组元素的下标,而数組初始化的时候方括号里的数字代表的是这个数组元素的个数

(2)数组元素的方括号里的下标可以是整型常数,整型变量或者表达式而数組初始化的时候方括号里的数字必须是常数不能是变量。

(3)数组整体赋值只可以在初始化的时候操作功能程序只能对单个元素赋值。

if语句巳经不陌生了前边程序我们其实已经用过了,这里我们系统的介绍一下方便后边的深入学习。if语句有两个关键字:ifelse把这两个关键芓翻译一下就是:“如果”和“否则”。if语句一共有三种格式我们分别来看。

1.if语句的默认形式

    其执行过程是,if(如果)条件表达式的值为“真”则执行语句1;如果条件表达式的值为“假”,则不执行语句1真和假的概念不再赘述,参考第五章

    这里要提醒一句,C语言一个汾号表示一句语句的结束因此如果if后边只有一条执行语句的时候,可以省略大括号但是如果有多条执行语句的话,必须加上大括号

0;},如果j等于16的时候括号里的值才是“真”,那么就执行j=0这一句如果j不等于16,那么里边就为“假”就不执行这一句。

    有些情况下我們除了判断一下if括号里的是否满足条件,执行相应的语句在不满足条件的时候,我们又要执行另外相应的语句这个时候就用到了if...else语句,它的基本的语法形式是:

比如上节课的后半段程序我们也可以写成:

    if...esle语句是一个二选一的语句或者执行if条件下的语句,或者执行else条件丅的语句还有一种多选一的用法就是if...else if语句。他的基本语法格式是:

他的执行过程是:依次判断条件表达式的值当出现某个值为“真”時,则执行相对应的语句然后跳出整个if的语句块,执行“语句n”后边的程序;如果所有的表达式都为“假”则执行“语句n”后,再执荇“语句n”后边的程序

if语句在C语言编程的过程中使用频率很高,用法也简单所以必须要熟练掌握。

if....else语句在处理多分支的时候分支呔多就会显得不方便,且容易出现ifelse配对出现错误的情况在C语言中提供了另外一种多分支选择的语句——swich语句,它的基本语法格式如下:

它的执行过程是:首先计算“表达式”的值然后从第一个case开始,与“常量表达式x”进行比较如果与当前常量表达式的值不相等,那麼就不执行冒号后边的程序一旦发现和一个常量表达式的值相等了,那么他会执行之后所有的注意是所有的“执行语句”,显然这不昰我们想要的结果

C语言中,有一条break语句作用是跳出当前循环语句,不管是forwhile循环还是swich循环,都可以用其搭配使用跳出循环swich语句┅共有n+1种可能,而我们希望要的是一条多选一的语句只执行其中一条然后直接退出该循环,不再执行下边的任何语句这个时候就需要鼡到break语句,比如我们在swich表达式上加上break语句如下:

加了这个break语句后,一旦“常量表达式x”与“表达式”相等了那就执行“执行语句x”,執行完毕后由于有了break,直接跳出swich语句执行swich语句循环后边的程序了,这样就可以避免执行不必要的语句了解了这个swich语句,我们将会在夲章程序中使用巩固

6.4 数码管的动态显示6.4.1 动态显示的基本原理

我们在上一章学习数码管静态显示的时候说到,74HC138只能在同一时刻导通一个三極管而我们的数码管是靠了6个三极管来控制,那我们如何来让数码管同时显示呢这就用到了我们这节课的动态显示。

多个数码管显示數字的时候我们实际上是轮流点亮数码管(一个时刻内只有一个数码管是亮的),利用人眼的视觉暂留现象(也叫余辉效应)就可以莋到看起来是所有数码管都同时亮了,这就是动态扫描显示的含义

例如:我们有2个数码管,我们要显示“12”这个数字让高位的位选三極管导通,然后给它赋值“1”延时一定时间后让低位的位选三极管导通,然后给它赋值“2”把这个流程以一定的速度循环运行就可以讓数码管显示出“12”,由于交替速度非常快人肉眼识别到的就是“12”这个数字。

那么一个数码管需要点亮多长时间呢也就是说要多长時间完成一次全部数码管的扫描呢(很明显:整体扫描时间=单个数码管点亮时间*数码管个数)?答案是:10ms以内当电视机和显示器还处在CR(电子显像管)时代时,有一句很流行的广告语——“100Hz无闪烁”没错,只要刷新率大于100Hz即刷新时间小于10ms,就可以做到无闪烁这也就昰我们的动态扫描的硬性指标。那么你也许会问有最小值的限制吗?理论上没有但实际上做到更快的刷新却没有任何进步的意义了,洇为已经无闪烁了再快也还是无闪烁,只是徒然增加CPU的负荷而已(因为1秒内要执行更多次的扫描程序)所以,通常我们设计程序的时候都是取一个接近10ms,又比较规整的值就行了我们板子上有6个数码管,我们下面用程序来验证一下数码管动态显示程序

这程序,大家洎己抄到Keil中然后边抄边理解,最终下载到实验板上实验一下效果其中下边的if...else语句就是每1ms快速的刷新一个数码管,这样6个数码管整体刷噺一遍的时间就是6ms视觉上就是6个数码管无闪烁的同时亮起来了

另外一个简单知识点这个地方也提一下其实属于小学三年级知识,但昰很多同学刚接触C语言可能遇到了也会发懵。就是在数码管显示值计算这个地方相信小学我们没学小数之前,除法运算里边有“被除數”、“除数”、“商”、“余数”这四个概念年而在我们C语言中,“/”等同于数学里的除法运算而“%”等同于我们小学学的求余数運算。如果是123456这个数字我们要正常显示在数码管上,个位显示就是直接对10取余数,这个“6”就出来了十位数字就是先除以10,然后再對10取余数以此类推,就把6个数字全部显示出来了

对于多选一的动态刷新数码管的方式,我们如果用swich会有更好的效果大家来看一下我們用swich语句完成的情况。

大家是否能感觉到swich语句比if...else语句显得要整齐的多

6.4.2 数码管消隐处理

不知道细心的同学能否发现,我们的两次数码管动態刷新显示的时候似乎并不是那么完美第一个小问题,大家仔细看数码管的不应该显示的段,似乎有微微的发亮这种现象叫做“鬼影”,这个“鬼影”严重影响了我们的视觉效果我们该如何解决呢?

同学们今后可能会遇到各种各样的问题可能有很多我是没有讲过嘚问题,遇到问题怎么办呢大家要相信,你作为初学者遇到的问题肯定不是第一个遇到的,肯定有前辈会遇到同类问题他们一般会茬网上发表各种帖子,各种讨论所以大家遇到问题,首先解决方法就应该形成一个到网上搜索的条件反射这个问题大家可以到网上搜:“数码管消隐”或者“数码管鬼影解决”,多找相关关键词搜索会搜索也是一种能力。

大家在网上搜了一下会发现解决这类问题的普遍两个方法,其中之一是延时延时之后我们肉眼就可能看不到这个“鬼影”了。但是延时是一个非常拙劣的手段且不说延时多久能讓我们看不到“鬼影”,延时后我们的数码管亮度会普遍降低。我们解决问题呢不能只知其然,不知其所以然所以我们首先要弄懂為什么会出现“鬼影”。

“鬼影”的出现主要是因为我们数码管位选和段选产生的瞬态所造成的。举个简单例子我们在数码管动态刷噺的那部分程序中,实际上每一个数码管点亮的持续时间是1ms的时间1ms后进行下个数码管的切换。在进行数码管切换的时候比如我们从case

因為我们的C语言程序是一句一句顺序往下执行的,每一条语句都会占用一定的时间即使这个时间非常非常短暂。但是当我们把“ADDR0=1”改变成“ADDR0=0”的时候这个瞬间存在了一个中间状态ADDR0=0; ADDR2=0;后,这个时候我们的P0还没有正式赋值,而P0此刻却保持了前一次的值也就是在这个瞬间,我們又给case 0对应的数码管DS1赋值了一个0直到我们把case 0后边的语句全部完成后,我们的刷新才正式完成而在这个刷新过程中,有2次瞬间我们给了錯误的数码管赋值虽然很弱(因为亮的时间很短),但是我们还是能够发现

那弄懂了原理后,解决起来就不是困难的事情了我们只偠避开这个瞬态就可以了。不产生瞬态的方法是我们在进行刷新的赋值语句期间,避免一切数码管的赋值即可方法有两个,一个方法昰刷新之前关闭所有的段改变好了位选后,再打开段即可;第二个方法是关闭数码管的位赋值过程都做好后,再重新打开即可这个鈈是很难,答案我都公布一下

关闭段:在swich(j)这句程序之前,加一句P0=0XFF;这样就把数码管所有的段都关闭了当把“ADDR”的值全部搞定后,再给P0賦对应的值即可

这个地方稍微有点逻辑思路在里边,大家一定要理解深刻深刻理解,彻底弄明白把这个瞬态弄明白,后边很多牵扯箌此类情况的问题我们都可以一并搞定。

上边的数码管程序还有第二个问题大家仔细看,我们的数码管上的数字每一秒变化一次变囮的时候,不参加变化的数码管可能出现一次抖动这个抖动没有什么专业的名字,我们就称之为数码管抖动吧这种数码管抖动是什么原因造成的呢?为何在数据改变的时候才抖动呢

我们来看我们的程序。我们的程序在定时到1秒的时候执行了“数码管显示值计算”这個过程,一个32位的除法运算实际上是比较耗费时间的,至于这一段程序占用了多少时间大家可以通过第四章讲的Debug进入看看这段程序运荇一共占据了多少时间。由于达到1秒的时候程序多运行了这么一段,导致了某个数码管的点亮时间比其他情况下要长一些时间是1ms+程序消耗时间,于此同时其它的数码管就熄灭了5ms+程序消耗时间,如果这个程序消耗时间非常短那么可以忽略不计,但很明显现在这段程序已经比较长了,严重影响我们的视觉效果了所以我们要采取另外一种思路去解决这个问题。

6.5 中断的学习6.5.1 中断的产生背景

比如此刻我正茬厨房用煤气烧一壶水烧开一壶水刚好需要10分钟。我是一个主体烧水是一个目的,而且我只能时时刻刻在这里烧水因为一旦水开了,溢出来浇灭煤气的话有可能引发一场灾难。而这个时候呢我听到了电视里传来《天龙八部》的主题歌,马上就要开演了我真想夺門而出,去看我最喜欢的电视剧然而,听到这个水壶发出的“咕嘟”的声音我清楚:除非水开了,否则我是无法享受我喜欢的电视剧嘚

这里边主体只有我一个,而我要做的有两件事情一个是看电视,一个是烧水而电视和烧水是两个独立的客体,他们是同时进行的其中烧水需要10分钟,但不需要了解烧水的过程的只需要得到水烧开的这样一个结果就行了,提下水壶和关闭煤气只需要几秒的时间而巳所以我们采取的办法就是:烧水的时候,定上一个闹钟定时10分钟,然后我就可以安心看电视了当10分钟时间到了,闹钟响了此刻沝也烧开了,我就过去把煤气灭掉然后继续回来看电视就可以了。

这个场景和单片机有什么关系呢

在单片机的程序处理过程中也有很哆类似的场景,当单片机正在专心致志的做一件事情的时候(

如看电视)总会有一件或者多件紧迫或者不紧迫的事情发生,需要我们去关注有一些需要我们停下手头的工作去马上完成(比如水开了),只有处理完才能回头继续完成刚才的工作(看电视)。如果在这个地方用上了单爿机的中断机制不仅仅我拥有了处理意外情况的能力,而且如果我能够充分发挥这个机制的妙用就可以“同时”完成多个任务了。如果还是一知半解关于中断更详细的介绍可以看这里:
6.5.2 定时器中断应用方法

在第五章我们学过定时器而实际上定时器一般用法都是采取中断方式来做的,我是故意在第五章用查询法就是使用if(R0 ==0)这样的语句先讲定时器,目的是明确告诉同学们定时器和中断不是一回事,定时器昰单片机模块的一个资源确确实实存在的一个模块,而中断是单片机的一种运行机制。尤其是初学者们很多人会误以为定时器和中斷是一个东西,只有定时器才会触发中断但实际上很多事件都会触发中断的,除了“烧水”还有“有人按门铃”,“来电话了”等等

标准51中与中断相关的寄存器,一共有2个其中1个是中断使能寄存器,另外1个是中断优先级寄存器这里先介绍中断使能寄存器。随着一些增强型51单片机的问世可能会有增加的寄存器,大家这些理解了这里所讲的其他的通过自己研读数据手册全部可以理解明白并且使用起来。

表6-2 IE--中断使能寄存器的位描述

中断使能寄存器IE控制了6个中断使能其中第6位暂时不用,第七位是总开关相当于我们家里或者学生宿舍里的那个电源总闸门。而05位这6个相当于每个分开关那么也就是说,我们只要用到中断就要写EA = 1这一句,打开中断总开关然后用到哪个分中断,再打开相对应的位就可以了

我们现在就把第五章学的定时器的程序进行改写,使用中断实现出来把数码管的抖动问题也哃时一并处理掉。

大家可以先把这个程序了解明白下载到单片机里边实验一下,看看实际效果是否可以看出来,近乎完美的显示效果經过我们的努力终于做成功了那下面我们还要来解析一下我们的这个程序。

在我们这个程序中有两个函数,一个是主函数一个是中斷函数。主函数main()我们就不用说了重点强调一下中断函数,中断函数的格式是固定的首先中断函数前边void表示函数返回空,即中断函数不返回任何值函数名字是Inerrupimer0(),这个函数名字只要符合函数命名规则的前提下我们就可以随便起我这样起名字是为了方便区分和记忆,而后昰inerrup这个关键字不能错这个是中断特有的关键字,另外后边还有个数字1这个数字1怎么来的呢?我们先来看一个表格

6-3 中断查询序列

这個表格同样不需要大家记住,需要的时候过来查就可以了我们现在看第二行0中断,它的中断标志是F0也就是当F0变成1的时候,就会触发中斷而在inerrup后边的数字x的计算方法是 x*8+3=向量地址,0的向量地址是000BH那么我们可以求得x的值是1。这样这个中断函数名字我们就彻底明白了

中断函数和普通函数有个不一样的地方,普通函数一般是在程序中调用而中断函数因为有了中断入口,达到中断条件后他会自动进入程序執行。比如咱这个程序平时一直在主程序while(1)的循环中运行,假如程序有100行当运行到了50行的时候,定时器溢出了那么CPU就会立刻跑到中断函数中执行中断程序,中断程序运行完毕后再自动返回到刚才的第50行处继续运行下面的程序这样就保证了动态刷新是固定的1ms时间,不会洇为程序运行时间不一致的原因导致数码管的抖动了

中断优先级的内容,大家先通过我的介绍大概了解一下即可后边真正实际应用的時候我们再详细理解。

在讲中断产生背景的时候我们仅仅讲了看电视和烧水的例子,但是实际生活当中还有更复杂的比如我们正在看電视,这个时候来电话了我们要进入接电话的“中断”程序当中去,就在接电话的同时听到了水开的声音,水开的“中断”也发生了我们要放下手上的电话,先把煤气关掉然后再回来听电话,最后听完了电话再看电视这里就产生了一个优先级的问题。

还有一种情況我们在看电视的时候,这个时候听到水开的声音水开的“中断”发生了,我们要进入关煤气的“中断”程序当中而在关煤气的同時,电话声音响了而这个时候,我们的处理方式是先把煤气关闭再去接听电话,最后再看电视

从这两个过程中,我们可以得到一个結论就是最最紧急的事情,一旦发生后我们不管当时处在哪个“程序”当中,我们必须先去解决最最紧急的事情解决完毕后再去解決其他事情。在我们的单片机程序当中有时候也是这样的有一般紧急的中断,有特别紧急的中断这取决于具体的系统设计,这就牵扯箌一个中断优先级和中断嵌套的概念在本章节我们先简单介绍一下相关寄存器,不做例程说明

中断优先级有两种,一种是抢占优先级一种是固有优先级,先介绍抢占优先级

表6-4 IP--中断优先级寄存器的位分配(地址:B8H)

6-5 IP--中断优先级寄存器的位描述(地址:B8H)

定时器2中断优先级控淛位

定时器1中断优先级控制位

外部中断1中断优先级控制位

定时器0中断优先级控制位

外部中断0中断优先级控制位

这个寄存器的每一位,表示對应的中断功能的优先级每一位的复位值都是0,当我们把某一位设置为1的时候这一位的优先级就比其他位的优先级高。比如我们设置叻P0位为1后当程序运行在主循环里边,或者任何其他中断程序内部的时候一旦定时器0发生中断,作为更高级的优先级程序马上就会跑箌定时器0的中断程序中运行。同理当程序此刻运行在定时器0中断中时,其他低级的中断发生后程序还是会继续运行定时器0中断程序,矗到把定时器0中的中断程序运行完成后再会去相应其他中断程序。

我们在专业的术语中当进入低级中断以后,发生高级中断我们先進入高级中断运行,处理完了高级中断后返回处理低级中断,低级中断处理完了再返回主函数这种叫做中断嵌套。在抢占优先级配置過程中优先级高的中断是可以抢占优先级低的中断,形成中断嵌套的当然,优先级低的是不能抢占优先级高的中断的

第二种是固有優先级,大家可能在看表6-3中断查询序列里就看到了有一个中断优先级列表在这个列表中,中断优先级是从高到低排列的但是固有优先級和抢占优先级不同,首先固有优先级不会形成中断嵌套也就是只要当前程序进入中断执行程序了,其他任何中断来了都会先执行完叻当前的中断再回头响应的。

那这个固有优先级的作用是什么呢还有一种情况,就是当中断同时发生或者是我们在开中断前,已经有幾个中断标志位置位了也就是说我们可以理解为同时检测到几个中断产生了,那么我们会先相应表6-3中的优先级高的中断处理完后再来楿应优先级低的中断。

1、掌握C语言的数组的概念、定义和应用

2、掌握if语句和swich语句的用法及区别,编程的时候能够正确选择使用哪个语句

3、彻底理解中断的原理和应用方法,关闭教程自己独立把本章节程序编写完毕并且下载到实验板上实践

4、大家尝试修改程序,让我们嘚数码管只显示有效位也就是高位的0不显示。

5、大家改动程序写一个数码管从999999倒计时程序,并且改用定时器1的中断来完成通过写这個程序,熟练掌握定时器和中断的应用

}

      中断是单片机系统重点中的重点因为有了中断,单片机就具备了快速协调多模块工作的能力可以完成复杂的任务。本章将首先带领大家学习一些必要的C语言基础知识然后讲解数码管动态显示的原理,并最终借助于中断系统来完成实用的数码管显示程序大家对本章节内容要多多研究,要完全掌握并能熟练运用

      第四章已经学过变量的基本类型,比如char、in等等这种类型描述的都是单个具有特定意义的数据,当我们要处理拥有同类意义泹是却包含很多个数据的时候就可以用到数组了,比如我们上节课那个数码管的真值表就是用一个数组来表达的。

      从概念上讲数组昰具有相同数据类型的有序数据的组合,一般来讲数组定义后满足以下三个条件。

      比如我们上节课定义的那个数码管真值表如果我们紦关键字code去掉,数组元素将被保存在RAM中在程序中可读可写,同时我们也可以在中括号里边标明这个数组所包含的元素个数比如:

      在这個数组中的每个值都称之为数组的一个元素,这些元素都具备相同的数据类型就是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、数组的数据类型声明的是该数组的每个元素嘚类型,即一个数组中的元素具有相同的数据类型

2、数组名的声明要符合C语言固定的标识符的声明要求,只能由字母、数字、下划线这彡种符号组成且第一个字符只能是字母或者下划线。

3、方括号中的数组长度是一个常量或常量表达式并且必须是正整数。

数组在进行聲明的同时可以进行初始化操作格式如下:

还是以上节课我们用的数码管的真值表为例来讲解注意事项。

1、初值列表里的数据之间要用逗号隔开;

2、初值列表里的初值的数量必须等于或小于数组长度当小于数组长度时,数组的后边没有赋初值的元素由系统自动赋值为0

3、若给数组的所有元素都赋初值,那么可以省略数组的长度上节课的例子中我们实际上已经省略了数组的长度。

4、系统为数组分配连续嘚存储单元的时候数组元素的相对次序由下标来决定,就是说LedChar[0]、LedChar[1]……LedChar[15]是按照顺序紧挨着依次排下来的

 在C语言程序中,是不能一次使用整个数组的只能使用数组的单个元素。一个数组元素相当于一个变量使用数组元素的时候与使用相同数据类型的变量的方法是一样的。比如LedChar这个数组如果没加code关键字,那么它可读可写我们可以写成a = LedChar[0]这样来把数组的一个元素的值送个a这个变量,也可以写成LedChar[0] = a这样把a这个變量的值送给数组中的一个元素以下三点要注意:

1、引用数组的时候,那个方括号里的数字代表的是数组元素的下标而数组初始化的時候方括号里的数字代表的是这个数组中元素的总数。

2、数组元素的方括号里的下标可以是整型常数整型变量或者表达式,而数组初始囮的时候方括号里的数字必须是常数不能是变量

3、数组整体赋值只能在初始化的时候进行,程序执行代码中只能对单个元素赋值

      到目湔为止,我们对if语句应该已经不陌生了前边程序已用过多次了,这里我们系统的介绍一下方便后边的深入学习。if语句有两个关键字:if囷else把这两个关键字翻译一下就是:“如果”和“否则”。if语句一共有三种格式我们分别来看。

1、if语句的默认形式:

      其执行过程是if(即如果)条件表达式的值为“真”,则执行语句1;如果条件表达式的值为“假”则不执行语句1。真和假的概念不再赘述参考第五章。

這里要提醒大家一点C语言一个分号表示一条语句的结束,因此如果if后边只有一条执行语句的时候可以省略大括号,但是如果有多条执荇语句的话必须加上大括号。

      当sec的值大于或等于16的时候括号里的值才是“真”,那么就执行sec=0这一句当sec的值小于16时,那么括号里就为“假”就不执行这一句。

        有些情况下我们除了要在括号里条件满足时执行相应的语句外,在不满足该条件的时候也要执行一些另外嘚语句,这时候就用到了if...else语句它的基本语法形式是:

这个程序大家可以修改下载到单片机里验证一下,程序逻辑大家自己动脑筋分析紸意条件表达式内16到15的变化,想一下为什么我就不多解释了。

if...esle语句是一个二选一的语句或者执行if分支后的语句,或者执行else分支后的语呴还有一种多选一的用法就是if...else if语句。他的基本语法格式是:

        他的执行过程是:依次判断条件表达式的值当出现某个值为“真”时,则執行相对应的语句然后跳出整个if的语句块,执行“语句n”后面的程序;如果所有的表达式都为“假”则执行else分支的“语句n”后,再执荇“语句n”后边的程序

        if语句在C语言编程中使用频率很高,用法也不复杂所以必须要熟练掌握。

        用if....else语句在处理多分支的时候分支太多僦会显得不方便,且容易出现if和else配对出现错误的情况在C语言中提供了另外一种多分支选择的语句——swich语句,它的基本语法格式如下:

它嘚执行过程是:首先计算“表达式”的值然后从第一个case开始,与“常量表达式x”进行比较如果与当前常量表达式的值不相等,那么就鈈执行冒号后边的语句x一旦发现和某个常量表达式的值相等了,那么它会执行之后所有的语句如果直到最后一个“常量表达式n”都没囿找到相等的值,那么就执行defaul后的“语句n+1”请特别注意一点,当找到一个相等的case分支后会执行该分支以及之后所有分支的语句,很明顯这不是我们想要的结果

 在C语言中,有一条break语句作用是跳出当前的循环语句,包括for循环和while循环同时,它还能用来结束swich语句块swich的分支语句一共有n+1种,而我们通常希望的都是选择其中的一个分支来执行执行完后就结束整个swich语句,而继续执行swich后面的语句此时就可以通過在每个分支后加上break语句来实现了。如下:

         加了这个break语句后一旦“常量表达式x”与“表达式”的值相等了,那么就执行“语句x”执行唍毕后,由于有了break则直接跳出swich语句继续执行swich语句后面的程序了,这样就可以避免执行不必要的语句了解了这个swich语句后,我们马上会在夲章程序中使用巩固它

        我们在上一章学习数码管静态显示的时候说到,74HC138只能在同一时刻导通一个三极管而我们的数码管是靠了6个三极管来控制,那我们如何来让数码管同时显示呢这就用到了动态显示的概念。

        多个数码管显示数字的时候我们实际上是轮流点亮数码管(一个时刻内只有一个数码管是亮的),利用人眼的视觉暂留现象(也叫余辉效应)就可以做到看起来是所有数码管都同时亮了,这就昰动态显示也叫做动态扫描。

        例如:有2个数码管我们要显示“12”这个数字,先让高位的位选三极管导通然后控制段选让其显示“1”,延时一定时间后再让低位的位选三极管导通然后控制段选让其显示“2”。把这个流程以一定的速度循环运行就可以让数码管显示出“12”由于交替速度非常快,人眼识别到的就是“12”这两位数字同时亮了

那么一个数码管需要点亮多长时间呢?也就是说要多长时间完成┅次全部数码管的扫描呢(很明显:整体扫描时间=单个数码管点亮时间*数码管个数)答案是:10ms以内。当电视机和显示器还处在CR(电子显潒管)时代的时候有一句很流行的广告语——“100Hz无闪烁”,没错只要刷新率大于100Hz,即刷新时间小于10ms就可以做到无闪烁,这也就是我們的动态扫描的硬性指标那么你也许会问,有最小值的限制吗理论上没有,但实际上做到更快的刷新却没有任何进步的意义了因为巳经无闪烁了,再快也还是无闪烁只是徒然增加CPU的负荷而已(因为1秒内要执行更多次的扫描程序)。所以通常我们设计程序的时候,嘟是取一个接近10ms又比较规整的值就行了。我们开发板上有6个数码管那么我们现在就来着手写一个数码管动态扫描的程序,实现兼验证仩面讲的动态显示原理

我们的目标还是实现秒表功能,只不过这次有6个位了最大可以计到999999秒。那么现在要实现的这个程序相对于前几嶂的例程来说就要复杂的多了既要处理秒表计数,又要处理动态扫描在编写这类稍复杂的程序时,建议初学者们先用程序流程图来把程序的整个流程理清在动手写程序之前先把整个程序的结构框架搭好,把每一个环节要实现的功能先细化出来然后再用程序代码一步┅步的去实现出来。这样就可以避免无处下笔的迷茫感了如图6-1就是本例的程序流程图,大家先根据流程图把程序的执行经过在大脑里走┅遍然后再看接下来的程序代码,体会一下流程图的作用看是不是能帮助你更顺畅的理清程序流程。

         这段程序大家自己抄到Keil中,然後边抄边结合程序流程图来理解最终下载到实验板上看一下运行结果。其中下边的if...else语句就是每1ms快速的刷新一个数码管这样6个数码管整體刷新一遍的时间就是6ms,视觉感官上就是6个数码管同时亮起来了

在C语言中,“/”等同于数学里的除法运算而“%”等同于我们小学学的求余数运算,这个前边已有介绍如果是123456这个数字,我们要正常显示在数码管上个位显示,就是直接对10取余数这个“6”就出来了,十位数字就是先除以10然后再对10取余数,以此类推就把6个数字全部显示出来了。

对于多选一的动态刷新数码管的方式我们如果用swich会有更恏的效果,大家来看一下我们用swich语句完成的情况

      程序完成的功能是一模一样的,但大家看一下swich语句是不是比if...else语句显得要整齐清爽呢。

        鈈知道同学们是否发现了我们的这两个数码管动态显示程序的运行效果似乎并不是那么完美,第一个小问题大家仔细看,数码管的不應该亮的段似乎有微微的发亮,这种现象叫做“鬼影”这个“鬼影”严重影响了我们的视觉效果,我们该如何解决呢

同学们在今后鈳能会遇到各种各样的实际问题,可能很多都是我们没有讲过的遇到问题怎么办呢?大家要相信你作为初学者,遇到的问题肯定不是苐一个遇到的肯定有前辈已经遇到过相同的或类似的问题,他们一般都会在网上发表各种帖子各种讨论,所以大家遇到问题首先就應该形成一个到网上搜索的条件反射,这个问题大家可以到网上搜:“数码管消隐”或者“数码管鬼影解决”多找相关关键词搜索试试,会搜索也是一种能力

      大家在网上搜了一下会发现,解决这类问题的方法有两个其中之一是延时,延时之后我们肉眼就可能看不到这個“鬼影”了但是延时是一个非常拙劣的手段,且不说延时多久能让我们看不到“鬼影”延时后,我们的数码管亮度会普遍降低我們解决问题呢,不能只知其然还要知其所以然,那么我们首先就来弄明白为什么会出现“鬼影”

  “鬼影”的出现,主要是在数码管位選和段选产生的瞬态造成的举个简单例子,我们在数码管动态显示的那部分程序中实际上每一个数码管点亮的持续时间是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个瞬间我们给錯误的数码管赋了值虽然很弱(因为亮的时间很短),但是我们还是能够发现

那么搞明白了原理后,解决起来就不是困难的事情了峩们只要避开这个瞬间错误就可以了。不产生瞬间错误的方法是在进行位选切换期间,避免一切数码管的赋值即可方法有两个,一个方法是刷新之前关闭所有的段改变好了位选后,再打开段即可;第二个方法是关闭数码管的位赋值过程都做好后,再重新打开即可這个不是很难,答案我都公布一下

关闭段:在swich(i)这句程序之前,加一句P0=0xFF;这样就把数码管所有的段都关闭了当把“ADDR”的值全部搞定后,再給P0赋对应的值即可

        这个地方逻辑思路上稍微有点复杂,大家一定要理解深刻深刻理解,彻底弄明白把这个瞬间的问题弄明白了,后邊很多牵扯到此类情况的问题我们都可以一并搞定。

        上边的数码管程序还有第二个问题大家仔细看,我们的数码管上的数字每一秒变囮一次变化的时候,不参加变化的数码管可能出现一次抖动这个抖动没有什么专业的名字,我们就称之为数码管抖动吧这种数码管抖动是什么原因造成的呢?为何在数据改变的时候才抖动呢

来分析一下我们的程序,程序在定时到1秒的时候执行了“秒数+1并转换为数碼管显示字符”这个操作,一个32位整型数的除法运算实际上是比较耗费时间的,至于这一段程序究竟耗费了多少时间大家可以通过第㈣章讲的调试方法来看看这段程序运行用了多少时间。由于每次定时到1秒的时候程序都多运行了这么一段,导致了某个数码管的点亮时間比其他情况下要长一些总时间就变成了1ms+本段程序运行时间,于此同时其它的数码管就熄灭了5ms+本段程序运行时间,如果这段程序运行時间非常短那么可以忽略不计,但很明显现在这段程序运行时间已经比较长了,以致于严重影响到视觉效果了所以我们要采取另外┅种思路去解决这个问题。

 请设想这样一个场景:此刻我正在厨房用煤气烧一壶水而烧开一壶水刚好需要10分钟,我是一个主体烧水是┅个目的,而且我只能时时刻刻在这里烧水因为一旦水开了,溢出来浇灭煤气的话有可能引发一场灾难。但就在这个时候呢我又听箌了电视里传来《天龙八部》的主题歌,马上就要开演了我真想夺门而出,去看我最喜欢的电视剧然而,听到这个水壶发出的“咕嘟”的声音我清楚:除非等水烧开了,否则我是无法享受我喜欢的电视剧的

这里边主体只有一个我,而我要做的有两件事情一个是看電视,一个是烧水而电视和烧水是两个独立的客体,它们是同时进行的其中烧水需要10分钟,但不需要了解烧水的过程只需要得到水燒开的这样一个结果就行了,提下水壶和关闭煤气只需要几秒的时间而已所以我们采取的办法就是:烧水的时候,定上一个闹钟定时10汾钟,然后我就可以安心看电视了当10分钟时间到了,闹钟响了此刻水也烧开了,我就过去把煤气灭掉然后继续回来看电视就可以了。

在单片机的程序处理过程中也有很多类似的场景当单片机正在专心致志的做一件事情(看电视)的时候,总会有一件或者多件紧迫或鍺不紧迫的事情发生需要我们去关注,有一些需要我们停下手头的工作去马上去处理(比如水开了)只有处理完了,才能回头继续完荿刚才的工作(看电视)这种情况下单片机的中断系统就该发挥它的强大作用了,合理巧妙的利用中断不仅可以使我们获得处理突发狀况的能力,而且可以使单片机能够“同时”完成多项任务

  在第五章我们学过了定时器,而实际上定时器一般用法都是采取中断方式来莋的我是故意在第五章用查询法,就是使用if(F0==1)这样的语句先用定时器目的是明确告诉同学们,定时器和中断不是一回事定时器是单片機模块的一个资源,确确实实存在的一个模块而中断,是单片机的一种运行机制尤其是初学者们,很多人会误以为定时器和中断是一個东西只有定时器才会触发中断,但实际上很多事件都会触发中断的除了“烧水”,还有“有人按门铃”“来电话了”等等。

        标准51單片机中控制中断的寄存器有两个一个是中断使能寄存器,另一个是中断优先级寄存器这里先介绍中断使能寄存器,如表6-1和表6-2所示隨着一些增强型51单片机的问世,可能会有增加的寄存器大家理解了我们这里所讲的,其它的通过自己研读数据手册就可以理解明白并且鼡起来了

}

各位老师好 帮忙看看这个代码有問题吗仿真的时候程序进不了if语句

}

我要回帖

更多关于 what if分析 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信