经常在网上看到不少人询问应如何学习嵌入式Linux。这确实是个非常困扰初学者的问题:一个新手面对着琳琅满目的开发板、各种各样的参考书和社会上五花八门的培训机构,照单全收吗?自己的精力和时间是绝对不够分配的。挑一个吧:自己又全无经验,而嵌入式Linux的学习又同时涉及硬件、软件等多方面的知识;万一选择不当,事倍功半还是好的;运气不好的话,搭上大量时间和精力辛辛苦苦学习半天最后还是一无所成,想想也是很让人气结的事。
本文打算从硬件选择、软件准备和学习步骤三方面做一简要分析。希望能对初学者起到一些参考作用。
硬件选择:
Linux是一种得到广泛应用的操作系统,其功能的强大和复杂的体系结构与微软的windows已经不相伯仲,所以就决定了承载Linux的硬件平台不能是如C51一样的单片机环境。一些arm7体系的CPU由于不带MMU(内存管理单元),无法实现标准Linux(这里指由Linus Torvalds同学不断在Gitweb上发布新内核的那个系列)的内存映射机制,就必须运行ucLinux,这是Linux的一个变种,没有内存的保护机制。用户程序可以访问内核。就安全性而言要低于标准Linux,但实时性要好于标准Linux。而arm9体系的CPU一般带MMU,其主频一般也高于arm7体系,运行标准Linux毫无问题。对于初学者而言,上述两种体系的CPU都可以考虑。如果更希望熟悉标准的Linux,还是选择arm9体系好一些。价格上,arm9的板子当然要贵一些,但支持的外设也多一些。过了入门阶段就会晓得,板子上支持的端口的种类和数量多些还是很有价值的,换句话说还是有可能省点银子的。早先买的一块板子的USB主口有两个,比起现在的许多只有一个USB主口的板子确实省了不少事。
除此以外,还应考虑的一点是:选择的硬件平台型号不要太偏门,对于希望自学的人士而言这点相当重要。前人们在自学过程中遇到的最大问题是遇到难题不知道向谁请教,现在有了网络,加上google和百度大神要好多了。但是非主流的东西毕竟研究的人少,原本就没有的东西百度也变不出来。在学习嵌入式Linux的过程中肯定会遇到问题,到网上求助或是查找资料,主流系统的信息会比较丰富,而非主流的就少。对自学者而言一切都是靠自己,所以还是选择网上支持信息更丰富的那种平台能减少自己解决问题的时间。毕竟,对初学者而言,最重要的是尽快掌握手中的第一种平台。否则,时间一长热情没了可就前功尽弃了。
市面上的arm9板子里友善(Friendly)和天嵌(TK)的算是比较主流的吧。第一种没用过,第二种感觉还行。更早用的是阳初板子,现在也还用,不过好像已经不怎么卖了。对于板子,如果能有售后支持自然好,没有的话也就那样了。记住自己要好好保养:别让板子上落满了灰尘,也尽量别带电插拔那些端口。重要的是:一定要用起来,板子和家用电器一样,不用才尽出毛病。
软件准备:
学习嵌入式linux要接触的软件种类根据每个人的情况会有所不同,但有三类是肯定会遇到的:boot loader、内核、文件系统。下面简单介绍一下三者的作用以及相互关系。
boot loader的作用是在加电后的最初几毫秒时间里把系统的硬件初始化,完成比如调整cpu的工作频率,从晶振的频率(一般远远低于cpu主频)经过倍频等手段提到主频。以及清理内存等工作。boot loader与硬件相关性很强,所以,没有哪种boot loader能包打天下所有的cpu。即便是uboot这种适应性强的软件也是针对所支持的每一种cpu准备一套程序。boot loader的准备工作结束后将控制权移交给linux内核,内核将建立内存映射、安装低层驱动并初始化相关外设等。文件系统在最后阶段粉墨登场,没有文件系统,我们熟悉的各种linux命令都无法执行:因为这些命令都是以二进制代码文件形式存在,平时由文件系统保管的,执行时才被内核调入内存。如果没有文件系统,内核根本不知命令在哪里,当然也就无从执行。
boot loader的种类现在也不少了,适合学习的有vivi,uboot等等。vivi是韩国mizi公司(已被美国风河收购)的产品,写得短小精悍,但只适合于三星的arm架构cpu,不过现在对vivi的支持随着风河的收购似乎也没落了,因为没看到vivi的后续版本了。这和uboot形成了鲜明的对照:uboot是德国人写的,名号和当年在北大西洋兴风作浪让英国皇家海军恨之入骨的德军潜艇u boat (U艇)系列只差了一个字母。uboot原本只是针对arm架构的,名为armboot,但是逐渐扩展对cpu类型的支持,随着种类的丰富,原来的armboot也就改为uboot了。uboot对于目前常见的arm体系的开发版支持不错,加之版本升级维护也一直在进行,因此使用者在今后一段时间只会增加,相应的资料也会更加丰富,所以对初学者而言,选择uboot作为入门的boot
loader是比较适宜的。对于smdk2410板(国内的2410系列开发板几乎都是以此为母板的),uboot原版只支持从nor flash的启动,学习者如果需要nand flash启动开发板,可以从vivi中复制相关代码到uboot的源码中,这方面网络上的资料很丰富,操作起来并不困难。而且uboot除了2410板之外还支持其它的开发板。初学者可以比较不同开发板的启动代码,会发现较之vivi,视角更丰富。
linux内核现在已经发布到2.6.36了。对于内核版本,不一定要最新的,选择比较新的就可以了,因为最新版本在编译时如果出现问题,在网络上很可能找不到有效的解决办法,而一行一行的从源代码中分析问题对于初学者来说要求又未免太高了。因此,内核选择2.6.10以上的版本就可以了。
因为空间小,开发板一般不用硬盘而用闪存flash存文件。一般选择yaffs文件系统,这种文件系统在设计时就考虑到适应flash器件的特点,能够对文件内容压缩存储,而且可以读写。制作文件系统一般选用busybox工具包。busybox里包括了linux的各种常用命令,初学者可以根据自己的喜好设置所需的各种linux命令,一般而言ps、ls、rm、cd等命令是肯定要有的。
这里也简单介绍一下当前linux开发群体中闻名遐迩的软件包qt:
qt是一个跨平台的图形库,用于开发图形化程序,由挪威TrollTech公司开发,2009年该公司被诺基亚兼并了,qt也就顺理成章成为诺记品系的一员了。虽说qt不光支持linux也支持windows,但windows毕竟已经有成熟的图形化界面和相关开发包,目前qt还是在linux领域里比较受宠。qt使用c++编写,不过它已经屏蔽了大量的c++细节了,使用者不必非要对c++很了解,知道些基础知识就行了。qt的核心是它的connection概念。connection使用起来很方便,最简单的用法是:在定义好的两个method之间确定出哪个是信号的发送方,哪个是信号的接收方(这个method也叫槽函数,slot
method);先运行信号的发送函数,然后就可以等着看槽函数被调用了。由于这两个method可以不在同一个类里,所以使用起来范围很广泛。qt是商业软件,因此在被诺基亚收购前如果使用它进行商业开发必须向TrollTech缴费,不过在被诺基亚收购后可以在一定范围上免费了,即在商业开发中只是调用qt的共享库时是免费的。
学习步骤:
学习嵌入式Linux一定要循序渐进,按照先易后难的次序进行:首先运行跑马灯之类的程序;在点灯成功后可以练习按键中断程序;再分析Uboot程序并动手移植到开发板上;Uboot移植成功后可以试着扩展一些Uboot的功能;然后进行Linux内核的移植;在内核移植成功后再移植文件系统;文件系统移植成功后就可以进入Linux界面了,这时就可以把一些常用库例如QT、SDL等拷入开发板根据自己的兴趣或者工作需要开发各种程序了。
如果没有开发过单片机程序,那么运行跑马灯之前需要知道的一些相关知识是:开发板一般也称作目标机或目标板,而编译程序的机器一般称作宿主机。宿主机就是平常使用的X86微机,操作系统是Windows或是Linux。使用宿主机的原因很简单:目标机的存储空间太小,容不下庞大的编译环境,只好在宿主机上把要运行的程序编译成可执行文件后拷到目标机上。普通的编译器编译出的二进制代码只能运行在和本机使用相同CPU的机器上,而宿主机的CPU和目标机的CPU不同,因此,使用的编译环境也不是普通编译器,而是交叉编译系统,英文简称CCS(Cross
Compiling System)。交叉编译系统编译出的二进制代码能运行在目标机上而不能运行在宿主机上,这就是“交叉”的含义。针对ARM平台的CCS有好几种,一种是ADS(Arm Development System)工具集里携带的CCS,这个工具集运行在Windows平台上;而在Linux平台上运行的CCS是arm-Linux-gcc系列编译器,这个系列中应用较多的是2.95.3、3.3.2、3.4.1、4.1.2、4.3.2等几个版本。宿主机和开发机需要连接起来,这又分为有Boot loader和没有Boot
loader两种情况:没有Boot loader是最原始的状态,这时需要用JTAG电缆(这属于目标板的标准配件)把宿主机的并口和目标板的JTAG端口连接起来。JTAG是联合测试工作组(Joint Test Action Group)的简称,它制定了一种电路板调试接口的国际标准IEEE—1149.1。跑马灯程序和Boot loader都是用这种方式写入到目标板的。当Boot loader在目标板上成功运行后,由于Boot loader已经把目标板的局域网口或USB口配置好了,这时就可以使用网线或USB线连接目标板和宿主机并传递数据了,传输速度要比JTAG高而且操作更方便。这是因为JTAG是为调试而设计的,高速传输数据并不是它关注的重点。
现在谈跑马灯。本文以用s3c2410芯片为CPU的开发板作例子:首先要仔细研究目标板的电路图文件,找到电路图中跑马灯的电路:其实就是连接到CPUI/O端口的一组发光二极管。但是不同厂商的目标板选择的端口地址可能不同,点亮二极管的电平也可能不同,所以要具体情况具体分析。现在根据分析出的端口地址和电平高低来修改跑马灯程序,如果刚好一致就不需要修改了。然后,编译这个程序。得到可执行文件后,从JTAG口把文件写入到目标板的闪存里。如果一切正常,按下板子的复位键,就可以看到跑马灯被点亮了。这个程序虽简单但是很有用处,详情后面再说。如果出现了问题,就要使用调试器。在跑马灯这个阶段较多使用AXD调试器,就是ADS携带的调试器。其它的调试器能不能使用呢?也可以使用,但相关资料少,不太适合初学者。AXD调试器是图形化界面的,很好用。记住要和JTAG一起才能使用。通过使用单步调试和查看内存,可以找出问题根源。
学习按键中断程序要注意查看CPU的相关资料,搞清楚中断向量表、CPU支持的中断种类,比如内部中断、外部中断等概念,还有相关的寄存器例如中断屏蔽寄存器和中断挂起寄存器的作用。对于缺乏硬件开发经验的学习者,从这时要学会查看芯片的数据手册即datasheet,手册会详细介绍芯片各个端口、各个寄存器的功能,用法。有了编译和调试跑马灯的经验,掌握中断程序并不是很困难。
Uboot移植是学习嵌入式系统的关键一步。为何要移植呢?一方面因为原版的Uboot对于采用三星s3c2410/2440为CPU的系列板卡仅仅支持从Nor flash启动,而市面上的一部分开发板上没有nor flash,只有nand flash。它比nor flash容量大,且便宜,但不能直接执行二进制文件。所以需要改造Uboot,添加从nand flash启动的功能(这段代码是从vivi中复制来的)。另一方面,没有移植过Uboot也不可能真正理解Uboot的机制和作用,所以从学习嵌入式Linux的角度看,移植vivi
的nand flash启动功能到Uboot是必须的。介绍移植细节的文章,网上很多,严格地执行就可以成功。不过,在移植过程中出些问题并不是坏事,在解决问题的过程中可以加深对于Uboot 的理解。在移植过程中,由于Uboot功能多,代码结构复杂,修改中容易丢这忘那,导致编译出来的二进制代码写入目标板后不能启动,而排查故障的时候又缺乏直观的调试手段,建议利用跑马灯程序协助排查:把点灯程序在源码中移动位置,待编译后观察二极管是否点亮,如跑马灯移到某位置后二极管未被点亮,这里就有可能是故障区域。这种方法在Uboot启动的初始阶段特别有用:因此时串口还没有配置好,无法从串口输出调试信息。在完成Uboot移植后,就可以扩展一些功能了,比如增加I2C或是yaffs功能模块。
内核移植的难度并不比Uboot大,而且内核已经很成熟,编译成二进制代码后都没大问题,如不能正常运行很可能是配置上的毛病。要注意Uboot中设置的启动参数与内核能否正常启动有很大关系。这里设置了内核的串口,文件系统等参数。常见的一个故障是串口名称设置错误,导致内核启动后串口不再输出启动信息。调试内核比Uboot强的是:移植过程中出问题一般可以直接在内核代码中加入printk(“”)语句输出相关信息。Printk是在调试内核时惯用的手段,用法与printf(””)相似,但后者需要libc库才能使用,而内核是没有libc库的。
移植busybox工具包生成的文件系统是最后一关了,但往往由于Uboot设置的参数不正确而使得内核找不到文件系统。但Busybox很成熟,故文件系统本身不怎么会出问题。所以出了问题的一个建议是仔细检查自己的Uboot启动参数:有时在参数中多写一个等号就导致内核不能识别文件系统。当然,文件系统的类型一定要与内核中选择的类型匹配,否则也不能识别。NFS文件系统设置起来比较简单,而且由于实际存储位置处于宿主机上,空间不受限制,因此不少初学者选择从NFS入手。但要注意宿主机的防火墙可能会阻断NFS的RPC(远过程调用)机制,因此不要忘记对防火墙的设置。
当文件系统成功加载后,Linux的超级用户提示符 # 就出现了。一个初学者独自做到这一步的时候就已经具备了开发嵌入式Linux最基本的素质了。不过万里长征只是走完了第一步,而很多人在这之后就不知道该干什么了。记住,不要满足于运行几个开发板配套光盘里的示范程序,那些都是别人的劳动成果,运行得再好也只是说明别人做得不错。要根据自己的兴趣、工作需要定一个明确的开发目标,抽出时间努力去实现它,从这一刻起,增长的才是属于自己的能力、经验和见解。
(完)