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

自从选择了物联网这个专业,智能XX的字样牵动着每一个学习这个专业的孩子。

大家兴致勃勃的来到了学校,结果一切想象和自己的设想并不一样。想象中的各种智能般梦幻的场景变成了真实的高数/电路/模电等等诸如此类!不知道这个世界什么时候变得如此的浮躁,当大家的一段时间的努力看不到结果的时候就往往会不太感兴趣,模电大家都没听懂,于是大家自我安慰tmd学这玩意到底干什么?本人当初也是这样,可是到了后来接触了单片机,接触了应用电路的设计才知道那些课程那个没用啊!当初还是too young,too simple呀!

这个学期也将要过去了,明年就开始去实习啦!趁着现在就实现自己的物联网梦想吧!没错,就是比较恶俗的智能家具系统。

考虑到单片机局限比较大,遂向同学借了一块友善之臂的板子,本人第一次接触arm,单片机系自学,文中又搞笑的地方大家笑笑就行了,如果能给予指正,那就更好了。先来实现一个比较简单的功能,即通过web远程控制灯光的亮灭。要实现这个功能,我们需要在嵌入式linux的平台上写led的驱动,要学习驱动我们直接写led的驱动可能难度比较大,而且不易成功,所以我们先用著名的helloworld来熟悉环境。

现在,越来越觉得和计算机打交道,要有两条准则:1 思路要清晰  2 实现过程要一步一步。因为计算机是机器,0和1的世界,程序过程中稍不注意就会错误百出。所以写程序过程中一定要步步为营,一点一点测试,一点一点推进,这样看似很慢,却非常有效也容易成功。好了,不为我写hellow辩护了。其实我在写helloworld的时候也是做准备了,那就是在pc端上已经成功的测试过了。

在补充一点,我们写的驱动程序最终都是要加载进内核的,我们有两种方法添加到内核中。第一种是直接编进内核里,第二种是动态编进内核里。动态加入内核就是我们把自己的驱动程序看成一个模块,然后把这个模块加载到内核内。直接编译进内核就是这个模块和内核一起编译,如果有什么这个驱动有什么问题的话,我们还需要调试编译整个内核,因此刚开始学习的时候我采用了模块化的动态加载内核。

下面就开发过程中的一些地方做一些记录:

1 在pc机上为目标机编写驱动程序的.c文件应该放在哪里比较好?

这个应该放在目标机内核的目录。比如我的是友善之臂的板子,因为helloworld是字符设备,因此helloworld.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和单片机真不愧是学习操作系统和计算机组成原理的法宝啊!

当然,过程中还会有很多的错误,这里没有一一列出。下面推荐一些这个过程中可能帮助我们的一些网友 的博客:

如果你的内核菜单没有出现你添加的模块,请参考

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

时间: 2024-12-31 03:37:15

嵌入式Linux驱动开发之helloword心得的相关文章

linux驱动开发之HelloWorld

最近实习,公司项目搞的是平板开发,而我分配的任务是将驱动加载到内核中. 准备工作,必要知识了解:加载有两种方式,一种是动态加载和卸载即模块加载,另一种是直接编译进入内核:Linux内核把驱动程序划分为3种类型:字符设备.块设备和网络设备.字符设备和块设备可以像文件一样被访问.它们的主要区别不在于能否seek,而是 在于系统对于这两种类型设备的管理方式.应用程序对于字符设备的每一个I/O操作,都会直接传递给系统内核对应的驱动程序:而应用程序对于块设备的操作, 要经过系统的缓冲区管理,间接传递给驱动

(56)Linux驱动开发之二

内核基础 1.linux内核主要是由进程调度.内存管理.虚拟文件系统(字符设备驱动和块设备驱动).网络接口(网络设备驱动)和进程通信5个子系统组成的. 1)进程调度控制系统中的多个进程对CPU的访问,使得多个进程能在CPU中"微观串行,宏观并行"地执行. 2)内存管理的主要作用就是控制多个进程安全的共享主内存区域,当CPU提供内存管理单元时,linux内存管理完成为每个进程进行虚拟内存到物理内存的转换.一般而言,linux的每一个进程享有4GB的内存空间.0~3GB为用户空间,3~4G

linux驱动开发之misc类设备介绍

