完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
(讲嵌入式移植的一篇非常好的文章,原文出自《基于arm+uClinux的嵌入式系统的开发》,这是节选了其中讲移植的这一段) 不同平台间程序的移植--简单程序的移植 研究程序移植的那两周是最痛苦的两周,没有太多可以借鉴的东西,只能摸黑向前走,于是更加坚定决心要整理些东西给后来的弟兄。不过话说回来,各位弟兄别被我前面说的吓倒,只要搞清你要作什么,程序移植其实是比较简单的事情。首先列出一些问题: (1)X86上运行的程序能不能在51单片机上运行,为什么,有没有可能,如果可以,应该做哪些工作才可以实现。 (2)相同CPU平台,DOS的程序为什么可以在windows下运行,能不能在linux下运行,为什么,作什么工作可能实现。 为什么可以移植程序,为什么要移植程序? 程序可以移植首先要感谢开发出高级语言的大牛们,记住,无论多么漂亮的代码经过编译以后都要变成CPU可以识别的机器语言,而几乎一千种CPU说着一千种语言。为保证大家有共同语言,规定一种高级语言――高级语言。每一个CPU派出自己的翻译――编译器。这个翻译精通两国语言,高级语言和自己的语言。(由此已经可以看出编译工具在程序移植中的重要性)。只要程序没有硬件上的约束,可以说这种沟通是无极限的,甚至于不同操作系统平台。(操作系统也是程序,也可以移植喽)举例:在x86的windows下用VC(或TC,BC)编一个c程序实现i=i+1,丝毫不改就可以用51的C编译器重新编译并在51单片机上运行。一次小型的移植就结束了。 可以移植已有的程序还要感谢开放源代码的弟兄,没有这些C文件和H文件让你重新编译一下,怎么在你的CPU上跑?其实不止这些,后面还会看见开源组织的牛人专为程序可移植性所作的专门的工作。 那么为什么要移植程序? 问这问题就像问地上有个钱包为什么要捡一样,答案不言而喻。现成的东西为什么不要。当然,移植程序可没有捡钱包那么简单,尤其是第一次,后面会说一些移植之前应该考虑的问题。(就像现在地上有个钱包也千万别上去就揣自己兜里,说不定就是套)。另一方面,你给我你写好的程序让我拿去用,我还要考虑一下,或许里头问题多的还不如自己写一个。我这里所说的可移植的程序应该是维护比较好,比较成熟的源代码(像我后面的所说的UCD-SNMP),目前的开放源代码运动决不仅仅是把自己的程序公开就行了,而是有了一套成熟完整的版本控制,BUG报告和PATCH提交流程。 这样的代码才有更大的使用价值。 什么时候可以考虑移植程序?在基于嵌入式操作系统进行开发时,具有一定规模的程序都可以到网上查一查都没有成熟的源代码可用。前面已经说到,程序的移植最终只针对CPU,其实和操作系统没什么关系,但另一方面,因为代码中可能会使用一些库函数,这些库会包括C语言标准库和操作系统提供的API(应用程序接口)库。假设源代码中只包括C标准库,那么该程序就可以跨操作系统去移植。例如hello world程序中使用了printf,因为该函数是C标准函数,所以在X86上使用TC(BC或VC)可以直接编译运行,在ARM+uClinux平台下也一样,但如果程序中调用了vfork函数,那么只有 linux一脉相承的操作系统支持这种特殊服务了,在window或dos操作系统下无法直接编译该程序了。只能找该操作系统支持的类似的功能来实现。再进一步,硬件上的生理缺陷也会为移植带来麻烦,S3C4510B不支持MMU,在其上运行的uClinux也不提供和MMU有关的服务(其实 uClinux本身可以支持MMU),于是在移植前相关的函数(比如FORK)都要被替代掉(使用VFORK)。好在uClinux和linux提供的应用接口大部分还是相同的。所以这样的工作还可以承受。 由上可知,如果是在各种嵌入式linux(除了uClinux以外,还有好几种)平台上开发,那么针对该平台以及linux平台上的源代码都可以使用,但是要牢记他们之间的差异。在我的系统中需要实现网络监控,所以想使用snmp协议,该协议和http,ftp一样属于应用层的成熟协议,专用于网络管理。目前已经有一些针对该协议成熟的代码,最有名的是ucd-snmp,不光软件本身功能强大,可移植性也比较好,在linux,unix等平台上都可以移植,于是决定将它移植到ARM+uClinux平台上(别看现在说的这么轻松,当时接这活时都有点哆嗦)。 简单总结一下,移植应用程序的前提是有源代码,移植的关键工具是编译器,源代码中和硬件平台相关的东西越少越好(这里主要指使用了汇编,或做了针对自己平台的事,比如将指针指向特定地址然后操作),另一方面,如果该程序是基于某个操作系统(利用了操作系统提供的特殊服务,即API),要看自己的操作系统是否提供了相关服务。 下面简单列出一些我认为移植时需要考虑的问题: (1) 自己的操作系统的特点以及在当前版本下支持的特性。 例如:uClinux不支持MMU,同样就无法支持相应的特性。 (2) 硬件资源。 因为嵌入式系统资源比较紧张,硬件资源考虑必须要周全: (1) 软件存储空间的大小 这一般要等用目标编译器重新编译完以后可能才会知道,所以只能大概估算,但千万不要看这个程序在linux下只有几十k,就认为程序很小,这是因为linux下程序多时使用动态库,而在嵌入式系统中,很有可能是把用到的库都链接在一起,所以程序的尺寸会大大增加。 (2)程序运行空间。. (3)硬件以及相应的驱动是否完备 以上工作应该尽量做,但有时事先无法把握,只能听天由命了(有没有搞错!!) 可能有人已经要晕菜了,振奋一下大家,如果找到了好的源代码(可移植性好),那么剩下的如要工作就是玩转你的编译器,只要你能顺利的把源代码用你的编译器重新编译一下。90%的工作就完成了(不是吗) 上回已经介绍了一些编译器方面的东西,下面针对我的ARM编译器的具体参数来讲解一些编译器主要参数的设置。 加入我已经有了hello.c,在x86的linux平台下编译链接一下。 gcc –c hello.c 产生.o gcc–o hello hello.o 产生可执行文件,上回说过,主机编译器参数都有环境变量保存,所以看起来很简单。这里我故意分两个步鄹。 下面看一下用我的编译器编这个程序。 arm-elf-gcc-Iroot/uClibc/include -msoft-float -mcpu=arm7tdmi -fomit-frame-pointer-fsigned-char -mcpu=arm7tdmi -Os –Wall -DEMBED -D_uClinux_ -c hello.c 这只是编译,将参数逐一讲解。Arm-elf-gcc 是gnu的arm编译工具 1)Include地址:参数:-I 值:root/uClibc/include(这是在主机上我的uClinux的头文件路径) 用法:-I root/uClibc/include -I参数保证后面的头文件路径在搜索系统头文件路径前被搜索从而有可能替代系统的头文件,如果有多个这样的参数,则搜索的顺序是从左到右,然后是系统的头文件。 2)-m 是针对CPU的选项。 -mcpu=arm7tdmi 说明CPU类型 -msoft-float 产生包含浮点库的输出 -fsigned-char 让char类型有符号 -fomit-frame-pointer对所有不需要帧指针的函数都不将其保存在寄存器中。 3) -Os –Wall -Wall:所有警告都显示 Os:优化尺寸,该选项使能所有所有不增加尺寸的O2优化,并且进一步根据尺寸优化 4) = -DEMBED -D_uClinux_ -D:将-Dmacro后的macro定义为字符串1。 以下是链接: arm-elf-ld-L/root/uClibc/lib -L/usr/local/gnu/arm-elf/lib -L/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1-elf2flt –o hello /root/uClibc/lib/crt0.o/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtbegin.o hello.o/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtend.o -lc -lgcc –lc 其中 1) 链接工具: arm-elf-ld 2) -L指明需要链接的库的路径,用法和-I一样,自己的库的路径也可以在这里加入。 -L/root/uClibc/lib-L/usr/local/gnu/arm-elf/lib -L/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.13) –o 后面紧跟生成的最终的文件名 4)/root/uClibc/lib/crt0.o/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtbegin.o OBJECTS.o/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtend.o 这是需要链接在一起的.o文件 5) -lc -lgcc –lc -l 后面紧跟的是需要链接的库的名字,一般库的名字是libxxx.a,使用时为-lxxx即可,不加lib和.a。还要注意位置,自己的库文件应该加在他的库前面。 编译通过后,移植就算完成了,对于比较小的源代码都可以这样,即先分析他的编译选项(用到了那些头文件,库文件等),然后用自己的编译器对照相应参数重新编译一下就行了。当然这只是简单程序的移植,复杂案例在下一次讲吧。 |
|
相关推荐
1个回答
|
|
q1:(1)X86上运行的程序能不能在51单片机上运行?
a1:因为CPU或者MCU的构架不同,程序能执行是家里在冯.诺依曼体系上的,为每次取操作指令和操作数进行运算,得到结果,然后再次取指令,操作数,运算得结果;这种方式就限定了以寻址的方式进行运算,现将指令存在某个地址的空间里面,操作数也放在另一些地址空间内,然后CPU的指令被固定再某个固定的地址内,比如说累加器是固定在0x0000030010这个地址内,(只是假设),然后他的方式是这样的,当寻址找到这个地方这个固定地址时,启动里面内容,然后这个里面的内容是累加之后他紧挨的两个32位的单元内数据。(这个是X86的32位机,51芯片就达不到32位的单元数据,只能达到8位的单元数据),然后结果放在第一个单元格内,覆盖到之前的其中那个近他的,(以汇编语言为例,可以很明显的观察到,add ax dx,之类的指令就能明白,如果能从CPU的数据手册就可以找到,累加器地址,就可以用MOV代替ADD,当然累加器要支持立即寻址才行,这跟CPU的型号和设计有关,如果不支持,就不能用MOV代替ADD),这样运算就结束了,所以不同的CPU就有不同的位置存放不同的CPU内部指令,这也就造就了CPU的指令集,不同的CPU有不同的CPU指令集,指令集对应的地址也不一样,这个可以参考百度搜索指令集里面有详细介绍,X86的指令集就能跑XP,X64就能跑64位的win10,X86就有不能用X64的系统、51芯片的指令集和电脑CPU的指令不一样,所以编译好的51.hex是不能在电脑上运行,因为调用的CPU地址不一样,比如,本来在51里面调用的累加器,但是放到X86的CPU上就可能调用就只是一个堆栈的单元地址。 q2:(2)相同CPU平台,DOS的程序为什么可以在windows下运行,能不能在linux下运行 a2:因为操作系统内核不一样,负责的解释器不一样,就像DOS下用dir看目录,LINUX下用ls看目录是一样一个道理,磁盘的管理方案也不一样,假如dos是将目录放在一个分区的开头位置地址前500个单元,每个目录占用2个单元,一个是名字,一个是目录对应文件内容的前端地址,(文件在磁盘上是以不连续的方式存放的。)而Linux有可能是存放目录在这个磁盘分区的后500个单元,这就是区别,当用Linux加载NTFS或者是FAT格式的分区后,不用任何工具,当然MOUNT这个也算是一种加载工具,当然用Windows挂载ext4格式的分区时,Windows会显示磁盘未格式化,要求格式化。程序调用接口也不一样,比如在win下调用是win内核里的某个已经加载模块,但是Linux调用的是Linux内核加载的某个模块,Linux下运行exe文件会报出未调用正确,或不能识别。编译器命令解释器也不一样,在Linux你可以看到shell的解释存放在,/bin/bush,而win下是放在Windows目录下的cmd.EXE |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
1129 浏览 0 评论
AD7686芯片不传输数据给STM32,但是手按住就会有数据。
1075 浏览 2 评论
2175 浏览 0 评论
如何解决MPU-9250与STM32通讯时,出现HAL_ERROR = 0x01U
1269 浏览 1 评论
hal库中i2c卡死在HAL_I2C_Master_Transmit
1693 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-25 19:14 , Processed in 0.848396 second(s), Total 70, Slave 54 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号