嵌入式Linux驱动开发之helloword心得

2019-04-13 11:18发布

自从选择了物联网这个专业,智能XX的字样牵动着每一个学习这个专业的孩子。 大家兴致勃勃的来到了学校,结果一切想象和自己的设想并不一样。想象中的各种智能般梦幻的场景变成了真实的高数/电路/模电等等诸如此类!不知道这个世界什么时候变得如此的浮躁,当大家的一段时间的努力看不到结果的时候就往往会不太感兴趣,模电大家都没听懂,于是大家自我安慰tmd学这玩意到底干什么?本人当初也是这样,可是到了后来接触了单片机,接触了应用电路的设计才知道那些课程那个没用啊!当初还是too young,too simple呀! 这个学期也将要过去了,明年就开始去实习啦!趁着现在就实现自己的物联网梦想吧!没错,就是比较恶俗的智能家具系统。
考虑到单片机局限比较大,遂向同学借了一块友善之臂的板子,本人第一次接触arm,单片机系自学,文中又搞笑的地方大家笑笑就行了,如果能给予指正,那就更好了。先来实现一个比较简单的功能,即通过web远程控制灯光的亮灭。要实现这个功能,我们需要在嵌入式linux的平台上写led的驱动,要学习驱动我们直接写led的驱动可能难度比较大,而且不易成功,所以我们先用著名的helloworld来熟悉环境。 现在,越来越觉得和计算机打交道,要有两条准则:1 思路要清晰  2 实现过程要一步一步。因为计算机是机器,0和1的世界,程序过程中稍不注意就会错误百出。所以写程序过程中一定要步步为营,一点一点测试,一点一点推进,这样看似很慢,却非常有效也容易成功。好了,不为我写hellow辩护了。其实我在写helloworld的时候也是做准备了,那就是在pc端上已经成功的测试过了。 在补充一点,我们写的驱动程序最终都是要加载进内核的,我们有两种方法添加到内核中。第一种是直接编进内核里,第二种是动态编进内核里。动态加入内核就是我们把自己的驱动程序看成一个模块,然后把这个模块加载到内核内。直接编译进内核就是这个模块和内核一起编译,如果有什么这个驱动有什么问题的话,我们还需要调试编译整个内核,因此刚开始学习的时候我采用了模块化的动态加载内核。 下面就开发过程中的一些地方做一些记录: 1 在pc机上为目标机编写驱动程序的.c文件应该放在哪里比较好? 这个应该放在目标机内核的目录。比如我的是友善之臂的板子,因为helloworld是字符设备,因此hello.c就建在了/opt/FriendlyARM/mini2440/linux-2.6.32.2/drivers/char的目录内。为什么要建在这里,因为这样我们可以借助与char内的makefile来编译出模块,也就是说我们不必自己单独写makefile,有点抱大腿的赶脚。 2 最简单的helloworld模块是什么样的?   复制代码 1 #include 2 #include 3 static int __init mini2440_hello_module_init(void) 4 { 5 printk("Hello, world ! "); 6 return 0; 7 } 8 static void __exit mini2440_hello_module_cleanup(void) 9 { 10 printk("Good-bye! "); 11 } 12 module_init(mini2440_hello_module_init); 13 module_exit(mini2440_hello_module_cleanup); 14 MODULE_LICENSE("GPL"); 复制代码   第一行和第二行就不说了,因为我们用到了内核的相关函数以及模块的一些东西,因此必须要声明。 最后一行是内核2.6以上版本建议大家把模块的lincense带上。 在我们动态加载和卸载模块的时候,我们需要init_module和exit_module这两个函数来加载,而上面的代码中并没有。原因在哪里呢? 原来是module_init和module_exit在作怪。以module_init为例,这个函数有两个功能,一个是验证传入的参数是否为正确的模块格式,另一个是将参数改名字为init_module。这样模块就能被成功的加载了。 虽然上面的module是个空架子,但是也可以让我们对模块有个感官的认识。 3 编译这个模块前需要做那些准备? 第一步,将模块添加到内核菜单,这样在我们启动内核菜单的时候才能对我们新添加的模块进行配置。 复制代码 1 config HELLO_MODULE 2 tristate "hello module" 3 depends on MACH_MINI2440 4 default m if MACH_MINI2440 5 help 6 hello module 复制代码 这个模块就是比照着周围的模块写的,当然了,用户手册上也是详细步骤的。 简单看下这段代码,tristate就是这个模块在内核菜单中显示的名字。 depends on 是依靠的平台,下面是说如果依靠这个平台默认的是动态加载到内核。 添加完以上的代码,回到linux内核目录下,make menuconfig调出内核编译菜单。在字符设备的地方可以找到新添加的hello模块。 第二步,添加完代码后,还需要将编译文件Makefile和源码联系起来,这样执行makefile的时候才能找到源码进行编译。因为我们是在内核下抱大腿写的makefile,所以我们不必重新写makefile,只需要在字符设备的makefile文件中添加这个关系就可以了。 1 obj-$(CONFIG_HELLO_MODULE) += hello.o 这样一来,make的时候就能找到hello模块的源文件啦! 4  编译模块和检验模块 在2.6以后版本的内核中,我们只需要在内核目录下执行make modules便可以编译模块。 在编译模块后一定要做最重要的检验工作,可以用modinfo命令查看生成的.ko文件的信息。最重要的是核对.ko文件的vermagic:   所显示的内核信息和你目标板的内核信息是否一致,这点灰常重要,否则即便你移植到了目标板,也不能加载成功。 5 加载测试 好了,现在我们该检验结果啦,鸡冻啊!在模块当前目录,用insmod来加载我们的hello.ko模块。 什么居然神马也木有,心顿时凉了半截。。。。。。 还好不是神马大问题,上面文章已经说过啦!printk是内核级别的函数,查看需要输出:dmesg | tail 另外,也可以采用lsmod指令来验证模块是否加载成功。 最后刻意验证了这个模块的生命周期,退出终端重新进入,查看模块还在。重新启动后发现模块不见鸟。   至此,所有的工作都完成鸟。第一个在嵌入式设备上开发的第一个鸡肋驱动就完成了。在整个过程中觉得,只要一步一步来问题都是可以解决的,机器是非常认真的,只要我们按照机器一样的思维认真的去一步一步解决问题的时候,发现你就可以hold住机器,md,以后会不会变成了一个和机器一样的人。。。。 发现了为什么大家伙都说嵌入式入门灰常难,其实就是灰常繁,大家可以看上面那么多东西基本上没有涉及到和智商有关的东西,全部都是步骤程式化的,当自己没有走完一个流程的时候发现这玩意太难了,当走完一个流程后会觉得,nima,什么玩意。。。。。 问题解决前前后后大概一两天吧!发现越是纠结的时候长的问题,当结果出来那一刻会更兴奋,越是容易的问题,解决后没有一点兴奋的赶脚! 又扯远啦!马上进入真枪实弹的驱动-led驱动啦!   加油!md,我是谁,这么帅气的男淫!   最后觉得linux里面的Makefile和Kconfig真强大,linux内核是一个大工程啦,通过Kconfig一层一层的去建立菜单,通过Makefile文件来批量的去编译,真是太强大了。这样以来觉得linux虽然做个什么东西都特别麻烦,但是更能让我们清楚的去了解系统的工作原理,linux和单片机真不愧是学习操作系统和计算机组成原理的法宝啊! 当然,过程中还会有很多的错误,这里没有一一列出。下面推荐一些这个过程中可能帮助我们的一些网友 的博客: 如果你的内核菜单没有出现你添加的模块,请参考 http://blog.csdn.net/hadise/article/details/6222538 inti_module/exit_module的详细分析,请参考 http://www.embedu.org/Column/Column517.htm 建议第一次接触的朋友可以现在pc机上的系统去实现一下,这样过渡一下效果会更好一点,下面是pc机上实现helloworld驱动的链接: http://www.cnblogs.com/heat-man/articles/4174899.html 最后想借此认识一些嵌入式linux的同伙,因为大家一走才能走的更欢乐,才能走的更远! 如果那个大牛有什么好的建议和方法,那这篇文章就赚大发啦!