1.什么是misc设备? misc是英文的简称,中文名一般叫做杂项设备/杂散设备. 我们知道大部分的设备都有一个明确的分类class,有一些设备进行分类时不太好分,我们不知道一些设备到底应该分到哪一类设备中去,所以最后将这些不知道分到哪类中的设备给分到misc设备中,也就是分到了杂散类中.像蜂鸣器还有ADC设备都被分到了misc设备杂散类设备中.杂散类设备对应的是一个类的概念.随意进到系统中的/sys/class目录中就会看到一个misc目录,这个misc就是杂散类设备,进入到misc目录中,我

linux驱动开发之GCC问题

最近正在学习驱动开发,进展到字符设备驱动开发阶段. 先不多说,首先把刚看的一篇学习驱动步骤的帖子记录如下: 1. 学会写简单的makefile 2. 编一应用程序,可以用makefile跑起来 3. 学会写驱动的makefile 4. 写一简单char驱动,makefile编译通过,可以insmod, lsmod, rmmod. 在驱动的init函数里打印hello world, insmod后应该能够通过dmesg看到输出. 5. 写一完整驱动, 加上read, write, ioctl, p

linux驱动开发之framebuffer驱动介绍

framebuffer是linux里面的显示设备.在驱动底下如何操作lcd实现图形的显示. 1.什么是framebuffer? (1)首先想一下在裸机中我们是怎么操作LCD的. Soc内部有lcd的控制器,Soc外面有lcd的驱动器,lcd的驱动器连接着lcd的屏幕,Soc的内部还有CPU,外部还有DDR内存.这些设备都参与到了lcd的显示中. 在裸机中我们是怎么搞的呢,lcd的驱动器和lcd的控制器之间通过排线进行链接,连接的接口就是lcd所特有的一个接口.lcd控制器里面是很多和lcd相关的

linux驱动开发之framebuffer应用编程实践(一)

1.framebuffer应用编程 (1)打开设备文件 (2)获取设备信息 宏定义的命令在/linux/fb.h中 不可变信息FSCREENINFO,使用ioctl参数有FBIOGET_FSCREENINFO宏名,表示用ioctl从驱动中获取lcd设备的不变的信息 可变信息VSCREENINFO,使用ioctl参数有FBIOGET_VSCREENINFO宏名,表示用ioctl从驱动中获取lcd设备的可变信息 fb的驱动框架将屏幕的所有硬件信息分为了两类,一类为不可变的,是通过软件不可更改的(比如

linux驱动开发之misc设备与蜂鸣器驱动(一)

1.板载蜂鸣器的驱动测试 我手里有一个BSP,九鼎的Bsp,里面有蜂鸣器的驱动,我们先测试一下好不好用.我们拿到一个BSP时,如果要做或移植蜂鸣器的驱动,首先要确定下这个内核 中究竟有没有蜂鸣器的驱动,我们可以用sourceInsight将内核放进去,搜索buzzer这个文件,看有没有,如果不行,也可以在内核中输入make menuconfig,利用这个配置界面来搜索buzzer英文,看不能找到相应的信息,从而也会知道这个设备在哪个路径下,通过对九鼎的内核进行make menuconfig后,搜

Android驱动开发之Hello实例

Android驱动开发之Hello实例: 驱动部分 modified:   kernel/arch/arm/configs/msm8909-1gb_w100_hd720p-perf_defconfig modified:   kernel/arch/arm/configs/msm8909-1gb_w100_hd720p_defconfig modified:   kernel/drivers/input/misc/Kconfig modified:   kernel/drivers/input/

驱动开发之I2C总线

驱动开发之I2C总线: I2C:数据线和时钟线. 起始信号:时钟线为高电平,数据线由高到低跳变. 结束信号:时钟线为高电平,数据线由低到高跳变. 应答信号:第九个时钟周期,时钟线保持为高电平,数据线为低电平,此时为成功应答. 读写位:站在主机的角度考虑. 0代表主机给从机发送数据. 1代表主机接收从机的数据. 硬件原理: 数据帧的封装:主机给从机发送数据: 起始信号 + 7位从机地址 写位 + 从机给主机应答 + 主机给从机发送8位数据 + 从机给主机应答 ... + 主机给从机发送8位数据 +