大家兴致勃勃的来到了学校,结果一切想象和自己的设想并不一样。想象中的各种智能般梦幻的场景变成了真实的高数/电路/模电等等诸如此类!不知道这个世界什么时候变得如此的浮躁,当大家的一段时间的努力看不到结果的时候就往往会不太感兴趣,模电大家都没听懂,于是大家自我安慰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 <linux/kernel.h>
2 #include <linux/module.h>
3 static int __init mini2440_hello_module_init(void)
4 {
5 printk("Hello, world !\n");
6 return 0;
7 }
8 static void __exit mini2440_hello_module_cleanup(void)
9 {
10 printk("Good-bye!\n");
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和单片机真不愧是学习操作系统和计算机组成原理的法宝啊!