几点补充
1.对于友善之臂mini2440 官方内核,和交叉编译工具链
autoconf.h位于.linux-2.6.32.2中的autoconf.h位于/include/linux/autoconf.h
2.ctrl + b 编译时,出现 make:***没有规则可言创建目标"all".停止
解决方法:原因是make 没有找到makefile。右击工程,选择properties->c/c++ builder->Environment->查看PWD为工程目录下的Debug(or Release)
所以只要将.c文件和makefile都放到Debug目录里,就可以进行编译
3.在工程属性中将自动生成makefile选项去掉,否则自己创建的makefile是无效的
=====================================================================
http://blog.csdn.net/cp1300/article/details/8266806
1.准备工作
首先得安装好gcc工具链,以及开发环境,可以看看我的前面的几步。
还得编译好内核,一般开发板都带了,现在我还不知道配置内核,只能按照开发板默认的去编译,编译前需要先编译uboot,建议像我一样的新手先学会驱动编写,慢慢的去学习 内核的配置裁剪,我觉得到时候这个肯定是非常简单的过程,只不过没有找到门道而已,等我会了一定和大家分享,如果有会的可以教我一下,在下感激不尽。
我的嵌入式内核编译的路径为:/home/cfan/linux/linux-3.0.1/ ,这个待会会用到的。
今天我就教大家使用eclipse开发一个最简单的驱动程序,LED驱动,其实使用eclipse编译这种简单的驱动可能显得有点麻烦,如果是大工程我想集成开发环境的优势就会体现出来了,还有就是eclipse的编辑器界面比较友好,跟RVDS4.0一样。
2.建立驱动工程,设置eclipse
打开eclipse
可以在桌面上面建立一个指向eclipse的快捷键,或者到eclipse的目录执行 ./eclipse 即可启动,我设置了eclipse的全局变量,因此只需要在终端中输入eclipse即可启动,如下图
建议将工程目录选择在NFS共享的那个目录,这样方便从开发板加载驱动或者执行程序,我的就选择在nfs6410这个共享目录里面。
新建一个C项目,空项目
一直下一步,直到下图位置,填好自己的arm-linux-gcc的路径。
设置完成后点击完成。
到这一步新建工程完成了,此时还有重要的一部要做,先别急着添加.c文件。在工程上面右键,属性,到 C/C++常规---->Code Analysis---->路径和符号
点击下方ExportSettings,将设置导出为xml文件.我的道出到桌面上面了,这个大家随意
点击确定 应用 退出即可。
到你刚才保存的位置处打开那个.xml文件,用文本编辑器打开即可,我的在桌面上面,右键单击,使用文本编辑器打开
现在我们还需要将autoconf.h中的宏定义加入到Eclipse中,执行如下步骤
打开内核的这个目录 include/generated/ 这个按个人实际情况而定,我的是 cd /home/cfan/linux/linux-3.0.1/include/generated/ ,另外打开一个终端 cd到这个目录
在刚刚这个终端中执行
- cat autoconf.h |grep define |awk '{print "<macro><name>" $2 "</name><value>" $3 "</value></macro>"}' > symbol.xml
此时打开这个目录/home/cfan/linux/linux-3.0.1/include/generated,会多了一个文件
此时文本编辑器里面打开了两个xml文件了,将之前导出eclipse的那个xml文件打开,需要添加一行代码,在这两行代码之间(如果有两个,是下面的那个)
- <language name="C 源文件">
- </language>
- <macro><name>__KERNEL__</name><value>1</value></macro>
如下图
添加后
再将刚刚那个symbol.xml文件里面的所有代码复制到
- <macro><name>__KERNEL__</name><value>1</value></macro>
在上面的一个
- <language name="C 源文件">
- </language>
添加
- <includepath>/home/cfan/linux/linux-3.0.1/include</includepath>
- <includepath>/home/cfan/linux/linux-3.0.1/arch/arm/include</includepath>
- <includepath>/home/cfan/linux/linux-3.0.1/arch/arm/plat-samsung/include</includepath>
- <includepath>/home/cfan/linux/linux-3.0.1/arch/arm/mach-s3c64xx/include</includepath>
保存退出即可。
再打开eclipse的刚刚那个导出的位置,现在将导出的文件导入即可
点击完成即可,如果导入出问题了,仔细对照我的教程。应用,退出即可,导入后会多了几个路径,内核目录里面的头文件路径
然后新建一个.c文件
代码是之前写的
- /****************************************************************************************************************
- * 文件名称 : led_drive.c
- * 简介 : OK6410 LED驱动
- * 作者 : 异灵元([email protected])
- * 创建时间 : 2012/08/27 17:28
- * 修改时间 : 2012/08/27
- * 说明 : OK6410 开发板(S3C6410)LED(GPIO)驱动
- ****************************************************************************************************************/
- //系统头文件
- #include <linux/miscdevice.h>
- #include <linux/delay.h>
- #include <asm/irq.h>
- #include <mach/hardware.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/mm.h>
- #include <linux/fs.h>
- #include <linux/types.h>
- #include <linux/delay.h>
- #include <linux/moduleparam.h>
- #include <linux/slab.h>
- #include <linux/errno.h>
- #include <linux/ioctl.h>
- #include <linux/cdev.h>
- #include <linux/string.h>
- #include <linux/list.h>
- #include <linux/pci.h>
- #include <asm/uaccess.h>
- #include <asm/atomic.h>
- #include <asm/unistd.h>
- //--------------------------//
- #include <mach/map.h>
- #include <mach/regs-clock.h>
- #include <mach/regs-gpio.h>
- //--------------------------//
- #include <plat/gpio-cfg.h>
- #include <mach/gpio-bank-e.h>
- #include <mach/gpio-bank-m.h>
- ///
- //驱动模块名称
- #define DEVICE_NAME "OK6410_LED"
- //函数声明
- ///
- static long OK6410_LED_ioctl(
- struct file *file,
- unsigned int cmd,
- unsigned long arg);
- static ssize_t OK6410_LED_write(
- struct file *file,
- const char __user *buff,
- size_t size,
- loff_t *loff);
- static ssize_t OK6410_LED_read(
- struct file *file,
- char __user *buff,
- size_t size,
- loff_t *loff);
- ///
- /* 这个结构是字符设备驱动的核心
- * 当应用程序操作设备文件所提供的open,read,write等函数,
- * 最终会调用到这个结构中的对应函数
- */
- static struct file_operations dev_fops = {
- .owner = THIS_MODULE, //这是一个宏,指向编译模块时自动创建的__this_module变量
- .unlocked_ioctl = OK6410_LED_ioctl,
- .read = OK6410_LED_read,
- .write = OK6410_LED_write
- };
- //注册驱动所使用的相关信息
- static struct miscdevice misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME, //驱动模块名称
- .fops = &dev_fops,
- };
- //LED设备访问信号量
- struct semaphore led_sem;
- /****************************************************************************************************************
- *函数名 : static int __init OK6410_LED_init(void)
- *功能 : LED模块初始化函数
- *参数 : 无
- *返回 : 0:成功;<0:失败
- *依赖 : linux底层宏定义
- *作者 : 异灵元([email protected])
- *创建时间 : 2012/08/27 17:28
- *最后修改时间: 2012/08/27 17:28
- *说明 : 初始化LED硬件,注册LED驱动
- ****************************************************************************************************************/
- static int __init OK6410_LED_init(void)
- {
- int ret;
- unsigned int reg;
- //GPIOM0-3 推挽输出
- reg = readl(S3C64XX_GPMCON); //获取GPIOM寄存器数据
- reg &= (~0xffff); //清除之前设置
- reg |= 0x1111; //推挽输出
- writel(reg,S3C64XX_GPMCON); //配置IO模式
- reg = readl(S3C64XX_GPMDAT); //读取输出寄存器之前数据
- reg |= 0xf;
- writel(reg,S3C64XX_GPMDAT); //写入1,让所有的灯都熄灭
- ret = misc_register(&misc); //注册驱动
- if(ret < 0)
- {
- printk(DEVICE_NAME " can't initialized LED!\n");
- return ret;
- }
- init_MUTEX(&led_sem); //注册信号量
- printk(DEVICE_NAME " initialized\n");
- return 0; //返回成功
- }
- /****************************************************************************************************************
- *函数名 : static long OK6410_LED_ioctl(
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
- *功能 : 发送命令给LED驱动模块,无实际作用,直接返回0
- *参数 : 无作用
- *返回 : 0
- *依赖 : 无
- *作者 : 异灵元([email protected])
- *创建时间 : 2012/08/27 17:28
- *最后修改时间: 2012/08/27 17:28
- *说明 : 无
- ****************************************************************************************************************/
- static long OK6410_LED_ioctl(
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
- {
- return 0;
- }
- /****************************************************************************************************************
- *函数名 : static ssize_t OK6410_LED_write(
- struct file *file,
- const char __user *buff,
- size_t size,
- loff_t *loff)
- *功能 : 写数据到LED驱动模块,低电平灯亮
- *参数 : file:文件指针(无作用);buff:数据缓冲区指针;buff:数据数量;loff:无作用
- *返回 : 0:成功;<0:失败
- *依赖 : linux底层宏
- *作者 : 异灵元([email protected])
- *创建时间 : 2012/08/27 17:43
- *最后修改时间: 2012/08/27 17:43
- *说明 : 点灯函数,低电平亮,0-3BIT有效;对应4个LED
- ****************************************************************************************************************/
- static ssize_t OK6410_LED_write(
- struct file *file,
- const char __user *buff,
- size_t size,
- loff_t *loff)
- {
- unsigned int reg;
- if(down_interruptible(&led_sem)) //获取信号量
- return -ERESTARTSYS;
- reg = readl(S3C64XX_GPMDAT);
- reg &= (~0xf);
- reg |= buff[0] & 0xf;
- writel(reg,S3C64XX_GPMDAT);
- up(&led_sem); //释放信号量
- return 0;
- }
- /****************************************************************************************************************
- *函数名 : static ssize_t OK6410_LED_read(
- struct file *file,
- char __user *buff,
- size_t size,
- loff_t *loff)
- *功能 : 读LED状态,低电平灯亮
- *参数 : file:文件指针(无作用);buff:数据缓冲区指针;buff:数据数量;loff:无作用
- *返回 : 0:成功;<0:失败
- *依赖 : linux底层宏
- *作者 : 异灵元([email protected])
- *创建时间 : 2012/08/27 17:48
- *最后修改时间: 2012/08/27 17:48
- *说明 : 读取灯的状态,低电平灯亮,0-3bit有效;对应4个LED
- ****************************************************************************************************************/
- static ssize_t OK6410_LED_read(
- struct file *file,
- char __user *buff,
- size_t size,
- loff_t *loff)
- {
- unsigned int reg;
- if(down_interruptible(&led_sem)) //获取信号量
- return -ERESTARTSYS;
- reg = readl(S3C64XX_GPMDAT);
- buff[0] = reg | 0xfffffff0;
- up(&led_sem); //释放信号量
- return 0;
- }
- /****************************************************************************************************************
- *函数名 : static void __exit OK6410_LED_exit(void)
- *功能 : 卸载LED驱动
- *参数 : 无
- *返回 : 无
- *依赖 : linux底层宏
- *作者 : 异灵元([email protected])
- *创建时间 : 2012/08/27 17:50
- *最后修改时间: 2012/08/27 17:50
- *说明 : 卸载驱动
- ****************************************************************************************************************/
- static void __exit OK6410_LED_exit(void)
- {
- unsigned int reg;
- //GPIOM0-3 输入
- reg = readl(S3C64XX_GPMCON); //获取GPIOM寄存器数据
- reg &= (~0xffff); //清除之前设置
- writel(reg,S3C64XX_GPMCON); //配置IO模式
- misc_deregister(&misc); //卸载驱动
- }
- //动态加载驱动接口(必须)
- module_init(OK6410_LED_init);
- module_exit(OK6410_LED_exit);
- //其它信息(非必需)
- MODULE_AUTHOR("[email protected]"); //驱动程序作者
- MODULE_DESCRIPTION("OK6410(S3C6410) LED Driver"); //一些描述信息
- MODULE_LICENSE("GPL"); //遵循的协议
此时会有警告,不管他。
在工程属性中将自动生成makefile选项去掉
3.新建一个makefile文件修改makefile,编译驱动文件
- ARCH=arm
- CROSS_COMPILE=arm-linux-
- obj-m := led.o
- KDIR :=/home/cfan/linux/linux-3.0.1
- PWD :=$(shell pwd)
- all:
- $(MAKE) -C $(KDIR) M=$(PWD) modules
- clean:
- $(MAKE) -C $(KDIR) M=$(PWD) clean
保存后按 ctrl+B编译工程。
完成后会发现目录里面多了一个led.ko,这就是编译好的LED驱动模块。
4.加载驱动
在开发板上面加载驱动,没有NFS的童鞋将led.ko复制到开发板中,不管是SD卡还是U盘,有NFS的就好办了,在串口终端中CD到工程目录
执行 insmod led.ko 加载驱动,加载成功后会发现LED灯都灭了,后面会添加这个驱动的测试程序
到这里使用eclipse编写驱动就完成了,新手肯定会觉得太繁琐,其实一共就三步,只不过我写的比较详细而已,以后每次建立工程可以直接复制工程或者导入之前的那个xml文件皆可,麻烦也之麻烦这一次,希望对大家有所帮助。
5.附加,解决OK6410驱动无法卸载问题。
在嵌入式驱动开发过程中需要频繁的加载卸载驱动,但是使用rmmod的时候你会发现,驱动无法卸载,如 rmmod led,卸载的时候不需要.ko,直接是模块名,我的写错了。
这个可以看我的这篇文章:http://blog.csdn.net/cp1300/article/details/7994014
解决后就可以卸载驱动了。