初学者之软件使用教程-avruserguide_第1页
初学者之软件使用教程-avruserguide_第2页
初学者之软件使用教程-avruserguide_第3页
初学者之软件使用教程-avruserguide_第4页
初学者之软件使用教程-avruserguide_第5页
已阅读5页,还剩28页未读 继续免费阅读

下载本文档

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

文档简介

第十章 C编译器的使 CExpanded3rdPartyIAREmbeddedWorkbench–CandEC++–IARvisualSTATEcase–IARMakeAppapplicationbuilderImageCraftICCAVR/ICCtiny–Supportsdeviceswithout–Embeddedapplication–Global–ISP-supportGNU –Free–Nosupport,butGNUusersactiveonmailing–Gettingstartedappnote–NextgenerationAVRStudiowillsupportGNUoutputformatCodeVision –IncludesadvancedCC本公司的ImageCraft的ICCAVR编译器理由:免费提供30天的使用版,可生成烧录文件,而IAR免费提供的使用版,只生成调试文件,不能生成烧录文件;功能适合,使用方便,技术支援好;不断增加新功能模块,如单总线,SPI,UART,IIC总线,LCD,LED(本公司也在帮助改进,增加新功能)等;价格适合,原售199,双龙公司成为ICCAVR中国独家,另售价为1380元人民币,与ICE-200实时仿真器(2200元)捆邦销售,仅售3200元/套ImageCraftICCAVR方法一a 双击光盘中文 SETUP.EXE的图d按照屏幕提示 方法二a 开始菜单中选择运行项目 运行框中填 确 键开始安注意1 按上述方法进行安装后得到的是一个只可以使用30天的未版对正式版 ICCAVR缩 30天的用户可以这样将正式版中附带的一张名称 UnlockDisk的软 你机器的软盘驱动器 IDE的Help菜单中寻找标题为ImportingaLicensefromaFloppyDisk的一项 ICCAVR自动进行 当完成后会提示你文件已从软盘移走当你确定并再次重新启动ICCAVR后会发现已经完成30天的用户可这样 对这类用户在程序启动时已不能进入IDE环境而是出现一个提示你的框你应该选择YES按钮 框上有一个标题为ImportingaLicensefromaFloppyDisk的按钮 将正式版中附带的一张名称为UnlockDisk的软盘你机器的软盘驱动器中单 ICCAVR自动进行当完成后会提示你文件已从软盘移走当你确定并再次重新启动ICCAVR后会发现已经完成aUnlockDisk软盘在时应打开写保护否则无法完完成后UnlockDisk软盘成为一张空盘不可以在另一台机器上进行安装当你需要在不同的电脑中使用ICCAVR或在同一台电脑中将ICCAVR重新安装在与原来不同的位置时应该首先在HelpTransferringYourLicensetoaFloppyDisk一项将你的文件传送到一张软盘上然后再按上述方法进行安装 ImageCraft的ICCAVR是一种使用符合ANSI标准的C语言来开发微控制器MCU程序的一个工具它有以下几个主要特点源文件全部被组织到工程之中文件的编辑和工程的构筑也在这个环境中完成编译错误显示在状态窗口中并且当你用鼠标单击编译错误时光标会自动跳转到编辑窗口中引起错误的那一行这个工程管理器还能直接产生您希望得到的可以直接使用的INHEX格式文件INHEX格式文件可被大多数的编程器所支持用于程序到中去位的程序支CICCAVR所必须具备的知识因此要求读者在阅读本书内容之前应对C语言有了一定程度的理解ICCAVR文件类型是由它们的扩展名决定的IDE和编译器可以使用以下几种类型的文件 C C.prj扩展名----IDE.a扩展名----库文件它可以由几个库封装在一起libcavr.aCAVR特殊程序调用的基本库如果库被器会将其到您的模块或文件中您也可以创建或修 C语言源文件.o由汇编文件汇编产生的目标文件多个目标文件可以成一个可执行文 HEX格式文件其中包含了程序的机器代码.eep HEX格式文件包含了EEPROM COFF格式输出文件用于ATMELAvrStudio环境下进行程序调 列表文 在这个文件中列举出了目标代码对应的最终地 内存映象文 它包含了您程序中有关符号及其所占内存大小的信.cmdNoICE2.xx NoICE3.xx ImageCraft #pragmainterrupt_handler<func1>:<vectornumber><func2>:<vector>这个附注必须在函数之前定义它说明函数func1func2是中断操作函数所以编译器在中断retiret并且保存和恢复函数所使用的全部寄存器同样编译器根据中断向量号vectornumber生成中断向量地址 k<func1>RTOS实时操作系统中让RTOS核直接管理寄存器#pragma改变代码段名称#pragmaEEPROM中时必须被使用读者可参考EEPROM的例子#pragma函数与全局数据不使用浮动定位重定位而是从<address>开始分配绝对地址这在中断#pragma结束绝对定位C++如果你选择了编译扩充(Project->Options- 你可以在你的源代码中使用C++的如果你选择了编译扩充(Project->Options->Compiler)你可以使0b<1|0>*来指定二进制常数例如0b10101等于十进制数21你可以使用asm("string")函数来指定汇编代码读者可参考汇IARCAVRCIAR编译ImageCraftANSIC标准的程序代码不需要转换IARC中IO寄存器的定义与ICCAVR也是相同的中断操作描述ICCAVR使用pragma附注描述中断操作函数而IAR引入了语法扩充关键字下面是一个对照在ICCAVR中#pragmainterrupt_handlerfunc:4//4是这个中断的向量号func为中断处理函数名称在IAR中interrupt[vector_name]func()//vector_name是某一个中断向量的名称IARC的中断向量地址使用中断名称来代替以增加程序的可读性IAR引入flash关键字将项目分配进入程序存贮空间FLASH存贮器ICCAVR使用const关在两个编译系统之间函数参数传递使用的寄存器是不同的这仅影响手工写的汇编函数汇编宏等IAR不支持汇编符号而ICCAVR支持汇编 IDE后首先从ProjectOpen命令进入\icc\examples.avr并且选择并打开led工程工程管理器显示在这个工程中只有一个文件led.cProject菜单中选择Options命令打开工程编译选项在""标号下选择目标处理器然后从Project菜单中MakeProject命令IDE将调用编译器编译这个工程文件并且在状态窗口中显示所有的信如果没有错误在与源文件同一个在这个例子中是\icc\examples.avr中输出一个文件led.hex这个文件是INHEX格式大多数能支持AVRMCU的编程器和模拟器都支持这种格式并且能这个程序进入你的目标系统这样就完成了一个程序的构筑COFFAVRStudio那么你需要从Project菜单中选择Options命令在编译下选择COFF输出文件格式对一些常用的功能你也可使用工具条或鼠标右键弹出菜单例如你可以在工程窗口单击鼠标右键选择编译选项在工程窗口中双击文件名IDEled.c作为试验可设置一些错误例如从一行中删除分号现在从Project菜单中选择MakeProject命令IDE首先自动保存已经改变的文件并且开始编译这个文件这时在状态窗口中会显示错误信息单击状态窗口中错误信息行或单击其左边的错误符号光标将移到编辑器中错误行的下面一行上基本上所有C编译器都是这样ProjectNew命令并且浏览至你希望输出工程文件的输出文件的名称取决于你的工程文件名称例如如果你创建一个名称为foo.prj的工程那么输出文件名称为foo.hex或foo.cof等自从创建你自己的工程后你可以开始写你的源代码(C或汇编格式)并且将这个文件加入到工程文件排列中单击中Build图标可以很容易地构筑这个工程IDE输出与ATMELAVRStudio完全兼容的COFF文件你可以使用ATMELAVRStudio来调试你的代码C一个C程序必须定义一个main调用函数编译器会将你的程序与启动代码和库函数一个可执行文件因此你也可以在你的目标系统中执行它启动代码的用途在启动文件中很详细地被描述了一个C程序需要设定目标环境启动代码初始化这个目标使其满足所有的要求通常你的main例程完成一些初始化后然后是无限循环地运行作为例子让 #include<io8515.h>/*LED的变化图案延时程序需要有足够的延时时间*/voidDelay(){unsignedchara,for(a=1;a;a++)for(b=1;b;;}voidLED_On(int{PORTB~BIT(i);/*LED*/}void{DDRB /*定义B口输出PORTB=0xFF; /*B口全部为高电平对应LED熄灭*/while(1){/*LED*/for(i0;i8;i++)/*LED*/for(i=8;i>0;i--)/*LED跳跃for(i=0;i<8;i+=2)for(i=7;i>0;i-=2)}}mainIOLEDLEDLED_OnLED_On例程中直IOCPULED_On例程调用了延时例程因为延时的实际延时值不能被确定这一对嵌套循环只能给出延时的近似延时时间如果这个实际定时时间是重要的那么这个例程应该使用硬件定时器来完成延时8515intr.cC写一个中断处理过程这四ICCAVRIDE有文件然而有时也需要将一个文件单独地编译为目标文件或最终的输出文件这时可以这样操作从IDE菜单File中选择CompileFile...命令来执行toObject和toOutput中的任意一个当你调用这个命令时文件应该是打开的并且在编辑窗口中可以编辑的编译一个文件为目标文件toObject对检查语法错误和编译一个新的启动文件是很有用的编译一个文件为输出文件toOutput对较小的并且是一个文件的程序较为有用注意这里使为创建一个新的工程从菜单Project中选择New命令IDE会弹出一个框在框中你可以指定工程的名称这也是你的输出文件的名称如果你使用一些已经建立的源文件你可在菜单Project中选择AddFile(s)命令另外你可以在菜单File 中选择New 命令来建立一个新的源文件来输入你的代码你可以在菜单File 中选择Save 或SaveAs 命令来保存文件然后你可以象上面所述调用 命令将文件加入到工程中也可在当前编辑窗口中单击鼠标右键选择Addto 将文件加入已打开的工程列表中通常你输出源文件在工程同一 中但也可不工程的编译选项使用菜单中 中的 命工程管理允许你将多个文件组织进同一个工程而且定义它们的编译选项这个特性允许你将工程分解成许多小的模块当你处理工程构筑时只有一个文件被修改和重新编译如果一个头文件作了修改当你编译包含这个头文件的源文件时IDE会自动重新编译已经改变的头文件一个源文件可以写成C或汇编格式的任意一种C文件必须使用.c扩展名汇编文件必须使用.s扩展名你可以将任意文件放在工程列表中例如你可以将一个工程文档文件放在工程管理窗口中工程管理器在构筑工程时对源文件以外的文件不予理睬对目标器件不同的工程可以在编译选项中设置有关参数当你新建一个工程时使用默认的编译选项你可以将现有编译选项设置成默认选项也可将默认编译选项装入现有工程中默认编译选项保存在default.prj文件中为避免你的工程你可以指定输出文件和中间文件到一个指定的通常这个目录是你的工程的一个子IDE当编译存在错误时用鼠标单击有关错误信息时编辑器会自动将光标定位在错误行的位置注意对C源文件中缺少分号的错误编辑器定位于其下面一行应用构筑向导是用于创 设备初始化代码的一个图形界面你可以单击工具条中Wizard按钮或菜单Tools中的ApplicationBuilder应用构筑向导使用编译选项中指定的目标MCU来产生相应的选项和代应用构筑向导显示目标MCU的每一个 设备子系统它的使用是很显而易见的在这里你可以设置MCU的所具有的中断内存定时器IO端口UART SPI和模拟量比较器等main()IDE有一个内置的终端仿真器注意它不包含任意一个ISP在系统编程功能但它可以作为一个简单的终端或以显示你的目标装置的调试信息也可一个ASC码文件6.20IDEISP 菜单解File文件菜 新建一个文 你可在编辑窗口是输入文字或代 重新打开历史文件有关历史文件显示的右边的子菜单中 打开一个已以经存在的文件用于编辑文件用浏览窗口选择Reload…formDisk放弃全部的修改从磁盘中重新装载当前文件Reload….fromBackUP从最后一次的备份文件中装载当前文件 保存当前文件如果环境设置中设置了保存备份文件则将原文件以<file>.~<ext>Save Close关闭当前文件如果文件有过修 系统会进行提CompileFile…toObject 编译当前文件成目标文件注意目标文件不可以直接用于对芯片编程或用于调试其主要用于语法检查为创建新的启动文件或库产生目标文件CompileFiletoOutputSave 保存所有打开的文ClossAll 打印当前文 撤消最后一次的修改 撤消最后一次的Undo Delete删除选择的Select 选择全部内Block Block MatchCase–区分大小写WholeWord–全字匹配Up/Down–往上或往下FindinFiles...–在当前打开的文件中或在当前工程的所有文件中或当前 中寻找一段文本它有以下选项CaseSensitive大小写敏感WholeWord-全字匹配RegularExpression–Replace...–FindAgain–GotoLineNumber转到指定行AddBookmark–DeleteBookmark删除NextBookmark跳转到下一个GotoBookmark跳转到指定的书StatusWindow–如果选中显示状态窗口ProjectMakefile–makefile文件OutputListingFile–以只读方式打开列表文件Project工程菜New...–Open打开一个已经存在的工程文件OpenAllFiles...打开工程的全部源文件CloseAllFiles–关闭全部打开的文件Reopen...–MakeProject–RebuildAll–AddFile(s)–RemoveSelectedFiles–Option...–打开工程编译选项Close–SaveAs...–将工程换一个名称存 EnvironmentOptions–打开环境和终端仿真器选项EditorandPrintOptions–打开编辑和打印选项AVRCalc–AVR计算器可以计算UART的波特率定时器的定时常数ApplicationBuilder–ConfigureTools–RunCompilerOptions编译选项总共有三个页面 和PathsIncludePath(s)–你可以指定包含文件的路径AssemblerIncludePath(s)–指定汇编包含文件的路径LibraryPath–器所使用的库文件的路径OutputDirectory–输出文件的CompilerStrictANSICChecking–ANSICAcceptExtensions–C++MacroDefine(s)–定义宏宏之间用空格或分号分 宏定义形式如name[:value]#defineDEBUG1#definePRINTprintfMacroUndefine(s)–同上但意义相反OutputFileFormat–输出文件格式COFF/HEXInHEX或COFFOptimizations–代码优化Default–基本优化象寄存器分 共用相同的子例程izeCodeSizeReduction–只有专业版才可使用它调用了代码压缩优化去除了无DeviceConfiguration选择目标MemorySizes–要选择"Custom"时指定内存大小包括ROMSRAMEEPROMTextAddress–通常代码地址开始于中断向量区域后面DataAddress–指定数据起始地址通常–EnhancedCore–IORegistersOffsetInternalSRAM–指定SRAM的偏移量例如,8515的SRAM起始于0x60,IO512Mega603,IOSRAM空间中因此SRAM也是从0开始的InternalExternalSRAM–SRAM类型PRINTFVersion–PRINTF的版本Small或Basic:只有%c,%d,%x,%X,%u,and%sLong:%ld,%lu,%lx,Floatingpoint:%f支持注意这个选项需要很大的AVRStudioSimulatorIO–如果选 AdditionalLibraries–使用标准库以外的附加–ReturnStackSize–指定编译器使用的硬件堆栈的大小编译器使用的堆栈的大小不需NonDefaultStartup–PathsIDEUnusedROMFillPatternROM C库函数与启动文这个器会自动将启动文件连接到您的程序之前并将标准库libcavr.a与你的程序相连接启动文件根据目标MCU的不同在crtavr.o和crtatmega.o中间任意选择一个启动文件定义了一个全局符号start 它也是您的程序的起点启动文件的功能有bss定义一个退出点如果你的主函数main一旦退出它将进入这个退出点进行无限循环启动文件也定义了复位向量你不需要修改启动文件来使用别的中断具体可参断操作cd\ ;进入你安装的编译器路<edit ;crtavr.s<opencrtavr.susingtheIDE>;IDEcrtavr.s<Choose"CompileFileTo->Object"> ;选择编译到目标文件创建一个新的crtavr.ocopycrtavr.o..\lib ;拷贝到库MCUMega你应该用"crtatmega"代替"crtavr"Mega的每个中断地址使用两个字word而非Mega每一个中断地址使用一个字word.你也可以有多个启动文件你可以在工程选项框中很方便地直接指定一个启动文件加入您的工程中注意您必须指定启动文件的绝对路径或启动文件必须位于工程选项库路径所指定的目1库源代这个库源代码缺省路径为c:\icc\libsrc.avr\libsrc.zip是一个保护的ZIP压缩文件你可以从互连网上任意一个UNZIP程序进行解压缩当本被开锁后显示在"About"框中例如unzip-s;unzip2AVR特殊函数-----ICCAVR有许多UARTEEPROM和SPI的函数堆栈检查函数对检测堆栈是否溢出很有用另外的互连网上有一个页专门存放用户写的源代码3io*.h(io2313.h,io8515.h,iom603.h,...等这些文件中是从ATMEL公开的定义IO寄存器的源文件经过修改得到的应该用这些文件来代替老的avr.h文件PORTB=1;uc=PORTA;45其它头文下列标准的C头文件是被支持的如果你的程序使用了头文件所列出的函数那么包含头文件是一个好#include预编译指令包含这些包含了这些函数的头文件读者可参考返回非整型值的函数assert.h-assert(),宏ctype.h–字符类型函数float.h–浮点数limits.h–math.h–浮点运算函数stdarg.h–变量参数表.stddef.h–标准定义stdio.h–标准输入输出IO函数stdlib.h–包含内存分配函数的标准库string.h–字符串处理函数ACSII字符集字符分类使用这些函数之前应当用"#includeintisalnum(int如果c是数字或字母返回非零数 否则返回intisalpha(int如果c是字母返回非零数 否则返回 trl(int如果c是控制字符如FF,BELL,LF..等返回非零数值否则返回intisdigit(int如果c是数字返回非零数 否则返回intisgraph(intc是一个可打印字符而非空格返回非零数值intislower(int如果c是小写字母返回非零数 否则返回intisprint(int如果c是一个可打印字符返回非零数 否则返回intispunct(int如果c是一个可打印字符而不是空 数字或字母返回非零数 否则返回intisspace(int如果c是一个空格字符返回非零数 包括空格CR,FF,HT,NL,和 否则返回intisupper(int如果c是大写字母返回非零数 否则返回intisxdigit(int如果c是十六进制数字返回非零数 否则返回inttolower(intcccinttoupper(intc)如果c是小写字母则返回c对应的大写字 其它类型仍然返回下列函数支持浮点数运算使用这些函数之前必须 #include 包floatasin(float os(floatfloatatan(floatfloatatan2(floatx,float返回y/x的反正 其范围在 之floatceil(float返回对应x的一个整型 小数部分四舍五floatcos(floatfloatcosh(floatfloatexp(floatexexfloatexp10(floatx)10为底的幂floatfabs(floatfloatfloor(floatfloatfmod(floatx,floatfloatfrexp(floatx,int把浮点数x分解成数字部分y尾数和以2为底的指数n两个部分即 2ny的范围 y值被函数返回npexpfloatfround(floatfloatldexp(floatx,int返回 2exfloatlog(floatfloatlog10(floatfloatmodf(floatx,floatpint指向的变量小数部分应当大于或等于0而小于1并且作为函数返回值返回floatpow(floatx,floatfloatsqrt(floatfloatsin(floatfloatsinh(floatfloattan(floatfloattanh(float5标准的文件输入输出是不能真正植入微控制器MCU的标准stdio.h的许多内容不可以使用不过有一些IO函数是被支持的同样使用之前应用"#include<stdio.h>"预处理并且需要初始化输出端口最低层的IO程序是单字符的输入(getchar)和输出(putchar)程序如果你针对不同的装置使用的IO函数例如用printf输出LCD你需要全部重新定义最底层的函数ATMELAVRStudioIOIO注意作为缺省单字符输出函数putchar是输出到UART装置没有修改无论如何为使输出能如期望的那样出现在程序终端窗口中'\n'字符必须被为成对的回车和换行CR/LFintintprintf(char*fmt,frm%d--输出有符号十进制整%o--输出无符号八进制%x输出无符号十六进制整%X除了大写字母使用'A'F'外同%u输出无符号十进制整–%c–ASCII字符形式输出只输出一个字%f以小数形式输出浮点%S–FLASHprintf支持三个版本取决于你的特别需要和代码的大小越高的要求代码越大基本形:只有%c,%d,%x,%u,和%s格式说明符是承认的长整形:%ld,%lu,%lx被支持,以适用于精度要求较高的领域浮点形:全部格式包括%f被支持你使用编译选项框来选择版本代码大小的增加是值得关注intputchar(int输出单个字符这个库程序使用了UART以查寻方式输出单个字符注意输出’\n’字符至程序intputs(charintsprintf(char*buf,charfrmprintf("constchar*"支持功能cprintfcsprintf是将FLASHprinfsprinf 标准库头文件<stdlib.h>NULLRAND_MAXsize_t并且描述了下列函数注意在你调用任意内存分配程序比如..callocmalloc和realloc)之前必须调用_NewHeap来初始化堆heapintabs(intintatoi(chars为整型数并返回它字符串s0doubleatof(constchar*s)s为双精度浮点数并返回它字符串slongatol(chars为长整型数并返回它字符串s0void*calloc(size_tnelem,size_tsize)分配"nelem"size0如果分配成功返回分配内存单元的首地址否则返回0void终止程序运行典型的是无限循 voidfree(voidsize0void_NewHeap(void*start,void*end)初始化内存分配程序的 一个典型的调用是将符号_bss_end+1的地址用作"start" _bss_end1的目的是堆栈检查函数使用_bss_end字节存贮为标志字节这个结束值不能被放入堆栈中externchar_NewHeap(&_bss_end+1,&_bss_end+201);//200intvoid*realloc(void*ptr,size_tptrsizesize可比原来大或小返回指向该内存区的地voidsrand(unsignedlongstrtol(char*s,char**endptr,int按照"base."的格式转换"s"中起始字符为长整型数如果"endptr"不为空*endptr将设定"s"中unsignedlongstrtoul(char*s,char**endptr,int除了返回类型为无符号长整型数外其余同用"#include<string.h>"预处理后编译器支持下列函 <string.h>定义了NULL类型void*memchr(void*s,intc,size_t在字符串snc相同的字符如果成功返回匹配字符的地址指针否则返回NULLintmemcmp(void*s1,void*s2,size_t对字符串s1和s2的前n个字符进行比较如果相同则返回0如果s1中字符大于s2中字符则返回1如果s1中字符小于s2中字符 void*memcpy(void*s1,void*s2,size_t拷贝s2中n个字符至 但拷贝区不可以重void*memmove(void*s1,void*s2,size_t拷贝s2中n个字符至 返回 其与memcpy基本相同但拷贝区可以重void*memset(void*s,intc,size_tsncschar*strcat(char*s1,char*s2)s2s1的结尾返回char*strchr(char*s,int在s1中搜索第一个出现的c 包括结束NULL字符如果成功返回指向匹配字符的指针如果没有匹配字符找到返回空指针intstrcmp(char*s1,char比较两个字符串如果相同返回0s1>s21s1<s2则返回-1char*strcpy(char*s1,char*s2)拷贝字符串s2至字符串 返回size_tstrcspn(char*s1,chars1搜索与字符串s2NULLs1中找到的匹size_tstrlen(char返回字符串s的长 不包括结束NULL字char*strncat(char*s1,char*s2,size_t拷贝字符串s2不含结束NULL字符中n个字符到 如果s2长度比n小则只拷贝intstrncmp(char*s1,char*s2,size_tstrcmp函数相同nchar*strncpy(char*s1,char*s2,size_tstrcpy函数相同nchar*strpbrk(char*s1,charstrcspn函数相同但它返回的是在s1匹配字符的地址指针否则返回NULLchar*strrchr(char*s,intsc并返回它的指针否则返回NULL.size_tstrspn(char*s1,char*s2)s1搜索与字符串s2NULLs1char*strstr(char*s1,chars1中找到与s2s1则返回NULL"constchar*"这些函数除了它的操作对象是在FLASH中常数字符串外其余同c中的函size_tcstrlen(constcharchar*cstrcpy(char*dst,constchar*src);intcstrcmp(constchar*s1,char*s2);<stdarg.h>va_listva_start(va_listfoo,<last-初始化变量foova_arg(va_listfoo,<promotedtype>)下一个参数分派指定的类型注意那个类型必须是高级类型如 long或小的整型类型如"char"不能被支例如printf()vfprintf()来实现#include<stdarg.h>intprintf(char*fmt,{va_start(ap,fmt);vfprintf(fmt,ap);}有几个库函数是用于检查堆栈是否溢出内存图如下如果硬件堆栈增长到堆栈中那么软件堆栈的内容将会被改变也就是说局部变量和别的堆栈项目被改变硬件堆栈是用作函数的返回地址如果你的函数调用层次太深偶然会发生这种情况同样地堆栈溢出进数据区域将会改变全局变量或其它静态分配的项目如果你使用动态分配内存还会改变堆项目这种情况在你定义了太多的局部变量或一个局部集合变量太大也会偶AVR硬 的编 AVR的低层硬件行AVR性能除了要最大程序地优化代码外很少使用汇编偶然情况下目标MCU的硬件特点在C语言中不能很好地使用很显然使用汇编和预处理宏能这些特点io*.hio8515.hiom603.h等定义了指定AVRMCUIO寄存器细节这些文件是从ATMEL发布的文件经过修改以匹配这个编译器的语法要求文件macros.h定义了许多有用的宏例如宏UART_TRANSMIT_ON()能使UART开始工作这个编译器的效率很高当由IO寄存器的内存时能产生单周期指令象inoutsbissbi等参考IO寄存器avr.hIObit有一些模糊尽管io*.hbit的位置io*.hIObitmacros.hBIT()宏例#define 0x80//外部RAM...(C程序)MCUCR|=#define ...(你的C程序)#include<macros.h>一个共同的任务是编程微控制器MCU打开或关闭IO寄存器的一些位bit很幸运标准CC结构C定义了一些按位进a|b–按位或这个表达式指示中a被表达式中的b按位进行或运算这惯用于打开某些位尤其常用|=的形式例如PORTA|=0x80;//打开位7(最a&b–按位与1时有用IfPORTA&0x81)0)//检查7注意圆括号需要括在&运算符的周围因为它和==相比运算优先级较低这是C程序中很多a^b–按位异或这个运算对一个位取反有用例如在下面的例子中7PORTA^=0x80;//~a–按位取反.在表达式中这个运算执行一个取反当用按位与运算关闭某些位时与这个运算组合使用尤其有用如这个编译器对这些运算能产生最理想的机器指令例如sbic指令可以用在根据位的状态进行AVRMCU的例如分开的地址空间允许AVR装置比传统结构的存贮器例如Atmega系列允许有超过64K字WORD的程序存贮器和64K字节的数据存贮器将来的MCU装置可能用到的程序存贮器而程序计数器仍保留在16位上不幸的是C不是在这种机器上发明的特别地C指针是任意一个数据指针或函数指针C规则已经指定你不可以假设数据和函数指针能被向前和向后修改同是哈佛结构的AVR要求C解决了这个问题ImageCraftAVR编译器使用"const"限定词表示项目是在程序存贮器中注意对指针描述这个const限定词可以应用于不同的场合不管是限定指针变量自己还是指向项目的指针例如constinttable[]={1,2,3};constchar*ptr1;char*constconstchar*const"table""ptr1"是一个项目在数据存贮器而指向数据的指针在程序存贮器"ptr2"是一个项目在程序存贮器而指向数据的指针在数据存贮器最后"ptr3"是项目在程序"table"和和"ptr1"是很典型的C编译器生成LPM指令来程序存贮器注意C标准不要求"const"数据是放入只读存贮器中而且在传统结构中除了正确就没有要紧的了因而在承认参数的Cconst限定是非传统的无论如何这样做与标准C函例如标准"strcpy"strcpy(char*dst,constchar*src)带有const限定的第二个参数表示函数不能修改参数然而在ICCAVR下const限定词表示第二个参数指向程序存贮器是不合适的因此这些函数定义设有const限制FLASHconstFLASH中而可能导致不明AVR中程序内存和数据内存分开给程序内存和数据内存的说明带来了一定的复杂性这页说明字符串这个编译器将带有const说明的表和项目放入程序存贮器中最的是字符串的分配和处理Cchar指针如果字符串是分配进程序存贮器中那么所有字符串库函数中的任意一个必须被成不同于指针的操作或者字符串也必须被分配在数据存贮器中贮器的为了确保它们的值是正确的在程序启动时字符串是由程序存贮器拷贝进数据存贮器中的因此只有单一的字符串拷贝函数是必须的编译器执行全局变量初始化也是这样处理的如果你希望节省空间你能使用常量字符型数组来将字符串只分配进程序存贮器 例constcharo[]="o在这个例子中o可以在上下文中作为字符串使用但不能用作标准C库中字符串函数Printf已被扩展成带%S格式字符来输出只存贮于FLASH中字符 另 新的字符串函数只分配全部字符串到FLASH存贮器当对应"Project->Options->->StringsInFLASHOnly"检查框被选中时你可以指挥编译器将字符串只放在FLASH中这时称必须很地调用库函数当这个选项是选中的字符串类型"constchar*""constchar*"与字符串有关系外创建了cprintfcsprintf函数承认字符串格式的类型参考标准输入输出函数注意当选项2只分配全部字符串到FLASH存贮器中时应使用cprintf()对constchar*及constcharptr[]类型字符串并且加%S参数当选项1时应使用 对constchar*及constcharptr[]类型字符串并且加%S参生成代码使用两个堆栈一个是用于子程序调用和中断操作的硬件堆栈一个是用于以堆栈结构传递的参数临时变量和局部变量的堆栈 堆栈通常如果你的程%fprintf()等库函数那么默认的16字节应该在大多数的例子中能良好工作在绝大多数程序中除了很繁重的递归调用程序再入式函数最多40个字节堆栈和数据内存的大小是受在编译器选项中的目标装置项设定限制的数据区从0x60开始分配在IO空间后面是正确的允许数据区和堆栈彼此相向生长如果你选择的目标装置带有32K或64K的外部SRAM那么堆栈是放在SRAM的顶部而且向低内存地址方向生长参考程序和数据内存的使用能溢出并且当一个堆栈溢出时会偶然产生坏的事情你可以使用堆栈检查函数检测溢出情况 除了在汇编文件中写汇编函数外汇编允许你写汇编代码进你的C文件中当然在你的工程使用汇编源文件作为一个部件是良好的汇编的语法是多个汇编可以被符号\n分隔成新的一行String可以被用来指定多个 加的ASM为了在汇编中一个C的变量可使用%<变量名>格式如registerunsignedcharuc;asm("mov%uc,R0\n"任意一个C变量都可以被如果你在汇编指令中需使用一个CPU寄存器你必须使用寄存器存贮类register来强制分配一个局部变量到CPU寄存器中通常使用汇编局部寄存器的能力是有限的如果你在函数中描述了太多的寄存器变量就很可能没有寄存器可用在这种情况你将从汇编程序得到一个错误那时也不能控制寄存器变量的分配所以你的汇编指令很可能失败作为例子使用LDI指令需要使用R16~R31中的一个寄存器但这里没有请求使用汇编同样也没有上半部分的整数寄存器汇编可以被用在C函数的或外部编译器将汇编的每行都分解成可读的不AVR汇编器ImageCraft汇编器允许放置在任意地方所以你可以在你的汇编代码中创建当汇编在函数外部时你可能得到一个警告你不要理睬这个警告 IOIOSREG可以被两条路线IO0x000x3f之间可以INOUTIO0x200x5F通数据指令和地址模式两种方法在C中都可使用数据内存地址一个直接地址可以通过加指针类型符号直接例如SREG的数据内存在地址是0x5F:unsignedcharc*(volatileunsignedchar*)0x5F;//*(volatileunsignedchar*)0x5F|0x80;//031CPU寄存器注意不要不注意地改变CPU寄存当在IO寄存器范围中的数据内存时编译器自动生成低级指令象inout sbrc等IO地址你可以使用汇编和预处理宏来IO地registerunsignedcharuc;asm("in%uc,$3F");//SREGasm("out //打开全局中断avr.hIObit有一些模糊尽管io*.hbit的位置io*.hIObitmacros.hBIT()宏例#define 0x80//外部RAMC程序)MCUCR|=SRE;#define ...(C程序#include<macros.h>MCUCR|=8你的程序可能需要使用绝对内存地址例如外部IO设备通常被成特殊的内存这些可能包括LCD界面和双口SRAM通常你可以使用汇编或单独的汇编文件来描述那些定位在特殊内存地址的数据在稍后版本的编译器中已在C语言中提供这些能力在下面有例子中假设有一个两字节的LCD0x1000地址LCD数据寄存器定位在0x1002地址并且有一个100字节的SRAM0x2000的地址使用汇编模式在一个汇编文件中输入以下内容.area _LCD_control_register::.blkw_LCD_data_register::.blkw .blkbexternunsignedintLCD_control_register,LCD_data_register;externchardual_port_SRAM[100];注意界面规定在汇编文件中外部变量名称是带'_'前坠 并且使用两个冒号定义为全局变使用汇汇编遵守同样的汇编语则除了它被附加了一个asm()伪函数在C文件中关于上asm(".areamemory(abs)" C中你仍然要使用"extern"C编译器不会真正知道在asm中的 C任务Tasks作为汇编界面的描述和调用规则编译器通常在生成代码来保存和恢复保护的寄存器在一些情况下这些行为可能是不合适的例如如果你使用RTOS实时操作系统RTOS管理着寄存器的保存和恢复并作为任务切换处理的一部分编译器如果再这些代码就变得多余了为了这种行为你可以使用"#pragma k"例如 kdrive_motoremit_sirenvoiddrive_motor(){...voidemit_siren()这个附注(pragma)必须被用在函数定义之前注意作为默认的情况从不返回的程序"main"是有这个属性的它也没有必要为返回保存和恢复任意一个寄存器 C中断操作中C中可以使用无论函数定义在文件的什么地方你必须用一个附注pragma在函#pragmainterrupt_handler<name>:<vectornumber>"vectornumber"1用RETIRET#pragma void{}rjmp ;对普通 jmp ;对 上述指令定位在0x06字节地址针对普通装置和0x0c字节地址针对Mega装置使用2个字作为中断向 非Mega使用1字作为中断向量 多个用空格分开的名称分别带有多个不同的向量号例如#pragmainterrupt_handlertimer_ovf:7你可以用汇编语言写中断操作如果在你的汇编操作调用C函数无论如何要 程序要保存和恢复挥发寄存器参考汇编界面C函数不做这些工作如果你使用汇编中断操作那么你必须自己定义向量你使用"abs"属性描述绝对区域用 来rjmp或jmp指令的正确地址注意这个 "使用的是字节地;ATMega.area ;中断 rjmp;ATMega.area ; jmp UARTgetcharputcharUART\icc\examples.avr有一个以中断方式工作的IO程序可以代替默认的程序 EEPROMEEPROM在运行时可以使用库函数 EEPROM_READ(intlocation,object)这个宏调用了EEPROMReadBytes函数从EEPROM指定位置数据送给数据对象可以是任意程序变量包括结构和数组intEEPROM_Read(0x1i);//2iEEPROM_WRITE(intlocation,object)是任意程序变量包括结构和数组EEPROM_WRITE(0x1, //写两个字节至这些宏和函数可以用于任意AVR装置对EEPROM单元少于256字节的MCU即使不需要高地址字节它们也是欠佳的因为它仍然是要写的如果它关系重大你可以为EEPROM较少EEPROMC源文件中它作为一个全局变量被分配到特殊调用区域"eeprom."中的这是可以用附注实现的结果是产生扩展名为.eep的输出文件例如#pragmadata:eepromintfoo=chartable[]={0,1,2,3,4,5};#pragmadata:dataintEEPROM_READ((int)&foo, //i等于第二个附注是必须的为返回默认的"data."区域需要重设数据区名注意因为AVR的硬件原因初始化EEPROM数据至0地址是不可以使用注意当使用外部描述比如在另一个文件中的foo你不需要加入这个附 例externintfoo;inti;EEPROM_READ((int)&foo,unsignedcharEEPROMread(intintEEPROMwrite(intlocation,unsignedcharEEPROM0voidEEPROMReadBytes(intlocation,void*ptr,int从EEPROM指定位置处开始"size"个字节至由"ptr."指向的缓冲voidEEPROMWriteBytes(intlocation,void*ptr,int个字节写的内容由 SPI一个以查寻模 SPI的函数是提供 的信息参考 一个带8K程序存贮器的装置全部范围内的跳转可以使用相对转移和调用指令(rjmp和rcall)8K为分界的例如一个较远的跳转跳转到0x2100字节处0x2000为8K实际上会跳转到地址0x100处8KC 类 长度(字节 范unsigned signed -char unsigned (signed) - (signed) - (signed) - +/-1.175e- (*)"char"等同于"unsignedchar"floats和doublesIEEE标准32位格式7位表示指数23位表示尾数1位表示符号unsignedsigned关键字而且将被包含在一个较小的空间中如可定义structunsigneda:1,b:这个结构体的长度只有一个1 位域是从右往左放置2C语言中的名称在汇编文件中是以下划线为前坠 如函数main()在汇编模块中是以_main(的名称的有效长度为32个字符在名称后面加两个冒号如.word1(C文件中externint

