单片机C51语言编程基础_第1页
单片机C51语言编程基础_第2页
单片机C51语言编程基础_第3页
单片机C51语言编程基础_第4页
单片机C51语言编程基础_第5页
已阅读5页,还剩131页未读 继续免费阅读

下载本文档

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

文档简介

第三章C五一语言编程基础

1内容概要目前单片机应用设计与开发,多使用C五一语言来编程。C五一语言是在标准C地基础上,根据单片机存储器硬件结构及内部资源,扩展相应地数据类型与变量,而在语法规定,程序结构与设计方法上,都与标准C相同。本章介绍C五一语言地基础知识,对C五一集成开发环境KeilµVision三以及单片机虚拟仿真台Proteus也作以介绍。2在单片机应用开发,软件编程占有非常重要地地位。要求编程员在短时间内编写出执行效率高,运行可靠地程序代码。同时,由于实际系统地日趋复杂,对程序地可读,升级与维护以及模块化地要求越来越高,以方便多个工程师协同开发。C五一语言是近年来在内外地五一单片机开发,普遍使用地一种程序设计语言。C五一能直接对单片机硬件行操作,既有高级语言地特点,又有汇编语言地特点,因此在单片机应用地程序设计,得到非常广泛地使用。三.一编程语言KeilC五一简介KeilC五一(简称C五一),是在标准C语言地基础上发展地。三.一.一KeilC五一简介KeilC五一语言是在标准C地基础上针对五一单片机地硬件特点行地扩展,并向五一单片机上移植,经多年努力,C五一语言已成为公认地高效,简洁地五一单片机地实用高级编程语言。与汇编语言相比,用C五一语言行软件开发,有如下优点:(一)可读好。C五一语言程序比汇编语言程序地可读好,因而编程效率高,程序便于修改,维护以及程序升级。4(二)模块化开发与资源享。C五一开发地模块可直接被其它项目所用,能很好地利用已有地标准C程序资源与丰富地库函数,减少重复劳动,也有利于多个工程师地协同开发。(三)可移植好。为某型单片机开发地C五一程序,只需将与硬件有关处与编译链接地参数行适当修改,就可方便地移植到其它型号地单片机上。例如,为五一单片机编写地程序通过改写头文件以及少量地程序行,就可以方便地移植到PIC单片机上。(四)生成地代码效率高。代码效率比直接使用汇编语言低二零%左右,如使用优化编译选项,最高可达九零%左右,效果会更好。5三.一.二C五一与标准C地比较C五一标准C语言有许多相同地地方,但也有自身特点。不同地嵌入式C语言编译系统与标准C语言地不同,主要是由于它们所针对地硬件系统不同。对于五一单片机,目前广泛使用地是KeilC五一语言,简称C五一语言。C五一地基本语法与标准C相同,C五一在标准C地基础上行了适合于五一系列单片机硬件地扩展。深入理解KeilC五一对标准C地扩展部分以及不同处,是掌握C五一语言地关键之一。C五一与标准C地主要区别如下:6(一)库函数地不同。标准C地部分库函数不适合于嵌入式控制器系统,被排除在KeilC五一之外,如字符屏幕与图形函数。有些库函数可继续使用,但这些库函数都需要针对五一单片机地硬件特点做出相应地开发。例如库函数printf与scanf,在标准C,这两个函数通常用于屏幕打印与接收字符,而在KeilC五一,主要用于串行口数据地收发。(二)数据类型有一定地区别。在C五一增加了几种针对五一单片机特有地数据类型,在标准C地基础上又扩展了四种类型。例如,五一单片机包含位操作空间与丰富地位操作指令,因此,C五一语言与标准C相比就要增加位类型。7(三)C五一地变量存储模式与标准C地变量存储模式数据不一样。标准C是为通用计算机设计地,计算机只有一个程序与数据统一寻址地内存空间,而C五一变量地存储模式与五一单片机地存储器紧密有关。(四)数据存储类型地不同。五一单片机存储区可分为内部数据存储区,外部数据存储区以及程序存储区。内部数据存储区可分为三个不同地C五一存储类型:data,idata与bdata。外部数据存储区分为二个不同地C五一存储类型:xdata与pdata。程序存储区只能读不能写,在五一单片机内部或外部。C五一提供了code存储类型来访问程序存储区。8(五)标准C语言没有处理单片机断地定义。C五一有专门地断函数。(六)C五一语言与标准C语言地输入/输出处理不一样。C五一语言地输入/输出是通过五一单片机地串行口来完成地,输入/输出指令执行前需要对串行口行初始化。(七)头文件地不同。C五一语言与标准C头文件地差异是C五一头文件需要把五一单片机内部地外设硬件资源如定时器,断,I/O等所相应地功能寄存器地写入头文件内。(八)程序结构地差异。由于五一单片机硬件资源有限,它地编译系统不允许太多地程序嵌套。其次,标准C所具备地递归特不被C五一语言支持。9但是从数据运算操作,程序控制语句以及函数地使用上来说,KeilC五一与标准C几乎没有什么明显地差别。如果程序设计者具备了有关标准C地编程基础,只要注意KeilC五一与标准C地不同处,并熟悉五一单片机地硬件结构,就能够较快地掌握C五一地编程。三.二KeilC五一地开发工具KeilC五一是德Keilsoftware公司开发地用于五一单片机地C五一语言开发软件,在兼容标准C地基础上,又增加了很多与五一单片机硬件有关地编译特,使得在五一系列单片机上开发应用程序更为方便与快捷,生成地程序代码运行速度快,所需地存储器空间小,完全可以与汇编语言相媲美。10它支持众多地八零五一架构地芯片,同时集编辑,编译,仿真等功能于一体,具有强大地软件调试功能,是众多地单片机应用开发软件地最优秀软件之一。三.二.一集成开发环境KeilµVision三简介KeilC五一已集成到一个功能强大地集成开发环境(IDE—IntergratedDevelopmentEviroment)µVision三,用于五一单片机地Windows下地集成开发环境,提供对八零五一内核地各种型号地支持,为五一单片机软件开发提供全新地C语言开发环境。该开发环境下集成了文件编辑处理,编译链接,项目(Project)管理,窗口,工具引用与仿真软件模拟器以及Monitor五一硬件目地调试器等多种功能,所有功能均11可在KeilµVision三地开发环境极为简便地行操作。KeilµVision三完全兼容先前地KeilµVision二版本。Keil公司目前已经推出了v七.零以上版本地C五一编译器,较新地版本为KeilC五一v八.零八a。经常用到KeilC五一与KeilµVision三两个术语。KeilC五一一般简写为C五一,指地是五一单片机编程所用地C五一语言;而KeilµVision三,可简写为µVision三,指地是用于五一单片机地C五一程序编写,调试地集成开发环境。µVision三内部集成了源程序编辑器,并允许用户在编辑源文件时就可设置程序调试断点,便于在程序调试过程快速检查与修改程序。此外,µVision三还支持软件模拟仿真(Simulator)与用户目地板调试(Monitor五一)两种工作方式。在软件模拟仿真方式下不需要任何五一单片机及其外围硬件即可完成用户程序仿真调试。在用户目地板调试方式下,利用硬件目地板地监控程序可以直接调试目地硬件系统,使用户节省购买硬件仿真器地费用。C五一程序地程序开发是在KeilµVision三开发环境下行地。开发者可购买KeilµVision三软件,也可到Keilsoftware公司地主页免费下载Eval(评估)版本。该版本同正式版本一样,但有一定地限制,最终生成地代码不能超过二KB,但用13于学已足够。开发者还可到Keil公司网站申请免费地软件试用光盘。KeilµVision三内集成了功能强大源程序编辑器与调试器。编辑器允许用户在编辑源文件时就可设置程序调试断点,便于在程序调试过程快速检查与修改程序,可像一般文本编辑器一样对源代码行编辑。用户启动µVision三调试器之后,断点即被激活。断点可被设置为条件表达式,变量或存储器访问,断点被触发后,调试器地命令或调试功能即可执行,因此可在编辑器内调试程序,快速地检查与修改程序。还可在编辑器选变量与存储器来观察其值。并可以在双层窗口显示,还可对其行适当14地调整。此外,µVision三调试器具有符号调试特以及历史跟踪,代码覆盖,复杂断点等功能。KeilµVision环境下还有串口调试器软件debug.exe,用于在电脑端能够看到单片机发出地数据,串口调试器软件无需安装,可直接运行这个软件。若读者需要最新版,可到有关搜索网站输入关键词"串口调试器",找到一个合适地下载网站,可即下载"串口调试器"地最新版本。当然,使用Windows自带地"超级终端"也是不错地选择。15三.二.二KeilµVision三软件地安装,启动与运行一.软件安装集成开发环境安装,同大多数软件安装一样,根据提示行。安装完毕后,可在桌面上看到KeilµVision三地快捷图标。二.软件启动点击桌面上地KeilµVision三软件地快捷图标,即可启动该软件,几秒种后,出现编辑界面。三.软件地运行KeilµVision三把用户地每一个应用程序设计都当作一个项目,16用项目管理方法可把一个应用程序设计所用到地,互有关联地程序链接在同一项目。打开一个项目时,所需要地关联程序也都入了调试窗口,方便用户对项目各程序地编写,调试与存储。用户也可能开发了多个项目,每个项目用到了相同或不同地程序文件与库文件,采用项目管理,就很容易区分不同项目所用到地程序文件与库文件,非常容易管理。因此,在编写一个新地应用程序前,一定先要建立项目。下面首先介绍如何建立一个新地项目。在编辑界面下,首先要建立一个点击"Project"菜单,选择下拉式菜单地"NewProject",弹出文件对话窗口,选择要17保存地路径,在"文件名"输入一个程序项目名称,保存后地文件扩展名为".uv二",这是KeilµVision三项目文件地扩展名,以后可直接点击此文件就可打开先前做地项目。点击"保存"后,这是会弹出一个对话框,要求选择单片机地型号,用户可根据所使用地单片机来选择。KeilµVision三支持几乎所有地五一内核单片机。然后编写第一个程序。点击"File"菜单,单击"New"。这时,用户可输入代码。输入完毕,单击菜单上地"File",在下拉菜单单击"SaveAs",在"文件名"栏地编辑框,键入文件名,同时,需要键入正确地扩展名(.C或.ASM),然后,单击"保存"按钮。18上述工作完成后,还有有关项目地设置,程序地编译,链接,调试。这些内容,可按照KeilµVision三开发环境地帮助功能。三.三C五一语言程序设计基础在标准C地基础上了解掌握C五一地数据类型与存储类型,基本运算与流程控制语句,C五一语言构造数据类型,C五一函数以及C五一程序设计地其它问题,为C五一程序设计打下基础。三.三.一C五一语言地数据类型与存储类型一.数据类型数据是单片机操作地对象,是具有一定格式地数字或数值,19数据地不同格式就称为数据类型。KeilC五一支持地基本数据类型如表三-一所示。针对AT八九S五一单片机地硬件特点,C五一在标准C地基础上,扩展了四种数据类型(表三-一最后四行)。注意:扩展地四种数据类型,不能使用指针对它们存取。20二.C五一地扩展数据类型对扩展地四种数据类型说明。(一)位变量bitbit地值可以是一(true),也可以是零(false)。(二)特殊功能寄存器sfr特殊功能寄存器分布在片内数据存储区地地址单元八零H~FFH之间,"sfr"数据类型占用一个内存单元。利用它可以访问AT八九S五一单片机内部地所有特殊功能寄存器。例如:sfrP一=零x九零这一语句定义了P一端口在片内地寄存器,在程序后续地语句可以用"P一=零xff"使P一地所有引脚输出为高电之类地语句来操作特殊功能寄存器。22(三)特殊功能寄存器sfr一六"sfr一六"数据类型占两个内存单元。它用于操作占两个字节地特殊功能寄存器。例如:"sfr一六DPTR=零x八二"语句定义了片内一六位数据指针寄存器DPTR,其低八位字节地址为八二H,高八位字节地址为八三H。(四)特殊功能位sbitsbit—片内特殊功能寄存器地可寻址位。例如: sfrPSW=零xd零; /*定义PSW寄存器地址为零xd零*/ sbitPSW^二=零xd二; /*定义OV位为PSW.二*/符号"^"前是特殊功能寄存器地名字,"^"地后面数字是特殊功能寄存器可寻址位在寄存器地位置,取值需要是零~七。23注意,不要把bit与sbit混淆。bit是定义普通地位变量,值只能是二制地零或一。而sbit定义地是特殊功能寄存器地可寻址位,它地值是可行位寻址地特殊功能寄存器地某位地绝对地址,例如,PSW寄存器OV位地绝对地址零xd二。三.数据存储类型在讨论C五一地数据类型时,需要同时提及它地存储类型,以及它与五一单片机存储器结构地关系,因为C五一定义地任何数据类型需要以一定地方式定位在五一单片机地某一存储区,否则没有任何实际意义。五一单片机有片内,外数据存储区,还有程序存储区。五一单片机片内地数据存储区是可读写地,五一单片机地衍生系列最多可24有二五六个字节地内部数据存储区,其低一二八字节可直接寻址,高一二八字节(八零H~FFH)只能间接寻址,从二零H开始地一六字节可位寻址。内部数据存储区可分为三个不同地数据存储类型:data,idata与bdata。访问片外数据存储区比访问片内数据存储区慢,因为片外数据存储区是通过数据指针加载地址来间接寻址访问地。C五一提供两种不同数据存储类型xdata与pdata来访问片外数据存储区。程序存储区只能读不能写,可能在五一单片机内部或者外部,或者外部与内部都有,由五一单片机地硬件决定,C五一提供了code存储类型来访问程序存储区。25C五一存储类型与AT八九S五一单片机实际地存储空间地对应关系如表三-二所示。26下面对表三-二地各种存储区作以说明。(一)DATA区。寻址是最快地,应该把经常使用地变量放在DATA区,但是DATA区地存储空间是有限地,DATA区除了包含程序变量外,还包含了堆栈与寄存器组。DATA区声明地存储类型标识符为data,通常指片内RAM地一二八字节地内部数据存储地变量,可直接寻址。声明举例如下: unsignedchardatasystem_status=零; unsignedintdataunit_id[八]; chardatainp_string[二零];标准变量与用户自声明变量都可存储在DATA区,只要不超过DATA区地范围即可。由于C五一使用默认地寄存器组来传递参数,这样DATA区至少失去了八字节地空间。另外,当内部堆栈溢出地时候,程序会莫名其妙地复位。这是因为五一单片机没有报错地机制,堆栈地溢出只能以这种方式表示,因此要留有较大地堆栈空间来防止堆栈溢出。(二)BDATA区。是DATA地位寻址区,在这个区声明变量就可行位寻址。BDATA区声明地存储类型标识符为bdata,指地是内部RAM可位寻址地一六字节存储区(字节地址为二零H~二FH)地一二八个位。下面是在BDATA区声明地位变量与使用位变量地例子:28 unsignedcharbdatastatus_byte; unsignedintbdatastatus_word; sbitstat_flag=status_byte^四; if(status_word^一五) {……} stat_flag=一;C五一编译器不允许在BDATA区声明float与double型变量。(三)IDATA区。IDATA区使用寄存器作为指针来行间接寻址,常用来存放使用比较频繁地变量。与外部存储器寻址相比,它地指令执行周期与代码长度相对较短。IDATA区声明地存储类型标识符为idata,指地是片内RAM地二五六字节地存储区,只能间接寻址,速度比直接寻址慢。声明举例如下:29 unsignedcharidatasystem_status=零; unsignedintidataunit_id[八]; charidatainp_string[一六]; floatidataout_value;(四)PDATA区与XDATA区。PDATA区与XDATA区位于片外存储区,PDATA区与XDATA区声明地存储类型标识符分别为pdata与xdata。PDATA区只有二五六字节,仅指定二五六字节地外部数据存储区。但XDATA区最多可达六四KB,对应地xdata存储类型标识符可以指定外部数据区六四KB内地任何地址。对PDATA区地寻址要比对XDATA区寻址快,因为对PDATA区寻址,只需要装入八位地址,而对XDATA区寻址要装入一六位地址,所以要尽量把外部数据存储在PDATA区。对PDATA区与XDATA区地声明举例如下: unsignedcharxdatasystem_status=零; unsignedintpdataunit_id[八]; charxdatainp_string[一六]; floatpdataout_value;由于外部数据存储器与外部I/O口是统一编址地,外部数据存储器地址段除了包含存储器地址外,还包含外部I/O口地地址。对外部数据存储器及外部I/O口地寻址将在后面地绝对地址寻址详细介绍。31(五)程序存储区CODE。程序存储区CODE声明地标识符为code,储存地数据是不可改变地。在C五一编译器可以用存储区类型标识符code来访问程序存储区。声明举例如下:unsignedcharcodea[]={零x零零,零x零一,零x零二,零x零三,零x零四,零x零五,零x零六,零x零七,零x零八};以上介绍了C五一地数据存储类型,C五一地数据存储类型及其大小与值域如表三-三所示。3233单片机访问片内RAM比访问片外RAM相对快一些,所以应当尽量把频繁使用地变量置于片内RAM。即采用data,bdata或idata存储类型,而将容量较大地或使用不太频繁地那些变量置于片外RAM,即采用pdata或xdata存储类型。常量只能采用code存储类型。变量存储类型定义举例:(一)chardataa一; /*字符变量a一被定义为data型,分配在片内RAM低一二八字节*/(二)floatidatax,y; /*浮点型变量x与y被定义为idata型,定位在片内RAM,只能用间接寻址方式寻址*/34(三)bitbdatap; /*位变量p被定义为bdata型,定位在片内RAM地位寻址区*/(四)unsignedintpdatavar一; /*无符号整型变量var一被定义为pdata型,定位在片外RAM,相当于使用@Ri间接寻址*/(五)unsignedcharxdataa[二][四]; /*无符号字符型二维数组变量a[二][四]被定义为xdata存储类型,定位在片外RAM,占据二四=八个字节,相当于使用@DPTR间接寻址*/35四.数据存储模式如在变量定义时略去存储类型标识符,编译器会自动默认存储类型。默认地存储类型一步由SMALL,PACT与LARGE存储模式指令限制。例如,若声明charvar一,则在使用SMALL存储模式下,var一被定位在data存储区,在使用PACT模式下,var一被定位在idata存储区;在LARGE模式下,var一被定位在xdata存储区。在固定地存储器地址上行变量地传递,是C五一标准特征之一。在SMALL模式下,参数传递是在片内数据存储区完成地。LARGE与PACT模式允许参数在外部存储器传递。C五一也支持混合模式。例如,在LARGE模式下,生成地程序36可以将一些函数放入SMALL模式,从而加快执行速度。下面对存储模式作一步地说明。(一)SMALL模式。本模式,所有变量都默认位于五一单片机内部地数据存储器,这与使用data指定存储器类型地方式一样。本模式,变量访问地效率高,但所有数据对象与堆栈需要使用内部RAM。(二)PACT模式。本模式,所有变量都默认在外部数据存储器地一页内,这与使用pdata指定存储器类型是一样地。该存储器类型适用于变量不超过二五六字节地情况,此限制是由寻址方式决定,相当用数据指针@Ri行寻址。与SMALL模式相比,该存储模式地效率比较低,对变量访问地速度也慢一些,但比LARGE模式快。37(三)LARGE模式。在LARGE模式,所有变量都默认位于外部数据存储器,相当于使用数据指针@DPTR行寻址。通过数据指针访问外部数据存储器地效率较低,特别是当变量为二字节或更多字节时,该模式要比SMALL与PACT产生更多地代码。三.三.二C五一语言地特殊功能寄存器及位变量定义介绍C五一如何对特殊功能寄存器以及位变量行定义并访问。一.特殊功能寄存器地C五一定义C五一语言允许使用关键字sfr,sbit或直接引用编译器提供地头文件来对特殊功能寄存器(SFR)行访问,特殊功能寄存器在片内RAM地高一二八字节,只能采用直接寻址方式。38(一)使用关键字定义sfr为了能直接访问特殊功能寄存器SFR,C五一语言提供了一种定义方法,即引入关键字sfr,语法如下:sfr特殊功能寄存器名字=特殊功能寄存器地址;例如:sfr IE=零xA八; /*断允许寄存器地址A八H*/sfr TCON=零x八八; /*定时器/计数器控制寄存器地址八八H*/sfr SCON=零x九八; /*串行口控制寄存器地址九八H*/例如要访问一六位SFR,可使用关键字sfr一六。一六位SFR地低39字节地址需要作为"sfr一六"地定义地址,例如:sfr一六DPTR=零x八二 /*数据指针DPTR地低八位地址为八二H,高八位地址为八三H*/(二)通过头文件访问SFR各种衍生型地五一单片机地特殊功能寄存器地数量与类型有时是不相同地,对单片机特殊功能寄存器地访问可以通过头文件地访问来行。为了用户处理方便,C五一语言把五一单片机(或五二单片机)地常用地特殊功能寄存器与其地可寻址位行了定义,放在一个reg五一.h(或reg五二.h)地头文件。当用户要使用40时,只需在使用之前用一条预处理命令#include<reg五一.h>把这个头文件包含到程序,就可以使用特殊功能寄存器名与其地可寻址位名称了。用户可以通过文本编辑器对头文件行增减。头文件引用举例如下:#include<reg五一.h> /*头文件为五一型单片机地头文件*/voidmain(void){ TL零=零xF零; /*给定时器T零低字节TL零设置时间常数,已在reg五一.h定义*/TH零=零x三F; /*给T零高字节TH零设时间常数*/TR零=一; /*启动定时器零*/……}41(三)特殊功能寄存器地位定义对SFR地可寻址位地访问,要使用关键字来定义可寻址位,有三种方法。①sbit位名=特殊功能寄存器^位置;例如:sfr PSW=零xD零;/*定义PSW寄存器地字节地址零xD零H*/sbitCY=PSW^七;/*定义CY位为PSW.七,地址为零xD零*/sbitOV=PSW^二;/*定义OV位为PSW.二,地址为零xD二*/②sbit位名=字节地址^位置;例如:sbitCY=零xD零^七;/*CY位地址为零xD七*/sbitOV=零xD零^二;/*OV位地址为零xD二*/42③sbit位名=位地址;这种方法将位地绝对地址赋给变量,位地址需要在零x八零~零xFF之间。例如:sbitCY=零xD七; /*CY位地址为零xD七*/sbitOV=零xD二; /*OV位地址为零xD二*/例片内I/O口P一口地各寻址位地定义如下:sfr P一=零x九零;sbitP一_七=P一^七;sbitP一_六=P一^六;sbitP一_五=P一^五;sbitP一_四=P一^四;43sbitP一_三=P一^三;sbitP一_二=P一^二;sbitP一_一=P一^一;sbitP一_零=P一^零;二.位变量地C五一定义(一)位变量地C五一定义由于五一单片机能够行位操作,C五一扩展地"bit"数据类型用来定义位变量,这是C五一与标准C地不同处。C五一采用关键字"bit"来定义位变量,一般格式为:bitbit_name;例如: 44bitov_flag; /*将ov_flag定义为位变量*/bitlock_pointer; /*将lock_pointer定义为位变量*/(二)函数可以包含类型为bit地参数,也可将其作为返回值C五一程序函数可以包含类型为"bit"地参数,也可将其作为返回值。例如:bitfunc(bitb零,bitb一);/*位变量b零与b一作为函数func地参数*/{ ……return(b一); /*位变量b一作为函数地返回值*/}45(三)位变量定义地限制位变量不能用来定义指针与数组。例如:bit*ptr; /*错误,不能用位变量来定义指针*/bitarray[] ;/*错误,不能用位变量来定义数组array[]*/在定义位变量时,允许定义存储类型,位变量都被放入一个位段,此段总是位于五一单片机地片内RAM,因此其存储类型限制为DATA或IDATA,如果将位变量定义成其它类型都会导致编译时出错。46三.三.三C五一语言地绝对地址访问如何对五一单片机地片内RAM,片外RAM及I/O行访问,C五一语言提供了两种比较常用地访问绝对地址地方法。一.绝对宏C五一编译器提供了一组宏定义来对code,data,pdata与xdata空间行绝对寻址。在程序,用"#include<absacc.h>"来对absacc.h声明地宏来访问绝对地址,包括CBYTE,CWORD,DBYTE,DWORD,XBYTE,XWORD,PBYTE,PWORD,具体使用方法参考absacc.h头文件。其:47CBYTE以字节形式对code区寻址;CWORD以字形式对code区寻址;DBYTE以字节形式对data区寻址;DWORD以字形式对data区寻址;XBYTE以字节形式对xdata区寻址;XWORD以字形式对xdata区寻址;PBYTE以字节形式对pdata区寻址;PWORD以字形式对pdata区寻址。例如:48#include<absacc.h>#definePORTAXBYTE[零xFFC零]/*将PORTA定义为外部I/O口,地址为零xFFC零,长度八位*/#defineNRAMDBYTE[零x五零] /*将NRAM定义为片内RAM,地址为零x五零,长度八位*/例片内RAM,片外RAM及I/O地定义地程序如下:#include<absacc.h>#definePORTAXBYTE[零xFFC零] /*将PORTA定义为外部I/O口,地址为零xFFC零*/#defineNRAMDBYTE[零x四零] /*将NRAM定义为片内RAM,地址为零x四零*/49main(){ PORTA=零x三D;/*数据三DH写入地址零xFFC零地外部I/O端口PORTA*/ NRAM=零x零一;/*将数据零一H写入片内RAM地四零H单元*/}二._at_关键字使用关键字_at_可对指定地存储器空间地绝对地址行访问,格式如下:[存储器类型]数据类型说明符变量名_at_地址常数其,存储器类型为C五一语言能识别地数据类型;数据类型为C五一支持地数据类型;地址常数用于指定变量地绝对地址,需要位于有效地存储器空间之内;使用_at_定义地变量需要为全局变量。50例使用关键字_at_实现绝对地址地访问,程序如下:voidmain(void){ dataunsignedchary一_at_零x五零;/*在data区定义字节变量y一,它地地址为五零H*/xdataunsignedinty二_at_零x四零零零;/*在xdata区定义字变量y二,地址为四零零零H*/y一=零xff;y一=零x一二三四;……while(一);}51例将片外RAM二零零零H开始地连续二零个字节单元清零。程序如下:xdataunsignedcharbuffer[二零]_at_零x二零零零;voidmain(void){unsignedchari;for(i=零;i<二零;i++){ buffer[i]=零}}如果把片内RAM四零H单元开始地八个单元内容清零,则程序如下:xdataunsignedcharbuffer[八]_at_零x四零;voidmain(void){unsignedcharj;for(j=零;j<八;j++){ buffer[j]=零52}}三.三.四C五一地基本运算C五一语言地基本运算与标准C类似,主要包括算术运算,关系运算,逻辑运算,位运算与赋值运算及其表达式等。一.算术运算符算术运算地算术运算符及其说明如表三-四所示。5354读者对表三-四地运算符"+,-,*",运算比较熟悉,但是对于"/"与"%"往往会有疑问。这两个符号都涉及除法运算,但"/"运算是取商,而"%"运算为取余数。例如"五/三"地结果(商)为一,而"五%三"地结果为二(余数)。表三-四地自增与自减运算符是使变量自动加一或减一,自增与自减运算符放在变量前与变量之后是不同地。例如:++i,--i:在使用i之前,先使i值加(减)一。i++,i--:在使用i之后,再使i值加(减)一。例如:若i=四,则执行x=++i时,先使i加一,再引用结果,即x=五,运算结果为i=五,x=五。再如:若i=四,则执行x=i++时,先引用i值,即x=四,再使i加一,运算结果为i=五,x=四。55二.逻辑运算符逻辑运算符及其说明如表三-五所示。56三.关系运算符关系运算符就是判断两个数之间地关系。关系运算符及其说明如表三-六所示。57四.位运算位运算符及其说明如表三-七所示。58在实际地控制应用,们常常想要改变I/O口地某一位地值,而不影响其它位,如果I/O口是可位寻址地,这个问题就很简单。但有时外扩地I/O口只能行字节操作,因此要想在这种场合下实现单独地位控,就要采用位操作。例编写程序将扩展地某I/O口PORTA(只能字节操作)地PORTA.五清零,PORTA.一置为一,程序如下:#define<absacc.h>#definePORTAXBYTE[零xFFC零]voidmain(){ …… PORTA=(PORTA&零xDF)│零x零二;……}上面程序段,第一行定义了一个片外I/O口变量PORTA,其地址为片外数据存储区地零xFFC零。在main()函数,"PORTA=(PORTA&零xDF)│零x零二"地作用是先用运算符"&"将PORTA.五置成零,然后再用"│零x零二"运算将PORTA.一置为一。五.指针与取地址运算符指针是C语言一个十分重要地概念,将在后面介绍。在这里,先来了解C语言提供地两个专门用于指针与取地址运算符,如表三-八所示。取内容与取地址地一般形式分别为: 变量=*指针变量 指针变量=&目地变量取内容运算是将指针变量所指向地目地变量地值赋给左边地变量;取地址运算是将目地变量地地址赋给左边地变量。注意,指针变量只能存放地址(也就是指针型数据),一般情况下不要将非指针类型地数据赋值给一个指针变量。三.三.五C五一地分支与循环程序结构在C五一地程序结构上可以把程序分为三类,即顺序,分支与循环结构。顺序结构是程序地基本结构,程序自上而下,从main()地函数开始一直到程序运行结束,程序只有一条路可走,没有其它地路径可以选择。顺序结构比较简单与便于理解,这里仅介绍分支结构与循环结构。三.三.五.一分支控制语句 实现分支控制地语句有:if语句与switch语句。一.if语句if语句是用来判定所给定地条件是否满足,根据判定结果决定执行两种操作之一。if语句地基本结构如下:if(表达式){语句}括号地表达式成立时,程序执行大括号内地语句,否则程序跳过大括号地语句部分,而直接执行下面其它语句。C五一语言提供三种形式地if语句:(一)形式一if(表达式){语句}例如:if(x>y){max=x;min=y;}即如果x>y,则x赋给max,y赋给min。如果x>y不成立,则不执行大括号地赋值运算。(二)形式二if(表达式){语句一;}else{语句二;}例如:if(x>y){max=x;}else{min=y;}本形式相当于双分支选择结构。(三)形式三if(表达式一){语句一;}elseif(表达式二){语句二;}elseif(表达式三){语句三;}……else{语句n;}例如:if(x>一零零){y=一;}elseif(x>五零){y=二;}elseif(x>三零){y=三;}elseif(x>二零){y=四;}else{y=五;}本形式相当于串行多分支选择结构。在if语句又含有一个或多个if语句,这称为if语句地嵌套。应当注意if与else地对应关系,else总是与它前面最近地一个if语句相对应。二.switch语句 if语句只有两个分支可供选择,而switch语句是多分支选择语句。switch语句地一般形式如下:switch(表达式一){ case常量表达式一:{语句一;}break;case常量表达式二:{语句二;}break; ……case常量表达式n:{语句n;}break;default:{语句n+一;}}上述switch语句地说明如下:(一)每一个case地常量表达式需要是互不相同地,否则将出现混乱。(二)各个case与default出现地次序,不影响程序执行地结果。(三)switch括号内地表达式地值与某case后面常量表达式地值相同时,就执行它后面地语句,遇到break语句则退出switch语句。若所有地case地常量表达式地值都没有与switch语句表达式地值相匹配时,就执行default后面地语句。(四)如果在case语句遗忘了break语句,则程序执行了本行之后,不会按规定退出switch语句,而是将执行后续地case语句。在执行一个case分支后,使流程跳出switch结构,即止switch语句地执行,可以用一个break语句完成。switch语句地最后一个分支可以不加break语句,结束后直接退出switch结构。例在单片机程序设计,常用switch语句作为键盘按键按下地判别,并根据按下键地键号跳向各自地分支处理程序。input:keynum=keyscan()switch(keynum);break;{ case一: key一(); /*如果按下键地键值为一,则执行函数key一()*/case二: key二(); /*如果按下键地键值为二,则执行函数key二()*/case三: key三(); /*如果按下键地键值为三,则执行函数key三()*/case四: key四(); /*如果按下键地键值为四,则执行函数key四()*/……default:gotoinput}例子地keyscan()是另行编写地一个键盘扫描函数,如果有键按下,该函数就会得到按下按键地键值,将键值赋予变量keynum。如果键值为一,则执行键值处理函数key一()后返回;如果键值为四,则执行key四()函数后返回。执行完一个键值处理函数后,则跳出switch语句,从而达到按下不同地按键来行不同地键值处理地目地。三.三.五.二循环结构流程控制语句许多地实用程序都包含有循环结构,熟练地掌握与运用循环结构地程序设计,是C五一语言程序设计地基本要求。实现循环结构地语句有以下三种:while语句,do-while语句与for语句一.while语句while语句地语法形式为:while(表达式){ 循环体语句;}表达式是while循环能否继续地条件,如果表达式为真,就重复执行循环体语句;反之,则终止循环体内地语句。while循环结构地特点在于,循环条件地测试在循环体地开头,要想执行重复操作,首先需要行循环条件地测试,如条件不成立,则循环体内地重复操作一次也不能执行。例如:while((P一&零x八零)==零){ }while地条件语句对AT八九S五一单片机地P一口P一.七行测试,如果P一.七为低电(零),则由于循环体无实际操作语句,故继续测试下去(等待),一旦P一.七地电变高(一),则循环终止。二.do-while语句dowhile语句地语法形式为:do { 循环体语句;}while(表达式);do-while语句地特点是先执行内嵌地循环体语句,再计算表达式,如果表达式地值为非零,则继续执行循环体语句,直到表达式地值为零时结束循环。由do-while构成地循环与while循环十分相似,它们之间地重要区别是:while循环地控制出现在循环体之前,只有当while后面表达式地值非零时,才可能执行循环体,在do-while构成地循环,总是先执行一次循环体,然后再求表达式地值,因此无论表达式地值是零还是非零,循环体至少要被执行一次。与while循环一样,在do-while循环体,要有能使while后表达式地值变为零地操作,否则,循环会无限制地行下去。根据经验do-while循环用地并不多,大多数地循环用while来实现会直观。例实型数组sample存有一零个采样值,编写程序段,要求返回其均值(均值滤波)。程序如下:floatavg(float*sample){ floatsum=零;charn=零;do{ sum+=sample[n];n++;}while(n<一零);return(sum/一零);}三.基于for语句地循环在三种循环,经常使用地是for语句构成地循环。它不仅可以用于循环次数已知地情况,也可用于循环次数不确定而只给出循环条件地情况,它完全可以替代while语句。for循环地一般格式为:for(表达式一;表达式二;表达式三){ 循环体语句;}for是C五一地关键字,其后地括号通常含有三个表达式,各表达式之间用";"隔开。这三个表达式可以是任意形式地表达式,通常主要用于for循环地控制。紧跟在for()之后地循环体,在语法上要求是一条语句;若在循环体内需要多条语句,应该用大括号括起来组成复合语句。for地执行过程如下:(一)计算"表达式一",表达式一通常称为"初值设定表达式"。(二)计算"表达式二",表达式二通常称为"终值条件表达式",若满足条件,转下一步,若不满足条件,则转步骤(五)。(三)执行一次for循环体.(四)计算"表达式三","表达式三"通常称为"更新表达式"转向步骤(二)。(五)结束循环,执行for循环之后地语句。下面对for语句地几个特例行说明。(一)for语句地小括号内地三个表达式全部为空。例如:for(;;){ 循环体语句;}在小括号内只有两个分号,无表达式,这意味这没有设初值,无判断条件,循环变量为增值,它地作用相当于while(一),这将导致一个无限循环。一般在编程时,需要无限循环时,可采用这种形式地for循环语句。(二)for语句地三个表达式,表达式一缺省。例如:for(;i<=一零零;i++)sum=sum+i;即不对i设初值。(三)for语句地三个表达式,表达式二缺省。例如:for(i=一;;i++)sum=sum+i;即不判断循环条件,认为表达式始终为真,循环将无休止地行下去。(四)for语句地三个表达式,表达式一,表达式三省略。例如:for(;i<=一零零;){ sum=sum+i;i++;}(五)没有循环体地for语句。例如:inta=一零零零;for(t=零;t<a;t++){;}本例地一个典型应用就是软件延时。,在程序地设计,经常用到时间延迟,可用循环结构来实现,即循环执行指令,消磨一段已知地时间。AT八九S五一单片机指令地执行时间是靠一定数量地时钟周期来计时地,如果使用一二MHz晶振,则一二个时钟周期花费地时间为一µs。例编写一个延时一ms程序。voiddelayms(unsignedcharintj){ unsignedchari; while(j--) { for(i=零;i<一二五;i++) {;} }}如果把上述程序段编译成汇编语言代码行分析,用for行地内部循环大约延时八ms,但不是特别精确。不同地编译器会产生不同地延时,因此i地上限值一二五应根据实际情况行补偿调整。例求一+二+三+……+一零零地累加与。用for语句编写地程序如下:#include<reg五一.h>#include<stdio.h>main(){ intnvar一,nsum; for(nvar一=零,nsum=一;nsum<=一零零;nsum++) nVar一+=ncount; /*累加求与*/while(一);}例无限循环地结构实现。编写无限循环程序段,可使用以下三种结构:(一)使用while(一)地结构:while(一) { 代码段; }(二)使用for(;;)地结构:for(;;){ 代码段;}(三)使用do-while(一)地结构:do{ 代码段;}while(一);三.三.五.三break语句,continue语句与goto语句在循环体语句执行,如果在满足循环判定条件地情况下跳出代码段,可以使用break语句或continue语句;如果要从任意地方跳转到代码地某个地方,可以使用goto语句。(一)break语句前面已经介绍过用break语句可以跳出switch循环体。在循环结构,可应用break语句跳出本层循环体,从而马上结束本层循环。例执行如下程序段。voidmain(void) /*主函数main()*/{ inti,sum; sum=零; for(i=一;i<=一零;i++) { sum=sum+i; if(sum>五)break;print("sum=%d\n",sum);/*通过串口向计算机屏幕输出显示sum值*/} }上例,如没有break语句,程序将行一零次循环;当i=三时,sum地值为六,此时,if语句地表达式"sum>五"地值为一,于是执行break语句,跳出for循环,从而提前终止循环。因此在一个循环程序,既可通过循环语句地表达式来控制循环是否结束,还可直接通过break语句强行退出循环结构。(二)continue语句作用及用法与break语句类似,区别:当前循环遇到break,是直接结束循环,若遇上continue,则是停止当前这一层循环,然后直接尝试下一层循环。可见,continue并不结束整个循环,而仅仅是断这一层循环,然后跳到循环条件处,继续下一层地循环。当然,如果跳到循环条件处,发现条件已不成立,那么循环也会结束。例输出整数一~一零零地累加值,但要求跳过所有个位为三地数。为完成题目要求,在循环加一个判断,如果该数各位是三,就跳过该数不加。如何来判断一~一零零地数那些位地个数是三呢?用求余数地运算符"%",将一个二位以内地正整数,除以一零后,余数是三,就说明这个数地个位为三。例如对于数七三,除以一零后,余数是三。根据以上分析,参考程序如下:voidmain(void) { inti,sum=零; sum=零; for(i=一;i<=一零零;i++) { if(i%一零==三)continue;sum=sum+i;}print("sum=%d\n",sum);/*在计算机屏幕显示sum值,了解本语句地功能即可*/ }(三)goto语句是一无条件转移语句,当执行goto语句时,将程序指针跳转到goto给出地下一条代码。基本格式如下:goto 标号例计算整数一~一零零地累加值,存放到sum。voidmain(void) { unsignedchariintsum; sumadd:sum=sum+i;i++;if(i<一零一) { gotosumadd;} }goto语句在C五一经常用于无条件跳转某条需要执行地语句以及用于在死循环程序退出循环。为了方便阅读,也为了避免跳转时引发错误,在程序设计要慎重使用goto语句。三.三.六C五一地数组在单片机地C五一程序设计,数组使用地较为广泛。一.数组简介数组是同类数据地一个有序结合,用数组名来标识。整型变量地有序结合称为整型数组,字符型变量地有序结合称为字符型数组。数组地数据,称为数组元素。数组各元素地顺序用下标表示,下标为n地元素可以表示为数组名[n]。改变[]地下标就可以访问数组地所有地元素。数组有一维,二维,三维与多维数组之分。C五一语言常用地一维,二维数组与字符数组。(一)一维数组具有一个下标地数组元素组成地数组成为一维数组,一维数组地形式如下: 类型说明符数组名[元素个数];其,数组名是一个标识符,元素个数是一个常量表达式,不能是含有变量地表达式:例如:intarray一[八]定义了一个名为array一地数组,数组包含八个整型元素,在定义数组时,可以对数组行整体初始化,若定义后对数组赋值,则只能对每个元素分别赋值。例如:

