arm+linux完整Led驱动

驱动入门快一个月了,今天来总结一下一个入门的驱动 --led驱动,进度是很慢啊,都快一个月了才来一个led的驱动,天资愚笨,那就只有加倍努力啊!前面已经有一篇文章比较详细的说明了怎么样从windows转到一无所知的linux,该怎么样一步一步的了解一些工具然后搭建整个linux+arm驱动学习的环境。今天就总结一下完整的编写一个led驱动,总结一些问题,分享一点点心得,走过来的路都不容易,刚刚开始走的人都是比较难的。

对于驱动程序现在还是很多时候是照着参考的手册来做的,自己对于驱动程序的一些框架,结构,还有其他的一些东西都还是很模糊的,更多的是知其然但不知其所以然。但经过一个led驱动后给我的感觉是,驱动程序都有一定的框架和一些处理的结构,对于驱动我的理解是首先要了解你所驱动的硬件或者设备在linux(我学的是linux)内核中的驱动的框架是怎样的,还有有哪些结构来描述这些驱动,当然还有很多设计到的函数,宏定义等等,这些会让你的驱动很有规则而且容易编写,因为很多的接口都已经做好了。比如led驱动,这个驱动应该是属于字符驱动,但是2.6内核里面还可以细分为字符驱动里面的杂项(misc)设备驱动,用杂项设备驱动的结构,框架来编写led的驱动显然更恰当。然后另一个重要的方面是对于硬件要熟悉,原因很简单,因为你驱动的是你的硬件设备,当然很去复杂的驱动都不会事自己写,都是修改加移植,但是对于熟悉硬件将会使你修改驱动更加游刃有余。才依葫芦画瓢写了一个led驱动,大道理就讲了这么多,这不可以信,这个一些感受,全当一些废话吧。

回到正题,有了对led设备驱动框架和一些有用的结构了解后,然后对于开发板上led设备了解后就可以开始我们的驱动编写之旅啦。led驱动的源代码,和友善之臂给出的是一样的,只不过自己动手敲了一下,这里就不贴出来了。编写好源代码。

因为是交叉编译,如果直接编译到内核还需要下载内核到开发板中去,所以一般就编译成模块,友善之臂手册给出的是直接编译到内核,所以这里我们编写自己的led驱动的时候要修改一下,注意俩个地方,一是开发板中的内核中已经有一个leds的设备存在了,所以你给自己的led设备取名字的时候就不要用“leds”这个名字啦,要不然加载模块的时候会发生严重的错误,内核的错误都不小啊。 第二的话最好自己改个名字写一个自己的驱动,不要直接把友善之臂的编译成模块了,学习hello模块的时候还可以,熟悉流程,但是现在要开始写驱动了,动手是很重要的,我完全照着敲的,当然是自己也弄明白了其他的原理,但是编译的时候发现有个头文件里面的一下定义的宏和函数没有定义,我第一反应是头文件的问题,我开始找到那个头文件,发现是在当前目录的一个子目录下面,我就到处找头文件的问题,上网到处搜,这一点还是做的比较好,遇到问题首先上网搜一下问题,然后再去问别人,大概是提问的智慧中的写的吧,但是这个问题是,自己根本就没有好好分析过这个问题,一遇到问题就上网搜这个习惯不好,一般都会有比较好的提示,首先应该做的 是要先分析问题,而不是去找答案。

编译成模块的话我知道有俩种方法,一是按照友善之臂手册上的,修改Kconfig , 然后make menuconfig , 然后修改makefile文件,最后make modules就可以生成模块的 .ko了。还有一种是韦东山的嵌入式linux应用开发上面说的修改makefile  添加一句obj-m    +=  XXXleds.o 然后make modules。其实这俩个方法是一样的,都是最终修改makefile。但是前一种办法我认为的好处在于以后在编译内核的时候可以修改内核配置就可以了,不用去修改makefile中的文件,而后一种的话就只能直接去修改makefile,对于菜单式的选择哪些模块编译进内核的时候或者编译成模块的时候要比修改makefile来的直观方便的多。对于第一方法,这里有一些关系需要理清一下,那就是Kconfig 是make menuconfig中的原配置文件,因为make menuconfig是吧Kconfig中的内容通过一个脚本文件用菜单的方式直观的显示出来而已。而配置的结果保存在一个隐藏文件 .config中,而makefile中我们添加的一句 #obj-$(CONFIG_ZMF_LEDS)         += zmf_leds.o的时候,obj后面的变量决定于.config文件。