可以定义成一个全局变量R16/R17R18/R19传递如果参数是长整型或浮点数则通过R16/R17/R18/R19传递其余参数通过堆栈传递比整型参数小的如char参数扩展成整型int长度传递即使函数原型是可用的如果R16/R17已传递了R18/R19传递而高半部分通过堆栈传递R16/R17R16/R17/R18/R19R28/R29YR10/R11/R12/R13/R14/R15/R20/R21/R22/R23这些寄存器是调用保护寄存器这些d)器别的寄存可以在汇编语言函数中使用而不被保护和恢复这些寄存器是调用挥发寄存器e中断处这不同于普通的函数调用在中断操作中必须保护和恢复它所使用的全部寄存器如果你是C函数来描述中断处理那么编译器有能力自动完成的如果使用汇编写中断处理而它又CC函数调用不保护它们中断处理操作同普通程序操作是异步的中断处理或它的函数调用不能改变任意一个MCU寄3数之前应当用#include语句包含头文件<math.h> 否则在这些程序返回它们的值后你的程序将不工作这和那些返回整型值的函数是有不同之处的(也就是传递结构的地址)和传递任意数据项目的地址是相同的,都是通过一个2字节的指针当一个返回结构的函数被调用时这个调用函数分配一个临时贮藏库而且传递一个隐藏指针给调用函数当这个函数返回时它拷贝返回值进这个临时贮藏库程序和数据区的使用程序存贮器是被用于保存你的程序代码常数表和确定数据的初始值比如字符串全局变量编译器可以生成一个对应程序存贮器映像的输出文件HEX文件这个文件可以被编程器用来对通常编译器不能使用任意64K字节以上的程序存贮器为了64K字节边界以上的存贮器如在Mega103装置中你需要在设定RAMPZ寄存器后直接调用ELPM指令数据存贮器仅指这个数据存贮器是被用于保存变量堆栈结构和动态内存分配的堆通常它们不产生输出文件但在程序运行时使用一个程序使用数据内存如下图堆CPUIO内存图的底部是地址0开始的960x60字节是CPU寄存器和IO寄存器编译器从0x60往上放置全局变量和字符串在变量区域的顶部你可以分配动态内存在高端地址硬件堆栈开始于SRAM的最后位置在它的下面是向下生长的堆栈它要求你作为程序师要确保硬件堆栈不生长进堆栈而堆栈不生长进堆否则将导致意外的结果数据存贮器外部如果你选择带有32K或64K外部SRAM的目标装置那么堆栈是放置在SRAM的顶部并且是朝内存地址向下生长数据内存是开始于硬件堆栈的顶部并且向上生长这样分配的原因是在多数场合SRAM比外部SRAM的速度要快分配堆栈到较快的内存是有很多好5编译器生成代码和数据到不同的区域"areas."区域按照内存地址增高的顺序被编译器使用interrupt func_lit–函数表区这个区的每个字包括了函数的地址为了与代码压缩完全兼容所有间接的函数索引必须通过间接的额外对准如果你在C中使用函数指针调用函数这是自动完成的在汇编中举例如下;_foo.areaPL_foo::.word_foo创建函数.arealdiR30,<PL_fooldiR31,>PL_foorcallxicall你可以间接地在函数表地址送入R30/R31寄存器对后使用库函数xicall调用这个函lit–text–这个区域包括程序代码data这个区域包括全局变量静态变量和字符串全局变量和字符串的初始值是保存在"idata"区域并且是在启动时被拷贝进数据区的bss这个区域包括未初始化的全局变量ANSIC0EEPROM存贮器eeprom-EEPROM数据EEPROM数据是写进扩展名为.eep ICCAVR可以输出COFF格式调试文使用户可ATMELAVRStudio中进行源程序级的AVRStudioIOICCAVR的编译选项中必须将AVRStudioSimulatorIO一项打钩ICCAVR汇编参1名(‘_’|[a-z])[[a-z]|[0-9]|‘_’ ICC中汇编名称必须由下划线或字母开始随后跟字母数字或下划线组成在这个文档中PC地址的标号名称中的任意一个一个名称的长度最多为30个字符长而且区分大小写汇编指令和汇编伪指令除外符号可以只用在程序模块中也可显式地被其它模块使用在以前的例子中符号

温馨提示

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

评论

0/150

提交评论