arm嵌入式软件开发

arm嵌入式软件开发

ID:21733237

大小:827.00 KB

页数:49页

时间:2018-10-20

上传者:U-3743
arm嵌入式软件开发_第1页
arm嵌入式软件开发_第2页
arm嵌入式软件开发_第3页
arm嵌入式软件开发_第4页
arm嵌入式软件开发_第5页
资源描述:

《arm嵌入式软件开发》由会员上传分享,免费在线阅读,更多相关内容在教育资源-天天文库

嵌入式软件开发 嵌入式开发过程“PC软件”独立的嵌入式应用当程序员开始开发一个基于ARM应用的时候,你可以使用ARM的ADS编写类似于“HELLOWORLD”的程序,使用ARMulator或者在评估板上来调试,但当你把他移植到独立的嵌入式应用设备中时,下面这些问题就成为我们首要考虑的:硬件环境中所使用的C库函数目标板上的存储器资源应用程序的初始化 议程PC软件的构造定制标准C库函数到目标板定制IMAGE的存储器映射到目标板复位和初始化深层次的存储器器映象考虑编译和调试IMAGE ADS默认的标准C库ANSICinput/outputerrorhandlingstack&heapsetupotherSemihostingSupport应用程序调用的C库函数eg:fputc()设备驱动层使用semihostingSWI’seg:_sys_write()调试工具环境CLibraryDebugAgentC库函数功能是支持PC软件的,而目标板上的可执行软件则依赖相关的硬件资源;在ARM体系中,我们可以采用semihosting通过相应的驱动来进行调试。 ADS默认的存储器映射在默认的情况下,我们链接、定位、运行在0x8000heap被直接放置在数据区的上面堆栈的基地址是通过调试环境从C库函数的StartupCode里读取出来的。ARMulator=>fromconfigurationfile(peripherals.ami)default=0x08000000Multi-ICE=>fromdebuggerinternalvariable$top_of_memorydefault=0x80000RORWZI0x8000链接时确定由调试环境提供Heap(malloc,alloc)Stack CLibraryUserCode应用程序启动__maincopycodeanddatazerouninitializeddata__rt_entrysetupapplicationstackandheapinitializelibraryfunctionscalltop-levelconstructors(C++)Exitfromapplicationmain()causesthelinkertopullinlibraryinitializationcode程序入口点 Agenda一个PC软件的构造定制标准C库函数到目标板定制IMAGE的存储器映射到目标板复位和初始化深层次的存储器器映象考虑编译和调试IMAGE 重定向C库函数(1)SemihostingSupportANSICinput/output你可以使用适合你目标板运行的驱动来替换标准C库中的设备驱动。Eg:printf()可打印到LCD上,而不是打印控制台上input/outputANSICCLibraryUserCodeDebugAgentTargetHardwareRetarget 重定向C库函数(2)要重定向C库函数,简单的办法是使用你自己的可执行的semihostingSWIs来代替原来的C库函数,从而来满足你的系统要求比如说,theprintf()系列函数(sprintf()除外)都会调用fputc().在默认情况下fputc()的执行使用了semihostingSWI.用下面的语句来代替:externvoidsendchar(char*ch);intfputc(intch,FILE*f) {/*e.g.writeacharactertoanLCD*/ chartempch=ch;sendchar(&tempch); returnch; }可查看在ADSEmbeddedexample目录下的retarget.c,可看到更多的重定向例子你可以确定有不在连接时使用semihostingSWI的吗?…... 消除C库函数中的semihosting为了确保在连接时没有函数使用了semihostingSWIs,你可以在程序中加入下面的句子:#pragmaimport(__use_no_semihosting_swi)如果在程序中仍然使用了semihosting,编译时将会报错:Error:Symbol__semihosting_swi_guardmultiplydefined修改:如果使用(check-verboselinkeroutputforoccurrencesofIuse_semihosting_swi),那么连接器将会把那些使用了smeihosting的程序列出来,然后:提供你自己可运行的功能函数。在ADS1.2编译器和库函数手册,表4-2给出了所有使用了semihosting的C库函数。注意:连接器在用户自己的应用代码中不会出现任何有关semihostingSWI使用的报告。 Agenda一个PC软件的构造定制标准C库函数到目标板定制IMAGE的存储器映射到目标板复位和初始化深层次的存储器器映象考虑编译和调试IMAGE 分散加载(Scatterloading)在一个实际应用当中,你可能并不想在0x8000处开始运行。大多数嵌入式系统都有存储器设备,他们的地址空间是在整个存储器映射中交叉出现的。分散加载提供了一种把你的代码和数据放在不同的存储器定位上的办法分散加载定义了两种类型的存储器区域。Load区:-在reset/load时保留了应用程序的代码和数据(典型应用为ROM).Execution区–在程序执行的同时保留了程序的代码和数据。在应用程序启动期间,每个load区都可创建一个或多个可执行区。分散加载了的应用把详细的存储器映射保存在一个描述文件中,作为一个参数给armlink使用eg:armlinkprogram.o-scatterscatter.scf-oprogram.axf ExecuteViewRAM0x100000x180000x40000x0000ROMRO分散加载(简单例子)只读代码和数据保存在ROM中C库函数初始化代码(在__main)将:从ROM拷贝RW数据到RAM在RAM中的ZI数据初始化RAM0x100000x180000x40000x0000ROMLoadViewRORWFillwithzerosZICopyRW ExecuteViewRAM0x100000x180000x40000x0000ROMROLOAD_ROM0x00000x4000{}RAM0x100000x180000x40000x0000ROMLoadViewRORWScatter描述文件通配符(*)语法允许简单的对CODE和DATA进行分组EXEC_ROM0x00000x4000{*(+RO)}RAM0x100000x8000{*(+RW,+ZI)}RWZI RORWZIROCODERO-DATARO-CODEAB链接器放置规则在每个可执行区,链接器通过一些基本的规则来放置CODE和DATA基本的排序方法是通过属性来安排的:RO领先于RW,RW领先于ZI有相同的属性时,CODE在DATA之前放置。更多的排序方法决定于:输入的组名按字母排序,在ARMLINK命令行中指定的顺序。eg:armlinkfile1.ofile2.o…AsectionAfromfile1.osectionAfromfile2.o 在SCATTOR文件中的对象排序为了把特定的CODE和DATA放在指定的地址上,你可以不考虑标准的放置规则使用+FIRST和+LAST,直接把第一个和最后一个对象放在可执行区。图例:把VECTOR表放在区的开始。LOAD_ROM0x00000x4000{EXEC_ROM0x00000x4000{vectors.o(Vectors,+FIRST)file1.o(+RO)file2.o(+RO)}:}在可执行区内,scattor文件中要排序的对象对输出image没有影响链接器的标准放置规则仍然适用 ROOT区LOAD_ROM0x00000x4000;startaddressandlength{EXEC_ROM0x00000x4000;root(load=execaddress){__main.o(+RO);copyingcode*(Region$$Table);RO/RWaddressestocopy*(ZISection$$Table);ZIaddressestozero}RAM0x100000x8000 {*(+RO);AllotherROareas*(+RW,+ZI);programvariables } }Mustbeinarootregionoutsiderootregion一个root区是一个可执行区,它的加载地址等于执行地址。 Root区要点一个root区是一个可执行区,它的加载地址等于执行地址.每个scatter描述文件必须最少包含一个root区,并且最少要包含下列内容:__main.o–含有拷贝code/data的代码Region$$Table和ZISection$$Table–含有将要拷贝的code/data的地址,他是由链接器产生的,不是一个对象文件。(所以*必须用)Error:L6202E:SectionRegion$$Tablecannotbeassignedtoanon-rootregion.Error:L6202E:SectionZISection$$Tablecannotbeassignedtoanon-rootregion.注意:如果*(+RO)被定位在root区,在此之前的将被自动放置Main应用程序的入口点必须放在root区。Error:L6203E:Entrypoint(0x08000000)lieswithinnon-rootregionEXE_FLASH. Run-time存储器管理SemihostingSupportANSICStack&HeapSetupStack&HeapSetupANSICCLibraryUserCodeDebugAgentTargetHardwareRetarget如何设置stack和heap来满足我们的目标存储器?我们已经通过执行__user_initial_stackheap()把C标准库的运行存储器模式修改到目标平台上。 Stack和Heap初始化CLibraryUserCode__maincopycodeanddatazerouninitializeddata__rt_entryinitializelibraryfunctionscalltop-levelconstructors(C++)Exitfromapplicationmain()causelinkertopullinlibraryinitializationcodeImageEntryPoint__user_initial_stackheap()setupapplicationstackandheap Run-time存储器模式你必须决定在放置stack和heap时所使用的区域是单一的区(one-regionmodel)或是不同的两个区(two-regionmodel)HeapStackStackOneregionmodelTworegionmodelHBSBSBHBHL单一存储器模式是默认方式为了实现多区域模式,你可以使用use_two_region_memory在所有的模式下,软件堆栈检查要许可。编译开关是:-apcs/swst指定堆栈限制(为two-region模式)heapischeckedagainststackpointerHeapheapischeckedagainstheaplimit(SL) __user_initial_stackheap()可以用C或汇编来写,他要返回:Heap基地址在R0,STACK的基地址在R1.Heap的限制地址在R2,STACK的限制地址在R3EXPORT__user_initial_stackheap__user_initial_stackheapLDRr0,=0x80000;HBLDRr1,=0x88000;SB;r2notused(HL);r3notused(SL)MOVpc,lrHeapStackHeap的限制地址在单一模式是不被使用的。Stack的限制地址只在软件堆栈检查许可的情况下才有效。HB=0x80000SB=0x88000 警告!当使用分散加载时你必须执行__user_initial_stackheap()在C库初始化代码内的__user_initial_stackheap()的默认执行是在映像文件的RW/ZI数据段后放置HEAP。使用Image$$RW$$Base/Image$$ZI$$Base连接符号这些符号对scatterloading是无效的。在ADS1.1和早期版本的软件中:符号被设置为0X0,heap被定位在这!Heap的并发使用,无论是直接(e.g.withmalloc())或间接(byuseofargc/argv)的都可能破坏向量表或其他代码,典型的结果是不可预知的程序在运行时出错了。在ADS1.2:符号没有定义,应用程序不会联接:Error:L6218E:UndefinedsymbolImage$$ZI$$Limit(referredfromsys_stackheap.o). Agenda一个PC软件的构造裁减标准C库函数到目标板定制IMAGE的存储器映射到目标板复位和初始化深层次的存储器器映象考虑编译和调试IMAGE TheVectorTableAREAVectors,CODE,READONLYIMPORTReset_Handler;importotherexceptionhandlers;…ENTRYBReset_Handler BUndefined_Handler BSWI_Handler BPrefetch_Handler BData_Handler NOP;ReservedvectorBIRQ_Handler;FIQ_HandlerwillfollowdirectlyEND在使用scatterloading+FIRST时直接定位在0X0(或0xFFFF0000)ENTRY直接告诉链接器这是一个入口点,防止某些段被删除中断向量表 初始化步骤CLibraryUserCode__maincopycodeanddatazerouninitializeddata__rt_entryinitializelibraryfunctionscalltop-levelconstructors(C++)Exitfromapplicationmain()tellslinkertolinkinlibraryinitializationcodeImageEntryPoint__user_initial_stackheap()setupstack&heapresethandlerinitializestackpointersconfigureMMU/MPUsetupcache/enableTCM$Sub$$main()enablecaches&interrupts ROMorRAMat0x0?需要一个有效的地址在0x0这项功能可被编码在像RESETHANDLER一样的模块中在本章结束的时候,我们还会讲到。ROM0x100000x180000x40000x0000AliasedROMResetHandlerROM/RAMRemappingRAM0x100000x180000x40000x0000ROMROMat0x0ResetHandlerVectorsResetHandler0x40000x0000RAMVectors ROM/RAMRemapping下面的例子可像Resethandler一样在源码中编码。;---IntegratorCMcontrolregCM_ctl_regEQU0x1000000C;AddressofCMControlRegisterRemap_bitEQU0x04;Bit2isremapbitofCM_ctlENTRY ;Onreset,analiasofROMisat0x0,sojumpto'real'ROM. LDRpc,=Instruct_2Instruct_2 ;RemapbysettingRemapbitoftheCM_ctlregister LDRr1,=CM_ctl_regLDRr0,[r1] ORRr0,r0,#Remap_bit STRr0,[r1] ;RAMisnowat0x0. ;TheexceptionvectorsmustbecopiedfromROMtoRAM(in__main) ;Reset_Handlerfollowsonfromhere这个功能也可在有mmu时使用ROM/RAM的重定向 初始化栈的指针;---Amountofmemory(inbytes)allocatedforstacksLen_FIQ_StackEQU256Len_IRQ_StackEQU256…Offset_FIQ_StackEQU0Offset_IRQ_StackEQUOffset_FIQ_Stack+Len_FIQ_Stack…Reset_HandlerLDRr0,stack_base;locatedbyscatterfile;EntereachmodeinturnandsetupthestackpointerMSRCPSR_c,#Mode_FIQ:OR:I_Bit:OR:F_Bit;NointerruptsSUBsp,r0,#Offset_FIQ_StackMSRCPSR_c,#Mode_IRQ:OR:I_Bit:OR:F_Bit;NointerruptsSUBsp,r0,#Offset_IRQ_Stack…;SystemmodestackissetuplastMSRCPSR_c,#Mode_SYS:OR:I_Bit:OR:F_Bit;NointerruptsSUBsp,r0,#Offset_SYS_Stack;SetupstacklimitifneededLDRr10,stack_limit;locatedbyscatterfile 局部存储器设置run-time的存储器必须在C库初始化前定义如果你使用的ARM7芯片还有MMU/MPU,它必须设置;ROM/RAM的重新映射必须完成。TCM’s(Tightlycoupledmemory)如果有TCM,典型的必须使能它。请注意:在TCM使能之前,要屏蔽ROM在Cache打开之前要返回。在c库初始化代码运行之后,如果cache被使能,可以避免与cache相关的问题; 扩展功能系统初始化代码通常在进入主应用之前运行当然,resethandler不是一个适合使能中断和使能caches地方。在resethandler最后应该放一个C运行库初始化代码EG。IMPORT__mainB__main我们可使用$Sub和$Super功能来包装符号externvoid$Super$$main(void);void$Sub$$main(void){cache_enable();//enablescachesint_enable();//enablesinterruptssys_to_usr_mode();//changemode-seenextslide$Super$$main();//callsoriginalmain()}相关描述可在ADS1.2LinkerandUtilitiesGuide-4.4章查阅到。 运行模式考虑主应用程序运行在何种模式是要考虑的重要问题。用户模式(Usermode)是非特权模式(unprivilegedmode)-保护你的系统系统初始化代码只能运行在特权模式(privilegedmode)。需要执行特权操作比如:使能中断。如果你的应用要运行在管理模式,简单的在管理模式下退出你的resethandler就可。如果你想在用户模式下运行你的应用,你需在$Sub$$main()改变为用户模式当然,__user_initial_stackheap()必须有权使用你的应用模式寄存器。解决办法是在系统模式里退出resethandler所有C库初始化代码有权使用用户寄存器,但是仍然可以执行特权操作。 Agenda一个PC软件的构造裁减标准C库函数到目标板定制IMAGE的存储器映射到目标板复位和初始化深层次的存储器器映象考虑编译和调试IMAGE 长跳转VeneersROM_LOAD0x0000{ROM_EXEC0x0000{*(+RO)}RAM0x80000000{farfunc.o(+RO)*(+RW,+ZI)}}/*main.c*/intmain(void) {farfunc(); }/*farfunc.c*/ voidfarfunc(void); { : }代码段可被远距离分开放置(比BL的跳转范围还远)链接器可自动增加长跳转Veneers,远距离的函数可被成功调用。0x00000000blVen$AA$L$$farfunc::Ven$AA$L$$farfunc ldrpc,[pc,#-4]dcd0x80000000::0x80000000 :movpc,lr 存储器映射寄存器你可以使用scatterloading来放置外设寄存器的存储器映射在文件中定义它e.g.timer_reg.cstruct{volatileunsignedreg1;/*timercontrol*/volatileunsignedreg2;/*timervalue*/}timer_reg;在存储器映射的请求地址上增加另外的可执行区来放置他们:LOAD_FLASH0x240000000x04000000{:TIMER0x40000000UNINIT{timer_reg.o(+ZI)}:}UNINIT显示在ZI段没有被初始化为0。 Stack和Heap区(1)你也可以在SCATTER文件中放置stack和heap在汇编原文件里定义stack和heap区比如.stackheap.s这个空间直接保留一个为0的存储器块AREAstack,DATA,NOINITSPACE0x3000;ReservestackspaceAREAheap,DATA,NOINITSPACE0x3000;ReserveheapspaceEND Stack和Heap区(2)增加一个可执行区来定位这个区域LOAD_FLASH0X240000000x04000000{:STACK0x1000UNINIT;length=0x3000{stackheap.o(stack);stack=0x4000to0x1000}HEAP0x15000UNINIT;length=0x3000{stackheap.o(heap);heap=0x15000to0x18000}}Heap的基地址起始为0x15000.Stack的最大地址为0x4000. Stack和Heap区(3)链接器将产生一个为每个可执行区的基地址和限制地址的符号指针在你的代码中引入这些符号IMPORT||Image$$STACK$$ZI$$Base||IMPORT||Image$$STACK$$ZI$$Limit||IMPORT||Image$$HEAP$$ZI$$Base||IMPORT||Image$$HEAP$$ZI$$Limit||stack_baseDCD||Image$$STACK$$ZI$$Limit||stack_limitDCD||Image$$STACK$$ZI$$Base||heap_baseDCD||Image$$HEAP$$ZI$$Base||heap_limitDCD||Image$$HEAP$$ZI$$Limit||使用DCD指令为这些段命名 __user_initial_stackheap()在resethandler,这个stack指针(r13)和stack限制值(r10)通常设置了,他们分别通过R1和R3作为参数传递给__user_initial_stackheapIMPORT__use_two_region_memoryEXPORT__user_initial_stackheap__user_initial_stackheapLDRr0,heap_base;SBvaluesetupinresethandlerLDRr2,heap_limit;SLvaluesetupinresethandlerMOVpc,lrStackHeap这个__user_initial_stackheap()例子实现了两个存储器区域模式。必须引用__use_two_region_memory,在这HEAP被检查,它是HEAP的限制值,而不是STACK指针 存储器映射例子16bitRAM0x100000x180000x40000x0000Fast32bitRAMVectorTableStackExceptionHandlersRW&ZIHeapFlash0x240000000x28000000ResetHandlerROPeripherals0x40000000外设控制寄存器的地址映射直接在FLASH运行的代码大小16位RAM被用来保存数据和HEAP区一些紧急的代码和数据可放在快速的RAM区 SCATTER文件例子::16bitRAM0x10000{*(+RW,+ZI)}HEAP0x15000UNINIT{stackheap.o(heap)}TIMER0x40000000UNINIT{timer_reg.o(+ZI)}}FLASH0x240000000x04000000{FLASH0x240000000x04000000{init.o(Init,+First)*(+RO)}32bitRAM0x0000{vectors.o(Vect,+First)handlers.o(+RO)}STACK0x1000UNINIT{stackheap.o(stack)}:这个scatter文件执行上页所显示的存储器映射。 Agenda一个PC软件的构造裁减标准C库函数到目标板定制IMAGE的存储器映射到目标板复位和初始化深层次的存储器器映象考虑编译和调试IMAGE 不使用段的消除和程序的入口点在默认的情况下,链接器将从最终的image文件中删除一些从不使用的代码段,或从未使用的数据段。要查看哪些段被删除了,在链接时用:‘-infounused’.为了确保不删除重要的段(比如:中断向量表):使用汇编指令entry标示所有的入口点(c库有一个入口点:__main()),使用‘-entry’选择其中一个入口点作为image的入口,否则,链接器将给警告:Imagedoesnothaveanentrypoint.(Notspecifiedor notsetduetomultiplechoices)在生成ROMmableimage使推荐使用下面的链接命令:armlinkobj1.oobj2.o-scatterscatter.scf-infounused-entry0x0-oprog.axf 输出选项链接器产生ELF/DWARF2格式的映像文件,选择适当的调试器下载调试为把elf映像文件转为‘ROMmable’格式使用fromelf,例如.:fromelfimage.axf-bin-oimage.bin产生binary格式的文件可烧入到适当的ROM,Flash或EPROM-Emulator,等.其他‘ROMmable’格式的文件也可由fromelf产生,例如.:Motorola32bitHex(-m32)Intel32bitHex(-i32)IntellecHex(-ihf). 调试ROM映像文件编译时加调试表(-G)来进行源码级调试。在ROM(EPROM、Flash、EPROM-Emulator)设备里烧入IMAGE文件,然后,把IMAGE文件加载到RAM里:ForAXD,select‘File->LoadMemoryFromFile’withloadaddress0x0Oncommandline,use:loadbinaryimage.bin0x0从ELF格式的IMAGE文件里装载含调试信息的符号表ForAXD,select‘File->LoadDebugSymbols’Oncommandline,use:loadsymbolsimage.axf 附加信息附加信息:例子代码在ADSExamplesembedded目录ADS1.2DeveloperGuide第6章:WritingCodeforROMADS1.2CompilersandLibrariesGuide第4章:TheCandC++LibrariesADS1.2LinkerandUtilitiesGuide 测验1.默认情况下,应用程序的STACK和HEAP如何放置的?2.如何确认在C库里没有链接进semihostingSWI功能?3.在scatter描述文件里,如何确定中断向量表放在0x0?4.哪个函数被用来放置应用stack和heap?5.在c库初始化(—main)前,使能cache,可以避免cache相关的问题?6.在两个区域的stack和heap模式,什么符号必须引入执行? ROM/RAM重定向(remmap)ROM0x100000x180000x40000x0000AliasedROMResetHandler0x40000x0000RAMVectors1.复位时,ROM通常定位到0x0;2.跳转到实际的ROM地址:0x100003.这时,把0x0的ROM替换为RAM,把中断向量表拷贝到0。ResetHandlerResetHandlerResetHandlerResetHandlerROM0x100000x18000BranchtorealROMRemovealias123

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
关闭