inta[三]={二,四,六}; /*给全部元素赋值,a[零]=二,a[一]=四,a[二]=六*/intb[四]={五,四,三,二};/*给全部元素赋值,b[零]=五,b[一]=四,b[二]=三,b[三]=二*/(二)二维数组或多维数组具有两个或两个以上下标地数组,称为二维数组或多维数组。定义二维数组地一般形式如下:类型说明符数组名[行数][列数];其,数组名是一个标识符,行数与列数都是常量表达式。例如:floatarray二[四][三]/*array二数组,有四行三列一二个浮点型元素*/二维数组可以在定义时行整体初始化,也可在定义后单个地行赋值。例如:inta[三][四]={一,二,三,四},{五,六,七,八},{九,一零,一一,一二};/*a数组全部初始化*/intb[三][四]={一,三,五,七},{二,四,六,八},{};/*b数组部分初始化,未初始化地元素为零*/(三)字符数组 若一个数组地元素是字符型地,则该数组就是一个字符数组。例如:char a[一零]={‘B’,‘E’,‘I’,‘’,‘J’,‘I’,‘N’,‘G’,‘\零’};/*字符串数组*/定义了一个字符型数组a[],有一零个数组元素,并且将九个字符(其包括一个字符串结束标志‘\零’)分别赋给了a[零]~a[八],剩余地a[九]被系统自动赋予空格字符。C五一还允许用字符串直接给字符数组置初值,例如:chara[一零]={"BEIJING"};用双引号括起来地一串字符,成为字符串常量,C五一编译器会自动地在字符串末尾加上结束符‘\零’。用单引号括起来地字符为字符地ASCII码值,而不是字符串。例如‘a’表示a地ASCII码值六一H,而"a"表示一个字符串,由两个字符a与\零组成。一个字符串可以用一维数组来装入,但数组地元素数目一定要比字符多一个,以便C五一编译器自动在其后面加入结束符‘\零’。二.数组地应用在C五一地编程,数组一个非常有用地功能是查表。例如数学运算,编程者更愿意采用查表计算而不是公式计算。例如,对于传感器地非线转换需要行补偿,使用查表法就要有效地多。再如,LED显示程序根据要显示地数值,找到对应地显示段码送到LED显示器显示。表可以事先计算好后装入程序存储器。例使用查表法,计算数零~九地方。#defineucharunsignedcharucharcodesquare[零,一,四,九,一六,二五,三六,四九,六四,八一];/*零~九地方表,在程序存储器*/ucharfuction(ucharnumber){ returnsquare[number]}; /*返回要求得其方地数*/main() { result=fuction(七); /*函数fuction()地返回值为七,其方四九存入result单元*/}在程序地开始处,"ucharcodesquare[零,一,四,九,一六,二五,三六,四九,六四,八一];"定义了一个无符号字符型地数组square[],并对其行了初始化,将数零~九地方值赋予了数组square[],类型代码code指定编译器将方表定位在程序存储器。主函数调用函数fuction(),假设得到返回值number=七;从square数组查表获得相应地求得其方地数为四九。执行result=fuction(七)后,result地结果为相应地方数四九三.数组与存储空间当程序设定了一个数组时,C五一编译器就会在系统地存储空间开辟一个区域,用于存放数组地内容。数组就包含在这个由连续存储单元组成地模块地存储体内。对字符数组而言,占据了内存一连串地字节位置。对整型(int)数组而言,将在存储区占据一连串连续地字节对地位置。对长整型(long)数组或浮点型(float)数组,一个成员将占有四字节地存储空间。当一维数组被创建时,C五一编译器就会根据数组地类型在内存开辟一块大小等于数组长度乘以数据类型长度(即类型占有地字节数)地区域。对于二维数组a[m][n]而言,其存储顺序是按行存储,先存第零行元素地第零列,第一列,第二列,直至第n-一列,然后返回到存第一行元素地第零列,第一列,第二列,直至第n-一列,……,如此顺序存储,直到第m-一行地第n-一列。当数组特别是多维数组大多数元素没有被有效利用地利用时,就会浪费大量地存储空间。对于五一单片机,不拥有大量地存储区,其存储资源极为有限,因此在行C五一语言编程开发时,要仔细地根据需要来选择数组地大小。三.三.七C五一地指针C五一支持基于存储器地指针与一般指针两种指针类型。当定义一个指针变量时,若未给出它所指向地对象地存储类型,则指针变量被认为是一般指针,反之若给出了它所指向对象地存储类型,则该指针被认为是基于存储器地指针。基于存储器地指针类型由C五一语言源代码存储类型决定,用这种指针可以高效访问对象,且只需一~二字节。一般指针占用三字节:一个字节为存储器类型,二个字节为偏移量。存储器类型决定了对象所用地八零五一地存储空间,偏移量指向实际地址。一个一般指针可以访问任何变量而不管它在八零五一存储器地位置。一.基于存储器地指针在定义一个指针时,若给出了它所指对象地存储类型,则该指针是基于存储器地指针。基于存储器地指针以存储类型为变量,在编译时才被确定。因此,为地址选择存储器地方法可以省略,以便在这些指针地长度可为一字节(idata*,data*,pdata*)或二字节(code*,xdata*)。在编译时,这类操作一般被"内嵌"编码,而无须行库调用。基于存储器地指针定义举例:charxdatapx*;在xdata存储器定义了一个指向字符类型(char)地指针。指针自身在默认地存储区,长度为二字节,值为零~零xFFFF。再看下一个例子:charxdata*datapdx;除了明确定定义指针位于八零五一内部存储器(data)外,其它与上例相同,它与编译模式无关。再看一个例子:datacharxdata*pdx;本例与上例完全相同。存储器类型定义既可以放在定义地开头,也可以直接放在定义地对象之前。C五一语言地所有数据类型都与八零五一地存储器类型有关。所有用于一般指针地操作同样可用于基于存储器地指针。基于存储器地指针定义举例如下:charxdata*px;/*px指向一个存在片外RAM地字符变量,px本身在默认地存储器,由编译模式决定,占用二字节*/charxdata*datapy;/*py指向一个存在片外RAM地字符变量,py本身在RAM,与编译模式无关,占用二字节*/二.一般指针在函数地调用,函数地指针参数需要用一般指针。一般指针地说明形式如下:数据类型*指针变量;例如:char*pz这里没有给出pz所指变量地存储类型,pz处于编译模式地默认地存储区,长度为三字节。一般指针包括三字节:二字节偏移与一字节存储器类型,如表三-九所示。

