• 322.00 KB
  • 2022-04-22 11:41:10 发布

山科大_《单片机的C语言程序设计与运用(第2版)》期末复习题及答案.doc

  • 60页
  • 当前文档由用户上传发布,收益归属用户
  1. 1、本文档共5页,可阅读全部内容。
  2. 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,可选择认领,认领后既往收益都归您。
  3. 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细先通过免费阅读内容等途径辨别内容交易风险。如存在严重挂羊头卖狗肉之情形,可联系本站下载客服投诉处理。
  4. 文档侵权举报电话:19940600175。
'一、填空1.KeiLC51软件中,工程文件的扩展名是____UV2______,编译连接后生成可烧写的文件扩展名是_____HEX_______。2.C51支持的指针有一般指针和存储器指针。3.C51中,没有专门的循环语句,我们可以用函数______crol_______完成一个字节的循环左移,用____irol_____完成二个字节的循环左移。4.函数     是C语言的基本单位。5.C语言中输入和输出操作是由库函数scanf        和   printf     等函数来完成。6.若有说明inti,j,k;则表达式i=10,j=20,k=30,k*=i+j的值为__900______。7.数组是一组有固定数目和相同类型成分分量的有序集合。8.变量的指针就是变量的地址;指针变量的值是指针。9.在C语言中,把多个不同类型的变量结合在一起形成的一个组合型变量,称为结构变量,简称结构。10.C51的数据类型有基本类型、构造类型、指针类型、空类型。11.C51的基本数据类型有位型、字符型、整形、长整形、浮点型、双精度浮点型。12.C51的构造数据类型有数组类型、结构类型、共用体型、梅举型。13.C51的存储类型有data、bdata、idata、pdata、xdata、code。14.C51的存储模式有SMALL、COMPACT和LARGE。15.C51程序与其他语言程序一样,程序结构也分为顺序结构、选择结构(或分支结构)、循环结构三种。16.数组的一个很重要的用途就是查表。17.二、单项选择题:1.89S51的内部程序存储器与数据存储器容量各为多少?(C)(A)64KB、128B(B)4KB、64KB(C)4KB、128B(D)8KB、256B2.在8x51芯片里,哪个引脚用于控制使用内部程序存储器还是外部程序存储器?(B)(A)XTAL1(B)/EA(C)/PSEN(D)ALE3.下列哪个不是KeilC的预处理命令?(C)(A)#include(B)#define(C)#exit(D)#if4.下列哪个不是KeilC的数据类型?(B)(A)void(B)string(C)char(D)float5.在8x51的输入/输出端口里,哪个输入/输出端口执行在输出功能时没有内部上拉电阻(A)(A)P0(B)P1(C)P2(D)P36.在KeilC的程序里,若要指定P0口的bit3,如何编写?(C)(A)P0.3(B)Port0.3(C)P0^3(D)Port^37.在8x51里,若要扩展外部存储器时,数据总线连接哪个输入/输出端口?(A)(A)P0(B)P1(C)P2(D)P38.点亮一般的LED所耗的电流约为多少?(D)(A)1~5uA(B)10~20uA(C)1~5mA(D)10~20mA9.在8x51的程序里,若要将个输入/输出端口设置成输入功能,应如何处理?(A)60 (A)先输出高电平到该输入/输出端口(B)先输出低电平到该输入/输出端口(C)先读取该输入/输出端口的状态(D)先保存该输入/输出端口的状态10.根据实验统计,当操作开关时,其不稳定关态大约持续多久?(B)(A)1~5ms(B)10~20ms(C)100~150ms(D)150~250us11.在KeilC里,判读开关状态时,使用if_elseif语句与使用switch语句有何差异?(B)(A)if-elseif语句较快(B)if-elseif语句有优先级(C)switch语句可判读较多开关状态(D)switch语句有优先级12.对于低电平动作(低电平触发)的开关而言,下列哪个不是在输入口上连接一个上拉电阻VCC的目的?(A)(A)提供足够的驱动电流(B)防止确定状态(C)保持输入高电平(D)防止噪声干扰13.中断功能具有什么好处?(C)(A)让程序更复杂(B)让程序执行速度更快(C)让程序更有效率(D)以上皆非14.8x51的IP缓存器的功能为何?(A)(A)设置中断优先级(B)启用中断功能(C)设置中断触发信号(D)定义CPU的网址15.在KeilC里,中断子程序与函数有何不同?(A)(A)中断子程序不必声明(B)函数不必声明(C)中断子程序必须有形式参数(D)中断子程序一定会有返回值16.若要同时启用INT0及INT1中断功能,则应如何设置?(B)(A)TCON=0x81(B)IE=0x85(C)IP=0x83(D)IE=0x0317.若要提高INT1的优先级,则应如何设置?(C)(A)IP=0x01(B)IE=0x01(C)IP=0x04(D)IE=0x0418.在8x51的Timer里,若使用Mode0,其最大计数值为多少个机器周期?(B)(A)65636(B)8192(C)1024(D)25619.12MHz的8x51系统里,哪一种方式一次可定时5ms?(A)(A)Mode0及Mode1(B)Mode1及Mode2(C)Mode2及Mode3(D)Mode3及Mode120.8x51的定时器,在下列哪种方式下具有自动加载功能?(C)(A)Mode0(B)Mode1(C)Mode2(D)Mode321.若将Timer0设置为外部启动,则可由哪个引脚启动?(A)(A)P3.2(B)P3.3(C)P3.4(D)P3.522.下列哪个IC具有将串行数据转换成并行数据的功能?(B)(A)74138(B)74164(C)74165(D)7416823.在同一时刻,只能接收或发送信号者称为什么?(A)(A)半双工(B)全双工(C)半单工(D)单工24‘在8x51的串行口里,在哪一种方式下,可利用Timer1产生波特率?(C)(A)Mode0(B)Mode1(C)Mode2(D)Mode325.在8x51里,若通过串行端口传出数据,则只要将数据放入哪个寄存器,CPU就会自动将它会传出?(B)(A)SMOD(B)SBUF(C)PCON(D)RBUF26.在8x51里,若CPU完成串行端口数据的接收将会如何?(D)60 (A)将TI标志变为0(B)将RI标志为0(C)将TI标志变为了1(D)将RI标志变为127.若要设置8x51串行端口方式,可在哪个寄存器中设置?(B)(A)SMOD(B)SCON(C)PCON(D)TCON28.在共阳极8*8LED点阵里,其阳极如何连接?(C)(A)各行阳极连接到行引脚(B)各列阳极连接到列引脚(C)各行阳极连接到列引脚(D)各列阳极连接到行引脚29.在共阳极8*8点阵里,其阴极如何连接?(A)(A)各行阴极连接到行引脚(B)各列阴极连接到列引脚(C)各行阴极连接到列引脚(D)各列阴极连接到行引脚30.通常8*8LED点阵的驱动方式是什么?(B)(A)直接驱动(B)扫描驱动(C)双向驱动(D)以上皆非31.若要对LCM下指令,则应如保设置?(A)(A)RS=0,R/~W=0(B)RS=1,R/~W=0(C)RS=1,R/~W=1(D)RS=0,R/~W=132.若要在LCM中显示些字符,则需把所要显示的字符放入何处?(B)(A)CGRAM(B)DDRAM(C)IRAM(D)GDRAM33.若要将数据写入LCM,则应如何设置?(B)(A)RS=0,R/~W=0(B)RS=1,R/~W=0(C)RS=1,R/~W=1(D)RS=0,R/~w=1341.利用下列(  D   )关键字可以改变工作寄存器组A、interrupt       B、sfrC、while          D、using35.C51中一般指针变量占用(  C     )字节存储。A、一个      B、两个      C、三个      D、四个36.使用宏来访问绝对地址时,一般需包含的库文件是( B )A、reg51.h    B、absacc.h  C、intrins.h   D、startup.h37.执行#definePA8255 XBYTE[0x3FFC];PA8255=0x7e;后存储单元0x3FFC的值是(A )A、Ox7e     B、8255H     C、未定     D、7e38.设有inta[]={10,11,12},*p=&a[0];则执行完*p++;*p+=1;后a[0],a[1],a[2]的值依次是(C)A、10,11,12B、11,12,12C、10,12,12D、11,11,12A、1B、2C、3D、4三、判断题:(每小题1分,共10分。正确的在括号内打“√”,错误的打“×”。判对得分,不判、判错均不得分。)1.若一个函数的返回类型为void,则表示其没有返回值。(√ )2.特殊功能寄存器的名字,在C51程序中,全部大写。(√ )3.“sfr”后面的地址可以用带有运算的表达式来表示。(  ×   )4.#include 与#include “reg51.h”是等价的。(√ )60 5.sbit不可以用于定义内部RAM的可位寻址区,只能用在可位寻址的SFR上。(× )6.Continue和break都可用来实现循环体的中止。(  ×   )7.所有定义在主函数之前的函数无需进行声明。(  ×   )8.inti,*p=&i;是正确的C说明。(√ )9.7&3+12的值是15。(  ×   )10.一个函数利用return不可能同时返回多个值。(√ )四、问答题1.简述C51语言和汇编语言的比较使用C51语言进行嵌入式系统的开发,有着汇编语言所不可比拟的优势:①编程调试灵活方便;②生成的代码编译效率高;③模块化开发;④可移植性好;⑤便于项目的维护;2.简述单片机的C语言和标准C的比较答:单片机的C语言和标准C的比较主要有以下几点不同:①C51中定义的库函数和标准的C语言定义的库函数不同;②C51中的数据类型和标准C的数据类型也有一定的区别;③C51变量的存储模式与标准C中变量的存储模式不一样;④C51与标准C的输入/输出处理不一样;⑤C51与标准C语言在函数使用方面有一定的区别。3.简述单片机的C语言的特点单片机的C语言的特点主要体现在以下几个方面:①无需了解机器硬件及其指令系统,只需初步了解MCS-51的存储器结构;②C51能方便的管理内部寄存器的分配、不同存储器的寻址和数据类型等细节问题,但对硬件控制有限;而汇编语言可以完全控制硬件资源;③C51在小应用程序中,产生的代码量大,执行速度慢;但在较大的程序中代码效率高;④C51程序由若干函数组成,具有良好的模块化结构,便于改进和扩充;⑤C51程序具有良好的可读性和可维护性;而汇编语言在大应用程序开发中,开发难度增加,可读性差;60 ⑥C51有丰富的库函数,可大大减少用户的编程量,显著缩短编程与调试时间,大大提高软件开发效率;⑦使用汇编语言编制的程序,当机型改变时,无法直接移植使用,而C语言程序是面向用户的程序设计语言,能在不同机型的机器上运行,可移植性好。4.简述使用KeilC51开发工具开发软件的流程使用KeilSoftware工具时,用户的项目开发流程和其它软件开发项目的流程极其相似,主要包括以下几个步骤: ① 创建一个项目,从器件库中选择目标器件并配置工具软件的设置; ② 用C语言或汇编语言创建源程序; ③ 用项目管理器生成用户的应用; ④ 修改源程序中的错误; ⑤ 调试链接后的应用。一个完整的8051工具集的框图可以很好地表述此开发流程,如图1-1所示。60 1.哪些变量类型是51单片机直接支持的?答:C51编译器支持的数据类型有:位型(bit)、无符号字符型(unsignedchar)、有符号字符型(signedchar)、无符号整型(unsignedint)、有符号整型(signedint)、无符号长整型(unsignedlong)、有符号长整型(signedlong)、浮点型(float)和指针型等。C51编译器支持的数据类型、长度和值域如表2-1所示。表2-1C51的数据类型数据类型长度/bit长度/byte值域bit10,1unsignedchar810~255signedchar81-128~127unsignedint1620~65535signedint162-32768~32767unsignedlong3240~4294967295signedlong324-2147483648~2147483647float324±1.176E-38~±3.40E+38(6位数字)double648±1.176E-38~±3.40E+38(10位数字)一般指针243存储空间0~655352.简述C51的数据存储类型答:我们都知道,8051单片机存储区可分为内部数据存储区、外部数据存储区以及程序存储区。8051单片机内部的数据存储区是可读写的,8051派生系列最多可有256字节的内部数据存储区,其中低128字节可直接寻址,高128字节(从0x80到0xFF)只能间接寻址,从20H开始的16字节可位寻址。内部数据区可分为3个不同的存储类型:data、idata和bdata。外部数据区也是可读写的,访问外部数据区比访问内部数据区慢,因为外部数据区是通过数据指针加载地址来间接访问的。C51提供两种不同的存储类型xdata和pdata访问外部数据。程序存储区是只能读不能写。程序存储区可能在805160 单片机内部或者在外部或者内外都有,这由8051单片机的硬件决定。C51提供了code存储类型来访问程序存储区。每个变量可以明确地分配到指定的存储空间,对内部数据存储器的访问比对外部数据存储器的访问快许多,因此应当将频繁使用的变量放在内部存储器中,而把较少使用的变量放在外部存储器中。各存储区的简单描述如表2-2所示。表2-2C51存储类型与8051存储空间的对应关系存储区描述DATA片内RAM的低128字节,可在一个周期内直接寻址BDATA片内RAM的位寻址区,16字节IDATA片内RAM的256字节,必须采用间接寻址XDATA外部数据存储区,使用DPTR间接寻址PDATA外部存储区的256个字节,通过P0口的地址对其寻址。使用MOVX@Ri,需要两个指令周期CODE程序存储区,使用DPTR寻址。以上介绍的是C51的数据存储类型,C51存储类型及其大小和值域如表2-3所示。表2-3C51存储类型及其大小和值域存储类型长度/bit长度/byte值域data810~255idata810~255pdata810~255code1620~65535xdata1620~655353.简述C51对51单片机特殊功能寄存器的定义方法答:MCS-51通过其特殊功能寄存器(SFR)实现对其内部主要资源的控制。MCS-51单片机有21个SFR,有的单片机还有更多的SFR,它们分布在片内RAM的高128字节中,其地址能够被8整除的SFR一般可以进行位寻址。关于MCS-51单片机的特殊功能寄存器参看附录A。对SFR只能用直接寻址方式访问。C51允许通过使用关键字sfr、sbit或直接引用编译器提供的头文件来实现对SFR的访问。60 (1)使用关键字定义sfr为了能直接访问特殊功能寄存器SFR,C51提供了一种自主形式的定义方法。这种定义方法与标准的C语言不兼容,只适用于对8051系列单片机进行C编程。这种定义的方法是引入关键字“sfr”,语法如下:sfr特殊功能寄存器名字=特殊功能寄存器地址;如:sfrSCON=0x98;/*串口控制寄存器地址98H*/sfrTMOD=0X89;/*定时器/计数器方式控制寄存器地址89H*/(2)通过头文件访问SFR8051系列单片机的寄存器数量与类型是极不相同的,因此对单片机特殊功能寄存器的访问可以通过对头文件的访问来进行。为了用户处理方便,C51编译器把MCS-51单片机的常用的特殊功能寄存器和特殊位进行了定义,放在一个“reg51.h”或“reg52.h”的头文件中。当用户要使用时,只需要在使用之前用一条预处理命令“#include”把这个头文件包含到程序中,然后就可以使用特殊功能寄存器名和特殊位名称了。用户可以通过文本编辑器对头文件进行增减。(3)SFR中位定义在8051单片机的应用问题中,经常需要单独访问SFR中的位,C51的扩充功能使之成为可能,使用关键字“sbit”可以访问位寻址对象。特殊位(sbit)的定义,像SFR一样不与标准C兼容。与SFR定义一样,用关键字“sbit”定义某些特殊位,并接受任何符号名,“=”号后将绝对地址赋给变量名。这种地址分配有三种方法:第一种方法:sbit位名=特殊功能寄存器名^位置;当特殊功能寄存器的地址为字节(8位)时,可使用这种方法。特殊功能寄存器名必须是已定义的SFR的名字。“^”后的“位置”语句定义了基地址上的特殊位的位置。该位置必须是0~7的数。如:第二种方法:sbit位名=字节地址^位置;这种方法是以一个整常数为基地址,该值必须在0x80~0xFF之间,并能被8整除,确定位置的方法同上。60 第三种方法:sbit位名=位地址;这种方法将位的绝对地址赋给变量,地址必须在0x80~0xFF之间。4.简述C51对51单片机片内I/O口和外部扩展的I/O口的定义方法答:C51对51单片机片内I/O口的定义方法是将片内I/O口看成SFR。C51对51单片机片外I/O的访问有两种比较常用的访问方法:(1)绝对宏C51编译器提供了一组宏定义来对51系列单片机的code、data、pdata和xdata空间进行绝对寻址。在程序中,用“#include”即可使用其中声明的宏来访问绝对地址,包括CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE、DWORD,具体使用方法参考absacc.h头文件。其中:CBYTE以字节形式对code区寻址;CWORD以字形式对code区寻址;DBYTE以字节形式对data区寻址;DWORD以字形式对data区寻址;XBYTE以字节形式对xdata区寻址;XWORD以字形式对xdata区寻址;PBYTE以字节形式对pdata区寻址;PWORD以字形式对pdata区寻址;(2)_at_关键字可以使用关键字_at_对指定的存储器空间的绝对地址进行访问,一般格式如下:[存储器类型]数据类型说明符变量名_at_地址常数;其中,存储器类型为C51能识别的数据类型,如省略则按存储器模式规定的默认存储器类型确定变量的存储器区域;数据类型为C51支持的数据类型;地址常数用于指定变量的绝对地址,必须位于有效的存储器空间之内;使用_at_定义的变量必须为全局变量。5.简述C51对51单片机位变量的定义方法答:除了通常的C数据类型外,C51编译器支持bit数据类型。采用关键字“bit”进行定义。如:bitdirection_bit;/*将direction_bit定义为位变量*/bitlock_pointer;/*将lock_pointer定义为位变量*/bitdisplay_invers;/*将display_invers定义为位变量*/60 6.C51和TurboC的数据类型和存储类型有哪些异同点?答:C51增加了位变量,取消了布尔变量。7.C51的data、bdata、idata有什么区别?答:data、bdata、idata是表明数据的存储类型,data是指片内RAM的低128字节,可在一个周期内直接寻址;bdata是指片内RAM的位寻址区,16字节;idata是指片内RAM的256字节,必须采用间接寻址。8.C51中的中断函数和一般的函数有什么不同?答:C51编译器允许用C51创建中断服务函数,中断函数是由中断系统自动调用的。中断函数的定义格式为:函数类型函数名interruptnusingn其中:interrupt和using为关键字;interrupt后面的n为中断源的编号,即中断号;using后面的n所选择的寄存器组,取值范围为0~3。定义中断函数时,using是一个选项,可以省略不用。如果不用using选项,则由编译器选择一个寄存器组作为绝对寄存器组。8051的中断过程通过使用interrupt关键字和中断号(0~31)来实现,中断号告诉编译器中断函数的入口地址。9.C51采用什么形式对绝对地址进行访问?答:绝对地址的访问包括片内RAM、片外RAM及I/O的访问。C51提供了两种比较常用的访问绝对地址的方法。(1)绝对宏C51编译器提供了一组宏定义来对51系列单片机的code、data、pdata和xdata空间进行绝对寻址。在程序中,用“#include”即可使用其中声明的宏来访问绝对地址,包括CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE、DWORD,具体使用方法参考absacc.h头文件。其中:CBYTE以字节形式对code区寻址;CWORD以字形式对code区寻址;60 DBYTE以字节形式对data区寻址;DWORD以字形式对data区寻址;XBYTE以字节形式对xdata区寻址;XWORD以字形式对xdata区寻址;PBYTE以字节形式对pdata区寻址;PWORD以字形式对pdata区寻址;如:#include#definePORTAXBYTE[0xFFC0]/*将PORT定义为外部I/O口,地址为0xFFC0,长度为8位*/#defineNRAMDBYTE[0x40]/*将NRAM定义为片内RAM,地址为40H,长度为8位*/(2)_at_关键字可以使用关键字_at_对指定的存储器空间的绝对地址进行访问,一般格式如下:[存储器类型]数据类型说明符变量名_at_地址常数;其中,存储器类型为C51能识别的数据类型,如省略则按存储器模式规定的默认存储器类型确定变量的存储器区域;数据类型为C51支持的数据类型;地址常数用于指定变量的绝对地址,必须位于有效的存储器空间之内;使用_at_定义的变量必须为全局变量。10.按照给定的数据类型和存储类型,写出下列变量的说明形式答:(1)在data区定义字符变量val1chardatavall;(2)在idata区定义整型变量val2intidataval2;(3)在xdata区定义无符号字符型数组val3[4]。unsignedxdataval3[4];(4)在xdata区定义一个指向char类型的指针px。charxdata*px;(5)定义可位寻址变量flag。bitflag;(6)定义特殊功能寄存器变量P3。SfrP3=0xB0;60 11.break和continue语句的区别是什么?答:break语句用于从循环代码中退出,然后执行循环语句之后的语句,不再进入循环。Continue语句用于退出当前循环,不再执行本轮循环,程序代码从下一轮循环开始执行,直到判断条件不满足为止。和break的区别是该语句不是退出整个循环。12.C51选择语句、循环语句包含那些?答:参看课本P68~P77。13.什么是重入函数?重入函数一般什么情况下使用,使用时有哪些需要注意的地方?答:多个函数可以同时使用的函数,称为重入函数。通常情况下,C51函数不能被递归调用,也不能应用导致递归调用的结构。有此限制是由于函数参数和局部变量是存储在固定的地址单元中。重入函数特性允许你声明一个重入函数。即可以被递归调用的函数。重入函数可以被递归调用,也可以同时被两个或更多的进程调用。重入函数在实时应用中及中断服务程序代码和非中断程序代码必须共用一个函数的场合中经常用到。14.如何消除键盘的抖动?答:由于按键是机械开关结构,所以当用手按下其中一个键时,往往会出现所按键在闭合位置和断开位置之间发生跳几下后才会稳定到闭合状态的情况。在释放一个键时,也会出现类似的情况,这就是键的抖动,抖动的持续时间不一,但通常不会大于10ms。若抖动问题不解决,就会引起对闭合键的多次读入。对于键抖动最方便的解决方法就是当发现有键按下后,不是立即进行扫描,而是延时大约10ms后再进行。由于一个键按下的时间一般会持续上百毫秒,所以延迟10ms后再扫描处理并不迟。15.在使用8051的定时器/计数器前,应对它进行初始化,其步骤是什么?答:  (1)确定T/C的工作方式——编程TMOD寄存器;      (2)计算T/C中的计数初值,并装载到TH和TL;      (3)T/C在中断方式工作时,须开CPU中断和源中断——编程IE寄存器;      (4)启动定时器/计数器——编程TCON中TR1或TR0位。16.外部中断源初始化步骤是什么?外部中断源初始化2个外部中断源:/INT0中断,外部中断0请求,占用P3.2引脚,其中断请求号为0。/INT1中断,外部中断1请求,占用P3.3引脚,其中断请求号为2。(1)TCO寄存器中的IT0、IT1位(2)IP寄存器中的PX0、PX1位(3)IE寄存器中的EA、EX0、EX1位五、读程序、编程序1.第四章中断:例4-1P104例4-9P116例4-10P117定时:例4-16P123例4-17P124例4-18P1262.第五章例5-9P154例5-10P155例5-11P1573.第六章静态:例6-1P169动态:例6-3P171独立式键盘:例6-6P178矩阵式键盘:例6-7P18160 LED阵列:例6-10P187LCD1602:例6-12P1944.第七章D/A:例7-4P208A/D:例7-6P213第四章中断类例4-1P104假设外部中断0和外部中断1均为下降沿触发,当外部中断0发生时,P0端口的电平反向,当外部中断1发生时,P1端口的电平反向。#includevoidIS0(void)interrupt0{P0=~P0;}//P0端口反向voidIS1(void)interrupt260 {P1=~P1;}//P1端口反向voidmain(){P0=0x00;P1=0xFF;IT0=1;IT1=1;EX0=1;EX1=1;EA=1;while(1);}【例4-9】外部中断示例在本实例中,首先通过P1.7口点亮发光二极管D1,然后外部输入一脉冲串,则发光二极管D1亮、暗交替。#includesbitP1_7=P1^7;voidinterrupt0()interrupt0using2//外部中断060 {P1_7=!P1_7;}voidmain(){EA=1;//开中断IT0=1;//外部中断0脉冲触发EX0=1;//外部中断0P1_7=0;do{}while(1);}如果有3个脉冲,则灯亮、暗交替一次,可如下面编程:#includeSbitP17=P1^7;unsignedchari=3;voidmain(){EA=1;IT0=1;EX0=1;P17=0;do{}while(1);}voidinterrupt0()interrupt0{i=i-1;if(i==0){P17=!P17;i=3;}}60 【例4-10】如图4-18所示,8只LED阴极接至单片机P0口,两开关S0、S1分别接至单片机引脚P3.2()和P3.3()。编写程序控制LED状态。按下S0后,点亮8只LED;按下S1后,变为闪烁状态。#includesbitP32=P3^2;voiddelay(unsignedintd)//定义延时子函数{while(--d>0);}voidmain(){P0=0xFF;//熄灭LEDIT0=1;IT1=1;//外中断0、1脉冲触发方式EA=1;EX0=1;EX1=1;//开中断60 for(;;)//延时等待中断发生{;}}voidINT0_ISR()interrupt0//外中断0中断服务函数{P0=0x00;}voidINT1_ISR()interrupt2//外中断1中断服务函数{while(P32!=0)//如果有外部中断0,则退出{delay(5000);P0=0x00;delay(5000);P0=0xFF;}}定时类【例4-16】设单片机的fosc=12MHz,要求在P1.0上产生周期为2ms的方波。要在P1.0上产生周期为2ms的方波,定时器应产生1ms的周期性定时,定时到对P1.0取反。要产生1ms的定时,应选择方式1,定时器方式。TMOD的确定:选择定时器/计数器T0,定时器方式。方式1,GATE不起作用,高4位为0000,TMOD=01H。60 TH、TL的确定:单片机的fosc=12MHz,则单片机的机器周期为1ms,1ms=1000ms,计数器的计数初值为65536-1000,TH0=(65536-1000)/256,TL0=(65536-1000)%256。①采用查询方式程序如下:#includesbitP1_0=P1^0;voidmain(void){TMOD=0x01;TR0=1;for(;;){TH0=(65536-1000)/256;TL0=(65536-1000)%256;do{}while(!TF0);P1_0=!P1_0;TF0=0;}}②采用中断方式程序如下:#includesbitP1_0=P1^0;60 voidtimer0(void)interrupt1using1{P1_0=!P1_0;TH0=(65536-1000)/256;TL0=(65536-1000)%256;}voidmain(void){TMOD=0x01;P1_0=0;TH0=(65536-1000)/256;TL0=(65536-1000)%256;EA=1;ET0=1;TR0=1;do{}while(1);}60 【例4-17】设系统时钟频率为12MHz,编程实现从P1.1输出周期为1s的方波。要输出周期为1s的方波,应产生500ms的周期性定时,定时到则对P1.1取反即可实现。由于定时时间较长,一个定时器/计数器不能直接实现,一个定时器/计数器最长定时时间为65ms多一点,可以用以下两种方法实现。(1)方法一用定时/计数器T0产生周期性为10ms的定时,然后用一个变量对10ms计数50次。系统时钟为12MHz,定时/计数器T0定时10ms,计数值N为10000,选方式1,方式控制字为00000001B(01H),则初值X为X=65536-10000。60 #includesbitP1_1=P1^1;unsignedchari;//定义计数变量voidmain(){i=0;//初始化TMOD=0x01;TH0=(65536-10000)/256;TL0=(65536-10000)%256;EA=1;ET0=1;TR0=1;while(1);}voidtime0_int(void)interrupt1//中断服务程序{TH0=(65536-10000)/256;//重载初始值TL0=(65536-10000)%256;//重载初始值i++;//每发生一次中断,计数变量加1if(i==50)//发生50次中断,定时0.5ms{P1_1=!P1_1;i=0;//计数变量清零}}60 (2)方法二用定时/计数器T1计数实现,对10ms计数50次。定时/计数器T1工作于计数方式时,计数脉冲通过T1(P3.5)输入。设定时/计数器T0定时时间到对P1.0取反一次,则T1(P3.5)每10ms产生一个计数脉冲,那么定时500ms只需计数25次,设定时/计数器T1工作于方式2,初值X=256-25=231,TH1=TL1=231。因为定时/计数器T0工作于方式1,定时方式,则这时方式控制字为01100001B(61H)。定时/计数器T0和T1都采用中断方式工作。#includesbitP1_1=P1^1;sbitP1_0=P1^0;voidmain(){TMOD=0x61;//初始化TH0=(65536-10000)/256;TL0=(65536-10000)%256;60 TH1=231;TL1=231;EA=1;ET0=1;ET1=1;TR0=1;TR1=1;while(1);}voidtime0_int(void)interrupt1//T0中断服务程序{TH0=(65536-10000)/256;//重载初始值TL0=(65536-10000)%256;//重载初始值P1_0=!P1_0;}voidtime1_int(void)interrupt3//T1中断服务程序{P1_1=!P1_1;}【例4-18】设系统时钟频率为12MHz,编程实现:P1.1引脚上输出周期为1s,占空比为20%的脉冲信号根据输出要求,脉冲信号在一个周期内高电平占0.2s,低电平占0.8s,超出了定时器的最大定时间隔,因此利用定时器0产生一个基准定时配合软件计数来实现。取50ms作为基准定时,采用工作方式1,这样这个周期需要20个基准定时,其中高电平占4个基准定时。#includesbitP1_1=P1^1;unsignedchari;//定义计数变量60 voidmain(){i=0;//初始化TMOD=0x01;TH0=(65536-50000)/256;TL0=(65536-50000)%256;EA=1;ET0=1;TR0=1;while(1);}voidtime0_int(void)interrupt1//中断服务程序{TH0=(65536-50000)/256;//重载初始值TL0=(65536-50000)%256;i=i+1;if(i==4)P1_1=0;//高电平时间到变低elseif(i==20)//周期时间到变高{P1_1=1;i=0;//计数变量清零}}60 第五章【例5-9】串行口自发自收#include#defineucharunsignedchar#defineuintunsignedintvoidmain()60 {uchari=0x55;uintj=0;TMOD=0X20;//设定定时器1模式2TL1=TH1=0XF4;PCON=0X00;SCON=0X50;TR1=1;while(1){SBUF=i;//发送数据do()while(!RI)RI=0;TI=0;i=SBUF;//读取接收数据P1=i;i=~i;//将发送数据取反for(j=0;j<12500;j++);}}【例5-10】两个单片机串行通信160 单片机1的C51源程序代码:#include#defineuintunsignedint#defineucharunsignedcharvoidmain(){uchari;TMOD=0x20;TH1=TL1=0xff;SCON=0x50;PCON=0x80;TR1=1;P1=0xff;while(1){P1=0xff;i=P1;SBUF=i;while(TI==0)60 {;}TI=0;}}单片机2的C51源程序:#include#defineuintunsignedint#defineucharunsignedcharvoidmain(){uchari=0;TMOD=0x20;TH1=TL1=0xff;SCON=0x50;PCON=0x80;TR1=1;while(1){while(RI==0){;}RI=0;i=SBUF;P1=i;}}两个单片机串行通信2C51源程序代码如下:60 #include#defineucharunsignedchar#defineTR1//TR=1,发送ucharidatabuf[10];ucharpf;voidmain(){int();//串行口初始化子函数if(TR==0){send(buf);}//发送else{receive(buf);}//接收}/*串口初始化子函数*/voidinit(void){TMOD=0x20;//T1工作于方式2TH0=0xE8;TL0=0xE8;TR1=1;SCON=0X50;//串行口工作于方式1,REN=1}/*发送子函数*/voidsend(ucharidata*d)60 {uchari;do{SBUF=0xAA;//发送联络信号while(TI==0);//等待一帧发送完毕TI=0;//发送完毕,标志位清0while(RI==0);//等待乙机应答信号RI=0;}while(SBUF^0xBB!=0);//乙机未准备好,继续联络do{pf=0;//校验和变量清0for(i=0;i<10;i++){SBUF=d[i];//发送一个数据pf+=d[i];//计算校验和while(TI==0);TI=0;}SBUF=pf;//发送校验和while(TI==0);TI=0;while(RI==0);RI=0;//等待乙机应答}while(SBUF!=0);//回答出错,则重新发送}/*接收函数*/voidreceive(ucharidata*d)60 {uchari;do{while(RI==0);RI=0;}while(SBUF^0xAA)!=0);//判断甲机是否请求SBUF=0xBB;//发应答信号while(TI==0);TI=0;while(1){pf=0;//清校验和for(i=0;i<10;i++){d[i]=SBUF;//接收数据pf+=d[i];}//计算校验和while(RI==0);RI=0;//接收甲校验和If((SBUF^pf)==0){//比较校验和SBUF=0x00;break;}//校验和相等,发0x00else{SBUF=0xFF;//校验和不相等,发0Xffwhile(TI==0);TI=0;}}}第六章静态:【例6-1】利用单片机的并行口作为静态显示的输出口的示例60 静态轮流显示“12”、“--”和“AB”的C51源程序如下:#include#defineucharunsignedcharuchardatadis_buf[2];//显示缓冲区ucharcodeable[18]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40,0x00};//显示的代码表voiddl(){unsignedinti;for(i=0;i<40000;i++);}voiddisplay(void)//显示函数{ucharsegcode;segcode=dis_buf[0];//P0口显示60 segcode=table[segcode];P0=segcode;segcode=dis_buf[1];//P3口显示segcode=table[segcode];P3=segcode;}voidmain(void)//主函数{while(1){dis_buf[0]=1;dis_buf[1]=2;//显示12display();dl();dis_buf[0]=16;dis_buf[1]=16;//显示--display();dl();dis_buf[0]=10;dis_buf[1]=11;//显示ABdisplay();dl();}}示例中的显示函数display()可以再简单一些,如下面程序段:voiddisplay(void){P0=table[dis_buf[0]];//P0口显示P3=table[dis_buf[1]];//P3口显示}动态:【例6-3】利用MCS-5160 单片机的并行口作为动态显示的段口与位口的示例6位数码管动态显示“123456”的C51源程序如下1)随机调用#include#defineucharunsignedcharuchardatadis_buf[6];//显示缓冲区ucharcodetable[18]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40,0x00};//代码表voiddl_ms()//延时1ms函数{unsignedintj;for(j=0;j<200;j++);}voiddisplay(void)//显示函数{ucharsegcode,bitcode,i;60 bitcode=0xfe;//位码赋初值for(i=0;i<6;i++){segcode=dis_buf[i];//显示缓冲器内容查表P0=table[segcode];P3=bitcode;dl_ms();P3=0xff;//关闭显示bitcode=bitcode<<1;//调整位码bitcode=bitcode|0x01;}}voidmain(void){dis_buf[0]=1;dis_buf[1]=2;dis_buf[2]=3;dis_buf[3]=4;dis_buf[4]=5;dis_buf[5]=6;while(1){display();}}(2)定时调用定时调用是通过定时器/计数器的定时功能来定时一定的时间(如20ms),定时时间到来调用显示函数。voidmain(void)//定时调用{TMOD=0x01;60 TH0=-20000/256;TL0=-2000%256;EA=1;ET0=1;TR0=1;dis_buf[0]=1;dis_buf[1]=2;dis_buf[2]=3;voidtime0_int()interrupt1{TH0=-20000/256;TL0=-2000%256;display();}dis_buf[3]=4;dis_buf[4]=5;dis_buf[5]=6;while(1);}【例6-6】独立式按键示例C51源程序如下:#include60 #defineucharunsignedchar#defineuintunsignedintuchardatakey2;codeuchardirtab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80};voiddl_6(){uinti;for(i=0;i<600;i++);}voidkey(){ucharkey1;P1=0xff;key1=P1;if(key1!=0xff){dl_6();P1=0xff;key1=P1;if(key1!=0xff){key1=P1;switch(key1){case0xff:key2=8;break;case0xfe:key2=0;break;case0xfd:key2=1;break;case0xfb:key2=2;break;60 case0xf7:key2=3;break;case0xef:key2=4;break;case0xdf:key2=5;break;case0xbf:key2=6;break;case0x7f:key2=7;break;default:break;}}}}voidmain(){key2=8;while(1){key();P3=dirtab[key2];}}【例6-7】4×4矩阵键盘示例60 #include#defineucharunsignedchar#defineuintunsignedintuchardatadir_buf;codeuchardirtab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};codeucharkeytab[]={0xee,0xed,0xeb,0xe7,0xde,0xdd,0xdb,0xd7,0xbe,0xbd,0xbb,0xb7,0x7e,0x7d,0x7b,0x77};voiddelay(void);voidkeyscan();voiddir();ucharkey;voidmain(void)60 {dir_buf=16;while(1){keyscan();dir();}}voiddir(){P2=dirtab[dir_buf];}voiddelay(void){uchari;for(i=0;i<200;i++);}voidkeyscan(){ucharcode_h,code_l,i1,i2;P1=0xf0;//所有的行输出0code_l=P1;//读列值code_l=code_l&0xf0;//屏蔽掉高4位if(code_l!=0xf0){delay();//P1=0xf0;code_l=P1;60 code_l=code_l&0xf0;if(code_l!=0xf0){code_h=0xfe;for(i1=0;i1<4;i1++){kk:P1=code_h;code_l=P1;code_l=code_l&0xf0;if(code_l==0xf0){code_h=(code_h<<1)|0x01;gotokk;}gotoll;}//for}ll:code_h=code_h&0x0f;key=code_h+code_l;for(i2=0;i2<16;i2++){if(key==keytab[i2]){dir_buf=i2;}}}}60 *【例6-10】一个8×8共阴极LED点阵显示“ILOVEYOU”(不用记)一个8×8在某一个时刻只能显示一个字符,要想显示字符串,必须在显示完一个字符后接着显示下一个字符,因此需要建立一个字符串库。由于每个字符有8个段码值,该字符串有8个字符,所以该字符串库中有8×8共64个段码值。字符串ILOVEYOU的字模段码值I00H,00H,42H,7EH,42H,42H,00H,00HL00H,42H,7EH,42H,02H,02H,02H,00HO00H,3CH,22H,42H,42H,22H,3CH,00HV00H,40H,70H,0CH,0CH,70H,40H,00HE00H,42H,7EH,4AH,4AH,5AH,22H,00HY00H,40H,62H,1EH,12H,60H,40H,00HO00H,3CH,22H,42H,42H,22H,3CH,00HU00H,7CH,46H,02H,02H,7CH,40H,00H#include60 #defineuintunsignedint#defineucharunsignedcharcodeuchartab1[]={0x00,0x00,0x42,0x7e,0x42,0x42,0x00,0x00,//I0x00,0x42,0x7e,0x42,0x02,0x02,0x02,0x00,//L0x00,0x3c,0x22,0x42,0x42,0x22,0x3c,0x00,0x00,0x40,0x70,0x0c,0x0c,0x70,0x40,0x00,x00,0x42,0x7e,0x4a,0x4a,0x5a,0x22,0x00,0x00,0x40,0x62,0x1e,0x12,0x60,0x40,0x00,0x00,0x3c,0x22,0x42,0x42,0x22,0x3c,0x00,0x00,0x7c,0x46,0x02,0x02,0x7c,0x40,0x00};//Ucodeuchartab2[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};dataucharj;voiddelay(uintn){datauinti;for(i=0;i>1;scancode=~scancode;}}60 }}voidmain()//主函数{uchar*s;ucharz;uchari=0,j=0;//i为LCD的行,j为LCD的列delay_LCM(15);init_1602();//1602初始化s="WELCOMETOHERE!";display_1602(s,0,0);//第一行显示"WELCOMTOHERE!"delay_LCM(200);delay_LCM(200);delay_LCM(200);while(1){if(checkkey()==0x00)continue;else{{i=1;//LCD在第二行显示z=keyscan();if(j>=16)60 {j=0;i=1;break;}elseDisplayOneChar(j,i,a[z]);j++;delay(100);}}}}第七章【例7-4】采用DAC0832实现的波形发生器#include#include#defineucharunsignedchar#defineDAC0832XBYTE[0x7fff]codeucharsintab[]={0x7f,0x89,0x94,0x9f,0xaa,60 0xb4,0xbe,0xc8,0xd1,0xd9,0xe0,0xe7,0xed,0xf2,0xf7,0xfa,0xfc,0xfe,0xff};sbitP10=P1^0;sbitP11=P1^1;sbitP12=P1^2;sbitP13=P1^3;ucharm=0;voiddelay(){ucharj;for(j=0;j<250;j++);}voidint0()interrupt0{P1=0xff;if(P10==0){m=1;}if(P11==0){m=2;}if(P12==0){m=3;}if(P13==0){m=4;}}60 voidmain(){uchardatai;EA=1;EX0=1;IT0=1;while(1){kkk:switch(m){case0:gotokk;break;case1:gotok0;break;case2:gotok1;break;case3:gotok2;break;case4:gotok3;break;default:break;}k0:while(1){for(i=0;i<0xff;i++){DAC0832=i;}gotokkk;}60 k1:while(1){for(i=0;i<0xff;i++){DAC0832=i;}for(i=0xff;i>0;i--){DAC0832=i;}gotokkk;}k2:while(1){DAC0832=0xff;delay();DAC0832=0;delay();delay();gotokkk;}k3:while(1){for(i=0;i<18;i++){DAC0832=sintab[i];}for(i=18;i>0;i--){DAC0832=sintab[i];}for(i=0;i<18;i++){DAC0832=~sintab[i];}60 for(i=18;i>0;i--){DAC0832=~sintab[i];}gotokkk;}kk:DAC0832=0;}}【例7-6】1路模拟输入A/D转换示例(1)无条件方式:#include#include#defineucharunsignedchar#defineADC08090XBYTE[0x78ff]60 voiddelay(){ucharj;for(j=0;j<250;j++);}voidmain(){while(1){ADC08090=0;//启动A/Ddelay();P1=ADC08090;//读取数据}}(2)查询方式#include#include#defineucharunsignedchar#defineADC08090XBYTE[0x78ff]sbitP33=P3^3;Voidmain(){while(1){ADC08090=0;//启动A/Dl1:P33=1;if(P33==0){P1=ADC08090;}//读取数据elsegotol1;}60 }(3)中断方式#include#include#defineucharunsignedchar#defineADC08090XBYTE[0x78ff]sbitP33=P3^3;voidmain(){EA=1;EX1=1;IT1=1;ADC08090=0;//启动A/Dwhile(1);}voidint0()interrupt2{P1=ADC08090;//读取数据ADC08090=0;//启动A/D}60'