Linux内核编译、启动和相关驱动构建
01
修改与编译内核
前面小哥主要是跟大家讲解了uboot的烧录、使用等等,而对于嵌入式Linux环境而言其实主要是分为三大块 : uboot,Linux Kernel(内核),文件系统,当然高版本的内核存在设备树文件等等,不过感觉还不足以认为是一大块,只能说是驱动的一部分。
那么本文就主要是通过uboot把Linux系统运行起来,而挂载根文件系统并进入终端命令行估计得到下一篇文章了。
如果大家手上有已经移植或制作OK的Linux内核image和文件系统,只需要将他们烧录到板子上的对应存储位置上,然后设置uboot中的一些启动参数即可完成整个Linux系统的启动。
然而每块开发板的外设总会存在或多或少的差异,这样就需要开发人员修改相应的与硬件交互的代码(Linux驱动),或者调整各部分在内存分布的大小与地址等,以适应新的硬件平台,这个过程就叫做移植。
看起来移植很高大上,相对Linux内核这么庞大的代码而言还是小部分,毕竟Linux系统在开发过程中都会考虑与硬件相关部分进行的分离,并且大部分开发板都会参考官方发布的单板来进行布局。
好了,那么小哥就大致讲解一下这个过程。
02
移植与烧录内核
这里使用的是百问网科技超级老的jz2440开发板,他们提供了对应内核版本的适配其开发板的移植补丁,也就是说在Linux-3.4.2原始的内核源码上通过提供的补丁包自动化的进行代码的修改以适应当前开发板,从单片机的角度看来就是修改一些引脚,配置驱动等等。
然而小哥手头板子的NandFlash上存在坏块,uboot,内核、文件系统等都会烧录到NandFlash上,你可以认为就是单片机的Flash,不过单片机的Flash大部分为NorFlash,而由于存在坏块所以对相关的分区进行调整,否则坏块会导致相应的烧录文件不完整而启动失败。
由于后面打算移植QT,而之前拿到的补丁包没有移植好触摸驱动和网卡驱动,即使打了之前拿到的补丁还需要继续进行相关代码上的移植,那慢慢来,先把一些适配的补丁打上:
1tar jxvf linux-3.4.2.tar.bz22cd linux-3.4.2/3patch -p1 <../linux-3.4.2_100ask.patch
Linux-3.4.2_100ask.patch就是百问网提供的补丁包,所谓的补丁包不是什么高级东西,就是通过patch命令根据.patch文件的修改描述,直接添加和修改内核中文件的内容,与我们手动修改本质上是一样的。
不过它把所有的差异都整理成了一个文件,实现了一种一键修改源文件工程的目的,因为内核源码官方都可以获取,而我们只需要一个补丁包就可以通过打补丁把官方源码改成适配自己单板的源码,美滋滋~
03
内核分区修改
前面uboot也存在这样的一个默认分区表kernel部分分了36M,同样内核中的默认分区表中的内核区域也分配了36M。
很多人该问了为什么有两个默认的分区表?
前面小哥讲解了存储地址与运行地址,Linux内核中的相关操作都是基于Linux内核中的分区表,比如uboot向Linux内核的传参中:
就是告诉内核加载文件系统需要在MTD的第三个区分进行加载,那么uboot在进行文件系统烧录的时候就需要把烧录文件放到第三个分区地址区域,且与内核中的分区地址一致,不然Linux内核挂载文件失败。
其实对于uboot中的分区,仅仅只是为了部署Linux环境而设置的,一旦启动内核了uboot的生命周期就结束了,比如之前我们在uboot中使用nand write 源地址 目的地址 长度,我们也可以直接简化为对对应分区的操作:nand write 0x30000000 kernel。从上图中uboot的环境变量bootcmd中使用的kernel,也是类似的使用方法。
那为什么要把Linux内核分区设置36M呢?
其实我当前编译的Linux内核映像也才几M的大小,主要是Linux内核中这种分区方式是连续的,前一个区的结束地址是后一个分区的起始地址,然而经过小哥测试,刚好文件系统分区前面一部分NandFlash存在坏块,这样会导致文件系统不完整,后期挂载会失败,所以这样通过加载内核分区以使得文件系统分区地址后移的方式来规避掉这些坏块,同时对于内核部分由于其烧录文件不大,有效文件不会烧录到坏块部分,算是一个捷径。
03
网卡驱动修改
uboot虽然可以进行TFTP网络服务,但是并不意味着Linux内核也可以正常使用,他们两个驱动是分开的,一旦uboot成功启动内核以后uboot的生命周期就结束了。Linux内核也必须拥有正确的网卡驱动才能够使用相应的网络服务,其实这跟我们windows系统是一样的,当我们电脑没有相应的驱动,相应的硬件也是无法使用的,比如最好用的网络文件系统NFS。
由于我们使用的是dm9000网卡,且mini2440单板已经得到很好的支持,所以我们参考直接移植过来即可,主要是填充相应的平台设备结构体以描述dm9000网卡资源并注册与驱动匹配。
首先要包含dm9000网卡的头文件,以便使用到其头文件中的宏定义或者数据,然后使用resource结构体描述dm9000网卡的一些资源,包括IO资源和中断资源,并且使用平台设备platformdevice来描述dm9000网卡,以便后续总线上device与driver匹配在,这样也就实现了设备与驱动的分离。
最后把平台设备作为smdk2440众多设备初始化中的一员加入到initdata中,以便系统启动的时候便加载设备和匹配驱动。
有了以上移植,基本上单板的网卡驱动就搞定了,相应的网络相关的服务就可以使用了,不像单片机那样你还要直接移植相应的网络协议栈等等,一旦搞定了驱动,基本上跟windows系统上面开发应用程序大同小异~
04
LCD驱动程序修改
对于LCD的支持其实与前面网卡驱动修改其实是类似的,还是采用Linux设备与驱动分开的思想,基本上驱动部分不用太多修改,仅仅只需要把驱动部分根据Linux提供的框架进行相应的描述填充即可,说得直白一点就是填充再赋值结构体,你可以认为这些都是固定的套路吧~
这里小哥使用的是4.3寸的屏幕,所以进行如下填充与配置:
同样因为2440是自带LCD控制器的,唯一要做的就是把LCD控制这块以及LCD屏幕的属性描述清楚,如上面各个结构体设置所选择LCD的相关属性,比如尺寸,刷新时序等等,最后把整个结构体填充好并注册。
同时记得确认一下menconfig里面是否已经选择了LCD_FB,并选择编译到Linux内核,执行命令:
1make menuconfig CROSS_COMPILE=/home/book/WorkSpace/Qt/src/arm-linux-gcc-4.4.3/opt/FriendlyARM/toolschain/4.4.3/bin/arm-linux- ARCH=arm -j8
1Device Drivers ---> 2 Graphics support ---> 3 Support for frame buffer devices ---> 4 S3C2410 LCD framebuffer support
配置好以后,记得save到对应的.config即可,这样编译前的配置才能够生效。
05
触摸驱动编译与移植
玩单片机的小伙伴都知道电阻触摸屏,就是通过获得屏幕横纵的AD采样值最终来定位屏幕上的位置,而S3C2440也是存在触摸屏的外设接口的,我们通过配置触摸屏外设接口,即可驱动触摸屏获得相应的ADC值最终定位到屏幕上所点击的位置。
在Linux中对于鼠标、触摸等等都属于输入设备,所以这类驱动都可以归为输入子系统input,那么我们只需要注册一个输入子系统即完成了触摸屏驱动。
触摸驱动程序主要分为这样几步,首先获得一个输入设备结构体,然后根据触摸的特性进行相关的事件的配置,因为输入系统都是以事件的方式上报给系统,不同的事件当然配置也就不同,配置好了以后就把输入设备结构体注册到系统,以便设备识别。
而当所配置的事件一旦条件触发,就会把触发信号和数据通过input_report上报给系统,供系统使用,所以单片机你想做得通用化,也可以直接这么玩,不过考虑到单片机的简洁,还是慎重考虑~
虽然我们可以直接把该驱动程序编译成.ko驱动程序,可是这样需要每次内核启动完成以后就需要重新加载驱动,有点麻烦,所以考虑把它编译到Linux内核中。
要把驱动程序添加到内核需要做三件事:
1)添加源码到相应目录;
2)在相应的Kconfig文件中增加编译选项;
3)在makefile中增加相应的编译项。
前面我们大致编写了源码并且放到了相应的目录,这里就只需要完成后面的两项,这两项可能相应的语法规则刚开始并不是很熟,不过可以查阅相应的知识补充,也可以直接照着其他touch驱动类似编写即可。
比如s3c2410的Kconfig如下编写:
于是我们可以模仿着把S3C2440类似的添加到后面:
第二步完成,接下来在当前目录的Makefile添加编译项目:
照着S3C2410的来即可。
虽然我们完成上面的三步,但只是完成了能够提供选择的是否编译进内核的驱动选项,在menuconfig菜单中你可以看到,而到底最终是否编译到内核,还需要在menconfig菜单中进行配置选择并保存到config中。
1Device Drivers ---> 2 Input device support ---> 3 Touchscreens --->
至此,触摸的驱动就编写并添加到了内核中。
06
内核的编译与烧录
一切准备就绪,那就是编译内核了,编译内核的目的就是为了获得Linux kernel映像文件,最终烧录到板子上的内容。
如下是我使用的编译命令,由于没有把相应的路径放到环境变量,所以这里就制定了编译器路径,比较长。
1make uImage CROSS_COMPILE=/home/book/WorkSpace/Qt/src/arm-linux-gcc-4.4.3/opt/FriendlyARM/toolschain/4.4.3/bin/arm-linux- ARCH=arm -j8
如果编译过程中遇到什么不理解的error,基本上都是根据所报错误的提示,进行网络查找,一般都可以得到解决,因为大家都遇到过~
最后顺利编译内核成功,如下是编译结果:
以上的输出信息,我们也可以了解到想要的uImage所在路径,以及文件的大小,类型和入口地址等。
我们借助uboot直接通过TFTP服务把uImage先下载到SDRAM中,然后在使用NandFlash命令烧录到Flash中对应的分区即可。
其实烧录过程在往期的uboot中已经说得很详细了,这里主要提两点:
1)由于uboot是一个单任务的裸机程序,所以连接好网线以后,你的电脑网络状态还是没有连接的,所以需要uboot主动发起网络,电脑端就会有网络链接状态显示了。
2)在进行网络通信过程中,要记得关掉电脑主机的防火墙,以便链接失败。
我们使用TFTP服务,需要设置好服务器IP,也就是我们的电脑主机IP地址,然后通过TFTP命令获得相应的内核文件,实验结果如下:
这样我们就把uimage下载到了SDRAM的0x30000000的位置,接下来我们需要把他烧录到对应的NandFlash的Kernel分区上,使用如下命令:
1nand erase.part kernel2nand write 0x30000000 kernel
重新启动开发板,即可看到成功启动了内核:
但是最终由于我们还没有为Linux系统构建文件系统,系统启动还需要一些必备的启动文件和工具,最终会报错。
不过我们今天的目标达到,Linux内核得到了启动,并且移植好了我们想要的一些驱动。