



版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
如何用C来完成SN8系
列芯片的程序设计松翰易爱华松翰科技(深圳)有限公司SONIXTECHNOLOGYCO.,LTD.内容概览ー、C与Assembly简述二、SN8Cstudio的安装与使用SN8Cstudio的安装SN8Cstudio应用实例三、数据类型与运算专有数据类型常量与变量数据的存储类型与存储结构四、程序流程控制顺序结构分支判断结构循环控制结构五、函数函数的定义函数参数传递与返回值函数参数与全局变量六、结构体、联合在SN8c程序中的应用结构体联合七、中断中断函数的定义中断过程的分析中断函数的结构ハ、位操作位的定义位的运算位比较在程序流程控制中的应用九、内嵌汇编如何内嵌汇编内嵌汇编时变量的传递十、程序结构主函数与子函数构建可复用文件构建具有实用性的程序ー、C与Assembly简述Assembly(汇编)的历史已经有半个多世纪了,从计算机的发明那天开始,汇编就注定要与其结缘,它作为第一种真正意义上的编程语言,在计算机的发展过程中具有无可替代的地位。汇编具有与机器语言ーー对应的高效率,就是由于汇编的高效率,和其紧贴硬件的特性,使其在半个世纪之后的今天依然是目前开发单片机程序的主流工具之一。但是由于编写汇编代码的工作量和难度都比较高,并且要求程序员对硬件必须有较深刻的了解,其入门就相对较难。而汇编的写法各异,不同的程序员编写的代码有着巨大的差异,这就带来了学习的难度,可读性差也成为了其推广的瓶颈,程序的维护更加艰难。随着程序复杂性的增加,开发团队的形成,应用而生的高级语言就逐渐取代了其位置。C就是高级语言中针对硬件操作最优秀的ー种,C也是基于汇编的,其相互关系如图所示。随着人们不断的对C进行改善,使C的效率得到了很大的提升,其编译效率逐渐的逼近汇编。而C以其模块化的编程模式,简洁的代码,良好的可读性和可维护性,成为了另ー种单片机开发的主流开发工具,相比汇编而言,其具有无可比拟的优异性。C代码 > 汇编代码 ►机器语言
图1、C与汇编的关系SN8C是基于标准C而又加入SONIXSN8系列芯片特征的专用C,它面对的是SONIXSN8系列芯片程序开发,它能产生非常简洁的能直接运行于SN8系列芯片的优化代码。具有良好的兼容性,易学易用,既方便客户开发,又继承了原有汇编的卓越性能。在本文当中,我们将结合汇编来讲述C开发的各方面知识,以期通过对比来加深对芯片的编程应用的了解,同时,对熟悉C的程序员来说,可以更加深入了解芯片;而对于熟悉汇编的程序员来说,我们可以从对比中找到从汇编到C的转换方法。二、SN8Cstudio的安装与使用SONIXSN8系列的芯片是RISC内核的高性能芯片。目前,由SON1X提供的SN8Assembly仅有59条指令,是一种高效的汇编语言,有S8ASM和M2ASM编译器分别支持其1系和2系芯片。在此基础上的SN8Cstudio整合了汇编和C的编译器,1.1、SN8Cstudio的安装在得到SN8Cstudio的安装包后,按照标准的Windows程序安装方法安装就可以将软件安装到机器上。1.2、SN8Cstudio应用实例ー、 创建一个工作区与VC类似,我们的工程管理模式是以工作区方式来管理,在你准备ー个项目的时候,你首先创建一个属于这个项目的工作区:1、打开IDS->文件->新建,单击出现如下界面:
2、单击workspace图标,进入workspace创建窗口,单击しocation编辑框后的按钮,选择你需要的工作目录,然后输入新建Workspace的name,在这里如下:图2-2新建Workspace对话框3、单击OK,IDS的界面生成了工作区的工作界面,出现了Workspace窗口和Output窗口。同时,打开目标文件夹,你会发现在你选定的文件夹下面,生成了一个以WorkspaceName命名的新文件夹,打开文件夹你可以看到ー个新生成的.wsp文件,这就是新生成的Workspace的配置文件:KBKB图2-3新生成的Workspace配置文件新建一个工程(Project)在我们成功创建一个工作区后,你会发现我们的Workspace窗口中的Projiect数目为〇,接着我们就要依据项目所用的芯片母体来创建一个工程项目了,跟创建Workspace相同,打开菜单中的文件,新建,单击出现新建窗口,此时默认为新建Project。在右边的ProjectList窗ロ中选择你要使用的芯片母体和要创建的工程类型(一般为普通项目),此时的目标路径Location中已经显示为你刚オ建立的Workspace目录,建议将工程保存在这个目录下,不修改默认路径。给新建工程取ー个有意义的Name,在框中输入。这里我们取与Workspace相同的名称,新建ー个2700系列芯片的普通项目工程,设置如下图所示:1、单击文件,新建,选择合适的选项:NewNewProjectListSN82700AProjectSN82600ProjectSN82500ProjectSN81900ProjectSN81800ProjectSN81700ProjectSN81600ProjectSN82700ASreiesStaticLibraryProjectSN82600SeriesStaticLibraryProjectSN82500SeriesStaticLibraryProjectSN81900SeriesStaticLibraryProjectSN81800SeriesStaticLibraryProjectSN81700SeriesStaticLibraryProjectSN81600SeriesStaticLibraryProjectName|SN8C_ExLocatioIgon.Yi\MyDocumentsVMyWork\SN8CCexample\SN8C_ExOKICancel图2イ新建Project2、单击OK。3、新工程选项配置,在确认建立工程后,出现ProjectSetting对话框,左边的Project列表中默认选中我们刚刚建立的工程,在这里我们只修改Chip和CodeOption项目下的选项,如图所示:4、选择正确的芯片:在Chip页中,Definition文本框中显示系统依据你选定的芯片母体系列而确定的需要调用的芯片定义文件。ChipList表列出了当前版本的IDS所支持的这ー系列的各个芯片,选中其中你需要使用的芯片母体,Selected文本框和Description文本框相应会自动改变。如图:图2-6选择芯片5、设置正确的CodeOption选项和ICEMODE:ICEMODE的设置在SN8Plxxx芯片中是用于选择是否是ICE仿真模式,在CodeOption列表中自动显示选定芯片母体的CodeOption选项,在OptionValue项的默认值上单击,出现ー个下拉列表,单击选择合适的选项。如图:图2-7设置正确的CodeOption6、单击OK,系统生成一个没有文件的工程,在Workspace管理窗口中我们可以看见当前生成的工程为Workingproject,正处被激活状态(工程名称为加粗字体):日Workspace-SN8C_Ex:1projet白ー卜SI8C_Ex,ロSourceロHeader图2-8新建Project的状态被激活打开相应的文件夹,我们会发现系统生成了很多个新的文件,其中的.pij文件即为工程文件,其他的文件为根据我们刚オ的设置和不同的芯片生成的配置文件及头文件。新建文件完成项目的新建后,我们发现项目管理器source文件夹下是空的,没有任何文件,文件就是我们编程的主要工作了!1、打开文件ー〉新建菜单,单击出现我们前面看过很多次的New对话框,不过比前面多了ー个选项 Files,选择New列表中的Files选项,单击按钮,对话框如图所示:图2-9新建文件对话框2、在FileList中选中需要创建的文件类型,这里我们创建一个CSourceFile,给文件命名从Name编辑框中输入,存放的地点为刚オ的设定位置,默认不做修改。3、单击OK按钮,IDS打开一个编辑窗口,最大化,显示为刚刚建立的文件名的页,系统允许我们在这里进行程序的编写。编写程序在新建一个文件后,系统自动打开编辑器,并打开ー个由用户命名的空文档,它与普通文档相比较没有什么不同的之处,只不过它可以对C的关键字进行高亮显示。在新的文档里编辑程序。
C本身就是•种模块化的编程语言,SN8c的编程保持C语言的编程风格。SN8C所具有的Non-ANSIC的特性请参看其他章节,在此不再做描述。下面是ー个简单的C程序举例。例2:*FileName:SN8C_Ex.cPostBy:Dragon.YiDate:2005/09/23TestHistory:V1.00.220*describe:test2708interrupt*#include<sn8p2708a.h>structword{unsignedfint:l;unsigned:7;Jintword;unsignedinttc0cvalue=0x64;unsignedintaccbuf=0x00;unsignedintpflagbuf=0;//Thedatawillautostore!_interruptintserv(void)//Thedatawillautostore!(_bCLR(&INTRQ,5);TC0C=tcOcvalue;intword.fint=1;voidinitlO(void);voidinitlNT(void);voidmain(void){STKP=0x07;initlOO;initINT();while(l)(if(intword.fint!=0)(Pl=0x00;P2=0x00;P3=0x00;P4=0x00;P5=0x00;PO=OxOO;PO=Oxff;Pl=Oxff;P2=0xff;P3=0xff;P4=0xff;P5=0xff;))}voidinitlO(void)(P0M=0xFF;PlM=OxFF;P2M=0xFF;P3M=0xFF;P4M=0xFF;P5M=0xFF;}voidinitlNT(void)(INTRQ=0x00;INTEN=0x00;TC0M=0x00;TC0M=0x20;TC0C=0x64;_bCLR(&INTRQ,5);_bSET(&INTEN,5);_bSET(&TC0M,7);_bSET(&STKP,7);}注:程序只作为程序架构提供参考!五、 调试程序在完成代码编辑后,想要马上顺利运行基本上是不可能的,每ー个程序都有经过调试的过程,程序的调试在IDS中非常方便,IDS有全面的可视化的调试工具。你可以在程序中设置断点,在程序中按自己的需求来运行。可以根据不同的需求来进行调试。1、compile和Build完成代码的编辑后,首先要进行Compile,在菜单Build->Compilecurrentfile单击或在エ具栏中单击Compile按钮和单击快捷键Ctrl+F7均可以启动系统的Compile程序。Compile程序会检查代码中存在的语法错误和软件设置错误,然后在Output窗口表列出来,用户可以根据表列出来的提示,对程序进行修改。若是语法错误,在列表中双击选项,系统会自动将光标移动到相对应的代码行,方便你的检查和修改。Compile通过后,进行Build,同样有3种方式可以启动Build程序,Build程序生成运行所需要的一些文件,同时检查硬件配置和连接错误,用户必须根据提示对程序进行修改才能顺利通过。2、选择仿真方式单击进入Project->setting,出现我们前面己经熟悉的projectsetting对话框,当前显示的是Genera!页,在primarysetting组下面找到复选项usesimulate。如果选中该选项,则程序就在系统提供的虚拟仿真器上仿真并显示相应结果;若不选中该选项,则程序必须在相应系列的仿真器上进行程序调试仿真。这里我们先选择它。3、进入调试模式完成compile和build之后,进入菜单Debug->beginDebug,单击菜单项或直接按快捷键F5或在图标选项中选择按會钮単击都可进入调试模式,进入调试模式后,系统界面变成如下图:图2-11调试程序界面系统界面出现Ramwindow、Watchwindow>variablewindow、Registerwindow>callstackwindowDisassemblywindow,这些窗口都是调试程序要用到的。程序的指针指向程序的入口处,即Main〇函数的第一条语句。4、应用Watchwindow:在调试过程中如何应用watchwindow?Watchwindow用在调试工程中对定义的变量进行监视,将需要进行监视的Variable在编辑窗口中双击,然后拖放到WatchWindow当中,运行程序就可以在watchwindow中看到Variable的存放地点和值的变化。如图:
图2-12watchwindow窗ロ系统会将产生了变化的项置成红色。为方便观察,你最多可以将Variable分别放到3个Watchwindow当中进行观察,他们的显示效果是ー样的!5、应用VariablewindowVariablewindow显示格式和watchwindow的显示格式ー样,但是variablewindow里面的项无法自己设定。它是以Auto的状态显示当前运行过程中被改变的LocalVariable,同样系统会将最近改变的量置成红色。6、应用RegisterWindow:Registerwindow里面显示的项是根据芯片的资源而定的,它显示的是当前母体芯片Ram中0x80~0xFF空间中专用存储器在当前运行状态下的值,单击前面的“+”号,将扩展项目展开,可以得到每ー个bit的当前值。同样,在运行过程中,系统会将刚改变的项的值置为红色高亮显示,便于我们观察跟踪。NameValue ±_因TCORCannotaccessv..HPCLOxAD因PCH0x00BPO0x00日PlOxFF 事FP1O0x01FP110x01FP120x01 -JFP130x01FP140x01FP150x01FP160x01FP170x01H〇・P20x00raP30x00 图2-13Register窗ロ7、应用CallstackwindowCallstackwindow显示当前运行状态下,stack的使用状况和入栈的函数,用户可以根据显示来判断程序的调用状况,进而判断当前的状态是否正确,有没有函数调用出错。匕JXcallmnapp callmnintgnd;interfacebetw<图2-14callstack窗ロ8、应用Memorywindow:在程序运行当中,当你想从具体的Ram地址而得到它的值的时候,你可以通过Memory窗口中实现,你可以直接在Goto后面的编辑框中输入地址,按Enter键确认,窗口会自动跳转到相应的位置显示该位置的值,方便我们在使用过程中的查找。9、应用Disassemblywindow:如果调试过程中,你需要知道程序的直接汇编码,你可以在这个窗口中观察。设置断点:程序调试的时候,往往需要知道程序运行过程中某个变量的运行状态,或者想知道某段程序是否执行,还有执行后的结果;或者,需要知道某段程序的运行过程是否正确。这时,就必须在程序的正确位置设置断点。将光标移到正确位置,在相应的行,单击设置断点的按钮或按快捷键F9或者从菜单项中设置都可以使该行程序被设置成为断点,被设置成断点后,运行程序,程序就会运行到断点处停下来,以便于用户控制程序的执行和观察执行的结果。如图:(PO=Oxff;Pl=Oxff;0 P2=0xff;P3=0xff;P4=0xff;P5=0xff;}}图2-15断点执行状态跟踪程序执行:应用系统提供的debug工具,用户可以很方便地対程序的执行效果进行跟踪。你可以单步,也可越过一段程序往下执行,这要看你的需要!调试工具同样可以以3种方式打开:Debug菜单、工具栏按钮和快捷键。六、程序输出1、仿真程序结果:将仿真器与主机连接好,在projectsetting中清除usesimulate选项的选中状态。将需要仿真的硬件系统目标板连接好。单击Debug按钮或者按快捷键进入调试状态,就可以在实际硬件上看到程序运行的结果,对程序进行修改直到得到正确结果。我们就得到ー个正确运行的可以烧录到实际芯片的程序。2、输出烧录文档在完成程序的调试仿真后,我们就需要将程序输出。系统已经在默认目录下生成程序的烧录档XXX.sn8,默认目录为当前工程文件的目录下。
三、数据类型与运算SN8C支持标准C的所有数据类型。具体有无符号字符(unsignedchar)、有符号字符(signedchar)、无符号整型(unsignedint)、有符号整型(signedint)、无符号长型(unsignedlong),有符号长型(signedlong)>浮点(float)和指针类型。还支持所有的构造类型。f 字符型(char)整型(int)r基本类型《
长整型(long)、浮点型(float)数据类型构造类型指针类型’数据类型构造类型指针类型’数组(array)结构体(struct)共用体(union)枚举(enum)图3・1数据类型专有数据类型虽然SN8c支持C的所有数据类型,但是由于它面对的是8-bit单片机,所以必然会考虑数据类型的定义方法和长度。在这些方面,SN8c有它自己的专有的定义特征和数据长度,在使用时一定要加于区分。请看下表:数据类型Size(Byte)数据取值范围Signedchar(short、int)1-128〜+127Unsignedchar(short>int)1〇〜255Signedlong2■32768〜+32767Unsignedlong2〇〜65535float、double4Pointer2enum1表3-1、数据类型长度定义表常量与变量在程序设计的过程中,我们有些量可能是参考值,也可能是预设的值。总之,我们希望它在整个程序中保持不变,并且在程序的任何地方可以提供我们调用,用来比对某些条件是否成立等等。对于这样的值,我们可以不去定义它,而直接参考数值,这在汇编编程过程中,经常会有ー些缺乏经验的程序员这样用。但这就需要程序员把程序中的这些值都牢牢记住,并且要将他们的值前后统ー,这是ー个非常容易出错的过程,而且也会影响了程序的可读性,这样,程序的修改和维护都非常艰难,必须处处小心翼翼,ー处不慎就全盘错误!这是任何人不想看到的后果。因此,建议对于程序中用到的不会改变的参考值或其他预设的值都进行ー个预先的定义,给它取ー个有意义的或与其相关的名字。这个过程就是常量定义,自然,这个不变的量就称为常量。在标准C中,由于面对的是功能强大的CPU系统和大内存,用户可以不去管它放置的地方。但是,面对一个单片机系统,它的Ram非常小,有时会显得很紧张。所以系统为了节省空间会将一些表格等放在系统的ROM中。而我们直接命名的常量,则山编译器自动将其替换为所需要的值,这些工作就由计算机来完成好了。我们先来看看我们用汇编编写程序时是怎么来定义常量的:如:door_service_cequ#80;80ms去门抖动tOint_cequ#224;t0中断时间segment_cequ#3;最多3段烹调注:上面数值前的#号,是SN8AsM的符号,用于提示后面的是立即数。上面定义了3个程序中会用到的参考值,顺便提一下,在定义的时候加上注释是有必要的,要不然时间久了你就又不知道你定义的到底是什么了。在上面的定义中,用的是汇编EQU关键字,在编译过程中,程序里但凡出现了EQU前面的字段都会被其后面的值直接代替,因此,很方便地减轻了程序员的工作。我们再来看看用SN8c是如何定义这些相同的常量的:如:#definedoor_service_c80//80ms去门抖动#definetOint_c 224 〃t0中断时间#definesegment_c 3 〃;最多3段烹调上面定义的3个量是与前面汇编当中定义的3个常量是完全相同的。在进行编译预处理时,这些量就会被数值代替。还有一个特殊的地方,那就是一些数值列表,在汇编当中,查表项都是放在Code当中的,作为Code来处理,其实这些值也是常量,只不过他们的处理不同于一般常量面已,并且它们共用一个入口。下面是ー个汇编的表:disp_automenu: ;显示菜单用第二数字表格dwOOOOhTOC\o"1-5"\h\zdw Oaelfh ;A-1dw Oae2fh ;dw Oae3fh ;dw 0ae4fh ;dw Oae5fh ;dw 0ae6fh ;dw 0ae7fh ;我们可以看到,汇编的表是用DW关键字定义ー个word,它是存放在.code段里面的,通过表头地址来得到每ー个相对应的值。那么在SN8c里面又如何来处理这些表呢?在讲到表的处理之前,必须先提ー提变量定义关健字的问题。SN8c定义ー个变量时,可以指明它所放置的地方(RAM或ROM),分别用关键字_RAM和一ROM来指定存放的地点,如:Unsignedint_RAMramVenable; 〃将变量存放在RAM中_RAMunsignedintramVeriable2;Unsignedint_ROMromVeriable; 〃将变量存放在ROM中_ROMunsignedintromVeriable2;我们可以知道,当ー个量放到了ROM当中就没法改变它的值了,其实就是我们说的常量。在C当中,可以通过ー个头名称来访问的变量类型比较多,其中数组是比较方便的ー•种,我们可以通过定义ー个数组来存储这些表的数值,然后通过对数组的访问来査询对应的值。如:unsignedlong_ROMdisp_automenu[]= 〃显示菜单用第二数字表格{0x0000,OxOae1f,0x0ae2f,0x0ae3f,0x0ae4f,0x0ae5f,0x0ae6f,0x0ae7f);这是ー个与上面的汇编表完全相同的表,我们将它存放在_ROM中,通过调用数组来查表,这在后面将详细介绍。而在程序中还有另外一类的量是会在程序中不断被改变的,比如程序中的计数器,状态寄存器等等都会随着程序的运行而改变。我们将这ー类量称之为变量。我们先来看看汇编的定义变量的方法:.DATAorgOhtempids1temp2ds1temp3ds1temp4ds1led_dpds1stepds1 ;当前状态上面的代码定义了tempi、temp2、temp3、temp4、led_dp、step6个变量,它们分别占用一•个Byte的RAM空间,那么程序当中就可以通过变量名对该变量的空间进行读写。当然在汇编中你也可以用一个变量名来访问两个或多个RAM空间,这类似于查表的操作,其定义如下:Job_modePowermodeJob_modePowermodeds2ds4对上面的job_mode变量可以通过job_mode和job_mode+l来读写定义的两个存储单元,以此类推power_mode或其他多个RAM空间定义的变量可以通过相同的方法来定义。可见,SN8AsM定义变量的方法主要是通过DS关键字来申请需要的变量空间,空间一旦被申请,就在整个程序流程里面被占用,也就是说定义ー个变量就少一个空间,这对于RAM本身就很少的单片机而言,不能不说是ー种浪费。那么SN8C又是如何来定义变量的呢?要说到C的变量,就不能不提ー提变量的有效作用域。我们知道,汇编当中定义的变量都是在整个程序中有效的,在程序中任何地方都可以改变变量的值,这样程序员就会经常遇到这样的情况:在调试程序时发现变量的值有误,却无法判断变量到底在什么地方被错误赋值或被赋予了错误的值,从而不得不在整个程序中到处设置断点,运行多次オ找出问题的所在。这就是汇编当中变量定义造成不方便的地方!而C的变量的定义区分了作用域,分为全局变量和局部变量,而区分定义这两种变量的方法很简单,只是在不同的位置定义就行了,这与标准C所规定的方法是ー样的,后面再详细讨论。下面是SN8C定义的几个变量:unsignedinttempi;unsignedinttemp2;unsignedinttemp3;unsignedinttemp4;unsignedintled_dp;unsignedintstep; 〃当前状态unsignedlongjob_mode;unsignedlongpower_model;floatpowerValue;inttempl_l; 〃有符号的longtemp2_2;从上面的定义可以看到C的变量不仅区分了作用域,还有不同长度的变量类型,这样就方便了程序员的使用。与面向数学运算的计算机相比,单片机的编程对变量类型或数据类型的选择更具有关键性意义。SN8系列单片机是8-bit处理器的单片机,只有Byte型的数据是处理器直接支持的。对于C这样的高级语言,不管使用何种数据类型,虽然某一行程序从字面上看,其操作十分简单,然而,实际上系统的C编译器需要用ー系列机器指令对其复杂的变量类型、数据类型的进行处理。相同的一行语句,变量选择的类型不同,处理时就会产生很大的差别,产生的代码更是差别很多。特别是当使用浮点变量时,将明显地赠加运算时间和程序的长度。当程序必须保证运算精度时,C编译器将调用相应的子程序库,把它们加到程序中。然而许多不熟练的程序员,在编写C程序时往往会使用大量的、不必要的变量类型。这就导致C编译器相应地增加所调用的库函数以处理大量增加的变量类型,并最终导致程序变得过于庞大,运行速度减慢,甚至因此会在Link时,出现因程序过大而装不进ROM的情况。所以必须特别慎重的选择变量和数据类型。而对于有符号与无符号的变量类型。在编写程序时,如果使用signed和unsigned两种数据类型,那么就得使用两种格式类型的库函数。这将使得占用的存储空间成倍增长。因此在编程时,如果只强调程序的运算速度而又不进行负数运算时,最好采用无符号(unsigned)格式。Note:1、选择数据类型的时候,在能够顺利完成功能的情况下,请尽量选择占空间少的数据类型,这样不管是在RAM空间使用上还是在产生代码效率上都是有益的!2、能使用无符号数的都使用无符号数,以免处理出错,因为芯片内部是以无符号数处理的。3、切记!C语言对大小写敏感,在开始定义变量的时候就要注意要有相应的规范可循,用驼峰式是ー个好的选择,这对于习惯于汇编的程序员来说,可能不大习惯!当然,在标准C编程当中,经常会有为书写方便,对数据类型进行缩写定义,这在SN8c也是允许的。如:#defineucharunsignedchar#defineuintunsignedint这样,在以后的编程中,就可以用uchar代替unsignedchar,用uint代替unsignedint来定义变量。数据的存储类型与存储结构在前面分析查表类型数据定义的时候已经提到了数据在单片机里的存储,会分为ROM和RAM两部分,我们分别称之为程序存储器(ROM)和数据存储器(RAM)。它们在我们编写汇编的时候,会分别用关键字.code和,data来预先声明。通常SN8系列单片机不用扩展存储器,它提供种类丰富的机型,你完全可以根据你的需求来选择合适的机型。因此,数据只存储在片内,寻址也不存在片内片外的区分。SN8P单片机的数据存储器(RAM)的结构如下:BankO0000H128Byte用户存储区通用存储区007FH0080H 80H〜FFH为系统寄存系统寄存器区器区域OOFFH 1Bankl00100通用存储区更多的用户存储区OlFFhL__ 1图3・2、RAM区结构SN8P系列单片机的通用RAM区的大小依不同的芯片而不同,但都是按BANK来划分,每ー个BANK内的地址为00H〜FFH。而所有芯片的Bank。的80H〜FFH的空间都是留给系统专用的系统寄存器区。其实我们上面定义的变量都是放在RAM区的数据结构,在定义ー个变量时,我们前面已经提到可以用—RAM和一ROM关键字来指定存储的地方,而变量定义一般都放在RAM当中,因而—RAM关键字是缺省项。看下面的例子:unsignedinttempi;unsignedint_RAMtemp2;—RAMunsignedinttemp3;unsignedlongjob_mode;unsignedlong_RAMjob_mode2;_RAMunsignedlongjob_mode3;floatpowerValue;float_RAMpowerValue;—RAMfloatpowerValue;inttempl_l; //W符号的int_RAMtemp1-2;_RAMtemp1-3;longtemp2_2;其实上面的定义都是相同的效果,就是定义变量,并将其放置在数据存储区(RAM)内。也就是说,我们在定义一个变量,按我们C常用的方法定义就自动地放到RAM里了,缺省项给我们带来了很大的方便。上面已经说明了,在RAM存储区内,80H〜FFH是系统寄存器区。那么我们又是如何来定义系统寄存器区呢?我们先来看・看系统寄存器都有哪些内容:0123456769ABCDEF
8LHRZYXPFLAGRBANK-9AMPM---A------P4CON-BDAMADMADBADR---PEDGEC-P2M-P4MP5M--INTRQINTENOSCMWDTRTCORPCLPCHDPO-P2-P4P5-TOM-TCOMTCOCTC1MTC1CTC1RSTKPEPOUR-P2UR-P4URP5UR@YZ--FSTK7LSTK7HSTK6LSTK6HSTK5LSTK5HSTK4LSTK4HSTK3LSTK3HSTK2LSTK2HSTK1LSTK1HSTKOLSTKOH表3-2、SN8P系列单片机系统寄存器列表根据不同的芯片的资源,寄存器的内容会随之改变,但是他们定义的区域都不变。这些寄存器都分别对应了芯片内部的资源,SN8c针对这部分的系统资源,对这些寄存器进行了定义,其定义的形式如下:#defineL(*((._RAMunsignedint*)Ox8O))#defineH(*((._RAMunsignedint*)Ox81))#defineR(*((-_RAMunsignedint*)Ox82))#defineZ(*((._RAMunsignedint*)Ox83))#defineY(*((._RAMunsignedint*)0x84))#defineX(*((一_RAMunsignedint*)Ox85))#definePFLAG(*((_RAMunsignedint*)Ox86))这些定义都被包含在相应芯片的头文档里(.h),因此并不需要用户自己去定义寄存器的相应名称,只需要在程序的开头包含相应的头文档就行了。如下所示:#include<sn8p2708a.h>Note:这些系统寄存器都是以大写字母进行定义的,在编写程序时要注意这ー点〇四、程序流程控制程序流程控制是程序的精华所在,正确的程序流程才能实现正确的程序功能。安排精巧的程序流程控制オ可能使程序具有高效率。程序流程控制不管是在C还是在汇编中都是程序设计者最值得思考的地方。4.1顺序结构顺序结构是程序最基本的流程,其语句顺序执行,PC指针依次下移,是CPU内部处理指令的初始方法。这也是编程思维的最初级方式,也是程序的最基本方式和流程。顺序结构流程:图4-1顺序结构流程其实,其描述的是ー个向量方式发展的问题,是所有问题发展和描述的基本方法,具有明确的方向性和时间性,如图中的A操作的发展方向只有一个,即接下来就是B操作,中间既没有反复也没有曲折变化,B一定发生在A的后面。我们平时可以看到的C语言的实现:(微波炉开机初始化)key_bibi_f=1; 〃上电响一声menu_disp_h=OxfOU; 〃上电显示0:00menu_disp_l=0;disp5=OxffU;上述简短的几条语句分别完成一定目的的初始化,虽然在这里没有强制要求什么样的顺序,但是执行过程中是严格按语句的先后来开关目标的。SN8ASM的实现:bObsetkey_bibi_f ;上电响一声
movmova,#OfOhmenu_disp_h,amovmova,#OOh ;上电显示0:00menu_disp_l,amovmova,#llllllllbdisp5,a这是一段功能完全与上面这段C相同的汇编实现,我们可以看见在顺序结构的汇编语句当中没有任何的跳转和判断,都是按原功能的顺序来描述。从对比当中我们完全可以找到它们的逻辑上的ーー对应关系。4.2选择结构事实上,完全顺序结构的流程的程序很少,因为很多事情并不是一帆风顺,顺流而下。很多情况下,事件的发生都需要具备一定的条件,只有一定条件下オ有可能实现。这就得使用选择结构来描述。选择结构流程图:简单选择结构只判断ー个条件的真假来决定下面的需要执行的操作,这是ー个最简单的判断。C语言实现:(BCD码调整)if(result_buf>0x0a)result_buf=input+6;elseresult_buf=input;我们看见在c语言当中,我们用判断语句来实现这样ー个判断逻辑,不管是什么样的条件,我们只计算其真假,做出判断。If……else……是一个非常好的判断组合,很容易用它来完成下列我们用汇编来实现的功能。SN8汇编实现:cmprsa,#OahnopbObtsOfcjmp$+3bOmova,yretbOmova,yadda,#6h ;调整后的数放在a事实上,在汇编程序中,我们会遇到几种情况的条件选择:ー是当条件是ー个位变量时,我们可以直接用bObts指令来进行判断。ー是当条件是是否满足ー个预定的值,我们可以用CMPRS指令来进行判断,当然也可以将其转化为标志位来判断。也可以用减法的方法将其转化成为标志位进行判断,就看需要来安排了更深入。OOOOOA、串行多分支结构流程:事实上大多数时候,单个的条件无法分析判断复杂的问题,有时候ー个结果的出现需要很多个条件同时成立,而每ー个条件成立,又都有不同的结果产生,这样就会有一系列的有层次的判断!图4-3串行多分支结构流程图C用if,elseif嵌套来实现:if(key_bibi_f)buzzejtime=d_buzzer_time1; 〃响声长短,200msbuzzer_not=d_buzzer_not3; 〃要响几次,#1)elseif(end_bibi_f))elseif(end_bibi_f)//buzzer10buzzer_time=d_buzzer_lime2; 〃响声长短,500msbuzzer_not=d_buzzer_notl; 〃要响几次,#10elseif(segment_bibi_f)//buzzer20elseif(segment_bibi_f)//buzzer20buzzer_time=d_buzzer_time2; 〃响声长短,500msbuzzer_not=d_buzzer_notl; 〃要响几次,#4;按键响声请求标志;烹调结束响声请求标志;段切换响声请求标志;按键响声请求标志;烹调结束响声请求标志;段切换响声请求标志按键响声:200ms”次结束响声:500ms/5次段切换响声:500ms/2次问:SN8AsM怎么实现?SN8ASM的实现:BuzzerOO:bObts1key_bibi_fjmpbuzzer10mova,@buzzer_timelmovbuzzer_time,amova,@buzzer_not3movbuzzer_not,ajmpbuzzer40buzzer10:bObts1end_bibi_fjmpbuzzer20mova,@buzzer_time2movbuzzer_time,amova,@buzzer_notlmovbuzzer_not,ajmpbuzzer40buzzer20:bObts1segment_bibi_fjmpbuzzer30mova,@buzzer_time2movbuzzer_time,amova,@buzzer_not2movbuzzer_not,ajmpbuzzer40B、并行多分支结构流程:;200ms;响声长短;#1;要响几次;500ms;响声长短;#10;要取反的次数;500ms;响声长短;#4;要取反的次数图4-4并行多分支结构流程C可以用switch...Case来实现:switch(step){case0: 〃侍机状态下第一次按时钟键ks81();break;caseONE_PRESS_CLOCK_KEY_C: 〃按第二次设置时钟的分钟位ks82();break;caseTWO_PRESS_CLOCK_KEY_C: 〃按第三次时钟设置完毕ks83();break;caseBESPOKE」NG_C: 〃设置好预约后再按下时钟键可以查询预约的时间ks84();break;caseSELECT_TIME_C: 〃按时钟键之前已经设置过烹调程序表示想预约,置预约标志bespoke_f=lif(job_model==DEFROST_MODE_C)break;elseks85();break;caseSTART_ING_C: 〃在开始过程中按时钟键可以查询当前正在运行的时间if(job_model=DEFROST_MODE_C)ks86();break;)汇编的实现如下:mova,stepbObtslfz ;=0侍机状态按时钟键设置小时jmpks82cmprsa,ONE_PRESS_CLOCK_KEY_Cjmpks83cmprsa,TWO_PRESS_CLOCK_KEY_Cjmpks84 ;完成时钟设置cmprsa,BESPOKE」NG_C ;预约状态下查询时间jmpks85cmprsa,SELECT_TIME_C ;选择过功能后再按时钟键表示要预约jmpks86key81:jmpkey89key82:jmp key89key83:jmp key89key84:jmp key89key85:jmp key89key86:jmpkey89key89:ret4343循环结构While型循环的C语言实现:tempbufWhile型循环的C语言实现:tempbuf=0;while(tempbuf==l5){++tempbuf;}//delayWhile型循环的汇编实现:ClryLoop:BOmova,yCmprsa,#15Jmp$+2Jmploop90deemsyjmploop;delayloop90:RET图4-6Do...while循环;Selectbank0图4-6Do...while循环;Selectbank0;Set@YZaddressfrom7fh;Clear@YZcontent;z=z-1,skipnextifz=0;Clearaddress0x00Do...while循环的C实现:unsignedint*pyz=(unsignedint*)0x7f;do{*pyz=0x00;-i;}while(i);Do...while循环的汇编实现:ClrRAM:clrYbOmovZ,#0x7fClrRAMlO:clr@YZdeemsZjmpClrRAMlOclr@YZmova,#00H五、函数函数在C当中占有相当重要的地位,它是组成程序的元素。函数的定义、函数的调用构成C程序的主要结构。而以单片机为对象的编程又与通用C的编程略有不同,主要表现在全局变量与函数参数的选择上以及参数类型的选择上。函数定义函数是完成一定的功能的模块,函数的出现使代码可以得以复用,可以大大减少产生的代码量,这是使用函数最为充分的理由。但是,从另一方面来看,函数基本都涉及参数的传递,若是处理不好就很容易产生参数传递的冗余代码,还有就是调用函数就一定涉及到入栈和出栈的处理,这些都占用CPU的资源,影响程序的实时性,若函数调用过于频繁,势必影响程序的执行效率。所以函数定义之前要考虑函数的划分,函数功能既不能覆盖过大,也不能覆盖过小,使函数的划分太细。SN8C函数声明方式:返回值类型函数名(形参1数据类型,形参2数据类型,……);函数定义方式:返回值类型函数名(参数列表)(函数体;无返回值,无参数函数定义:Void函数名(void){}Note:对于无返回值的函数,都要声明是void,以免系统为其预留空间!在C当中,函数都应该先声明,后调用,标准C的写法一般是先在程序的开头声明要用到的函数,然后在程序中编写完整的函数,我们先来看看C的函数。如:unsignedintbcd(unsignedint);clock_min=bcd(clock_min);unsignedintbcd(unsignedintinput){unsignedintresult_buf;result_buf=input&OxOf;i
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 煤炭制品企业市场竞争力提升策略与考核试卷
- 游乐设施施工法律法规知识考核试卷
- 电气静电及雷电安全防护技术考核试卷
- 稀土金属冶炼工艺考核试卷
- 玻璃防眩光涂层开发考核试卷
- 矿山电气系统设计与优化考核试卷
- 畜牧机械质量管理与可靠性考核试卷
- 海底古海洋学研究中心考核试卷
- 海上旅游目的地营销策略考核试卷
- 辽宁省葫芦岛市高中名校2024-2025学年高三第10次统练数学试题含解析
- 白细胞疾病及其检验(血液学检验课件)
- 案例3 哪吒-全球首个“海空一体”跨域航行器平台
- T-CTSS 3-2024 茶艺职业技能竞赛技术规程
- 车队运营中的司机管理策略研究
- 新生儿脐部出血的护理
- 实验室的智能化设计与建设
- 《中国海洋大学》课件
- 排污许可管理培训课件
- 《盐津铺子公司盈利能力探析实例报告(10000字论文)》
- 2025年中考语文课内名著阅读专题复习:第10部 《水浒传》课件
- 案例:中建八局绿色施工示范工程绿色施工(76P)
评论
0/150
提交评论