为了正常的体验网站,请在浏览器设置里面开启Javascript功能!
首页 > ARM嵌入式系统C语言编程

ARM嵌入式系统C语言编程

2020-07-06 3页 doc 91KB 10阅读

用户头像 个人认证

xxj7584

暂无简介

举报
ARM嵌入式系统C语言编程童梦无忧网试管婴儿论坛www.tm51.com本文由jiechurensheng贡献pdf文档可能在WAP端浏览体验不佳。建议您优先选择TXT,或下载源文件到本机查看。AIm嵌入式系统C语言编程姜换新(惠普中国软件研发中心上海201206)摘要本文详细介绍了嵌入式平台上用c语言编写系统软件和应用软件的方法.虽然是针对删平台介绍的,但基本经验和算法也适合于其他嵌入式平台的软件设计.关键词无操作系统支持的嵌入式系统软件,包括系统引导(BO研),驱动程序,动态内存管理,UO,通信以及应用软件等方面.嵌入式系统软件c语言删PRoGRA...
ARM嵌入式系统C语言编程
童梦无忧网试管婴儿论坛www.tm51.com本文由jiechurensheng贡献pdf文档可能在WAP端浏览体验不佳。建议您优先选择TXT,或下载源文件到本机查看。AIm嵌入式系统C语言编程姜换新(惠普中国软件研发中心上海201206)摘要本文详细介绍了嵌入式平台上用c语言编写系统软件和应用软件的方法.虽然是针对删平台介绍的,但基本经验和算法也适合于其他嵌入式平台的软件设计.关键词无操作系统支持的嵌入式系统软件,包括系统引导(BO研),驱动程序,动态内存管理,UO,通信以及应用软件等方面.嵌入式系统软件c语言删PRoGRA~mⅡNGC0NARME恤皿DEDPI^7IToR^1(凸妇s砷蝴msD跏Wuc脚,胁d哦一№一G"咿,.,甄ln咖i删2嘶)aJiarIgHu∞)【in舢s咖ctPr啊钟】『lljngc∞ARM锄beddedpla妇misand印plic枷0ns00m曲cated嗍n,U0ink如e,c四Ⅲ训011snlor画v啪a【k出eddescdp妇IpaperisIls幽lforPmj耐.M0dld髑indu血唱8)咖}州,出ve珥,dy呻II】ic畔I砌y埘mge一e1PedeIlce加ARMerrIbeddBdsh吼ddbeco啮ide商caId加y.w血阻哪dl即tKey帅rds酬洲8y栅S陆w唧pm丹删.19iIldi8paper0noⅡ此lIledmd吕蚰dpl"jmHd鲥tlm坞ab叫pro舯I眦ing删.n肌曲ARM扭恤.山discu8}edifem,"s删是投有意义的(如果返回,表明系统出现严重错误).另外,为了避免产生混淆,我们还必须给nlain函数另外取一个名字.比sys岫,meau-8Hyd山erd止edde(ICpH謦趼ⅡI】iIIg1明目189e1引言无操作系统支持的嵌入式软件包括系统引导(BOdT),外围驱动程序,存储管理,系统ⅣO,通信,应用程序等方面,需要结合采用汇编语青(约占10%)和c语言(约占90%).本文结如M血n.否则.编译器将会给m正n函数生成一大堆初始化代码.导致c程序的主人口与系统引导模块的接口错误.系统引导模块完成各种初始化工作后,用一条跳转指令进人c的主人口MairI,控制权从此移交给了c应用程序.合作者实践,详细介绍删嵌入式平台的c编程方法.考虑到通信软件涉及范围较大,本文不进行讨论.3存储管理存储管理是一个复杂的课题.从广义的角度来说,磁盘文件系统,内存,片内高速cadle等都属于这个范畴.嵌入式系统中,较有意义的是内存的动态分配与释放及凡a出存储器管理两方面.本文要介绍的是我们在嵌人式系统中实现的动态内存管理.2系统引导与雎IiII函数通常c语言是从腿m函数开始的.In越n函数的原型是:inI州n(inta1酽,ch口**日1F)其中a僻是参数的个数,畔v是指向各参数的指针的数组.m撕n函数由操作系统内核启动.操作系统内核完成函数所需的c语言中动态内存分配与释放主要由znalk和矗ee两个库函数实现.n诅uoc从系统空闲内存中分配合适的内存块,矗∞函数完成内存块的回收.这两个函数一般需要操作系统内棱的支持,在^RM裸平台上,不能直接调用.为此,我们编写变量初始化工作,并在调用结束后检查m血函数的返回值,若返回值为0,表明程序运行正常,否则表明程序运行出错.在嵌入式系统中,由于没有操作系统内核存在,对m越n函数的初始化工作只能由系统引导(BOdr)模块完成.系统引导(Ⅸx玎)部分完成系统韧始化工作.用汇编语言实现.它的工作包括硬件初始化,栈寄存器的设置,全局变量的初始化或清0,RAM中运行的模块的加载,堆参数的初始化等.完成这些工作后,再把控制权交给c的"H血函数.品然.了m—aIk和m一丘ee两个函数,实现动态存储管理的功能.典型应用程序内存映象分成代码区,数据区和栈区,三个区从低地址到高地址依次分布.代码区从最低地址开始,栈区收稿日期:2002—05一10.姜换新,硕士,主研领域:数字通信,嵌^式及网络编程.对嵌入式系统的m越n而言,a肾和aw这两个参数及返回值都·15·万方数据则占据最高地址.代码区和数据区可以丰}|连,也可以分开.嵌采用的算法之一,但使用下来发现它没有本文所描述的算法的效率高,且容易形成很多内存碎片.人式系统里,代码区位于只读存储器(如Fl曲)中,数据区和栈区则位于RAM中,因此代码区和数据区一般并不相连".数据区和栈区是分开的,它们之问的空隙称作堆.4LCD终端(系统I,o)T工=D终端软件是系统ⅣO范畴的重要内容,主要包括LCD堆作为一个连续的可利用空间,是系统的初始可分配块.每次应用程序申请内存,m..alk便从堆中分割出一块(从低地址开始)给它.随着申请次数的增加,原来一个完整的内存块便被分割为多个独立的块分配给应用程序.由于内存释放的先后顺序是随机的,因此·定时间后,系统中将存在多个互不字符M示(英文8×16点阵,汉字】6×16点阵),I£D绘图(点,线,圆,而,位罔,图形旋转等).320×240象素的LcD显示器,能显示15行×40列英文字符,或】5行×20列汉字字符,并基本实现有较好分辨率的图形/图像的显示.【cD显示的最基本程序是画点程序,其原型如下:vmd相连的内存块.这就使得整个内存医呈现出占用块和空闲块犬牙交错的状态,如图1所示.图中灰色部分表示内存被占用,白色部分表示未被占用.IfDHxcI(Imx,jntv,char∞I叮)其中,x和v是点的坐标,坐标原点在左上角,color是点的灰度.字符和位圈的显示利用了点阵方式.线,固和面则利用相应的算法实现.图形旋转需要使用坐标变换函数.这罩要详细介绍的是把MD作为(英文)字符型终端时的(b)部分已分配块释放之后l茎|I动态存储管理过程中的内存状态相应软件设计.把LcD作为字符型终端时.一个关键点是定义好光标:乱Ⅱcu唧口10d为了进行内存动态管理,需要维护两张全局表,一张是可CuⅡ删Une,cu册tCdumn利用空间表(a删一Hst),管理空闲内存块的信息,另一张是已分配空间表(usedHm),管理占用内存块.这两张表都用双向循环链表实现.随着系统的运行,可利用空间表中往往会有多个空闲块存在,究竟分配哪一块呢?文[1]介绍了三种不同的分配策略,即首次拟合法,最佳拟合法和最差拟合法,各有优缺点.笔者实现的是首次拟合法.可利用空间表和已分配空间表采用相同的"表元"数据结构,定义如下:自由ld这里cuⅡ衄山m和ct盯er越01u皿分别定义了光标的横坐标和纵坐标(坐标原点在左上角),取值范围分别是(o~39)和(0—14),对应于横行柏个字符和纵列15个字符.定义好光标后,每次向屏幕输出字符时,总是从光标处升抬,这样就保证了输出的有序性和连贯性.向屏幕输出字符串的基本函数是PrirⅡ,其原型如下:"d蹦n似叫叫chHr*h【,…)n蝴0ckI这是一个可变参数函数,功能上与面甜标准库函数完全相似.为丁实现可变参数的处理.要使用std孵h中定义一些宏.栅dltddE-d(+tlen:咖】dmH(K血'I"ev:酊zet胁Ⅱ分析每个格式字符,并列各转义字符(如\n,\t,\b,\r,,v等)进行相应处理.在屏幕的台适位置打日J格式化后的字dze;符串.胁d还调用一个滚屏函数screeI】ScmⅡ,当光标位于末行时让屏幕向上滚动若干行.Prin正函数不仅为【cD作为字符型终端提供了一个好的手c}l盯.8p日ce;在系统初抬化时,整个可分配内存块是一个连续的存储区,可利用空间表的元索只有一个.m一旦uoc函数每次分配内段.同时也为程序的调试提供了便利.我们可以在程序可能出错的地方用蹦甜函数打印一些信息,这为我们对程序的跟踪提供了相当大的方便.蹦甜函数在嵌入式系统编程中使用是十分明显的.存时.先榆查耐ze(m一龇的参数)是否合法(如是否超出堆的范围),若合法,再将其与32一乩字对齐,然后从avail一ust中搜索合适的内存块,井将其分配给应用程序.如果内存块的太小比d∞大得较多,则对内存块进行分裂,低地址的一块分配给应用程序,高地址的一块仍然放人a面l—bn中.如果搜索不到合适的空闲块,m—a11∞返回(vdd*)0.m5驱动程序设计驱动程序包括最底层的中断处理程序设计和建立在其上的驱动程序设计两个部分,其实现与具体的外围设备有关,复杂性较大.这里只介绍用c语言设计驱动程序时需要注意的一些方面.k函数释放内存时,根据参数addr给定的地址.在hn中去.然后,在avad—bm中检查是否有相-lsed一№中搜索相应的表元,找到后,将它标识的内存块释放.并插入到a面l邻的空闲块,并进行空闲块的台并.有三种不同的情况要分别处理:(1)左相邻:相邻块在当前释放块的低地址端.(2)右相邻:相邻块在当前释放块的高地址端.(3)左右相邻:当前释放块的低地址端和高地址端都有相邻块.在具体的分配算法上,文[1]介绍了边界标识法和伙伴系统.前者直接将链表管理信息插入到内存块的前端和后端,回收算法效率较高,但如果应用程序改写了超出它所申请范围的内存区,则会破坏整个数据结构,鲁棒性差一些.后者是笔者·16-外围硬件设备一般通过中断与凹u进行通信.中断是一种外部异步事件.在处理与中断相关的变量时.需要小心.通常,编译器的优化选项打开后,对变量的操作,将尽量安排在寄(下转第53页)1)使用ARM72盯r内核中的MMU(内存管理单元).可以把各物理上分开的内存块映射成逻辑上连续的内存空问.这样.从缩程角度肴,内存布局是连续的.这实际上是虚拟存储的概念.万方数据Efl8吲3dUb"daIi∞();ⅦfEne.剐3d(pr—HdgId,prmxDes'myA"y(prwj血11'pr法.spacc,pr—hl【ensily);c语言的编写风格也要为效率考虑?例如对数组元素的操作"A曲y[idx,4]=&~1;"就不如改为"Amy[i(jx>>2]=&~1;"这里.">>"是移位运算,有相应的机器指令.而"/,.是除k咖);nⅨrksllw^"y(pr—SPa.c);HⅨ【)cs伽yA删y【prInknsi'y);……}其中cspot是为丁,方便分析而写的描述微光斑各个属性的法运算,算法要夏杂得多.当然,先进的编译器一般能优化这类的语句.但不能保证所有编译器都有此功能.cH类.En盯科3血bTe皿i眦【e()在cMyApp类的重载函数尉Ⅱn—stance()中捌用.6应用程序设计嵌入式裸平台上的应用程序设计也有与Pc机上的应用程序设计不同的地方,需要格外注意.要特别注意:1)传递的矩阵大小必须相雎配,要不然运行时会出错.2)注意数据数组下标,MAllAB数组是按列存储的,而vc中是按行存储的.2)微光斑能量分布图上述代码绘制的微光斑某个截而和三维能量分布图见图3,网4.首先.凡是由需操作系统支持的标准库函数均不能使用,除非自己编写(如malk和m丘髓).其次,由于内存资源有限,栈容量有限且不能自动扩展,使用时要格外小心.常常能见到这样的局部变量的应用:mtbt】f【2048]其目的是要申请一个2048woId(8192Bm)的缓冲区,对于嵌入式系统来说.开销过于庞大.同时,栈空间巾用于嵌套调用的升销是不可见的,在嵌套层数较多时尤其如此.一下子申请这么大的栈空间,埘系统是一个大的挑战,搞不好系统会崩溃.图3微光斑截面能量分布圈图4微兜斑三维能量分布图我们可以采用类似于下而的替代方式:int.h心:讧【(hI】f=mdk℃(2048*si砷d(int)))==N叽J.)5结论冉微光斑分析软件的实例可以看出本文提出的用埘cros出Visual,"""p四q',m—he(}1l】f);堆的操作比栈更灵活,也更好控制.如果m一且110c调用成remmE|m0R:c++编写光学测量分析软件的主要部分,而经常涉及到功,它将返回分配的内存块的地址,否则返回0.如果返回O.表明系统内存已经所剩不多,这时程序员可以采取别的措施来解决问题,而不至于使系统崩溃.第三,同样的道理,坚决避免使用递归函数.的光能量二维及三维分布图则由vc调用MAⅡAB编写,编译生成的c/c++sharedLjbrary(DLL)绘制的方法不仅可以完全脱离MAllAB运行环境,可以根据需要传递参数,而且确实非常简单方便,可视性强,从而也有利于光学分析.同时这种方法也可以应用于光学以外的其它领域,提高编程速度,缩短开发周期.第四,使用m如函数时要注意两点:一是要检查返回值是否为0;二是要适时调用m一丘钟函数释放内存.前者可以避免系统陷人不必要的崩溃,后者可以防止出现内存泄漏.参考文献【lJMg山wo^s公司主页,hltp:‖www.叫It}1w∞ks.哪n.情号处理",《计算机与信息技术》,2000,9第五,对于编译器给出的警告信息不耍忽略.有良好的编程习惯的程序员是不会放过任何一个警告的.实际上,警告常常隐含着严重的逻辑错误.即使是无关痛痒的警告,比如变量声明了却没有使用的警告,也要予以重视,因为这类警告多了之后,会把一些有意义的警告掩盖掉,最终导致错误出现.第六,对一些速度要求较高的关键模块,采用汇编语言实现.[2]梧卫明,赵敏,张进芳,"vc++下如何利用M^1IAB工具箱进行数宁[3]齐被,董能力,"通过vc+十词用MAlLAB".(计算机世界),2000,29[4]亓漱,董能力,"实现vⅫ一c十+6.O与mⅡ.诣的混合编程".他腑编稚技巧与维护》2000,12:62.5]薛定宇,科学运算语盲M^Ⅱ^B53程序设计与应用.北京:清华大学出版社,2000t}E{{{{E{{{∈{{七E{E{{{E{E∈£{E{{{{{《{"《{岳∈{{{{{{}7结束语目前,嵌入式应用日渐普及,嵌人式软件越来越受到关注.本文仅从作者的实践出发,谈了一些粗浅经验,不当之处请读者指正.(上接第16页)存器中.中断服务程序常常通过改变一些全局变量来通知应用程序某个外部事件已经发生,这些全局变量是不应该被优化的.解决的办法是在声明变量时加上v01at丑e修饰符,"通知编译器这是一个可能被异步事件改变的量.这个问题看似简单,但如果不注意,实际运行时,程序将出现错误,且调试时很难定位故障.参考文献[1]严蔚敏,吴伟民,1992,数据结椅(第二版).清华大学出版社.运行效率是设计驱动程序的另外一个问题.中断比较频繁的外设,其中断处理程序的速度对整个系统的性能影响是很大的.这些模块应该直接用汇编语言编写,并尽可能优化算[2]w碰曲删&evell8.1992,Adv曲cedPm酽州n吨抽ckuNⅨh"r晴I[3]AR^m^Ⅱn"hshed(ARMn瑚t·Ad出蝴WdyhINI如"gC呷呻y.DDl0029E)·53·万方数据ARM嵌入式系统C语言编程作者:作者单位:刊名:英文刊名:年,卷(期):引用次数:姜换新惠普中国软件研发中心,上海,201206计算机应用与软件COMPUTERAPPLICATIONSANDSOFTWARE2003,20(10)14次参考文献(3条)1.严蔚敏.吴伟民数据结构19922.WRichardStevensAdvancedProgrammingintheUNIXEnvironment19923.查看详情相似文献(10条)1.学位论文张佳楠基于嵌入式系统的车站信号安全软件设计与实现2006嵌入式安全关键系统对组成系统的软件,硬件安全性级别要求极高,这种系统的运行直接关系着人员生命和财产安全.如果嵌入式系统安全关键软件质量不好,可能对系统的安全构成严重威胁.因此,嵌入式系统安全关键软件在设计上应采取多种安全保障技术.安全核技术就是其中的重要措施之一.本文在嵌入式系统环境下,针对典型的安全关键系统一车站信号计算机联锁系统,基于安全核技术进行了安全软件开发方法的研究与实践,主要工作如下:1.在大量收集阅读相关文献的基础上,分析并总结了安全关键软件设计方法和联锁系统的研究现状及存在的问题.2.在对比多种嵌入式操作系统的基础上,选择并构建嵌入式操作系统uClinux平台.3.对安全核技术的机制,安全策略,及安全核设计进行了详细的研究.4.将安全核技术应用于计算机联锁软件中,提出了基于安全核技术的计算机联锁软件设计方法,并给出了安全核各模块的设计和实现.5.针对铁路安全技术,给出了适合的安全策略制定方法.6.基于风险评价,分析了安全核对联锁软件安全性的影响.7.在嵌入式系统平台上,对安全核进行了测试,验证了安全核的功能.2.会议论文刘韬.王方.殷峰嵌入式系统动态补丁升级软件设计研究2006嵌入式系统动态补丁升级软件的功能是在不影响系统正常运行的情况下完成对系统错误的修复,即在软件运行过程中,用补丁模块替换旧的模块,不打断原有程序的执行顺序.本文介绍了嵌入式系统动态补丁升级软件的总体设计,组成模块及应用方式.3.学位论文陈瑜面向嵌入式系统的在轨软件维护技术研究2006随着计算机技术和软件技术的发展,具有高度自治性和灵活性的实时嵌入式系统在航天,航空,航海,通信和网络设施等领域的应用越来越广泛.这些嵌入式系统往往具有较高的长时间运行的自治性和可靠性的要求,因此,对系统的在轨维护能力提出了迫切的需求.本文面向实时嵌入式系统的在轨维护需求,提出了一种在轨软件动态维护系统的体系结构,并对系统实现过程中的维护补丁自动化生成和在轨软件动态修改等关键技术进行了深入研究和论述.主要内容如下:本文首先介绍在轨软件维护技术的研究现状和论文的研究背景,包括在轨软件维护的概念,需求和发展概述,以及国内外在轨软件维护技术的发展状况和发展趋势等.在此基础上提出了本文的研究目标与研究内容.第二部分,提出面向实时嵌入式系统的在轨软件维护系统的体系结构,根据系统各个组成部分的功能,分为三个子系统:软件开发子系统,补丁加工子系统和在轨动态修改子系统.第三部分,论述在轨维护系统中维护补丁的自动化生成的关键技术.本文设计了软件开发子系统自动分析修改后的软件源代码,生成维护补丁初始信息,然后通过补丁加工子系统对初始补丁进行自动加工.在两个子系统功能相结合的基础上,实现完整补丁生成的自动化.第四部分,论述在轨维护系统中在轨软件动态修改的关键技术.首先论述作为目标系统内核部分的在轨修改子系统的设计原理,分析子系统与目标系统内核部分的关系,然后详细论证了通过软件维护补丁动态修改目标系统的实现方法,包括对变量补丁,函数补丁和任务补丁的各种处理方法.第五部分,在充分论述在轨软件维护系统关键技术的基础上,给出了在轨软件维护系统的实例,并通过几个模拟在轨维护实例进一步验证系统功能的正确性和有效性.最后,对面向嵌入式系统的在轨软件维护系统的研究进行总结,并对在轨软件维护技术的发展趋势进行了展望.4.会议论文赵()嵌入式系统硬件/软件协同设计技术2001在无损检测设备中,嵌入式系统往往是很重要的组成部分之一.嵌入式系统作为一类重要的计算机应用系统,实时性和可靠性是其主要特性.同时嵌入式系统也是一种将软件和硬件结合在一起的系统,通常的,大部分功能由软件来实现,硬件只起辅助作用,当系统功能变得日益复杂的时候,软件的处理速度将会受到影响,从而影响整个系统的实时性.于是,将一部分功能从软件部分转移到硬件部分就成为必然趋势,而硬件/软件协同设计技术则是解决这个问题的好方法.本文简要介绍了硬件/软件协同设计技术的由来和基本概念,以及这一领域的新进展.5.学位论文舒国丽嵌入式系统底层软件的研究与开发2002嵌入式实时操作系统(RTOS)的出现,使得当前嵌入式系统软件形成了底层软件,RTOS,支撑软件,应用软件的架构.嵌入式系统底层软件负责与硬件相关的操作,完成底层硬件的初始化和驱动,并引导RTOS的启动.当前嵌入式底层软件的开发包括两类:板级支持包(BSP)的开发和设备驱动程序的开发.该学位论文的主要目标是讨论嵌入式系统底层软件的研究与开发.文中首先概述了嵌入式技术的发展,嵌入式系统的特点和典型的嵌入式系统.接着讨论了嵌入式系统软件开发及底层软件开发在其中的地位.随后介绍了基于VxWorks的嵌入式系统底层软件的开发--BSP的设备驱动程序的开发.接着以一网络电视机顶盒系统为例,从其硬件结构出发,描述了机顶盒底层软件的设计,并重点讨论了该系统BSP和视频显示驱动程序的具体实现.最后讨论了一千兆以太网交换机驱动程序的实现.6.学位论文戴鸿君基于异构多核体系与组件化软件的嵌入式系统研究2007随着集成电路制造工艺的发展和计算机体系结构研究的深入,多核芯片(即片上多处理器)技术将普遍的应用到嵌入式系统的芯片设计中,使得线程级并行在嵌入式软件中得到广泛的使用.这将引起一场硬件体系,操作系统,编译器和软件开发方法等多方面的重大变革,而迄今还没有明确的办法.所以如今嵌入式系统研究,无论硬件方面还是软件方面,都聚焦于此.通常嵌入式系统在体系设计上都会采取异构多核的架构,嵌入式操作系统通过线程支持,线程调度,中断支持和设备管理的更新来支持和管理各个处理器核.在软件开发层面,使用线程库,并行编程语言或者组件化软件等方法编写可并行程序.随着嵌入式系统的应用范围越来越广,密集计算型的应用将普遍的出现在嵌入式设备上,其中字符串匹配是其中一个典型重要应用.如何设计定制的芯片以提升嵌入式系统的性能,是本文研究的出发点.本研究面向嵌入式系统中密集计算,尤其是字符串匹配计算的需求,探索使用软硬件协同的方法,实现异构多核嵌入式系统的性能提升.在硬件上,研究定制面向密集计算的多核架构;在软件上,实现面向处理器核的组件化改造机制.本研究的主要工作集中在以下几点:(1)设计硬件加速的异构多核体系.其中SIMD辅核能面向字符串进行硬件级并行匹配,高速的核间总线支持稳定而高效的核间通信.(2)针对异构多核体系,研究嵌入式操作系统对辅核的支持,板级支持包对硬件的封装和辅核的编程方法,使用户软件能发挥多核体系的特点.(3)设计面向嵌入式系统,对线程进行封装的线程级组件模型,研究线程级组件的应用方式和并行调度策略.同时研究应用于嵌入式系统的组件非功能属性约束,设计组件复用框架.本研究通过软件模拟和开发板实验对设计进行了验证.通过周期精确模拟器验证了硬件实现的算法;使用XilinxML403FPGA开发板进行了SoC集成,通过二进制数据的匹配实验进行了系统验证,而且在这些应用中可以验证线程级组件的调度.最后,以简单的网络协议组件化拆分和动态组装运行为例,验证了嵌入式系统中组件化软件的动态组装和组件复用.本研究设计了通过软件的组件化对线程模型进行封装,实现面向多核体系的并行编程和动态调度的方法:探索了在多核引入嵌入式系统环境下,软硬件协同处理计算密集型任务的新方法.这将推动面向嵌入式系统,基于多核体系和组件化软件的软硬件设计方法研究.7.会议论文林聪仁.钟文荣.胡晓毅嵌入式系统中纯软件全双工串行口的实现2002本文论述了在以单片机或DSP芯片为核心的嵌入式系统中,用纯软件方法实现异步全双工串行口的方法.以此方法的扩展全双工串行口,不需增加任何硬件成本,除了必不可少的两I/O引脚外,只需一些存储单元和一个定时器.数据发送和接收可以完全按全双工方式进行,而且主程序还可照常进行其他的操作,互不干扰.8.学位论文冯翔嵌入式系统中闪存设备管理技术研究与实现2004随着嵌入式系统领域不断增长的应用需求,嵌入式系统的复杂性日益提高.闪存设备作为嵌入式系统中最常见的非易失数据存储器,其管理和访问技术对整个系统的性能和可移植性都有较大的影响.在嵌入式系统中,闪存设备的管理技术通过闪存设备I/O软件来实现,目前常见嵌入式系统采用的闪存管理实现技术在可移植性,易用性上都存在不足.因此,如何设计一种统一,高效且可移植性强的闪存管理技术实现成为了嵌入式应用领域的迫切需要.该文首先从理论上分析了嵌入式系统中闪存设备I/O软件设计思想及基本原则.在对操作系统软件设计中策略与机制分离原则的应用分析基础上,提出了闪存设备管理技术分层实现的软件体系结构,从而将闪存I/O软件由底向上划分成了硬件驱动层,原始设备层及设备层,并对其在用户空间和内核空间实现方案进行了优劣比较.然后,深入剖析嵌入式LinuxI/O设备管理机制,结合分层体系结构设计了一种模块性,移植性更强且更易使用的闪存设备I/O软件实现方案,对该实现方案中的关键技术问题,如阻塞型I/O,读写请求处理函数设计等进行分析,并提出了相应的解决办法.最后,选择基于PowerPC860T开发板的嵌入式Linux为目标系统平台,对闪存设备I/O软件进行了具体开发和实现.I/O软件可移植性的高低可以从设备无关及相关代码的比例进行衡量,在该实现方案中,设备相关代码仅占代码总量的24.1%,说明该方案具有较好的可移植性.对实现方案的数据吞吐率及文件操作时间代价等典型性能指标进行了测试.结果表明:该文提出的设计实现方案不仅能够为应用程序提供统一,易用的闪存使用接口,同时还具有较强的模块性,易于在不同类型闪存芯片上进行移植.9.期刊论文基于移动嵌入式系统硬/软件协同设计的EHSC算法-计算机科学2005,32(10)嵌入式系统设计的一个重要任务就是寻找硬/软件最佳搭配方案.随着系统复杂性的不断提高,采用嵌入式系统硬/软件协同设计是提高设计质量的有效手段.本文在讨论嵌入式系统设计一般方法的基础上,阐述了系统的硬/软件协同设计技术和硬件/软件划分方法,提出了以系统硬/软件划分策略为基础,系统组件的权重值为参考,组成元素划分为依据的设计理念,构造了基于移动环境的系统的硬/软件协同设计的EHSC(EmbeddedHardware/SoftwarreCodesign)模型.并依照此模型,实现了一种移动嵌入式系统"电子包"阅读器的设计和开发.10.会议论文魏洪兴.王田苗软件专业嵌入式系统课程体系研究2005嵌入式系统已被广泛地应用于工业控制系统,信息家电,通信设备,医疗仪器,智能仪器仪表等众多领域,可谓无处不在.为了向学生介绍嵌入式系统知识,培养嵌入式系统技术人才,在高等院校范围内开设嵌入式系统课程已成为一种趋势.本文围绕清华大学及北京航空航天大学软件学院的嵌入式系统课程,提出了"32位嵌入式微处理器"+"实时操作系统"+"实践"的嵌入式系统教学模式,并介绍了课程的目标和考核体系,以及具体的教学环节和实践环节的课程设置,最后对谍程的教学效果进行了分析.引证文献(14条)1.王兴亮.任雅祥.陈岁生光纤溶解氧在线测量仪表的设计[期刊论文]-机电工程2009(1)2.杨庚.阳春华.王罡.赵旭坦克配电装置硬件组态检测平台的设计与开发[期刊论文]-计算机测量与控制2007(11)3.辛鑫.蒙建波.罗根由C到ARM汇编指令及程序优化[期刊论文]-单片机与嵌入式系统应用2007(06)4.金丽.包志华.陈海进基于ARM嵌入式系统的C程序优化设计方法[期刊论文]-南通大学学报(自然科学版)2006(03)5.张伟WINDOWSCE.NET嵌入式系统硬盘数据传输的实现和优化[学位论文]硕士20066.谭伟复杂零件几何量检测技术研究与检测仪器的研制[学位论文]硕士20067.严技凯基于ARM处理器的数字伺服系统测试仪的设计[学位论文]硕士20068.彭岩嵌入式系统在楼宇变配电监控系统中的应用研究[学位论文]硕士20069.赵奎.张帆一种嵌入式系统存储管理方案[期刊论文]-企业技术开发2005(01)10.刘爱辉电力系统广域测量中数据采集系统的实现[学位论文]硕士200511.王宁基于ARM与μC/OS-Ⅱ的数字伺服系统信号发生器的开发[学位论文]硕士200512.闫迎春嵌入式操作系统的研究与应用[学位论文]硕士200513.黄世荣基于DOCSIS实现的ARM开发平台的设计实现与调试[学位论文]硕士200514.韩冰车流量检测信号处理系统设计[学位论文]硕士2005本文链接:http://d.g.wanfangdata.com.cn/Periodical_jsjyyyrj200310007.aspx下载时间:2010年5月15日1本文由zjq859112贡献ARM嵌入式系统C语言编程摘要无操作系统支持的ARM嵌入式平台软件编程包括系统引导(BOOT)、驱动程序、动态内存管理、系统I/O设计、通信以及应用软件等方面。本文结合作者的实践,详细介绍了嵌入式平台上用C语言编写系统软件和应用软件的方法。虽然是针对ARM平台介绍的,但基本经验和算法也适合于其他嵌入式平台的软件设计。ProgrammingWithConARMEmbeddedPlatform(2002-03-11)AbstractProgrammingwithConARMembeddedplatformisacomplicatedproject.Severalmodulesincludingsystemboot,drivers,dynamicmemorymanagement,I/Ointerface,communicationsandapplicationsshouldbeconsideredcarefully.WithanexcellentexperienceonARMembeddedsystem,theauthorgivesadetaileddescriptioninthispaperonthemethodsandalgorithmsaboutprogrammingARM.ThoughARMistheonlydiscusseditem,thispaperisusefulforprogrammingonanyotherembeddedplatforms.1引言包含ARMIP核(IntellectualPropertyCore)的各种微处理器芯片已在全世界获得广泛应用,并日益成为嵌入式平台的首选器件。ARM嵌入式系统软件可以分为系统软件和应用软件两部分。如果采用嵌入式操作系统,则系统软件主要是指操作系统本身;如果不采用嵌入式操作系统,则整个系统是一个单任务系统,系统软件的设计是难点和重点。如无特殊声明,本文中“嵌入式系统”均指无操作系统的裸系统。一般来说,嵌入式平台上的软件编程包括如下几个方面:系统引导模块(BOOT)外围驱动程序(Drivers)存储管理系统I/O通信应用程序除应用程序一项外,以上各项均属于“系统软件范畴”。ARM平台软件的编写可以采用汇编语言,也可以采用C语言。C语言由于其高效性、灵活性和易维护性,是嵌入式系统的首选编程语言之一。一般来说,系统软件主要用汇编语言和C语音实现,应用软件基本上用C语言实现,除非对性能或存储空间有特殊要求。嵌入式系统编程是一个复杂的课题。限于篇幅,本文仅就ARM平台上的C语言编程,结合作者的实践进行一些介绍。同时,考虑到通信软件设计范围较大,本文没有涉及。本文针对的系统,处理器采用的是CirrusLogic公司的EP7209(ARM720T内核),存储系统由FLASHROM和SRAM组成,显示终端采用的是320×240的单色LCD。2系统引导与Main函数通常C语言是从main函数开始的。main函数的原型是:intmain(intargc,char**argv);其中argc是命令行中参数的个数,argv是指向各参数的指针的数组。main函数由操作系统内核启动,操作系统内核完成函数所需的变量初始化工作,并在调用结束后检查main函数的返回值,若返回值为0,表明程序运行正常,否则表明程序运行出错。在嵌入式系统中,由于没有操作系统内核存在,对main函数的初始化工作只能由系统引导(BOOT)模块完成。系统引导(BOOT)部分主要完成系统初始化工作,用汇编语言实现。它的工作包括硬件初始化、各种处理器模式下栈(stack)寄存器的设置、全局变量的初始化、未定义全局变量的清0、RAM中运行的软件模块的加载、与堆(heap)相关的参数的初始化等等。完成这些工作后,再把控制权交给C的main函数。显然,由于嵌入式系统的特殊性,argc和argv这两个参数是没有意义的。main函数的返回值也没有意义,因为实际上它根本不会返回(如果返回,表明系统出现严重错误)。另外,为了避免编译器产生混淆,我们还应该给main函数另外取一个名字,比如Main。否则,编译器将会给main函数生成一大堆初始化代码,而这些初始化代码对嵌入式系统来说是没有意义的,并且会导致C程序的主入口与系统引导模块的接口错误。系统引导模块完成各种初始化工作后,用一条跳转指令进入C的主入口Main(参见文献【3】):BLMain该指令执行后,系统引导模块的工作即宣告结束,控制权从此移交给了C应用程序。3存储管理在软件世界里,存储管理是一个复杂的课题。从广义的角度来说,磁盘文件系统、磁带备份系统、内存、CPU片内高速Cache等都属于这个范畴。在嵌入式系统中,比较有意义的是内存的动态分配与释放以及Flash存储器系统的管理两个方面。Cache的影响虽很明显,但基本上与软件关系不大。Flash存储器目前被广泛应用于嵌入式系统中。对Flash的管理主要体现在对Flash的编程以及基于Flash的文件系统上。本文要介绍的是我们在嵌入式系统中实现的动态内存管理。C语言中动态内存分配与释放主要由两个函数malloc和free实现。以下是两个函数的原型(定义在stdlib.h中):void*malloc(size_tsize);voidfree(void*addr);其中,malloc函数实现内存的动态分配,从系统空闲内存中申请一块合适大小(由size定义)的连续内存块,并将内存块的首地址返回给应用程序;free函数实现内存的回收,它从所有已占用内存块中,找到与addr地址一致的那一块,并把它标记为“未用”,如果该内存块与其他块有邻接关系,还要进行内存块的合并,避免内存碎片出现。这两个函数进行内存的动态管理时,一般是通过操作系统内核实现的。在ARM嵌入式系统平台上进行应用软件编程时,由于缺乏操作系统的支持,往往不能直接调用标准库中的这两个函数,这时候就需要自己动手编写这两个函数。为了避免混淆,我们分别把自己编写的这两个函数命名为m_alloc和m_free,以示区别。下面根据我们的实践,详细介绍两个函数算法的实现。典型应用程序内存分配可以分成三个主要的区:代码区(coderegion)、数据区(dataregion)和栈区(stack),三个区从低地址到高地址依次分布。代码区从最低地址(0x00000000)开始,栈区则占据最高地址。代码区和数据区可以相连,也可以分开。一般嵌入式系统里,代码区位于只读存储器(如Flash)中,数据区和栈区则位于RAM中,因此代码区和数据区一般并不相连。数据区和栈区是分开的,它们之间的空隙称作堆(heap)。在动态存储管理中,所针对的对象便是堆(heap)。一般来说,栈向下(低地址)生长,而堆则向上(高地址)生长。我们进行动态存储管理时,把堆作为一个连续的可利用空间(已为栈预留好足够的空间的情况下),这就是系统的初始可分配块。每次应用程序提出内存申请(即调用malloc函数)时,便从该内存块中拿出一块(从低地址开始)来分配给应用程序。随着申请次数的增加,原来一个完整的内存块被分割为多个独立的块分配给不同的应用程序。由于内存释放的先后顺序是随机的,因此一定时间后,系统中将存在多个互不相连的内存块。这就使得整个内存区呈现出占用块和空闲块犬牙交错的状态,如图1所示。图中灰色部分表示内存被占用,白色部分表示未被占用。U1U2U3U4U5U6U7(a)系统运行初期U1U4U5U7(b)部分已分配块释放之后图1动态存储管理过程中的内存状态如果此时又有新的应用程序请求分配内存,系统将如何做呢?一种策略是系统搜索整个内存区中的所有空闲块,从中找出一块大小合适的分配给应用程序。当然,该空闲块的大小一般来说并不会恰巧等于所申请的大小,天下没有这样巧合的事情。所以此时需要把该空闲块进行再次分割(除非超过部分不是太大),一块给应用程序,一块供下次分配时用。为了进行内存动态管理,需要维护两张全局表,一张是可利用空间表(avail_list),管理空闲内存块的信息,一张是已分配空间表(used_list),管理占用内存块。这两张表都用双重循环链表实现。随着系统的运行,可利用空间表中往往会有多个空闲块存在,究竟分配哪一块呢?【严蔚敏1992】介绍了三种不同的分配策略,即首次拟合法、最佳拟合法和最差拟合法,各有优缺点。笔者实现的是首次拟合法。可利用空间表和已分配空间表采用相同的“表元”数据结构,定义如下:structmblock{structmblock*next;structmblock*prev;size_tsize;char*space;};其中,next和prev分别为指向后一个和前一个表元的指针,size为内存块的大小(为方便管理,实际分配时,size大小是按照32bit对齐的),space为指向相应内存块的指针。系统维护的总的表元数由宏NUM_MBLOCK定义。全局数组mblocks[NUM_MBLOCK]的声明定义了全部的表元。NUM_MBLOCK的大小影响到系统对内存的占用,因此要根据实际需要定义一个合适的值。在系统初始化时,整个可分配内存块是一个连续的存储区,因此可利用空间表的元素只有一个,占用一个表元。该内存块由如下两个参数定义:unsignedlong__heap_base;unsignedlong__heap_limit;其中,__heap_base定义了该内存块的首地址,__heap_limit定义了该内存块的尾地址的下一个地址。这两个参数是由系统引导模块根据编译器给出的各数据区的大小定义的。初始化时,该存储区大小(size)等于(__heap_limit-__heap_base),起始地址space等于(char*)__heap_base。剩下的表元全部被链接到一个空闲(free_list)链表(这是系统维护的第三张表)。空闲链表中所有表元的size被初始化为0,space被初始化为NULL。在初始状态下,已占用空间表是一张空表,其表头指针为NULL。m_alloc函数每次分配内存时,先检查size的值是否合法(如是否小于0),若合法,再将其与32-bit字(word)对齐:size=(size+3)&~3;然后,m_alloc从avail_list中搜索合适的内存块,找到后如果其大小刚好等于或略大于size,则将该内存块分配给应用程序,并返回其首地址;如果其大小比size大得较多,则先将该内存块分裂成两块,低地址的一块分配给应用程序,并从free_list中申请空闲表元,将刚才分配的内存块的信息(size和space)填入表元后将表元插入到used_list中,然后修改avail_list中相应表元的size和space值,使其指向高地址的空闲块(即把高地址块插回到avail_list中)。在以上过程中,如果free_list中已没有剩余表元,说明系统资源已耗尽,此时将停止对空闲块的分裂,直接把它分配给应用程序。如果在avail_list中搜索不到合适的空闲块,m_alloc返回(void*)0。m_free函数释放内存时,根据参数addr给定的地址,在used_list中搜索相应的表元,找到后,将相应的表元所标识的内存块释放,并将其插入到avail_list中去。然后,在avail_list中检查是否有相邻的空闲块,如果有,则要进行空闲块的合并。合并后剩余的表元,要重新插入到free_list中去,供下次申请时使用。空闲块的合并有三种不同的情况:左相邻:相邻块在当前释放块的低地址端右相邻:相邻块在当前释放块的高地址端左右相邻:当前释放块的低地址端和高地址端都有相邻块这三种情况要分别处理。在具体的分配算法上,【严蔚敏1992】介绍了边界标识法(boundarytagmethod)和伙伴系统(buddysystem)。前者直接将链表管理信息插入到内存块的前端和后端,回收算法效率较高,但如果应用程序改写了超出它所申请范围的内存区,则会破坏整个数据结构,鲁棒性差一些。后者是笔者采用的算法之一,但使用下来发现它没有本文所描述的算法的效率高,且容易形成很多内存碎片。4LCD终端软件LCD终端软件是系统I/O范畴的重要内容,主要包括LCD字符显示(英文8×16点阵,汉字16×16点阵),LCD绘图(点、线、圆、面、位图、图形旋转等)。320×240象素的LCD显示器,能显示15行×40列英文字符,或15行×20列汉字字符,并基本实现有较好分辨率的图形/图象的显示。LCD显示的最基本程序是画点程序,其原型如下:voidLCDPixel(intx,inty,charcolor);其中,x和y是点的坐标,坐标原点在左上角,color是点的灰度。字符和位图的显示利用了点阵方式。线、圆和面则利用相应的算法实现。图形旋转需要使用坐标变换函数。这里要详细介绍的是把LCD作为(英文)字符型终端时的相应软件设计。把LCD作为字符型终端时,一个关键点是定义好光标:staticunsignedCurrentLine,CurrentColumn;CurrentLine和CurrentColumn分别定义了光标的横坐标和纵坐标(坐标原点在左上角),取值范围分别是(0~39)和(0~14),对应于横行40个字符和纵列15个字符。定义好光标后,每次向屏幕输出字符时,总是从光标处开始,这样就保证了输出的有序性和连贯性。向屏幕输出字符串的基本函数是Printf,其原型如下:voidPrintf(constchar*fmt,…);这是一个可变参数函数,功能上是对标准输出函数printf的仿真。格式字符串fmt中的“%”个数定义了Printf函数的参数个数,具体的解释是由编译器完成的。为了实现可变参数的处理,在Printf函数中,要使用如下的数据结构/宏(在stdarg.h中定义),以最终得到格式化的字符串String:va_list:可变参数列表数据结构va_start:取得可变参数列表中第一个参数的地址va_end:列表结束va_arg:取得一个参数然后,Printf调用针对该LCD的基本的格式化字符串打印函数PrintStr,将字符串打印到屏幕上。PrintStr的原型如下:voidPrintStr(constchar*String);PrintStr函数依次分析输入字符串String中的每个字符,并对各格式控制字符(如\n,\t,\b,\r,\v等)进行相应处理后,在屏幕的合适位置依次输出相应当字符。此外,作为字符型终端,还有一个滚屏函数,使得当光标位于末行时让屏幕向上滚动若干行(具体行数视需要而定)。该函数原型如下:voidScreenScroll(intiNumLine);其中,iNumLine参数给出要滚动的行数。Printf函数不仅为LCD作为字符型终端提供了一个好的手段,同时也为程序的调试提供了便利。我们可以在程序可能出错的地方用Printf函数打印一些信息,这为我们对程序的跟踪提供了相当大的方便。Printf函数在嵌入式系统编程中作用是十分明显的。5驱动程序设计驱动程序包括最底层的中断处理程序设计和建立在其上的驱动程序设计两个部分,其实现与具体的外围设备有关,复杂性较大。这里只介绍用C语言设计驱动程序时需要注意的一些方面。外围硬件设备一般通过中断与CPU进行通信。中断是一种外部异步事件。在处理与中断相关的变量时,需要小心。这里举一个例子说明。假设当某个外部事件(周期性事件)发生时,其中断驱动程序将全局变量StopWait从0改写为1,该全局变量的声明如下:intStopWait=0;同时,在程序的另外一个地方,要对StopWait的值进行查询,并一直等待直到不等于0才进行其他操作,以取得CPU与异步事件的同步:while(StopWait==0){}StopWait=0;在上面的程序段中,CPU循环检查StopWait的值,直到StopWait不等于0才继续执行后面的指令。这样编程,在没有进行优化编译的情况下是可以的,因为周期性外部事件总会发生,而事件一发生,就会把StopWait置1,从而循环结束。但是,如果启动优化编译,问题就会出现。先看下面的汇编语言程序段,它是上面的程序段经优化编译后的结果:LDRr0,=StopWait;LoadaddressofStopWaitLDRr0,[r0];LoadcontentofStopWait10CMPr0,#0;Comparer0with0BEQ%b10;Ifr0==0,compareagain在这一段程序代码中,变量StopWait首先被从内存装载到CPU的寄存器r0,然后CPU循环检查r0的内容是否为0,若是则继续检查。显然,如果装载StopWait时其值为0,则该循环将永远进行下去。这是我们所不希望的,因为我们的本意是要查询变量StopWait本身的值是否为1,而不是揪住寄存器r0不放。这里,编译器并没有意识到StopWait是一个可以被异步事件改变的量,因而对它做了不正确的优化。解决的办法是在声明变量时加上volatile修饰符,以通知编译器这是一个可能被异步事件改变的量:intvolatileStopWait=0;加上volatile修饰符后,编译器将不对StopWait进行优化,这样每次比较时,CPU都要从内存中取得StopWait的值,而不是只load一次。这个问题看似简单,但如果不注意,可能给调试带来很大的麻烦。除了以上问题外,设计驱动程序时,还要注意效率问题。中断比较频繁的外设,其驱动程序尤其中断处理程序的速度对整个系统的性能影响是很大的,对这些模块应该直接用汇编语言编写,并尽可能优化算法。C语言的编写风格也要为效率考虑。例如对数组元素的操作:Array[idx/4]|=~1;就不如改为:Array[idx>>2]|=~1;这里,idx>>2是移位运算,有相应的机器指令对应,而idx/4是除法运算,算法要复杂得多。6应用程序设计嵌入式平台上的应用程序设计也有与PC机上的应用程序设计不同的地方,需要格外注意。首先,嵌入式系统缺乏操作系统支持,因此凡是由操作系统提供的系统调用在嵌入式系统上均无法实现。在调用库函数时要留意这一点。必要时需要自己编写一些相应的系统函数,比如前面介绍的m_alloc和m_free。其次,嵌入式系统的内存往往是十分有限的,使用系统资源要格外小心。比如,在程序内部申请局部变量时,要考虑到栈的大小是否足够。嵌入式系统不同于PC平台,栈的容量往往很有限,且不能自动扩展。常常能见到这样的局部变量的申请:intbuf[2048];其目的是要申请一个2048word大小的缓冲区,作临时存储用。这个缓冲区实际上要占用8192byte(8KB)的栈空间,对于嵌入式系统来说,开销过于庞大。同时,栈空间中用于嵌套调用的开销是不可见的,在嵌套层数较多时尤其如此。一下子申请这么大的栈空间,对系统是一个大的挑战,搞不好系统会崩溃。我们可以采用下面的替代方式:int*buf;buf=m_alloc(2048*sizeof(int));if(buf==0)returnERROR;m_free(buf);……堆的操作比栈更灵活,也更好控制。如果m_alloc调用成功,它将返回分配的内存块的地址,否则返回0。如果返回0,表明系统内存已经所剩不多,这时程序员可以采取别的措施来解决问题,而不至于使系统崩溃。第三,使用m_alloc函数时要注意两点,一是要检查返回值是否为0,二是要适时调用m_free函数释放内存。前者可以避免系统陷入不必要的崩溃,后者可以防止出现内存泄漏(memoryleak)。第四,对于编译器给出的警告(warning)信息不要忽略。有良好的编程习惯的程序员是不会放过任何一个警告的。实际上,警告常常隐含着严重的逻辑错误。比如函数调用时类型不匹配的问题,本来应该用short型参数,函数调用时却用了long型参数,结果导致溢出,也许得费九牛二虎之力才能查出来。这类错误编译器一般会给出警告,只要对警告信息留意一下,问题就会迎刃而解。即便是无关痛痒的警告,比如变量声明了却没有使用的警告,也要予以重视,因为这类警告多了之后,会把一些有意义的警告掩盖掉,最终导致错误出现。第五,对一些速度要求较高的关键模块,采用汇编语言实现。7结束语嵌入式系统软件设计是一个比较复杂的工程,有很多需要注意和值得研究的地方。目前,嵌入式操作系统日渐普及,嵌入式系统的编程越来越受到人们的关注,嵌入式系统上的应用软件也将日益繁荣。本文从作者从事嵌入式系统编程的实践出发,谈了一些粗浅经验,不当之处请读者指正。8附录:参考文献【1】严蔚敏,吴伟民,1992,数据结构(第二版),清华大学出版社。【2】W.RichardStevens,1992,AdvancedProgrammingintheUNIXEnvironment,AddisonWeslyPublishingCompany.【3】AdvancedRISCMachinesLtd(ARM),1995,ARM7TDMIDataSheet(ARMDDI0029E).1本文由forcici贡献pdf文档可能在WAP端浏览体验不佳。建议您优先选择TXT,或下载源文件到本机查看。ARM嵌入式系统C语言编程姜换新(惠普中国软件研发中心上海201206)thorgivesadetaileddescriptioninthispaperonthemethodsandalgorithmsaboutprogrammingARM.ThoughARMistheonlydiscusseditem,thispaperisusefulforprogrammingonanyotherembeddedplatforms.ment,I∏interface,communicationsandapplicationsshouldbeconsideredcarefully.WithanexcellentexperienceonARMembeddedsystem,theauO2KeywordsEmbeddedsystemSoftwareprogramminglanguageCARM1引言2系统引导与main函数摘要无操作系统支持的嵌入式系统软件,包括系统引导(BOOT),驱动程序,动态内存管理,O,I∏通信以及应用软件等方面.本文详细介绍了嵌入式平台上用C语言编写系统软件和应用软件的方法.虽然是针对ARM平台介绍的,但基本经验和算法也适合于其他嵌入式平台的软件设计.关键词嵌入式系统软件语言CARMPROGRAMMINGCONARMEMBEDDEDPLATFORMJiangHuanxin(ChinaSoftwareSolutionsCenter,Hewlett-PackardCompany,Shanghai201206)围驱动程序,存储管理,系统I∏,O通信,应用程序等方面,需要合作者实践,详细介绍ARM嵌入式平台的C编程方法.考虑到通信软件涉及范围较大,本文不进行讨论.通常C语言是从main函数开始的.main函数的原型是:intmain(intargc,char33argv)结合采用汇编语言(约占10%)和C语言(约占90%).本文结其中argc是参数的个数,argv是指向各参数的指针的数组.main函数由操作系统内核启动,操作系统内核完成函数所需的变量初始化工作,并在调用结束后检查main函数的返回值,若返回值为0,表明程序运行正常,否则表明程序运行出错.在嵌化工作只能由系统引导(BOOT)模块完成.入式系统中,由于没有操作系统内核存在,对main函数的初始系统引导(BOOT)部分完成系统初始化工作,用汇编语言实现.它的工作包括硬件初始化,栈寄存器的设置,全局变量等.完成这些工作后,再把控制权交给C的main函数.显然,的初始化或清0,RAM中运行的模块的加载,堆参数的初始化对嵌入式系统的main而言,argc和argv这两个参数及返回值都AbstractProgrammingConARMembeddedplatformisacomplicatedproject.Modulesincludingsystemboot,drivers,dynamicmemorymanage2是没有意义的(如果返回,表明系统出现严重错误).另外,为了避免产生混淆,我们还必须给main函数另外取一个名字,比如Main.否则,编译器将会给main函数生成一大堆初始化代码,导致C程序的主入口与系统引导模块的接口错误.无操作系统支持的嵌入式软件包括系统引导(BOOT),外系统引导模块完成各种初始化工作后,用一条跳转指令进入C的主入口Main,控制权从此移交给了C应用程序.3存储管理存储管理是一个复杂的课题.从广义的角度来说,磁盘文件系统,内存,片内高速Cache等都属于这个范畴.嵌入式系统中,较有意义的是内存的动态分配与释放及Flash存储器管理两方面.本文要介绍的是我们在嵌入式系统中实现的动态内存管理.C语言中动态内存分配与释放主要由malloc和free两个标准库函数实现.malloc从系统空闲内存中分配合适的内存块,核的支持,在ARM裸平台上,不能直接调用.为此,我们编写了malloc和mfree两个函数,实现动态存储管理的功能.典型应用程序内存映象分成代码区,数据区和栈区,三个free函数完成内存块的回收.这两个函数一般需要操作系统内区从低地址到高地址依次分布.代码区从最低地址开始,栈区收稿日期:2002-05-10.姜换新,硕士,主研领域:数字通信,嵌入式及网络编程.·15·则占据最高地址.代码区和数据区可以相连,也可以分开.嵌入式系统里,代码区位于只读存储器(如Flash)中,数据区和栈区则位于RAM中,因此代码区和数据区一般并不相连1).数据区和栈区是分开的,它们之间的空隙称作堆.堆作为一个连续的可利用空间,是系统的初始可分配块.每次应用程序申请内存,malloc便从堆中分割出一块(从低地址开始)给它.随着申请次数的增加,原来一个完整的内存块便被分割为多个独立的块分配给应用程序.由于内存释放的先后顺序是随机的,因此一定时间后,系统中将存在多个互不相连的内存块.这就使得整个内存区呈现出占用块和空闲块犬牙交错的状态,如图1所示.图中灰色部分表示内存被占用,白色部分表示未被占用.效率高,且容易形成很多内存碎片.图1动态存储管理过程中的内存状态为了进行内存动态管理,需要维护两张全局表,一张是可利用空间表(availlist),管理空闲内存块的信息,另一张是已分配空间表(usedlist),管理占用内存块.这两张表都用双向循环链表实现.随着系统的运行,可利用空间表中往往会有多个空闲块存在,究竟分配哪一块呢?文[1]介绍了三种不同的分配策略,即首次拟合法,最佳拟合法和最差拟合法,各有优缺点.笔者实现的是首次拟合法.可利用空间表和已分配空间表采用相同的"表元"数据结构,定义如下:structmblock{};区,可利用空间表的元素只有一个.malloc函数每次分配内存时,先检查size(malloc的参数)是否合法(如是否超出堆的范围),若合法,再将其与32-bit字对齐,然后从availlist中搜索合适的内存块,并将其分配给应用程序.如果内存块的大小比size大得较多,则对内存块进行分裂,低地址的一块分配给应用程序,高地址的一块仍然放入availlist中.如果搜索不到合适的空闲块,malloc返回(void3)0.并插入到availlist中去.然后,在avail块的低地址端和高地址端都有相邻块.mfree函数释放内存时,根据参数addr给定的地址,在list中检查是否有相usedlist中搜索相应的表元,找到后,将它标识的内存块释放,邻的空闲块,并进行空闲块的合并.有三种不同的情况要分别处理:(1)左相邻:相邻块在当前释放块的低地址端.(2)右相邻:相邻块在当前释放块的高地址端.(3)左右相邻:当前释放在具体的分配算法上,文[1]介绍了边界标识法和伙伴系统.前者直接将链表管理信息插入到内存块的前端和后端,回收算法效率较高,但如果应用程序改写了超出它所申请范围的内存区,则会破坏整个数据结构,鲁棒性差一些.后者是笔者采用的算法之一,但使用下来发现它没有本文所描述的算法的·16·structmblock3next;sizetsize;char3space;在系统初始化时,整个可分配内存块是一个连续的存储structmblock3prev;4LCD终端(系统I∏O)好光标:时让屏幕向上滚动若干行.十分明显的.LCD终端软件是系统I/O范畴的重要内容,主要包括LCD字符显示(英文8×点阵,汉字16×点阵),LCD绘图(点,1616线,,,圆面位图,图形旋转等).320×象素的LCD显示器,240能显示15行×列英文字符,或15行×列汉字字符,并基4020本实现有较好分辨率的图形/图像的显示.LCD显示的最基本程序是画点程序,其原型如下:voidLCDPixel(intx,inty,charcolor)其中,x和y是点的坐标,坐标原点在左上角,color是点的灰度.字符和位图的显示利用了点阵方式.线,圆和面则利用相这里要详细介绍的是把LCD作为(英文)字符型终端时的应的算法实现.图形旋转需要使用坐标变换函数.相应软件设计.把LCD作为字符型终端时,一个关键点是定义staticunsignedCurrentLine,CurrentColumn这里CurrentLine和CurrentColumn分别定义了光标的横坐标和纵坐标(坐标原点在左上角),取值范围分别是(0~39)和(0~14),对应于横行40个字符和纵列15个字符.定义好光标后,每次向屏幕输出字符时,总是从光标处开向屏幕输出字符串的基本函数是Printf,其原型如下:始,这样就保证了输出的有序性和连贯性.)voidPrintf(constchar3fmt,…这是一个可变参数函数,功能上与printf标准库函数完全相似.为了实现可变参数的处理,要使用stdarg.h中定义一些宏.Printf分析每个格式字符,并对各转义字符(如\n,\t,\b,\r,\v等)进行相应处理.在屏幕的合适位置打印格式化后的字符串.Printf还调用一个滚屏函数ScreenScroll,当光标位于末行Printf函数不仅为LCD作为字符型终端提供了一个好的手段,同时也为程序的调试提供了便利.我们可以在程序可能出错的地方用Printf函数打印一些信息,这为我们对程序的跟踪提供了相当大的方便.Printf函数在嵌入式系统编程中使用是5驱动程序设计驱动程序包括最底层的中断处理程序设计和建立在其上的驱动程序设计两个部分,其实现与具体的外围设备有关,复杂性较大.这里只介绍用C语言设计驱动程序时需要注意的一些方面.外围硬件设备一般通过中断与CPU进行通信.中断是一种外部异步事件.在处理与中断相关的变量时,需要小心.通常,编译器的优化选项打开后,对变量的操作,将尽量安排在寄(下转第53页)1)使用ARM720T内核中的MMU(内存管理单元),可以把各物理上分开的内存块映射成逻辑上连续的内存空间.这样,从编程角度看,内存布局是连续的.这实际上是虚拟存储的概念.Energy3dlibInitialize();MlfEnergy3d(prHeight,prWidth,prSpace,prmxDestroyArray(prLength);mxDestroyArray(prSpace);mxDestroyArray(prIntensity);……}Intensity);法.C语言的编写风格也要为效率考虑.例如对数组元素的其中CSpot是为了方便分析而写的描述微光斑各个属性的stance()中调用.C++类.Energy3dlibTerminate()在CMyApp类的重载函数ExitIn26应用程序设计要特别注意:1)传递的矩阵大小必须相匹配,要不然运行时会出错.2)注意数据数组下标,MATLAB数组是按列存储的,而VC中是按行存储的.2)微光斑能量分布图上述代码绘制的微光斑某个截面和三维能量分布图见图3,4.图图3微光斑截面能量分布图图4微光斑三维能量分布图5结论由微光斑分析软件的实例可以看出本文提出的用MicrosoftVisualC++编写光学测量分析软件的主要部分,而经常涉及到的光能量二维及三维分布图则由VC调用MATLAB编写,编译生成的C/C++SharedLibrary(DLL)绘制的方法不仅可以完全脱离MATLAB运行环境,可以根据需要传递参数,而且确实非常简单方便,可视性强,从而也有利于光学分析.同时这种方法也可以应用于光学以外的其它领域,提高编程速度,缩短开发周期.参考文献[1]MathWorks公司主页,http:‖www.mathworks.com.[2]潘卫明,赵敏,张进芳"VC++下如何利用MAT,LAB工具箱进行数字[3]齐波,董能力"通过VC++调用MAT,LAB"《计算机世界》,,2000,291局变量来通知应用程序某个外部事件已经发生,这些全局变量修饰符,以通知编译器这是一个可能被异步事件改变的量.这误,且调试时很难定位故障.是不应该被优化的.解决的办法是在声明变量时加上volatile个问题看似简单,但如果不注意,实际运行时,程序将出现错运行效率是设计驱动程序的另外一个问题.中断比较频繁的外设,其中断处理程序的速度对整个系统的性能影响是很大的.这些模块应该直接用汇编语言编写,并尽可能优化算[4]亓波,董能力"实现VisualC++610与MAT,LAB的混合编程"《电脑,[5]薛定宇,科学运算语言MATLAB513程序设计与应用,北京:清华大(上接第16页)存器中.中断服务程序常常通过改变一些全信号处理"《计算机与信息技术》,,2000,91编程技巧与维护》2000,12:6211学出版社,200017结束语操作Array[idx∏]=&1;""4~就不如改为Array[idx>>2]=&"~1;"这里.>>""是移位运算,有相应的机器指令,而/""是除法运算,算法要复杂得多.当然,先进的编译器一般能优化这类的语句,但不能保证所有编译器都有此功能.嵌入式裸平台上的应用程序设计也有与PC机上的应用程首先,凡是由需操作系统支持的标准库函数均不能使用,其次,由于内存资源有限,栈容量有限且不能自动扩展,使序设计不同的地方,需要格外注意.除非自己编写(如malloc和mfree).用时要格外小心.常常能见到这样的局部变量的应用:intbuf[2048]其目的是要申请一个2048word(8192Byte)的缓冲区,对于嵌入式系统来说,开销过于庞大.同时,栈空间中用于嵌套调用的开销是不可见的,在嵌套层数较多时尤其如此.一下子申请我们可以采用类似于下面的替代方式:int3buf;这么大的栈空间,对系统是一个大的挑战,搞不好系统会崩溃.功,它将返回分配的内存块的地址,否则返回0.如果返回0,表明系统内存已经所剩不多,这时程序员可以采取别的措施来解决问题,而不至于使系统崩溃.第三,同样的道理,坚决避免使用递归函数.值是否为0;二是要适时调用mfree函数释放内存.前者可以避免系统陷入不必要的崩溃,后者可以防止出现内存泄漏.程习惯的程序员是不会放过任何一个警告的.实际上,警告常常隐含着严重的逻辑错误.即使是无关痛痒的警告,比如变量之后,会把一些有意义的警告掩盖掉,最终导致错误出现.现.声明了却没有使用的警告,也要予以重视,因为这类警告多了本文仅从作者的实践出发,谈了一些粗浅经验,不当之处请读者指正.[1]严蔚敏,吴伟民,1992,数据结构(第二版),清华大学出版社1ment,AddisonWeslyPublishingCompany.[3]ARM7TDMIDataSheet(ARMDDI0029E).[2]W.RichardStevens,1992,AdvancedProgrammingintheUNIXEnviron2∏3otherprocessing3∏mfree(buf);if((buf=malloc(20483sizeof(int)))==NULL)returnERROR;……堆的操作比栈更灵活,也更好控制.如果malloc调用成第四,使用malloc函数时要注意两点:一是要检查返回第五,对于编译器给出的警告信息不要忽略.有良好的编第六,对一些速度要求较高的关键模块,采用汇编语言实目前,嵌入式应用日渐普及,嵌入式软件越来越受到关注.参考文献·53·1
/
本文档为【ARM嵌入式系统C语言编程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索