编译完成后我们需要把模块对应的 .ko文件下载到开发板上面去,这里要注意一下,当你编译完成后,有几个ko结尾的文件,还有一个隐藏的文件也是ko结尾,这个我们要下载到开发板的是我们的模块名加上.ko的那个。我开始用串口传的时候,(那是第一次用minicom)就传了那个隐藏的,结果传到开发板找不到文件,后面才只是是隐藏文件,一开始我以为,是minicom为了传输做的一些修改了,想想好笑啊!这里传输的话用nfs比较好,而且后面的测试用的应用程序可以直接运行。nfs的配置我已经详细总结了。

现在需要编写测试的应用程序,这个我也是在网上参考的别人的,最后为了方便编译后修改错误,我就写了一个makefile,这样的话就可以方便使用quickfix,还有也可以学习一下编写makefile。

quickfix 这里写一些常用的命令,首先是启动,打开你写好的测试程序,然后输入 :make 如果没有错误就不需要用quirkfix啦,具体quirkfix的详细手册自己可以上网搜一下,我只总结一个入门的完整流程,看手册内容太多。出现错误后输入 :cw 这时候就打开了quickfix窗口,现在就可以一个错误对着出现的地方了,开始光标是指到第一个错误,要让光标指到程序中的错误的地方,回车一下。修改了一个错误后输入:cn 进入下一个错误,输入:cp上一个错误,最后修改完了输入:ccl关闭quickfix窗口,囧啊,第一次不知道怎么关闭quickfix窗口,后面这个那个的最后整个文档关闭了,在打开的时候已经被删除的差不多了。至于其他的还有很多高级用法,以后在慢慢学。

真的是废话写了这么多,完整led驱动程序都不见一个,囧啊!算了,还啰唆····??嘿嘿

2011--09--03 17:00




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 Linux LED驱动程序示例,适用于基于 ARM 架构的嵌入式 Linux 系统: ```c #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/io.h> #include <linux/uaccess.h> #define LED_MAJOR 200 #define LED_MINOR 0 #define LED_NUM 1 #define LED_NAME "led" #define GPIO_BASE 0x01c20800 #define GPIO_SIZE 0x00001000 #define LED_OFF 0 #define LED_ON 1 static void __iomem *gpio_base; struct led_dev { dev_t devno; struct cdev cdev; }; static struct led_dev led; static int led_open(struct inode *inode, struct file *filp) { return 0; } static int led_release(struct inode *inode, struct file *filp) { return 0; } static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { unsigned char led_state; int ret; ret = copy_from_user(&led_state, buf, 1); if (ret < 0) { return -EFAULT; } if (led_state == LED_ON) { iowrite32((1 << 0), gpio_base); } else { iowrite32(0, gpio_base); } return 1; } static const struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_release, .write = led_write, }; static int __init led_init(void) { int ret; led.devno = MKDEV(LED_MAJOR, LED_MINOR); ret = register_chrdev_region(led.devno, LED_NUM, LED_NAME); if (ret < 0) { printk(KERN_ERR "Failed to register char device region\n"); return ret; } cdev_init(&led.cdev, &led_fops); led.cdev.owner = THIS_MODULE; ret = cdev_add(&led.cdev, led.devno, LED_NUM); if (ret < 0) { printk(KERN_ERR "Failed to add char device\n"); unregister_chrdev_region(led.devno, LED_NUM); return ret; } gpio_base = ioremap(GPIO_BASE, GPIO_SIZE); if (!gpio_base) { printk(KERN_ERR "Failed to map GPIO memory\n"); cdev_del(&led.cdev); unregister_chrdev_region(led.devno, LED_NUM); return -ENOMEM; } iowrite32(0x00000000, gpio_base + 0x00); iowrite32(0x00000000, gpio_base + 0x04); iowrite32(0x00000000, gpio_base + 0x08); iowrite32(0x00000000, gpio_base + 0x0c); iowrite32(0x00000000, gpio_base + 0x10); iowrite32(0x00000000, gpio_base + 0x14); iowrite32(0x00000000, gpio_base + 0x18); iowrite32(0x00000000, gpio_base + 0x1c); return 0; } static void __exit led_exit(void) { iounmap(gpio_base); cdev_del(&led.cdev); unregister_chrdev_region(led.devno, LED_NUM); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Linux LED Driver"); ``` 这个驱动程序使用了一个 GPIO 控制一个 LED 灯的开关状态,LED 灯连接在开发板的某个 IO 引脚上。用户可以通过 write 系统调用向驱动程序发送控制命令,打开或关闭 LED 灯。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值