天天看点

基于JZ2440开发板的第一个嵌入式Linux驱动程序

经过两个星期的学习,终于成功写出了第一个驱动程序->点亮LED小灯;感觉听上去很简单似的,哈哈,楼主我可是经过了无数次实验才成功移植U-boot、Linux内核、最小根文件系统,并完成了LED控制的驱动,过程经历了多次百度CSDN论坛,还有请教韦老师,最后能够在开发板上完全运行,并对Linux驱动开发有了初步的了解 ;这里补点鸡汤:发这篇文章的初心出于让更多人了解下相关领域,让相关专业、领域的人士知道嵌入式Linux驱动的开发流程;空间就应该多一点这种正能量的文章,学到是自己的,大学的教学很少有这种嵌入式Linux驱动的开发,不喜勿喷!

该文属自身感受和开发过程中遇到的问题在此和大家分享一下 :

U-boot基础:很多人可能会说,现在直接用基于windows的MDK就可以完成开发(例如:keil),那为什么还要用基于Linux的环境开发嵌入式呢?Keil这种集成的MDK适合中小型公司开发,不适合大公司协作,它隐藏了太多的技术细节,比如:单板上电后是如何执行程序的?是不是必须要有main函数?在C调用函数之前需要对硬件进行什么操作?中断服务子程序是如何执行的?等等这写问题,我想对技术有浓厚兴趣的人一定很想了解。我来依照上述问题给出自己的一些想法 ;单板上电后从程序链接地址(依赖.lds、Makefile文件)处执行程序,涉及到地址就要用汇编,板子上电的启动需要依靠start.S这个启动文件 ,这个文件里包含各硬件的初始化,如:系统时钟初始化(如果没设置时钟板子以外部晶振12M速度运行,S3C2440),关看门狗,初始化SDRAM、初始化NAND FLASH(如果为NAND启动)、代码重定位(从FLASH将代码全拷贝到SDRAM,为什么要进行代码重定位呢?如果程序从NOR FLASH启动,那可以直接读取代码到处理器,但没进行一些操作不能写数据到NOR FLASH,并且NOR FLASH上运行程序效率会大打折扣;如果是NAND FLASH启动,系统上电会把NAND FLASH前4K内容拷贝到片内SRAM(stepping-stone)运行,程序小于4K还可以,那大于4K呢?岂不是要丢失程序代码,不能实现功能,所以需要进行代码重定位)、初始化栈指针 ldr sp, =0x34000000(栈向下增长,将栈设置到内存顶边境处 C函数的调用需要进栈出栈);那上面这些有关硬件启动的就是bootloader的第一阶段,第二阶段是将内核从NAND FLASH读出到SDRAM,并设置启动参数(向内核传递内存、命令行参数、内核入口等),最后跳转到内核处执行。U-boot在内核启动后便不会起作用了。U-boot是一种很强大的bootloader,它可以支持多种单板,可以去服务器ftp://ftp.denx.de/pub/u-boot/下载压缩包进行解压缩、修改、裁剪配置自己的bootloader。

这里总结一下,U-boot的最终目的为启动内核。

内核的启动:内核可以到官网https://www.kernel.org/下载;为什么需要启动内核呢?Linux内核的最终目的是挂接根文件系统,让上层软件开发人调用内核标准接口。首先,解压内核

基于JZ2440开发板的第一个嵌入式Linux驱动程序

解压后的源码如下:

基于JZ2440开发板的第一个嵌入式Linux驱动程序

想知道内核的组成结构可以参考它的顶层Makefile、.config文件。

将内核打补丁:

基于JZ2440开发板的第一个嵌入式Linux驱动程序

直接使用厂家的配置文件

基于JZ2440开发板的第一个嵌入式Linux驱动程序

在Linxu终端执行make menuconfig

基于JZ2440开发板的第一个嵌入式Linux驱动程序

配置完成后执行make命令

基于JZ2440开发板的第一个嵌入式Linux驱动程序

这里可能会遇到上面的问题,更改配置如下

基于JZ2440开发板的第一个嵌入式Linux驱动程序

不要选这项

基于JZ2440开发板的第一个嵌入式Linux驱动程序

3.4.2版本以上的内核不必更改此项。

再执行

基于JZ2440开发板的第一个嵌入式Linux驱动程序

等待内核编译完成

基于JZ2440开发板的第一个嵌入式Linux驱动程序

内核映像文件在这个文件夹内

基于JZ2440开发板的第一个嵌入式Linux驱动程序

