【嵌入式linux】(第五步):使用eclipse集成开发环境开发第一个嵌入式Linux驱动

1.准备工做html

  首先得安装好gcc工具链,以及开发环境,能够看看个人前面的几步。linux

   还得编译好内核,通常开发板都带了,如今我还不知道配置内核,只能按照开发板默认的去编译,编译前须要先编译uboot,建议像我同样的新手先学会驱动编写,慢慢的去学习 内核的配置裁剪,我以为到时候这个确定是很是简单的过程,只不过没有找到门道而已,等我会了必定和你们分享,若是有会的能够教我一下,在下感激涕零。shell

个人嵌入式内核编译的路径为:/home/cfan/linux/linux-3.0.1/  ,这个待会会用到的。eclipse

今天我就教你们使用eclipse开发一个最简单的驱动程序,LED驱动,其实使用eclipse编译这种简单的驱动可能显得有点麻烦,若是是大工程我想集成开发环境的优点就会体现出来了,还有就是eclipse的编辑器界面比较友好,跟RVDS4.0同样。
编辑器


2.创建驱动工程,设置eclipse函数

 打开eclipse工具

 能够在桌面上面创建一个指向eclipse的快捷键,或者到eclipse的目录执行 ./eclipse 便可启动,我设置了eclipse的全局变量,所以只须要在终端中输入eclipse便可启动,以下图学习


建议将工程目录选择在NFS共享的那个目录,这样方便从开发板加载驱动或者执行程序,个人就选择在nfs6410这个共享目录里面。测试

新建一个C项目,空项目this


一直下一步,直到下图位置,填好本身的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,会多了一个文件


将 symbol.xml这个文件用文本编辑器打开


此时文本编辑器里面打开了两个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>
这里面的路径要看本身实际的内核路径进行修改,也能够在工程属性中一个一个的添加,就是添加一个linux有关的路径而已。


保存退出便可。


再打开eclipse的刚刚那个导出的位置,如今将导出的文件导入便可


点击完成便可,若是导入出问题了,仔细对照个人教程。应用,退出便可,导入后会多了几个路径,内核目录里面的头文件路径




而后新建一个.c文件


代码是以前写的

/****************************************************************************************************************
 * 文件名称	:	led_drive.c
 * 简介		:	OK6410 LED驱动
 * 做者		:	异灵元(cp1300@139.com)
 * 建立时间	:	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底层宏定义
*做者       :	异灵元(cp1300@139.com)
*建立时间	:	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
*依赖       : 	无
*做者       :	异灵元(cp1300@139.com)
*建立时间	:	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底层宏
*做者       :	异灵元(cp1300@139.com)
*建立时间	:	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底层宏
*做者       :	异灵元(cp1300@139.com)
*建立时间	:	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底层宏
*做者       :	异灵元(cp1300@139.com)
*建立时间	:	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("cp1300@139.com");						//驱动程序做者
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
led.o文件就是你的编译文件的名称,按照本身实际状况修开


保存后按 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

解决后就能够卸载驱动了。


                                                                                                                                                                                                                                            《注意:转载请标明出处》