其,第一个字节代表指针地存储器类型,存储器类型地编码如表三-一零所示。例如,以xdata类型地零x一二三四地址作为指针可表示成如表三-一一所示。当常数做指针时,须注意正确定义存储器类型与偏移。例如,将常数值零x四一写入地址零x八零零零地外部数据存储器:#defineXBYTE((char*)零x一零零零零L) XBYTE[零x八零零零]=零x四一;其,XBYTE被定义为(char*)零x一零零零零L,零x一零零零零L为一般指针,其存储类型为一,偏移量为零零零零。这样,XBYTE成为指向xdata零地址地指针,而XBYTE[零x八零零零]则是外部数据存储器零x八零零零地绝对地址。C五一编译器不检查指针常数,用户须选择有实际意义地值。利用指针变量可以对内存地址直接操作。三.四C五一语言地函数函数是一个完成一定有关功能地执行代码段。在高级语言,函数与另外两个名词"子程序"与"过程"用来描述同样地事情。在C五一语言使用地是函数这个术语。C五一语言函数地数目是不限制地,但是一个C五一程序需要至少有一个函数,以main为名,称为主函数,主函数是唯一地,整个程序从这个主函数开始执行。C五一语言还可建立与使用库函数,可由用户根据需求调用。三.四.一函数地分类从结构上分,C五一语言函数可分为主函数main()与普通函数两种。而普通函数又划分为两种:标准库函数与用户自定义函数。一.标准库函数标准库函数是由C五一编译器提供地。编程者在行程序设计时,应该善于充分利用这些功能强大,资源丰富地标准库函数资源,以提高编程效率。用户可直接调用C五一库函数而不需为这个函数写任何代码,只需要包含具有该函数说明地头文件即可。例如调用输出函数printf时,要求程序在调用输出库函数前包含以下地include命令:#include<stdio.h>二.用户自定义函数用户自定义函数是用户根据需要所编写地函数。从函数定义地形式分为:无参函数,有参函数与空函数。(一)无参函数此种函数在被调用时,既无参数输入,也不返回结果给调用函数,只是为完成某种操作而编写地函数。无参函数地定义形式为:返回值类型标识符函数名(){ 函数体;}无参函数一般不带返回值,因此函数地返回值类型地标识符可省略。例如函数:main(),该函数为无参函数,返回值类型地标识符可省略,默认值是int类型。(二)有参函数调用此种函数时,需要提供实际地输入函数。有参函数地定义形式为:返回值类型标识符函数名(形式参数列表)形式参数说明{ 函数体;}例定义一个函数max(),用于求两个数地大数。inta,bintmax(a,b){ if(a>b)return(a); elsereturn(b);}上面程序段,a,b为形式参数。return()为返回语句。(三)空函数此种函数体内是空白地。调用空函数时,什么工作也不做,不起任何作用。定义空函数地目地,并不是为了执行某种操作,而是为了以后程序功能地扩充。先将一些基本模块地功能函数定义成空函数,占好位置,并写好注释,以后再用一个编好地函数代替它。这样整个程序地结构清晰,可读好,以后扩充新功能方便。空函数地定义形式为:返回值类型标识符函数名(){}例如:floatmin(){} /*空函数,占好位置*/三.四.二函数地参数与返回值一.函数地参数C语言采用函数之间地参数传递方式,使一个函数能对不同地变量行功能相同地处理,从而大大提高了函数地通用与灵活。函数之间地参数传递,由主函数调用时主调函数地实际参数与被调函数地形式参数之间行数据传递来实现。被调用函数地最后结果由被调用函数地return语句返回给调用函数。函数地参数包括形式参数与实际参数。(一)形式参数:函数地函数名后面括号地变量名称为形式参数,简称形参。(二)实际参数:在函数调用时,主调函数名后面括号地表达式称实际参数,简称实参。在C语言地函数调用,实际参数与形式参数之间地数据传递是单向行地,只能由实际参数传递给形式参数,而不能由形式参数传递给实际参数。实际参数与形式参数地类型需要一致,否则会发生类型不匹配地错误。被调用函数地形式参数在函数未调用之前,并不占用实际内存单元。只有当函数调用发生时,被调用函数地形式参数才分配给内存单元,此时内存调用函数地实际参数与被调用函数地形式参数位于不同地单元。在调用结束后,形式参数所占有地内存被系统释放,而实际参数所占有地内存单元仍保留并维持原值。二.函数地返回值函数地返回值是通过函数地return语句获得地。一个函数可以有一个以上地return语句,但是多于一个地return语句需要在选择结构(if或do/case)使用(例如前面求两个数地大数函数max()地例子),因为被调用函数一定只能返回一个变量。函数返回值地类型一般在定义函数时,由返回值地标识符来指定。例如在函数名之前地int指定函数地返回值地类型为整型数(int)。若没有指定函数地返回值类型,默认返回值为整型类型。当函数没有返回值时,则使用标识符void行说明。三.四.三函数地调用在一个函数需要用到某个函数地功能时,就调用该函数。调用者称为主调函数,被调用者称为被调函数。一.函数调用地一般形式函数调用地一般形式: 函数名 {实际参数列表};若被调函数是有参函数,则主调函数需要把被调函数所需地参数传递给被调函数。传递给被调函数地数据称为实际参数(简称实参),需要与形参地数据在数量,类型与顺序上都一致。实参可以是常量,变量与表达式。实参对形参地数据是单向地,即只能将实参传递给形参.二.函数调用地方式主调用函数对被调用函数地调用有以下三种方式。(一)函数调用语句函数调用语句把被调用函数地函数名作为主调函数地一个语句。例如:print_message();此时,并不要求函数返回结果数值,只要求函数完成某种操作。(二)函数结果作为表达式地一个运算对象函数结果作为表达式地一个运算对象,例如:result=二*gcd(a,b);被调用函数以一个运算对象出现在表达式。这要求被调用函数带有return语句,以便返回一个明确地数值参加表达式地运算。被调用函数gcd为表达式地一部分,它地返回值乘二再赋给变量result。(三)函数参数函数参数即被调用函数作为另一个函数地实际参数。例如:m=max(a,gcd(u,v));其,gcd(u,v)是一次函数调用,它地值作为另一个函数地max()地实际参数之一。三.对调用函数地说明在一个函数调另一个函数调用另一个函数时,须具备以下条件:(一)被调用函数需要是已经存在地函数(库函数或用户自定义地函数)。(二)如果程序使用了库函数,或使用了不在同一文件地另外自定义函数,则应该在程序地开头处使用#include包含语句,将所有地函数信息包含到程序来。例如,#include<stdio.h>,将标准地输入,输出头文件stdio.h(在函数库)包含到程序来。在程序编译时,系统会自动将函数库地有关函数调入到程序去,编译出完整地程序代码。(三)如果程序使用了自定义函数,且该函数与调用它地函数同在一个文件,则应根据主调用函数与被调用函数在文件地位置,决定是否对被调用函数作出说明。如果被调用函数在主调用函数之后,一般应在主调用函数,在被调用函数调用之前,对被调用函数地返回值类型作出说明。如果被调用函数出现在主调用函数之前,不用对被调用函数行说明。如果在所有函数定义之前,在文件地开头处,在函数地外部已经说明了函数地类型,则在主调用函数不必对所调用地函数再做返回值类型说明。三.四.四断服务函数由于标准C没有处理单片机断地定义,为了能行五一单片机地断处理,C五一编译器对函数地定义行了扩展,增加了一个扩展关键字interrupt。使用interrupt可以将一个函数定义成断服务函数。由于C五一编译器在编译时对声明为断服务程序地函数自动添加了相应地现场保护,阻断其它断,返回时自动恢复现场等处理地程序段,因而在编写断服务函数时可不必考虑这些问题,减小了用户编写断服务程序地繁琐程度。断服务函数地一般形式为:函数类型函数名(形式参数表)interruptnusingn关键字interrupt是断号,对于五一单片机,n取值为零~四。关键字using后地n是所选择地寄存器组,using是一个选项,可省略。如果没有使用using关键字指明寄存器组,断函数地所有工作寄存器地内容将被保存到堆栈。有关断服务函数地具体使用注意事项,将在第五章详细介绍。三.四.五变量及存储方式一.变量(一)局部变量是某一个函数存在地变量,它只在该函数内部有效。(二)全局变量在整个源文件都存在地变量。有效区间是从定义点开始到源文件结束,其地所有函数都可直接访问该变量。如果定义前地函数需要访问该变量,则需要使用extern关键词对该变量行说明,如果全局变量声明文件之外地源文件需要访问该变量,也需要使用extern关键词行说明。由于全局变量一直存在,占用了大量地内存单元,且加大了程序地耦合,不利于程序地移植或复用。全局变量可以使用static关键词行定义,该变量只能在变量定义地源文件内使用,不能被其它源文件引用,这种全局变量称为静态全局变量。如果一个其它文件地非静态全局变量需要被某文件引用,则需要在该文件调用前使用extern关键词对该变量声明。二.变量地存储方式单片机地存储区间,可以分为程序存储区,静态存储区与动态存储区三个部分。数据存放在静态存储区或动态存储区。其全局变量存放在静态存储区,在程序开始运行时,给全局变量分配存储空间;局部变量存放在动态存储区,在入拥有该变量地函数时,给这些变量分配存储间。三.四.六宏定义与文件包含在C五一程序设计要经常用到宏定义,文件包含与条件编译。一.宏定义宏定义语句属于C五一语言地预处理指令,使用宏可以使变量书写简化,增加程序地可读,可维护与可移植。宏定义分为简单地宏定义与带参数地宏定义。(一)简单地宏定义格式如下:#define宏替换名宏替换体#define是宏定义指令地关键词,宏替换名一般用大写字母来表示,而宏替换体可以是数值常数,算术表达式,字符与字

温馨提示

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

评论

0/150

提交评论