资源描述
计算机与信息学院
电子信息工程系综合课程设计报告
专 业 班 级
电子信息工程11-2班
学生姓名及学号
陈雪莹 20112661
指导教师
方静
课题名称
函数信号发生器
2013 ~2014 学年第 三 学期
函数信号发生器的设计与实现
一.课题的基本描述
在科学研究和实际工业测量控制系统开发过程中,方波、三角波和正弦波等是常用的基本测试信号,函数信号发生器就是用来产生、模拟这些真实信号源的通用电子设备。本课题要求设计一种以单片机为控制器的简易函数信号发生器,包含:主控电路、D/A转换电路、按键和波形选择电路以及显示输出电路,可以输出正弦波、三角波和方波三种信号,输出信号的频率可用按键进行增、减调整,并在LCD(12864)实时显示输出波形。
二. 设计的基本要求
1. 正弦波、三角波频率调节范围:0.1-50HZ 输出幅值:1.0-1.5V
方波频率调节范围:1Hz-1KHz 输出幅值:5V
2. 通过按键选择输出信号类型,幅值、频率等相关指标;
3. 具有显示输出波形的频率和幅度的功能。
三.技术方案及关键问题
(1).总体方案:
数字信号可以通过数/模转换器转换成模拟信号,因此可通过产生数字信号再转换成模拟信号的方法来获得所需要的波形。89C51单片机本身就是一个完整的微型计算机,具有组成微型计算机的各部分部件:中央处理器CPU、随机存取存储器RAM、只读存储器ROM、I/O接口电路、定时器/计数器以及串行通讯接口等,只要将89C51再配置按键、数模转换及波形输出等部分,即可构成所需的函数信号发生器。因此本系统利用单片机AT89C51采用程序设计方法产生三角波、正弦波、矩形波三种波形,再通过D/A转换器PCF8591T将数字信号转换成模拟信号,最终由液晶屏12864显示出来。通过按键来控制三种波形的类型选择、频率和幅度的变化,并通过数码管显示其各自的类型,液晶屏显示幅度和频率的大小。系统大致包括信号发生部分、数/模转换部分以及液晶显示部分三部分。
(2).信号发生电路方案:
通过单片机控制D/A,输出三种波形。此方案输出的波形不够稳定,抗干扰能力弱,不易调节。但此方案电路简单、成本低。
(3).显示方案:
采用一位共阴LED数码管,显示波形类型(1-3)。由于人眼具有视觉暂留特性,当每只数码管显示的时间间隔小于1/16s时人眼感觉不到闪动,看到的是每只数码管常亮。使用数码管显示编程较易,但要显示内容多,而且数码管不能显示字母。采用LCD液晶显示器12864,显示波形以及它的幅度和频率。12864其功率小,效果明显,显示编程容易控制,可以显示波形。
(4).按键方案:
独立按键,当没有键闭合时,呈高电平。当某一个键闭合时,该键所对应的线被短路。
系统的总体框图 :
波形选择按键
液晶显示
单片机
幅值调节按键
输出
运放
D/A转换
频率调节按键
图(1) 系统的总体框图
四.系统(算法)设计实现
硬件实现及单元电路设计:
AT89C51是片内有ROM/EPROM的单片机,因此,这种芯片构成的最小系统简单﹑可靠。用AT89C51单片机构成最小应用系统时,只要将单片机接上时钟电路和复位电路即可,如图(2)AT89C51单片机最小系统所示。由于集成度的限制,最小应用系统只能用作一些小型的控制单元。其应用特点:
(1) 有可供用户使用的大量I/O口线。
(2) 内部存储器容量有限。
(3) 应用系统开发具有特殊性。
图(2) AT89C51单片机最小系统
芯片的引脚描述如下:
1、主电源引脚VCC和VSS
VCC——(40脚)接+5V电压;
VSS——(20脚)接地。
2、外接晶体引脚XTAL1和XTAL2
XTAL1(19脚)接外部晶体的一个引脚。在单片机内部,它是一个反相放大器的输入端,这个放大器构成了片内振荡器。当采用外部振荡器时,对HMOS单片机,此引脚应接地;对CHMOS单片机,此引脚作为驱动端。
XTAL2(18脚)接外晶体的另一端。在单片机内部,接至上述振荡器的反相放大器的输出端。采用外部振荡器时,对HMOS单片机,该引脚接外部振荡器的信号,即把外部振荡器的信号直接接到内部时钟发生器的输入端;对XHMOS,此引脚应悬浮。
3、控制或与其它电源复用引脚RST/VPD、ALE/PROG、PSEN和EA/VPP
①RST/VPD(9脚)当振荡器运行时,在此脚上出现两个机器周期的高电平将使单片机复位。推荐在此引脚与VSS引脚之间连接一个约8.2k的下拉电阻,与VCC引脚之间连接一个约10μF的电容,以保证可靠地复位。
VCC掉电期间,此引脚可接上备用电源,以保证内部RAM的数据不丢失。当VCC主电源下掉到低于规定的电平,而VPD在其规定的电压范围(5±0.5V)内,VPD就向内部RAM提供备用电源。
②ALE/PROG(30脚):当访问外部存贮器时,ALE(允许地址锁存)的输出用于锁存地址的低位字节。即使不访问外部存储器,ALE端仍以不变的频率周期性地出现正脉冲信号,此频率为振荡器频率的1/6。因此,它可用作对外输出的时钟,或用于定时目的。然而要注意的是,每当访问外部数据存储器时,将跳过一个ALE脉冲。ALE端可以驱动(吸收或输出电流)8个LS型的TTL输入电路。
对于EPROM单片机(如8751),在EPROM编程期间,此引脚用于输入编程脉冲(PROG)。
③PSEN(29脚):此脚的输出是外部程序存储器的读选通信号。在从外部程序存储器取指令(或常数)期间,每个机器周期两次PSEN有效。但在此期间,每当访问外部数据存储器时,这两次有效的PSEN信号将不出现。PSEN同样可以驱动(吸收或输出)8个LS型的TTL输入。
④EA/VPP(引脚):当EA端保持高电平时,访问内部程序存储器,但在PC(程序计数器)值超过0FFFH(对851/8751/80C51)或1FFFH(对8052)时,将自动转向执行外部程序存储器内的程序。当EA保持低电平时,则只访问外部程序存储器,不管是否有内部程序存储器。对于常用的8031来说,无内部程序存储器,所以EA脚必须常接地,这样才能只选择外部程序存储器。
对于EPROM型的单片机(如8751),在EPROM编程期间,此引脚也用于施加21V的编程电源(VPP)。
4、输入/输出(I/O)引脚P0、P1、P2、P3(共32根)
①P0口(39脚至32脚):是双向8位三态I/O口,在外接存储器时,与地址总线的低8位及数据总线复用,能以吸收电流的方式驱动8个LS型的TTL负载。
②P1口(1脚至8脚):是准双向8位I/O口。由于这种接口输出没有高阻状态,输入也不能锁存,故不是真正的双向I/O口。P1口能驱动(吸收或输出电流)4个LS型的TTL负载。对8052、8032,P1.0引脚的第二功能为T2定时/计数器的外部输入,P1.1引脚的第二功能为T2EX捕捉、重装触发,即T2的外部控制端。对EPROM编程和程序验证时,它接收低8位地址。
③P2口(21脚至28脚):是准双向8位I/O口。在访问外部存储器时,它可以作为扩展电路高8位地址总线送出高8位地址。在对EPROM编程和程序验证期间,它接收高8位地址。P2可以驱动(吸收或输出电流)4个LS型的TTL负载。
④P3口(10脚至17脚):是准双向8位I/O口,在MCS-51中,这8个引脚还用于专门功能,是复用双功能口。P3能驱动(吸收或输出电流)4个LS型的TTL负载。
作为第一功能使用时,就作为普通I/O口用,功能和操作方法与P1口相同。
作为第二功能使用时,各引脚的定义如表所示。
值得强调的是,P3口的每一条引脚均可独立定义为第一功能的输入输出或第二功能。
第二功能描述如下:
P3.0 10 RXD(串行输入口)
P3.1 11 TXD(串行输出口)
P3.2 12 INT0(外部中断0)
P3.3 13 INT1(外部中断1)
P3.4 14 T0(定时器0外部输入)
P3.5 15 T1(定时器1外部输入)
P3.6 16 WR(外部数据存储器写脉冲)
P3.7 17 RD(外部数据存储器读脉冲)
(1) 波形产生模块设计
由单片机采用编程方法产生三种波形、通过DA转换模块PCF8591之后输出。其电路图如下:
图(3) PCF8591芯片
在与CPU的信息传输过程中仅靠时钟线SCL和数据线SDA就可以实现。
PCF8591是具有I2C总线接口的8位A/D及D/A转换器。有4路A/D转换输入,1路D/A模拟输出。这就是说,它既可以作A/D转换也可以作D/A转换。A/D转换为逐次比较型。
AIN0~AIN3:模拟信号输入端。
A0~A3:引脚地址端。
SDA、SCL:I2C总线的数据线、时钟线。
OSC:外部时钟输入端,内部时钟输出端。
EXT:内部、外部时钟选择线,使用内部时钟时EXT接地。
AGND:模拟信号地。
AOUT:D/A转换输出端。
VREF:基准电源端。
PCF8591采用典型的I2C总线接口器件寻址方法,即总线地址由器件地址、引脚地址和方向位组成。飞利蒲公司规定A/D器件地址为1001。引脚地址为A2A1A0,其值由用户选择,因此I2C系统中最多可接23=8个具有I2C总线接口的A/D器件。地址的最后一位为方向位R/ ,当主控器对A/D器件进行读操作时为1,进行写操作时为0。总线操作时,由器件地址、引脚地址和方向位组成的从地址为主控器发送的第一字节。
控制字节用于实现器件的各种功能,如模拟信号由哪几个通道输入等。控制字节存放在控制寄存器中。总线操作时为主控器发送的第二字节。其格式如下所示:
其中:D1、D0两位是A/D通道编号:00通道0,01通道1,10通道2,11通道3
D2 自动增益选择(有效位为1)
D5、D4模拟量输入选择:00为四路单数入、01为三路差分输入、10为单端与差分配合输入、11为模拟输出允许有效
当系统为A/D转换时,模拟输出允许为0。模拟量输入选择位取值由输入方式决定:四路单端输入时取00,三路差分输入时取01,单端与差分输入时取10,二路差分输入时取11。最低两位时通道编号位,当对0通道的模拟信号进行A/D转换时取00,当对1通道的模拟信号进行A/D转换时取01,当对2通道的模拟信号进行A/D转换时取10,当对3通道的模拟信号进行A/D转换时取11。
在进行数据操作时,首先是主控器发出起始信号,然后发出读寻址字节,被控器做出应答后,主控器从被控器读出第一个数据字节,主控器发出应答,主控器从被控器读出第二个数据字节,主控器发出应答…一直到主控器从被控器中读出第n个数据字节,主控器发出非应答信号,最后主控器发出停止信号。
(2)显示模块的设计
通过液晶12864显示输出的波形以及波形的频率和幅度频率,其电路图如下:
图(4) 液晶显示模块
如上图所示,12864的八位数据端接单片机的P0口,其三个使能端RS、RW、E分别接单片机的P2.2—P2.4。通过软件控制液晶屏可以显示波形以及波形的幅度和频率。
带中文字库的128X64是一种具有4位/8位并行、2线或3线串行多种接口方式,内部含有国标一级、二级简体中文字库的点阵图形液晶显示模块;其显示分辨率为128×64, 内置8192个16*16点汉字,和128个16*8点ASCII字符集.利用该模块灵活的接口方式和简单、方便的操作指令,可构成全中文人机交互图形界面。可以显示8×4行16×16点阵的汉字. 也可完成图形显示.低电压低功耗是其又一显著特点。
LCD12864基本特性:
(1)、低电源电压(VDD:+3.0--+5.5V)(2)、显示分辨率:128×64点 (3)、内置汉字字库,提供8192个16×16点阵汉字(简繁体可选) (4)、内置 128个16×8点阵字符 (5)、2MHZ时钟频率 (6)、显示方式:STN、半透、正显 (7)、驱动方式:1/32DUTY,1/5BIAS (8)、视角方向:6点 (9)、背光方式:侧部高亮白色LED,功耗仅为普通LED的1/5—1/10 (10)、通讯方式:串行、并口可选 (11)、内置DC-DC转换电路,无需外加负压 (12)、无需片选信号,简化软件设计(13)、工作温度: 0℃ - +55℃ ,存储温度: -20℃-+60℃
图(5) 数码管显示模块
通过一位数码管显示波形类型(1-3).
(3)按键显示模块的设计
本系统采用独立按键,其连接电路图如下:
图(6) 按键显示模块
图中四个按键引出的四个线分别接单片机的P3.2—P3.6口,如图KEY1用来切换输出波形、KEY2和KEY3用来调节频率的加减,KEY4和KEY5用来调节幅度的加减。当按KEY2时输出波形的频率增加,按KEY3时输出波形的频率减小。当按KEY4时输出波形的幅度增加,按KEY5时输出波形的幅度减小。
软件设计流程:
本系统采用AT89C51单片机,用编程的方法来产生三种波形,并通过编程
来切换三种波形以及波形频率的改变。
具体功能有:(1)各个波形的切换;(2)各种参数的设定;(3)频率增减等。
软件调通后,通过编程器下载到AT89C51芯片中,然后插到系统中即可独立完成所有的控制。
软件的流程图如下:
开始
I2C总线协议
主函数
初始化
输出方波液晶显示#include <reg51.h>
#include <intrins.h>
#include"LCD12864.h"
#define uchar unsigned char
#define uint unsigned int
#define _Nop() _nop_()
#define AddW 0x90
#define AddR 0x91
//sbit SDA=P1^2;
//sbit SCL=P1^7;
//sbit dula=P2^0;
//sbit wela=P2^1;
sbit SDA=P2^0;
sbit SCL=P2^1;
sbit key1=P3^2;
sbit key2=P3^3;
sbit key3=P3^4;
sbit key4=P3^5;
sbit key5=P3^6;
bit back;
uchar wave=0;
uchar f=1;
uchar A1=10;
uchar A2=1;
uint num=0;
//unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,}; //共阳
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,}; //共阴
unsigned char code juxing[]=
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,255,255,255,255,
255,255,255,255,255,255,
255,255,255,255,255,255,
255,255,255,255,255,255,
255,255,255,255,255,255,
255,255,255,255,} ;
unsigned char code sanjiao[]
={
0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,
60,64,68,72,76,80,84,88,92,96,100,104,
108,112,116,120,124,128,124,120,116,112,
108,104,100,96,92,88,84,80,76,72,68,64,
60,56,52,48,44,40,36,32,28,24,20,16,12,8,4,0,
};
unsigned char code sin[]
={
135,145,158,167,176,188,199,209,218,226,
234,240,245,249,252,254,254,253,251,247,
243,237,230,222,213,204,193,182,170,158,
146,133,121,108,96,84,72,61,50,41,32,24,
17,11,7,3,1,0,0,2,5,9,14,20,28,36,45,55,66,
78,90,102,114,128
} ;
void delay1(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=60;y>0;y--);
}
void Start_I2c()
{
SDA=1;
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0;
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0;
_Nop();
_Nop();
} //结束总线
void Stop_I2c()
{
SDA=0;
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1;
_Nop();
_Nop();
_Nop();
_Nop();
}
void SendByte(uchar c) //发送字节函数
{
unsigned char Bit;
for(Bit=0;Bit<8;Bit++) //要传送的数据长度为8位
{
if((c<<Bit)&0x80) //判断发送位
SDA=1;
else
SDA=0;
_Nop();
SCL=1; //置时钟线为高 ,通知被控器开始接收数据位
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0;
}
_Nop();
_Nop();
SDA=1;
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
if(SDA==1)
back=0;
else
back=1;
SCL=0;
_Nop();
_Nop();
}
bit WriteDAC(uchar dat, uchar num)
{
unsigned char i;
Start_I2c();
SendByte(AddW);
if(back==0)
return(0);
SendByte(0x40);
if(back==0)
return(0);
for(i=0;i<num;i++)
{
SendByte(dat);
if(back==0)
return(0);
}
Stop_I2c();
}
void keys()
{
if(key1==0)
{
delay1(10);
if(key1==0)
{
num++;
while(!key1);
//
// dula=1;
// P0=table[num];
// dula=0;
// P0=0xff;
// wela=1;
// P0=0xfe;
// wela=0;
// delay1(5);
wave++;
if(wave==4)
{
wave=1;
num=1;
}
P1=table[num];
}
}
}
void keys1()
{
if(key2==0)
{
delay1(10);
if(key2==0)
{
while(!key2);
f++;
if(f>500)
f=500;
}
}
if(key3==0)
{
delay1(10);
if(key3==0)
{
while(!key3);
if(f>1)
f--;
}
}
if(key4==0)
{
delay1(10);
if(key4==0)
{
while(!key4);
A1++;
if(A1>15)
A1=15;
}
}
if(key5==0)
{
delay1(10);
if(key5==0)
{
while(!key5);
if(A1>1)
A1--;
}
}
}
void keys2()
{
if(key2==0)
{
delay1(10);
if(key2==0)
{
while(!key2);
f++;
if(f>100)
f=100;
}
}
if(key3==0)
{
delay1(10);
if(key3==0)
{
while(!key3);
if(f>1)
f--;
}
}
if(key4==0)
{
delay1(10);
if(key4==0)
{
while(!key4);
A2++;
if(A2>5)
A2=5;
}
}
if(key5==0)
{
delay1(10);
if(key5==0)
{
while(!key5);
if(A2>1)
A2--;
}
}
}
void main()
{
unsigned char i,temp,Atemp,Ftemp;
LcdInit();
P1=table[0];
// dula=1;
// P0=table[1];
// dula=0;
// P0=0xff;
// wela=1;
// P0=0xfe;
// wela=0;
// delay1(5);
while (1)
{
temp=wave;
keys();
if(temp!=wave)
ClrGDRAM();
if(wave==1)
{
Atemp=A1;Ftemp=f;
keys1();
if(Atemp!=A1)
ClrGDRAM();
if(Ftemp!=f)
ClrGDRAM();
for(i=0;i<65;i++)
WriteDAC(1.0*sanjiao[i]*A1/20,21-f);
drawTri(A1,f); //绘制 三角波图像。
delayms(10);
}
else if(wave==2)
{
Atemp=A1;Ftemp=f;
keys1();
if(Atemp!=A1)
ClrGDRAM();
if(Ftemp!=f)
ClrGDRAM();
for(i=0;i<64;i++)
WriteDAC(1.0*sin[i]*A1/40,21-f);
drawSin(A1,f); //绘制 A*sin(2*Pi*f)图像。
delayms(10);
}
else if(wave==3)
{
Atemp=A2;Ftemp=f;
keys2();
if(Atemp!=A2)
ClrGDRAM();
if(Ftemp!=f)
ClrGDRAM();
for(i=0;i<64;i++)
WriteDAC(1.0*juxing[i]*A2/40,21-f);
drawSquare(A2,f); //绘制方波图像。
delayms(10);
}
else if(wave==3)
{
for(i=0;i<64;i++)
if(i%2==0)
WriteDAC(1.0*0xff*A2/40,21-f);
else
WriteDAC(0,21-f);
}
}
}
#include <reg51.h>
#include <intrins.h>
#include"LCD12864.h"
#define uchar unsigned char
#define uint unsigned int
#define _Nop() _nop_()
#define AddW 0x90
#define AddR 0x91
//sbit SDA=P1^2;
//sbit SCL=P1^7;
//sbit dula=P2^0;
//sbit wela=P2^1;
sbit SDA=P2^0;
sbit SCL=P2^1;
sbit key1=P3^2;
sbit key2=P3^3;
sbit key3=P3^4;
sbit key4=P3^5;
sbit key5=P3^6;
bit back;
uchar wave=0;
uchar f=1;
uchar A1=10;
uchar A2=1;
uint num=0;
//unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,}; //共阳
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,}; //共阴
unsigned char code juxing[]=
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,255,255,255,255,
255,255,255,255,255,255,
255,255,255,255,255,255,
255,255,255,255,255,255,
255,255,255,255,255,255,
255,255,255,255,} ;
unsigned char code sanjiao[]
={
0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,
60,64,68,72,76,80,84,88,92,96,100,104,
108,112,116,120,124,128,124,120,116,112,
108,104,100,96,92,88,84,80,76,72,68,64,
60,56,52,48,44,40,36,32,28,24,20,16,12,8,4,0,
};
unsigned char code sin[]
={
135,145,158,167,176,188,199,209,218,226,
234,240,245,249,252,254,254,253,251,247,
243,237,230,222,213,204,193,182,170,158,
146,133,121,108,96,84,72,61,50,41,32,24,
17,11,7,3,1,0,0,2,5,9,14,20,28,36,45,55,66,
78,90,102,114,128
} ;
void delay1(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=60;y>0;y--);
}
void Start_I2c()
{
SDA=1;
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0;
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0;
_Nop();
_Nop();
} //结束总线
void Stop_I2c()
{
SDA=0;
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1;
_Nop();
_Nop();
_Nop();
_Nop();
}
void SendByte(uchar c) //发送字节函数
{
unsigned char Bit;
for(Bit=0;Bit<8;Bit++) //要传送的数据长度为8位
{
if((c<<Bit)&0x80) //判断发送位
SDA=1;
else
SDA=0;
_Nop();
SCL=1; //置时钟线为高 ,通知被控器开始接收数据位
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0;
}
_Nop();
_Nop();
SDA=1;
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
if(SDA==1)
back=0;
else
back=1;
SCL=0;
_Nop();
_Nop();
}
bit WriteDAC(uchar dat, uchar num)
{
unsigned char i;
Start_I2c();
SendByte(AddW);
if(back==0)
return(0);
SendByte(0x40);
if(back==0)
return(0);
for(i=0;i<num;i++)
{
SendByte(dat);
if(back==0)
return(0);
}
Stop_I2c();
}
void keys()
{
if(key1==0)
{
delay1(10);
if(key1==0)
{
num++;
while(!key1);
//
// dula=1;
// P0=table[num];
// dula=0;
// P0=0xff;
// wela=1;
// P0=0xfe;
// wela=0;
// delay1(5);
wave++;
if(wave==4)
{
wave=1;
num=1;
}
P1=table[num];
}
}
}
void keys1()
{
if(key2==0)
{
delay1(10);
if(key2==0)
{
while(!key2);
f++;
if(f>500)
f=500;
}
}
if(key3==0)
{
delay1(10);
if(key3==0)
{
while(!key3);
if(f>1)
f--;
}
}
if(key4==0)
{
delay1(10);
if(key4==0)
{
while(!key4);
A1++;
if(A1>15)
A1=15;
}
}
if(key5==0)
{
delay1(10);
if(key5==0)
{
while(!key5);
if(A1>1)
A1--;
}
}
}
void keys2()
{
if(key2==0)
{
delay1(10);
if(key2==0)
{
while(!key2);
f++;
if(f>100)
f=100;
}
}
if(key3==0)
{
delay1(10);
if(key3==0)
{
while(!key3);
if(f>1)
f--;
}
}
展开阅读全文