《单片机原理及接口》课程
设计报告
题 目: 时钟系统设计 姓 名:
专 业: 电信 班 级: 1 学 号: ***** 指导教师:
信息工程学院 二0一六年一月
时钟系统设计
摘要:本系统是基于AT89C51单片机的具有准点报时、调时、以及可设闹钟功能的简单数字时钟系统的设计。以AT89C51为核心控制器,系统分为时钟模块、显示模块、按键模块及闹钟模块。系统以单片机内部定时器作为时钟模块的主要控制模块,通过频率计数实现计时功能,采用了8位数码管来显示时间,采用独立按键做为时间调时以及闹钟设置按键,采用蜂鸣器作为报时闹钟系统。通过Keil软件C语言程序的编写、编译、调试以及硬件单片机的连接,实现了时间显示(24小时制)、闹钟设置、时间调试以及准点报时,可复位的功能,并运行了该电路的程序,得出了符合实验设计要求的结果。 关键字:数字时钟;AT89C51;数码管;C语言;闹钟;调时
1 系统设计内容
1.1 前言
随着近年来科技的进步,单片机在近十年也取得了飞速的发展。目前,单片机已经渗透到我们生活的各个领域,几乎很难找到哪个领域没有单片机的踪迹。导弹的导航装置,飞机上各种仪表的控制,计算机的网络通讯与数据传输,工业自动化过程的实时控制和数据处理,广泛使用的各种智能IC卡,民用豪华轿车的安全保障系统,录像机、摄像机、全自动洗衣机的控制,以及程控玩具、电子宠物等等,这些都离不开单片机。更不用说自动控制领域的机器人、智能仪表、医疗器械以及各种智能机械了。因此,单片机的学习、开发与应用将造就一批计算机应用与智能化控制的科学家、工程师。现在虽然单片机的品种繁多,各具特色,但仍以MCS-51为核心的单片机占主流,兼容其结构和指令系统的有PHILIPS公司的产品,ATMEL公司的产品和中国台湾的WinBond系列单片机。以8031为核心的单片机占据了半壁江山,在一定的时期内,这种情形将得以延续,将不存在某个单片机一统天下的垄断局面,走的是依存互补,相辅相成、共同发展的道路。
数字电子时钟作为单片机基础学习的一个重要的典型,是很多初学的学习单片机的很好的例子,是对单片机的定时器的一个重要的应用。可以说,学习单片机的两个重点就是中断和定时器,学会了数字时钟的编程就是对单片机学习的一个很好的综合应用。
1.2 设计要求
应用知识:I/O口应用、数码显示、定时/计数器、中断。
基本要求:设计一时钟系统,系统具有时钟功能,能准确显示时、分、秒。系统还应具有校正功能:能够修改当前的时间。
扩展部分:具备设定闹钟和定时闹钟响功能。
1.3 设计思路
2
通过软件程序的编程,硬件电路的调试,实现了简单时钟系统的设定,使得该系统具有正常走时,能够正确的显示时、分、秒;能够进行调时,修改当前的时间,并且能够设定闹钟,使闹钟能够定时响。
首先是时间的调试:若要进行正常的时间调试需要有进入时间的调时试状态按键以及调试是加或者是减的按键,即每个状态要3个按键,共有时、分、秒三个状态,这样就一共需要9个按键,这样的编程太过复杂,硬件需要的按键也比较多。考虑到,可通过同一个按键来控制定时器的走与停,以及通过按键的次数来控制所进入的调试状态是时、分还是秒。当调试进入某一状态时,需要分别通过两个按键来控制时间的加与减,而此时的加与减的按键相当于是局部变量,可以在三个状态中分别使用。这样一共就只需要3个按键,大大节省了硬件并且简化了软件的编程。设3个按键分别为key1、key2和key3,设计为当按键key1被按下时,停止走时,进入调时状态,当key1被按下1次,进行秒的调整;当key1被按下2次,进行分的调整,当key1被按下3次,进行时的调整;当key1被按下4次,停止调时,继续进行走时。在key1被按下4次以下的情况下,若按下key2键,则进行时间加,若按下key3,则进行时间减。
其次是闹钟设定:基于时间调试的设置思路,同样将进入闹钟状态以及闹钟设定的按键分开,考虑到此时闹钟设置时,数码管的显示问题以及定时器的走时问题,故将进入闹钟的设定状态和时设定、分设定的按键分开,而分和时的设定又都需要时间的加和减,即各需要两个按键,再加上进入闹钟设定状态的一个按键,共需要5个按键来实现闹钟的设定。设这5个按键分别为key0、key4、key5、key6和key7,其中,key0为进入闹钟状态按键,key4和key6分别为分钟设定的加和减的按键,key5和key7分别为时设定的加和减设定的按键。依照思路可设定为当key0一直被按住的情况下,此时进入闹钟设定状态,但是定时器仍然在工作。在key0一直被按住的情况下,若key4或key5被按下,则分别进行分和时的累加状态;若key6和key7被按下,则分别进行分和时的减状态,当放开key0时,继续进行走时。若想再次进行闹钟的设定,重复上述的步骤,不过当再次按下按键key0时,则显示上次设置的闹钟时间。
2 系统方案设计
2.1 方案论证 (1)、核心控制模块
方案一:采用FPGA作为核心控制模块。由于FPGA具有强大的资源,使用方便灵活,易于进行功能扩展,特别是结合了EDA,可以达到很高的效率。此方案逻辑虽然简单一点,但是一块FPGA的价格很高,对于做数字钟来说有一点浪费,而且FPGA比较难掌握,本设计中不作过多研究,也不采用此方案。;
方案二:采用AT89C51作为核心控制模块。此方案中AT89C51单片机的入门学习相对交容易,易于理解,外围电路比较简单,成本比较低,此系统控制灵活能很好地满足本课题的基本要求和扩展要求,因此选用该方案。
3
(2)、显示模块
方案一:采用LCD1602液晶显示屏,液晶极其省电,但是使用有温度范围限制,且因是反光式的,在外界光线很明亮的情况下很容易看不清楚。数码管是LED发光的效果,液晶是分子偏转引起的暗影效果,显示不是很清晰。
方案二:采用LED数码管显示,数码管在低电压小电流的驱动下就能够发光,发光响应时间短,高频性好,单色性好,亮度高,显示相对而言比较清晰。而且体积小,重量轻,抗冲击性能好,寿命长,成本低。
(3)、按键模块
方案一:采用矩阵按键,矩阵按键需要通过扫描控制和译码,设计时需要有数值移位寄存器对已有数值进行存储和调用。软件程序设计比较繁琐,硬件连接复杂。
方案二:采用独立按键。单片机仿真板上有专用独立按键,连接方便,使用简单。且易于软件编程,适合本系统的设计。
2.2 设计原理
本系统数字时钟设计原理主要利用AT89C51单片机,由单片机的P0口控制数码管的位显示,P2口控制数码管的段显示,P3口与按键相接用于时间的校正以及闹钟的设定。设计的主要方面有计时原理,中断及定时器原理以及调时方式、按键的消抖。
整个系统工作时,秒信号产生器是整个系统的时基信号,它直接决定计时系统的精度,将标准秒信号送入“秒计数器”,“秒计数器”采用60进制计数器,每累计60秒发出一个“分脉冲”信号,该信号将作为“分计数器”的时钟脉冲。“分计数器”也采用60进制计数器,每累计60分钟,发出一个“时脉冲”信号,该信号将被送到“时计数器”。“时计数器”采用24进制计时器,可实现对一天24小时的累计。显示电路将“时”、“分”、“秒”计数器的输出,通过六个七段LED显示器显示出来。校时电路是直接加一个脉冲信号到时计数器或者分计数器或者秒计数器来对“时”、“分”、“秒”显示数字进行校对调整。在本设计中,24小时时钟显示、秒表的设计和显示都是依靠单片机中的定时器完成。使用定时器T0产生1s的中断,在中断程序中完成每一秒数字的变化,并在主程序中动态显示该字符。
典型的8051单片机有5个中断源(外部中断0、1,内部定时器中断0、1,串口中断),具有两个中断优先级。与中断系统有关的特殊功能寄存器有中断允许寄存器IE、中断优先级控制寄存器IP、中断控制寄存器TCON和SCON中有关位。MCS-51单片机基本的中断系统结构如下图所示。
4
图2-1 MCS-51的中断系统
MCS-51的CPU对中断源的开放或屏蔽,即每一个中断源是否被允许中断,是由内部的中断允许寄存器IE(地址A8H)控制的。IE中具体各位的意义如下所示:EA:CPU的中断开放标志。 EA = 1,CPU开放中断;EA = 0,CPU屏蔽所有的中断申请。EX0:外部中断0中断允许位。 EX0 = 1,允许中断;EX0 = 0,禁止中断。ET0:T0的溢出中断允许位。ET0 = 1,允许T0中断;ET0 = 0,禁止T0中断。EX1:外部中断1中断允许位。EX1 = 1,允许外部中断1中断;EX1 = 0,禁止外部中断1中断。ET1:定时器/计数器 T1的溢出中断允许位。ET1 = 1,允许T1中断;ET1 = 0禁止T1中断。ES:串行口中断允许位。ES = 1,允许串行口中断;ES = 0禁止串行口中断。
中断优先级管理寄存器IP(地址8BH):MCS-51有两个中断优先级,一个正在被执行的低优先级中断服务程序能被高优先级中断所中断,但不能被另一个同级的或低优先级中断源所中断。CPU的查询顺序是:①外部中断0,②定时器T0中断,③外部中断1,④定时器T1中断,⑤串行口中断(先外部后内部,先0后1)。 中断服务函数的格式如下所示:
void 函数名(void) interrupt n using m
{ 函数体语句 }
其中,interrupt和using是为编写C51中断服务程序而引入的关键字,interrupt表示该函数是一个中断服务函数,interrupt后的整数n表示该中断服务函数是对应哪一个中断源。每个中断源都有系统指定的中断编号:
表1 中断编号表
中断源 中断编号 外部中断0 0 定时器中断T0 1 外部中断1 2 定时器中断T1 3 串行口中断 4 5
51单片机有三个内部中断,16位定时器/计数器T0、T1的溢出中断源和串行口的发送/接收中断。对T0和T1中断,当定时计数回‘0’溢出时,由硬件自动置位TCON中的TF0或TF1中断请求标志位。定时/计数器实际上是一个加1计数器,它可以工作于定时方式,也可以工作于计数方式。两种工作方式实际上都是对脉冲计数,只不过所计脉冲来源不同。定时器的脉冲是由51单片机的内振荡器经过12分频后产生的,故当单片工作于定时状态时,计数脉冲的最高频率为f=fosc/12。
51单片机的寄存器有方式控制寄存器TMOD;加法计数寄存器TH0、TH1 (高八位),TL0、TL1 (低八位);定时/计数到标志TF0、TF1(中断控制寄存器TCON);定时/计数器启停控制位TR0、TR1(TCON);定时/计数器中断允许位ET0、ET1(中断允许寄存IE);定时/计数器中断优先级控制位PT0、PT1(中断优IP)。在定时器工作前,必须将控制命令写入定时器的控制寄存器,即进行初始化。TMOD的低四位为T0的方式字,高四位为T1的方式字。TMOD不能位寻址,必须整体赋值。TMOD各位的含义如下:
1. 工作方式选择位M1、M0 :M1、M0的状态决定定时器的工作方式:
表2 工作方式选择表
M1 0 0 1 1 M0 0 1 0 1 功能说明 工作方式0(13位方式) 工作方式1(16位方式) 工作方式2(8位自动装入计数初值方式) 工作方式3(T0为两个8位方式) 2. 定时和计数方式选择位C/T。当C/T=1时为计数方式;C/T=0时为定时方式。
3. 门控位GATE。GATE与TR0、TR1配合决定定时/计数器的启停。当GATE = 0时,软启动。定时器/计数器的启停只受定时器运行控制位(TR0、TR1)的控制。当GATE = 1时,软硬启动。定时器/计数器的启停除受TR0、 TR1控制外,还受外部引脚(INT0、 INT1)输入电平的控制(为高)。即TR0和INT0控制T0的运行,TR1和INT1控制T1的运行。
MCS-51的定时器有方式0、方式1、方式2和方式3这4种工作方式。以方式1为例,当M1M0=01时,定时/计数器工作在方式1。MCS-51单片机定时计数器在方式1时的工作原理如下图所示:
振荡器÷12C/T = 0C/T = 1TR1GATETL1(8位)控制TH1(8位)TF1中断T1端INT1端
图2-1 计数/定时器图 定时/计数器 6
当C/T=0时,工作在定时器状态,由振荡器经12分频后输入,否则由T1端输入。在定时时,对工作频率的12分频进行计数,先记入TL后记入TH,直到溢出为止,根据TL、TH内的初值不同可以定出不同的时间;在计数工作方式时,对T0(T1)引脚的输入脉冲进行计数,将计数值记入TL、TH。当定时/计数溢出时,会引起中断。计数初值与定时时间的关系为:T = 12×(T_all – a)/fosc定时间隔为T,计数初值为a。所以有计数初值a =–T×fosc/12,THx = a / 256,TLx = a % 256。定时器均有一个最大定时时间,对于长时间的定时需要,可以将定时间隔为固定的较小时间,通过另设一全局变量ah1用于计数,累加固定的较小定时时间来进行。 使用MCS-51单片机的定时/计数器的步骤是:
1.设定TMOD,确定:工作状态(用作定时器/计数器);工作方式;控制方式。
2.设置合适的计数初值,以产生期望的定时间隔。由于定时/计数器在方式0、方式1和方式2时的最大计数间隔取决于使用的晶振频率fosc,如下表所示,当需要的定时间隔较大时,要采用适当的方法,即将定时间隔分段处理。
3.确定定时/计数器工作于查询方式还是中断方式,若工作于中断方式,则在初始化时开放定时/计数器的中断及总中断:ET0 = 1;EA = 1; 还需要编写中断服务函数:
void T0_srv(void) interrupt 1 using 1 {
TL0 = a % 256;
TH0 = a / 256; 中断服务程序段 } 4.启动定时器:TR0(TR1)= 1。
时间调整有多种方式。一、可以直接进入相关状态进行有关操作,二、将调整分两步,先进入状态,然后执行操作,这两步分别由两个键控制。方式一,比较直接,设计思想也比较简单,但是,这种方式存在操作时间和控制键数目的矛盾。如果用比较少的键,那么可能会在进入状态后处于数据调整等待状态,这样会影响到显示的扫描速度。当然在这种方式下,还可以使用多个状态键,每个状态键,完成一个对应数据的调整。如果采用二的方式,就不会出现这种情况。因为状态的调整,与状态的操作可以分别由两个键控制,其状态的调整数可以多达256个(理论上),操作的完成是这样的,一键控制状态的调整,一键控制数据的调整。以上两种方式的实现都可以采用查询和中断的方式。两种方式必须注意的问题是两者进行相关操作的过程不能太长否则会影响显示的扫描。基于本系统的设置:将时间调整分为状态调整和数据调整两部分,每次进入中断只
7
执行一次操作,然后返回,这样,就不必让中断处于调整等待状态,可以使中断的耗时很小。将定时器中断的优先级设置为最高级,那么中断的方式和查询的方式一样不会影响到时钟的记数。
通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作
用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。按键消抖的方式有硬件和软件消抖,本系统采用软件消抖。软件消抖有定时器定时,和利用延时子程序的方式。一,定时器定时消抖可以不影响显示模块扫描速度,其实现方法是:设置标志位,在定时器中断中将其置位,然后在程序中查询。将其中断优先级设置为低于时钟定时中断,那么它就可以完全不影响时钟定时。二,在采用延时子程序时,如果显示模块的扫描速度本来就不是很快,此时可能会影响到显示的效果,一般情况下,每秒的扫描次数不应小于50次,否则,数码的显示会出现闪烁的情况。因此,延时子程序的延时时间应该小于20毫秒,如果采用定时器定时的方式,延时时间不影响时钟。如果,设计时采用的是中断的方式来完成有关操作,同样可以采用软件的方式来消抖,其处理思想是:中断不能连续执行,两次之间有一定的时间间隔。
2.3 整体设计框图
AT89C51芯片 时钟模块 调时功能 按键模块 显示模块 时间显示 闹钟功能 蜂鸣器模块
3 硬件设计
硬件电路的设计包括核心时间控制模块、显示模块、按键模块,以及闹钟的蜂鸣器模块。
3.1 显示模块
通过外部连线,将P0线接到J16,使得P0口控制数码管的位显示,将P2线接到J12,使得P2口控制数码管的段显示。
8
图3-1 数码管显示原理图
3.2 按键模块
使用8路独立按键,通过改变排线连接,可以实现按钮的普通输入和中断输入
通过外部接线,将P3口与JP5相连,实现由P3口控制时间调试和闹钟设定的按钮连接。
图3-2 按键原理图
3.3 蜂鸣器模块
通过单片机的内部设定,将P1.5口作为蜂鸣器的控制端口,通过跳帽连接J8口,使得蜂鸣器可以工作,再由软件程序编写,使得蜂鸣器能够在特定的时刻响。
图3-3 蜂鸣器原理图
9
3.4 核心控制以及时间控制模块
图3-4 复位电路图
图3-5晶振连接图
图3-6 P0口外接上拉电阻图
图3-7 单片机核心控制模块原理图
4 软件及编程分析
4.1 主程序流程图如下所示
经过思路总结以及分析整个时钟系统的硬件与软件需求,可画出程序流程图如图4-1所示:
10
主程序 初始化 按键扫描 有按键? N Y 闹钟按键? Y 正常走时 闹钟设定 N 时间调试 设定完毕
图4-1 程序设计流程图
4.2 软件编程及分析
本系统的编程环境为keil uvision2,依照流程图编写程序并进行程序的编译,烧录软件使用PZISP将程序烧录进硬件中,软件得编译结果及烧录结果分别如图4-1,4-2所示:
图4-2 编译结果图
图4-3 烧录结果图
11
编写具体程序见附录所示,以下给出部分主要程序的分析说明:
(1)、按键消抖
按键抖动会引起一次按键被误读多次。为确保CPU对键的一次闭合仅作一次处理,必须去除键抖动。抖动时间的长短由按键的机械特性决定,一般为5ms~10ms,通过delay函数的延时作用,在按键被按下时,延时一段时间,确定按键是否真的被按下,如果是就执行程序,达到了消除按键的抖动的目的,程序如下: if(key1==0) //按键被按下 { delay(10); //延时 if(key1==0) //判断按键是否被按下 { while(!key1); //进一步确认按键被按下 } }
(2)、初始化程序
开中断并选择定时器 void init() { TMOD=0x01; TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1; ET0=1; TR0=1; }
//选择定时器工作方式为1 //写入计数初值,设定t=50ms //开总中断
//允许定时器中断 //启动定时器
(3)、中断程序
中断程序采用定时器0,在中断程序中进行显示时间的准点走时,以及实现准点报时的功能。 void timer0() interrupt 1 //中断、定时器0、准点报时 { TH0=(65536-50000)/256; //工作方式1,定时时间为50ms TL0=(65536-50000)%256; aa++; if(aa==20) //每20次为1s { aa=0; //aa清零 s++; //秒加1 if(s==60) //判断秒是否为60 { s=0; //秒清零 m++; //秒为60后,秒清零,分加1 if(m==60) //判断分是否为60 { m=0; //分清零 h++; //分为60后,分清零,时加1 if(h==24) //若时为24,时清零 h=0; } } } }
12
5 实验结果图
硬件的位选、段选,以及按键的连线如下所示:
图5-1 正常走时图
当按下按键0,并且一直按着时,进入闹钟的设定状态,开始的时候显示为00—00—00
图5-2 进入闹钟编辑状态图
6 结论
在此次的课程设计中,我了解到了自己能力的不足之处,在编程和调试的过程中,我体会到了自己所学的知识是远远的不够,需要学习的方面还有很多。通过资料的收集和文件的检索,我
13
找到了设计过程中出现的问题及问题的解决方法,从而算是较为顺利的完成了此次课程设计的任务。此次的课程设计使我学到了很多新的,课堂上无法学到的知识,同时也加深了课堂上学到的知识,并且增强了自己理论联系实际的操作能力。我取得了一些宝贵的经验,比如,理论必须和实际结合才能承购,知识必须通过应用才能实现其价值。我还提高了自己的动手能力,为将来的学习和工作奠定了良好的基础。
参考文献:
[1]余发山.王福忠.单片机原理及应用技术[M].徐州:中国矿业大学出版社.2008.10-100 [2]钟睿.MCS-51.单片机原理及应用开发技术[M].北京:中国铁道出版社.2006.20-30 [3]彭伟.单片机C语言程序设计实训100例:基于8051+Proteus仿真[J].电子工业出版社 [4]张靖武,周灵彬.单片机系统的PROTEUS设计与仿真[J].电子工业
[5]郭天祥.新概念51单片机C语言教程——入门,提高,开发,拓展全攻略[M].北京:电子工业出版社
附录:
实验程序
#include #define uchar unsigned char #define uint unsigned int uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f}; sbit Bueer=P1^5; sbit key1=P3^1; sbit key2=P3^2; sbit key3=P3^3; sbit key4=P3^4; sbit key5=P3^5; sbit key6=P3^6; sbit key7=P3^7; sbit key0=P3^0; uchar aa,h,m,s,n,i,sec,min,hour; void delay(uint z) { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } void display() { if(key0==1) { P2=0xfe; P0=table[s%10]; delay(1); P2=0xfd; P0=table[s/10]; delay(1); 14 P2=0xfb; P0=0x40; delay(1); P2=0xf7; P0=table[m%10]; delay(1); P2=0xef; P0=table[m/10]; delay(1); P2=0xdf; P0=0x40; delay(1); P2=0xbf; P0=table[h%10]; delay(1); P2=0x7f; P0=table[h/10]; delay(1); } } void timer0() interrupt 1 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; aa++; if(aa==20) { aa=0; s++; if(s==60) { s=0; m++; if(m==60) { m=0; h++; if(h==24) h=0; } } } } void keyscan() { if(key1==0) { delay(10); if(key1==0) { while(!key1); n++; TR0=0; if(n==1) { P2=0xfe; P0=table[s%10]; delay(1); P2=0xfd; P0=table[s/10]; 15 delay(1); } { while(!key2); if(n==2) { P2=0xf7; P0=table[m%10]; delay(1); P2=0xef; P0=table[m/10]; delay(1); } if(n==3) { P2=0xbf; P0=table[h%10]; delay(1); P2=0x7f; P0=table[h/10]; delay(1); } if(n==4) { TR0=1; n=0; } } } if(n!=0) { if(key2==0) { delay(10); if(key2==0) if(n==1) { s++; if(s==60) s=0; } if(n==2) { m++; if(m==60) m=0; } if(n==3) { h++; if(h==24) h=0; } } if(key3==0) { delay(10); if(key3==0) { while(!key3); if(n==1) { if(s==0) s=60; s--; } 16 } if(n==2) { if(m==0) m=60; m--; } if(n==3) { if(h==0) h=24; h--; } } } } void keyscan2() { if(key0==0) { P2=0xfe; P0=table[sec%10]; delay(1); P2=0xfd; P0=table[sec/10]; delay(1); P2=0xfb; P0=0x40; delay(1); P2=0xf7; P0=table[min%10]; delay(1); P2=0xef; P0=table[min/10]; } delay(1); P2=0xdf; P0=0x40; delay(1); P2=0xbf; P0=table[hour%10]; delay(1); P2=0x7f; P0=table[hour/10]; delay(1); } if(key4==0) { delay(10); if(key4==0) { while(!key4); { min++; if(min==60) min=0; } } } if(key5==0) { delay(10); if(key5==0) { while(!key5); 17 { hour++; if(hour==24) hour=0; } } } if(key6==0) { delay(10); if(key6==0) { while(!key6); { if(min==0) min=60; min--; } } } if(key7==0) { delay(10); if(key7==0) { while(!key7); { if(hour==0) hour=24; hour--; } } } Bueer=1; if((h==hour)&(m==min)&(s==sec)) { for(i=0;i<100;i++) { Bueer=!Bueer; delay(40); } } } void init() { TMOD=0x01; TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1; ET0=1; TR0=1; } void main() { init(); while(1) { keyscan(); keyscan2(); display(); } } 18 因篇幅问题不能全部显示,请点此查看更多更全内容