单片机c语言程序设计基础_第1页
单片机c语言程序设计基础_第2页
单片机c语言程序设计基础_第3页
单片机c语言程序设计基础_第4页
单片机c语言程序设计基础_第5页
已阅读5页,还剩138页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1,单片机C语言程序设计基础,1,2,2,第14章 目录14.1 编程语言Keil C51简介 14.1.1 Keil C51简介 14.1.2 Keil C51的开发环境 14.1.3 C51与标准C的主要区别14.2 C51语言程序设计基础 14.2.1 C51语言中的数据 14.2.2 C51的位变量定义 14.2.3 一个简单的C51程序 14.2.4 C51的运算符 14.2.5 C51的分支与循环程序结构,3,14.2.6 AT89S51不同存储区的C51定义 14.2.7 C51中断服务函数的定义14.3 C51的程序设计举例 14.3.1 中断程序的编写 14.3.2 定时器程序的编写 14.3.3 串行口方式0应用程序的编写 14.3.4 独立式键盘查询方式 14.3.5 行列式键盘查询方式 14.3.6 DAC0832应用程序的编写,4,14.3.7 ADC0809应用程序的编写14.4 C51的集成开发环境Keil Vision3介绍 14.4.1 集成开发环境Keil Vision3简介 14.4.2 Keil Vision3软件的安装、启动和运行 14.4.3 C51程序的开发流程14.5 C51与汇编语言的混合编程 14.5.1 C51与MCS-51汇编语言的比较 14.5.2 C51与汇编语言混合编程的方法,5,内容概要本章在假定读者已掌握标准C语言前提下,初步介绍如何使用C51来编写AT89C51单片机的应用程序。C51是在标准C的基础上,根据单片机存储器硬件结构及内部资源,扩展了相应的数据类型和变量,而C51在语法规定、程序结构与设计方法上,都与标准C相同。本章重点介绍C51对标准C所扩展的部分,并通过一些例程来介绍C51的程序设计思想。最后还对C51的集成开发环境Keil Vision3以及C51与汇编语言的混合编程作以介绍。,6,14.1 编程语言Keil C51简介目前51系列单片机编程的C语言都采用Keil C51(简称C51),Keil C51是在标准C语言基础上发展起来的。14.1.1 Keil C51简介C语言是美国国家标准协会(ANSI)制定的编程语言标准,1987年ANSI公布87 ANSI C,即标准C语言。Keil C51语言是在ANSI C的基础上针对51单片机的硬件特点进行的扩展,并向51单片机上移植,经过多年努力,C51语言已经成为公认的高效、简洁而又贴近51单片机硬件的实用高级编程语言。,6,7,目前大多数的51单片机用户都在使用C51语言来进行程序设计。用C51进行单片机软件开发,有如下优点:(1)可读性好。C51语言程序比汇编语言程序的可读性好,因而编程效率高,程序便于修改。(2)模块化开发与资源共享。用C51开发出来的程序模块可以不经修改,直接被其他项目所用,这使得开发者能够很好地利用已有的大量的标准C程序资源与丰富的库函数,减少重复劳动。,7,8,(3)可移植性好。为某种型号单片机开发的C语言程序,只需将与硬件相关之处和编译连接的参数进行适当修改,就可以方便地移植到其他型号的单片机上。例如,为51单片机编写的程序通过改写头文件以及少量的程序行,就可以方便地移植到PIC单片机上。(4)代码效率高。当前较好的C51语言编译系统编译出来的代码效率只比直接使用汇编语言低20%左右,如果使用优化编译选项,效果会更好。,8,9,14.1.2 Keil C51的开发环境Keil C51是德国Keil software公司开发的用于51系列单片机的C51语言开发软件。Keil C51在兼容ANSI C的基础上,又增加很多与51单片机硬件相关的编译特性,使得开发51系列单片机程序更为方便和快捷,程序代码运行速度快,所需存储器空间小,完全可以和汇编语言相媲美。它支持众多的MCS-51架构的芯片,同时集编辑、编译、仿真等功能于一体,具有强大的软件调试功能,是众多的单片机应用开发软件中最优秀的软件之一。,9,10,Keil公司目前已推出V7.0以上版本的C51编译器,为51单片机软件开发提供了全新的C语言环境,同时保留了汇编代码高效、快速的特点。现在,Keil C51已被完全集成到一个功能强大的全新集成开发环境(IDE)Vision3中,该环境下集成了文件编辑处理、编译链接、项目(Project)管理、窗口、工具引用和仿真软件模拟器以及Monitor51硬件目标调试器等多种功能,这些功能均可在Keil Vision3环境中极为简便地进行操作。,10,11,本章经常用到Keil C51和Keil Vision3两个术语。Keil C51一般简写为C51,指的是51单片机编程所用的C语言;而Keil Vision3,可简写为Vision3,指的是用于51单片机的C51程序编写、调试的集成开发环境。Vision3内部集成了源程序编辑器,并允许用户在编辑源文件时就可设置程序调试断点,便于在程序调试过程中快速检查和修改程序。此外,Vision3还支持软件模拟仿真(Simulator)和用户目标板调试(Monitor51)两种工作方式。在软件模拟仿真方式下不需任何51单片机及其外围硬件即可完成用户程序仿真调试。,11,12,在用户目标板调试方式下,利用硬件目标板中的监控程序可以直接调试目标硬件系统,使用户节省购买硬件仿真器的费用。14.1.3 C51与标准C的主要区别不同的嵌入式处理器的C编译系统与标准C的不同之处,主要是它们所针对的嵌入式处理器的硬件系统不同。Keil C51的基本语法与标准C相同,但对标准C进行了扩展。深入理解Keil C51对标准C的扩展部分是掌握Keil C51的关键之一。,12,13,C51与标准C的主要区别如下:(1)头文件的差异。51系列单片机厂家有多个,它们的差异在于内部资源如定时器、中断、I/O等数量以及功能的不同,而对使用者来说,只需要将相应的功能寄存器的头文件加载在程序内,就可实现所具有的功能。因此,Keil C51系列的头文件集中体现了各系列芯片的不同资源及功能。(2)数据类型的不同。51系列单片机包含位操作空间和丰富的位操作指令,因此Keil C51与ANSI C相比又扩展了4种类型,以便能够灵活地进行操作。,13,14,(3)数据存储类型的不同。C语言最初是为通用计算机设计的,在通用计算机中只有一个程序和数据统一寻址的内存空间,而51系列单片机有片内、外程序存储器,还有片内、外数据存储器。标准C并没有提供这部分存储器的地址范围的定义。此外,对于AT89C51单片机中大量的特殊功能寄存器也没有定义。(4)标准C语言没有处理单片机中断的定义。(5)Keil C51与标准C的库函数有较大的不同。由于标准C的中的部分库函数不适于嵌入式处理器系统,因此被排除在Keil C51之外,如字符屏幕和图形函数。,14,15,有一些库函数可以继续使用,但这些库函数都必须针对51单片机的硬件特点来作出相应的开发,与标准C库函数的构成与用法有很大的不同。例如库函数printf和scanf,在标准C中,这两个函数通常用于屏幕打印和接收字符,而在Keil C51中,它们主要用于串行口数据的收发。(6)程序结构的差异。由于51单片机的硬件资源有限,它的编译系统不允许太多的程序嵌套。其次,标准C所具备的递归特性不被Keil C51支持,在C51中,要使用递归特性,必须用reentrant进行声明才能使用。,15,16,但是从数据运算操作、程序控制语句以及函数的使用上来说,Keil C51与标准C几乎没有什么明显的差别。如果程序设计者具备了有关标准C的编程基础,只要注意Keil C51与标准C的不同之处,并熟悉AT89S51单片机的硬件结构,就能够较快地掌握Keil C51的编程。,16,17,14.2 C51语言程序设计基础本节介绍C51语言程序设计的有关基础知识。14.2.1 C51语言中的数据1. 数据类型Keil C51的基本数据类型如表14-1所示。针对AT89S51单片机的硬件特点,C51在标准C的基础上,扩展了4种数据类型(见表中最后4行)。注意:扩展的4种数据类型,不能使用指针对它们存取。,17,18,表14-1 Keil C51支持的数据类型,18,19,2. C51的扩展数据类型下面对表14-1中扩展的4种数据类型进行说明。(1)位变量bitbit的值可以是1(true), 也可以是0(false)。(2)特殊功能寄存器sfrAT89S51特殊功能寄存器在片内RAM区的80HFFH之间,“sfr” 数据类型占用一个内存单元。利用它可访问AT89S51内部的所有特殊功能寄存器。例如:sfr P1=0x90这一语句定义P1口在片内的寄存器,在后面语句中可用“P1=0xff”(使P1的所有引脚输出,19,20,为高电平)之类的语句来操作特殊功能寄存器。(3)特殊功能寄存器sfr16 “sfr16”数据类型占用两个内存单元。sfr16和sfr一样用于操作特殊功能寄存器。所不同的是它用于操作占两个字节的特殊功能寄存器。 例如: sfr16 DPTR=0x82语句定义了片内16位数据指针寄存器DPTR,其低8位字节地址为82H。在后面的语句中可以对DPTR进行操作。,20,21,(4)特殊功能位 sbitsbit 是指AT89S51片内特殊功能寄存器的可寻址位。例如: sfr PSW=0xd0;/*定义PSW寄存器地址为0xd0*/sbit PSW 2 = 0xd2;/*定义OV位为PSW.2*/符号“”前面是特殊功能寄存器的名字,“”的后面数字定义特殊功能寄存器可寻址位在寄存器中的位置,取值必须是07。注意,不要把bit与sbit混淆。bit用来定义普通的位变量,值只能是二进制的0或1。而sbit定义的是特殊功能,21,22,寄存器的可寻址位, 其值是可进行位寻址的特殊功能寄存器的位绝对地址,例如PSW寄存器OV位的绝对地址0xd2。3. 数据的存储类型C51完全支持51单片机硬件系统的所有部分。在51单片机中,程序存储器与数据存储器是完全分开的,且分为片内和片外两个独立的寻址空间,特殊功能寄存器与片内RAM统一编址,数据存储器与I/O端口统一编址。C51编译器通过将变量、常量定义成不同存储类型的方法将它们定义在不同的存储区中。,22,23,C51存储类型与AT89S51的实际存储空间的对应关系见表14-2。下面对表14-2作以说明。(1)片内数据存储器片内RAM可分为3个区域:data:片内直接寻址区,位于片内RAM的低128字节。bdata:片内位寻址区,位于片内RAM位寻址区20H2FH。idata:片内间接寻址区,片内RAM所有地址单元 (00HFFH)。,24,24,25,(2)片外数据存储器pdata:片外数据存储器页,一页为256字节。xdata:片外数据存储器RAM的64KB空间。(3)片外程序存储器code:外部程序存储器的64KB空间。对单片机编程,正确地定义数据类型以及存储类型,是所有编程者在编程前都需要首先考虑的问题。在资源有限的条件下,如何节省存储单元并保证运行效率,是对开发者的一个考验。只有对C51中的各种数据类型以及存储类型非常熟练的掌握,才能运用自如。,25,26,定义变量类型应考虑如下问题:程序运行时该变量可能的取值范围,是否有负值,绝对值有多大,以及相应需要的存储空间大小。在够用的情况下,尽量选择8位即一个字节的char型,特别是unsiged char。对于51系列这样的定点机而言,浮点类型变量将明显增加运算时间和程序长度,如果可以的话,尽量使用灵活巧妙的算法来避免浮点变量的引入。定义数据的存储类型通常遵循如下原则:只要条件满足,尽量选择内部直接寻址的存储类型data,然后选择idata即内部间接寻址。对于那些经常使用的变量要使用内,26,27,部寻址。在内部数据存储器数量有限或不能满足要求的情况下才使用外部数据存储器。选择外部数据存储器可先选择pdata类型,最后选用xdata类型。需指出,扩展片外存储器,原理上虽很简单,但在实际开发中,很多时候,会带来不必要的麻烦,如可能降低系统稳定性、增加成本、拉长开发和调试周期等,推荐充分利用片内存储空间。另外,通常的单片机应用都是面对小型的控制,代码比较短,对于程序存储区的大小要求很低,常常是片内RAM很紧张而片内Flash ROM很富裕,因此如果实时性,27,28,要求不高,可考虑使用宏,以及将一些子函数的常量数据做成数据表,放置在程序存储区,当程序运行时,进入子函数动态调用下载至RAM即可,退出子函数后立即释放该内存空间。,28,29,14.2.2 C51的位变量定义由于AT89C51能够进行位操作,C51扩展了“bit”数据类型用来定义位变量,这是C51与标准C的不同之处。C51中位变量bit的具体定义如下:1. 位变量的C51定义方法C51通过关键字“bit”来定义位变量,格式为:bit bit-name;例如:bit ov-flag;/* 将ov-flag定义为位变量*/,29,30,2. C51程序函数的“bit”参数及返回值C51程序函数可以包含类型为“bit”的参数,也可将其作为返回值。例如:bit func(bit b0, bit b1); /* 位变量b0,b1作为函数func的参数*/return(b1); /* 位变量b1作为函数的返回值*/,30,31,3. 位变量的限制位变量不能用来定义指针和数组。例如:bit *ptr;/* 错误,不能用位变量来定义指针*/bit a-array ;/* 错误,不能用位变量来定义数组*/在定义位变量时,允许定义存储类型,位变量都被放入一个位段,此段总是位于AT89S51片内RAM中,因此其存储器类型限制为bdata,data 或idata,如果将位变量定义成其他类型都会在编译时出错。,31,32,14.2.3 一个简单的C51程序一个C51源程序是由一个个模块化的函数所构成,函数是指程序中的一个模块,main()函数为程序的主函数,其他若干个函数可以理解为一些子程序。一个C51源程序无论包含了多少函数,它总是从main()函数开始执行,不论main()函数位于程序的什么位置。程序设计者就是编写一系列的函数模块,并在需要的时候调用这个函数,实现程序所要求的功能。,32,33,1. C51程序与函数下面通过一个简单C51程序,认识C51程序与函数。【例14-1】 在AT89S51的P1.0脚接有一只发光二极管,二极管的阴极接P1.0脚,阳极通过限流电阻接+5V,现在让发光二极管每隔800ms闪灭,占空比为50%。已知单片机时钟晶振为12MHz,即每个机器周期1s,采用软件延时的方法,参考程序如下:,33,34,#include /* 包含reg51.h 头文件*/sbit P10=P10; /* 定义位变量P1.0 ,也可使用sbit P10=0x90*/void Delay(unsigned int i) /* 延时函数Delay( ),i是形式参数 */* 两个花括号之间为函数Delay( )的函数体*/unsigned int j;/* 定义变量j */for(;i0;i-) /* 如果i0,则i减1 */for(j=0;j333;j+) /* 如果j 333,则j加1 */ ;/*空函数*/ ,34,35,void main(void)/* 主函数main( )*/while(1)/* 主程序轮询 */ P10=1;/* P1.0输出高电平,发光二极管灭*/ Delay(800) ; /* 将实际参数800传递给形式参数i,延时800ms*/P10=0; /* P1.0输出低电平,发光二极管亮*/Delay(800) /* 将实际参数800传递给形式参数i,延时800ms*/,35,36,下面对程序进行简要说明。程序的第1行是“文件包含”,是将另一个文件“reg51.h”的内容全部包含进来。文件“reg51.h”包含了51单片机全部的特殊功能寄存器的字节地址及可寻址位的位地址定义。程序包含reg51.h的目的就是为了使用P1这个符号,即通知程序中所写的P1是指AT89S51的P1端口,而不是其他变量。,36,37,打开reg51.h文件可以看到“sfr P1=0x90;”,即定义符号P1与地址0x90对应,而P1口的地址就是0x90。虽然这里的“文件包含”只有一行,但C编译器在处理的时候却要处理几十行或几百行。 程序的第2行用符号P10来表示P1.0引脚。在C51中,如果直接写“P1.0”编译器并不能识别,而且P1.0也不是一个合法的C51语言程序变量名,所以必须给它起一个另外的名字,这里起的名字是P10,可是P10是否就是P1.0呢,所以必须给它们建立联系,这里使用了C51的关键字“sbit”来进行定义。,37,38,第3行第9行对函数Delay进行了事先定义,只有这样,才能在主程序中被主函数main ( )调用。自行编写的函数Delay( )的用途是软件延时,调用时使用的这个“800”被称为“实际参数”,以延时800ms的时间。注意,内层循环for(j=0;j0;i-) 时的这1+2+i*(1002+1+2)可以近似为 i*1002,即i个ms。编程者可在一定范围内对i、j调整(不超过i、j的取值范围),来控制延时时间的长短。注意,若Delay( )的定义写在main函数的后面,则需要先作出声明,否则编译无法通过,因为编译到main函数中的Delay( )语句时,找不到相应的函数体。,39,40,main为“主函数”,每一个C语言程序有且只有一个主函数,主函数后面一定有一对花括号“”,在花括号里面书写该函数的代码行。2. 用户自定义函数与库函数从结构上划分,函数分为主函数main ( )和普通函数两种。对普通函数,从用户使用的角度划分有两种:一种是标准库函数;另一种是用户自定义函数。(1)标准库函数Keil C51具有功能强大、资源丰富的标准库函数,由C51编译器提供。进行程序设计时,应该善于充分利用,40,41,这些功能强大、资源丰富的标准库函数,提高编程效率。用户可以直接调用C51的库函数而不需要为这个函数写任何代码,只需要包含具有该函数说明的头文件即可。例如调用输出函数printf时,要求程序在调用输出库函数前包含以下的include 命令: #include (2)用户自定义函数用户根据自己需要所编写的函数。如例14-1中的Delay函数。编写时,需要注意以下几点。,41,42,函数的首部(函数的第1行),包括函数名、函数类型、函数属性、函数参数(形式参数)名、参数类型。例如:void Delay(unsigned int i)函数体,即函数首部下面的花括号“ ”内的部分。如果一个函数体内有多个花括号,则最外层的一对“ ”为函数体的范围。C51区分大小写,例如Delay与delay,编译时是不同的两个名称。每个语句最后必须有一个分号,分号是C语句的必要组成部分。,42,43,从函数的定义的形式上划分可以有三种形式:无参数函数、有参数函数和空函数。(1)无参数函数此种函数在被调用时,既无参数输入,也不返回结果给调用函数,只是为完成某种操作而编写的。(2)有参数函数调用此种函数时,必须提供实际的输入函数,必须说明与实际参数一一对应的形式参数,并在函数结束时返回结果,供调用它的函数使用。例如,例14-1中的第3行的“i”是形式参数。,43,44,(3)空函数函数体内无语句,是空白的。调用空函数时,什么工作也不做,不起任何作用。定义空函数的目的,是为以后程序功能的扩充。程序最初设计时,往往只涉及最基本的功能模块的函数,其他模块的功能函数可以在以后补上。因此先将非基本模块的功能函数定义成空函数,用一个空语句“;”占好位置,并写好注释,以后再用一个编好的函数代替它。例如,例14-1中的第8行的“;”就是一个空语句。,44,45,3. 函数调用程序设计者的任务就是编写一系列的用户自定义函数模块,并在需要的时候调用这些函数以及库函数,实现程序所要求的功能。调用分为以下几种。(1)简单调用调用格式为:函数名(实际参数1,实际参数2,)例如,例14-1中主函数main ( )里的子函数调用语句“Delay(800);”,其中800为实际参数。,45,46,(2)嵌套调用是在被调用的函数中又调用其他函数的语句。(3)递归调用函数的递归调用就是一个函数在其函数体内有调用自身。再入函数是一种可以在函数体内直接或间接调用自身的一种函数,在Keil C51中递归函数必须是可重入的,可重入的函数需要加上reentrant。再入函数有以下几点规定。,46,47,再入函数不能包括位操作以及51单片机的未寻址区。 在同一个程序中可以定义和使用不同存储模式的再入函数,任何模式的再入函数不能调用不同模式的再入函数。 在参数的传递上,实际参数可以传递给间接调用的再入函数。14.2.4 C51的运算符在程序中实现运算,要熟悉常用的运算符。本节对C51中用到的标准C运算符进行复习,为C51的程序设计打下基础。1. 算术运算符如表14-3所示。,47,48,表14-3 算术运算符及其说明,48,49,对于“/”和“%”往往会有疑问。这两个符号都涉及除法运算,但“/”运算是取商,而“%” 运算为取余数。例如“5/3”的结果(商)为1,而“5%3”的结果为2(余数)。表14-3中的自增和自减运算符是使变量自动加1或减1,自增和自减运算符放在变量前和变量之后是不同的。+i,-i:在使用i之前,先使i值加(减)1。i+,i-:在使用i之后,再使i值加(减)1。,49,50,例如:若i=4,则执行x=+i时,先使i加1,再引用结果,即x=5,运算结果为i=5,x=5。再如:若i=4,则执行x=i+时,先引用i值,即x=4,再使i加1,运算结果为i=5,x=4。在例14-1中可以看到有关自增和自减运算符的应用。2. 逻辑运算符逻辑运算符及其说明如表14-4所示。,50,51,表14-4 逻辑运算符及其说明3. 关系运算符判断两个数之间的关系。关系运算符及其说明如表14-5。,51,52,表14-5 关系运算符及其说明,52,53,4. 位运算位运算符及其说明如表14-6所示。 表14-6 位运算其说明,53,54,5. 赋值、指针和取值运算符是对变量操作的基本运算。赋值、指针和取值运算符及其说明如表14-7所示。 表14-7 位运算其说明,54,55,14.2.5 C51的分支与循环程序结构程序结构上可把程序分为三类,即顺序、分支和循环结构。顺序结构是程序的基本结构,程序自上而下,从main()的函数开始一直到程序运行结束,程序只有一条路可走,没有其他的路径可以选择。顺序结构比较简单和便于理解,这里重点介绍分支结构和循环结构。,55,56,1. 分支结构程序(1) 只有两条分支的时候用If (条件) 分支1else 分支2(2) 分支较多时在分支较多时的情况下使用switch语句。,56,57,switch ( ) case( ):语句; break; case( ):语句;break; default:语句; break; ,57,58,注意:每个switch分支必须有一个break语句,否则程序并不能跳出switch,就会继续执行case后面的case语句。如果看一下上述结构的程序对应的汇编语言源程序可看到,每一条break语句对应了汇编语言中的一条SJMP指令,而没有SJMP指令程序会继续向下执行,并不能跳出分支选择语句。实际上在对应的汇编语言源程序中,case(0),case(1) 只是确定了分支的地址,真正的判断是在switch语句开始的。,58,59,2. 循环结构程序循环语句有以下三种。(1)for循环格式:for(循环体初始化;循环体执行条件;循环体执行后操作)花括号中为循环体内容。这里有一个值得注意的现象,能够反映出C51在编译中对于执行时间和占用的存储单元的权衡。例如,for (i=0;i10;i+)对应的汇编语句为:,59,60,CLR A ;1个机器周期MOV R7,A ;2个机器周期LOOP:INC R7 ;1个机器周期CJNER7,#0AH,LOOP ;2个机器周期而for (i=2;i10;i+)对应的汇编语句为:MOV R7, #02H ;2个机器周期LOOP:INC R7 ;一个机器周期CJNER7,#0AH,LOOP ;两个机器周期,60,61,为什么当i=0时,编译器要多花一个机器周期对for循环初始化?这是因为在使用立即数时,单片机需要在代码空间(程序存储器)中为该立即数申请一个存储单元,用来存放该立即数,作为MOV指令的操作数;而累加器A是单片机中的寄存器,使用A可以节省一个字节的存储空间,从而实现以时间换取空间。(2)while循环格式为:while(循环体执行条件),花括号中为循环体内容。,61,62,(3)do while循环格式为:do ,花括号 中为循环体内容while(循环体执行条件)前两种循环是先进行循环条件是否满足的判断,才决定循环体是否执行;而“do while循环”是在执行完循环体后再判断条件是否满足,再决定循环体是否继续执行。三种循环中,经常使用的是for语句。下面来说明for语句的应用。,62,63,【例14-2】求1到100之间整数的和。程序如下:#include #include main( )int nVar1, nSum;for(nVar1=0,nSum=1;nSum 0;i- -)for(j=0;j333;j+)/*晶振为12MHz,j的范围见例14-1*/;/*空函数*/,76,77,void main( )/*主函数*/EA=1;/*总中断允许*/EX0=1;/*外部中断0中断允许*/IT0=1;/*选择外部中断0为跳沿触发方式*/While(1)/*循环*/ P1=0;/* P1口的8只LED全亮*/,77,78,void int0( ) interrupt 0 using 0 /*外中断0的中断服务函数*/EX0=0;/*禁止外部中断0中断*/P1=0x0f;/*低4位LED灭,高4位LED亮*/Delay(800) ;/*延时800ms*/P1=0xf0;/*高4位LED灭,低4位LED亮*/Delay(800); /*延时800ms */EX0=1;/*中断返回前,打开外部中断0中断*/,78,79,14.3.2 定时器程序的编写【例14-4】在AT89S51的P1口上接有8只LED。下面采用定时器T0的方式1的定时中断方式,使P1口外接的8只LED每0.5s闪亮一次。(1)设置TMOD寄存器定时器T0工作在方式1,应使TMOD寄存器的M1、M0=01;定时器模式,应设置C/ =0;对T0的运行控制仅由TR0来控制,应使GATE0=0。定时器T1不使用,各相关位均设为0。所以,TMOD寄存器应初始化为0x01。,79,80,(2)计算定时器T0的计数初值设定时时间5ms(即5000s),设定时器T0的计数初值为X,假设晶振的频率为11.0592MHz,则定时时间为:定时时间=(216X)12/晶振频率则5000=(216X)12/11.0592得:X=60928,转换成十六进制后为:0xee00,其中0xee装入TH0,0x00装入TL0。(3)设置IE寄存器本例由于采用定时器T0中断,因此需将IE寄存器中的EA、ET0位置1。,80,81,(4)启动和停止定时器T0将定时器控制寄存器TCON中的TR0=1,则启动定时器T0;TR0=0,则停止定时器T0定时。定时器T0方式1中断定时的参考程序:#includeChar i=100;/*给变量i赋初值*/void main()TMOD=0x01;/*设置定时器T0为方式1*/TH0=0xee;/*向TH0写入初值的高8位*/,81,82,TL0=0x00;/*向TL0写入初值的低8位*/P1=0x00; /*P1口8只LED点亮*/EA=1; /*总中断允许*/ET0=1; /*定时器T0中断允许*/ TR0=1; /*启动定时器T0*/while(1) ;/*循环*/; ,82,83,Void T0_int(void) interrupt 1 /*定时器T0中断服务程序*/TH0=0xee;/*给T0装入16位初值,计数4608后,T0溢出*/TL0=0x00; i-; /*循环次数减1*/if(i =0)P1= P1;/*P1口按位取反*/i =100;/*重新设置循环次数*/,83,84,14.3.3 串行口方式0应用程序的编写AT89S51的串口的方式0是同步串行通信接口。方式0的典型应用是外扩串行输入并行输出的同步移位寄存器74LS164,实现并行I/O的扩展。【例14-5】图14-2是利用串行口方式0通过74LS164外接8个LED发光二极管的接口电路,编写程序使发光二极管轮流显示。图中CLK端为同步脉冲输入端。STB为控制端,当STB=0时,则8位并行输出端关闭,但是允许串行数据从A和B端输入。当STB=1时,A和B输入端关闭,但允许8位并行数据输出。,84,85,当8位串行数据发送完毕后,引起中断,在中服务程序中,串行发出下一个8位数据。参考程序如下。,85,图14-2 串行口的方式0 外接8个LED发光二极管的接口电路,86,#include #includesbit P10 = 0x90;Xdata char nIndex;Delay();main() SCON = 0x00; /* 串行口初始化为方式0*/ES=1;EA=1;/* 全局中断允许 */ nIndex=1; SBUF=nIndex; P10=0; while(1),86,87,;void Serial_Port() interrupt 4 using 0if(TI=1)P10=1;Delay( );P10=1;nIndex=1;if(nIndex=0) nIndex=1;SBUF=nIndex;TI=0;RI=0;,87,88,Delay( )int nCounter;for(nCounter=0;nCounter128;nCounter+);,88,89,14.3.4 独立式键盘查询方式【例14-6】采用查询方式对实现独立式键盘的键值读取。独立式键盘的接口电路如图14-3所示。,89,图14-3 独立式键盘的接口电路,90,由图14-3,当P1口某一位为0时,表明该位所接的按键被按下,然后在计算机屏幕上输出按下的按键的信息。例如,3号键按下,则在PC机显示屏上显示“key No. 3 down”。程序如下:#include#include/*因为用到printf( )函数,所以要包含stdio.h */Uart_Init();sbit P10=0x90;/*定义P1口各位的位名*/sbit P11=0x91;sbit P12=0x92;,91,sbit P13=0x93;sbit P14=0x94;sbit P15=0x95;sbit P16=0x96;sbit P17=0x97;main( ) Uart_Init( );while(1); /*无限循环,不断查询按键状态,键值判断和提示信息的显示*/,91,92,if(P10=0)printf(key No.0 downn);/*如0号键按下,串行输出按下键信息*/if(P11=0)printf(key No.1 downn);/*如1号键按下,串行输出按下键信息*/if(P12=0)printf(key No.2 downn);/*如2号键按下,串行输出按下键信息*/if(P13=0)printf(key No.3 downn);/*如3号键按下,串行输出按键信息*/if(P14=0),92,93,printf(key No.4 downn);/*如4号键按下,串行输出按键信息*/if(P15=0)printf(key No. 5 downn);/*如5号键按下,串行输出按键信息*/if(P16=0)printf(key No. 6 downn);/*如6号键按下,串行输出按键信息*/if(P17=0)printf(key No. 7 downn);/*如7号键按下,串行输出按键信息*/,93,94,Uart_Init( ) /* 实验系统初始化函数,因要通过实验系统串行输出显示*/ SCON=0x52; /* 设置串行口控制寄存器SCON */ TMOD=0x20; /* 12MHz时钟时波特率为2400 */ TCON=0x69; /* TCON */ TH1=0xf3; /* TH1 */,94,95,14.3.5 行列式键盘查询方式【例14-7】本例为2008年某省电子大赛D题“电能质量检测装置”某参赛者设计中的一部分。单片机外接行列式键盘,用于控制单片机向FPGA(现场可编程门阵列)发出指令启动AD对信号采样分析,并控制单片机向LCD传输数据以显示波形和参数。采用查询方式对实现行列式键盘的键值读取。独立式44键盘的接口电路如下页图(图10-10)。,95,96,96,图 行列式键盘的接口电路,97,图中,每个按键行数与P1口高半字节对应,列数与P1口低半字节对应,行列号结合即可确定键位。首先查询判断是否有键按下:驱动P1口高半字节为全0,低半字节为全1,当读回P1口低半字节存在某位为0时,表明有键按下。延时去抖后,获取键位。先获取键位列号:按照之前的驱动方式对P1口驱动高半字节为全0,低半字节全1,读回P1口的

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论