内核映像文件uImage包括64字节头部(包含内核版本、加载位置、大小等信息)和zImage(真正的内核 压缩过的内核)。这里的vmLinux是未压缩的内核映像,为elf格式,用于kernel-debug,不能直接加载,不可以作为启动内核。

基于JZ2440开发板的第一个嵌入式Linux驱动程序

编译出uImage后用u-boot烧写Kernel

基于JZ2440开发板的第一个嵌入式Linux驱动程序

注:如果没有烧写根文件系统,启动时会卡死在内核。

制作根文件系统:

根文件系统的任务是运行应用程序。使用busybox制作根文件系统,busybox是一个开源的工具,下载busybox源码并解压

基于JZ2440开发板的第一个嵌入式Linux驱动程序

安装方法:用Vi编辑器打开INSTALL文档

基于JZ2440开发板的第一个嵌入式Linux驱动程序

根据文档提示,我们知道这个安装步骤为红圈圈中的三步。

基于JZ2440开发板的第一个嵌入式Linux驱动程序

执行命令配置busybox

基于JZ2440开发板的第一个嵌入式Linux驱动程序
基于JZ2440开发板的第一个嵌入式Linux驱动程序
基于JZ2440开发板的第一个嵌入式Linux驱动程序

选中Tab代码补全功能。

在make之前修改Makefile

基于JZ2440开发板的第一个嵌入式Linux驱动程序

加上交叉编译脚本,不然会出错。

编译完成后千万别直接make install

基于JZ2440开发板的第一个嵌入式Linux驱动程序

这样会破坏掉主机系统。

应该这样

基于JZ2440开发板的第一个嵌入式Linux驱动程序

加上红圈里的东西再安装。安装完成后我们去到安装目录

基于JZ2440开发板的第一个嵌入式Linux驱动程序

下面我们需要导入glib库,库中包含一些动态库和静态库,我们只需要动态库,可以从制作交叉编译工具文件夹下复制过来到lib目录

基于JZ2440开发板的第一个嵌入式Linux驱动程序
基于JZ2440开发板的第一个嵌入式Linux驱动程序
基于JZ2440开发板的第一个嵌入式Linux驱动程序
基于JZ2440开发板的第一个嵌入式Linux驱动程序
基于JZ2440开发板的第一个嵌入式Linux驱动程序

上电自动挂接文件系统,无需手动挂载。

烧写根文件系统到单板,开始编写驱动程序:

基于JZ2440开发板的第一个嵌入式Linux驱动程序

驱动的核心为该结构体,驱动的框架如下:

基于JZ2440开发板的第一个嵌入式Linux驱动程序

实现这两个接口,open函数为初始化LED硬件接口用,write函数为LED具体操作

基于JZ2440开发板的第一个嵌入式Linux驱动程序

我们写的内容需要告诉内核,那内核怎样才知道有这个驱动呢?这就需要写一个驱动注册函数和卸载函数

基于JZ2440开发板的第一个嵌入式Linux驱动程序

仅仅这样内核也不知道该驱动是否被加载,所以还需要一个宏来实现入口和出口:

基于JZ2440开发板的第一个嵌入式Linux驱动程序

最后再写出测试函数就可以运行该驱动了

基于JZ2440开发板的第一个嵌入式Linux驱动程序

还要编写Makefile(Linux编译工具)

基于JZ2440开发板的第一个嵌入式Linux驱动程序

这里注意选择linux内核版本,由于用的是2.6.22.6内核的接口,所以换成其他版本的内核可能编译不通过,会有错误。

再编译测试程序,编译测试程序时要用和根文件系统下lib库所用的版本一样的交叉编译器编译,最后才会顺利实现。

基于JZ2440开发板的第一个嵌入式Linux驱动程序

我这用的是3.4.5的,由于上次用了4.3.2的交叉编译器,导致最后运行失败,搞了很久才弄出来,编译后会产生这两个文件 把它们拷贝到根文件目录下

基于JZ2440开发板的第一个嵌入式Linux驱动程序

最后在单板上装载驱动!

基于JZ2440开发板的第一个嵌入式Linux驱动程序
基于JZ2440开发板的第一个嵌入式Linux驱动程序

这样就可以测试了。

注:如何修改arm-linux-gcc 交叉编译器版本

基于JZ2440开发板的第一个嵌入式Linux驱动程序
基于JZ2440开发板的第一个嵌入式Linux驱动程序

红圈部分为交叉编译器PATH,再执行

基于JZ2440开发板的第一个嵌入式Linux驱动程序

继续阅读