【嵌入式linux】(第五步):使用eclipse集成開發(fā)環(huán)境開發(fā)第一個(gè)嵌入式Linux驅(qū)動(dòng)
1.準(zhǔn)備工作
? 首先得安裝好gcc工具鏈,以及開發(fā)環(huán)境,可以看看我的前面的幾步。
?? 還得編譯好內(nèi)核,一般開發(fā)板都帶了,現(xiàn)在我還不知道配置內(nèi)核,只能按照開發(fā)板默認(rèn)的去編譯,編譯前需要先編譯uboot,建議像我一樣的新手先學(xué)會(huì)驅(qū)動(dòng)編寫,慢慢的去學(xué)習(xí) 內(nèi)核的配置裁剪,我覺得到時(shí)候這個(gè)肯定是非常簡(jiǎn)單的過程,只不過沒有找到門道而已,等我會(huì)了一定和大家分享,如果有會(huì)的可以教我一下,在下感激不盡。
我的嵌入式內(nèi)核編譯的路徑為:/home/cfan/linux/linux-3.0.1/? ,這個(gè)待會(huì)會(huì)用到的。
今天我就教大家使用eclipse開發(fā)一個(gè)最簡(jiǎn)單的驅(qū)動(dòng)程序,LED驅(qū)動(dòng),其實(shí)使用eclipse編譯這種簡(jiǎn)單的驅(qū)動(dòng)可能顯得有點(diǎn)麻煩,如果是大工程我想集成開發(fā)環(huán)境的優(yōu)勢(shì)就會(huì)體現(xiàn)出來了,還有就是eclipse的編輯器界面比較友好,跟RVDS4.0一樣。
2.建立驅(qū)動(dòng)工程,設(shè)置eclipse
?打開eclipse
?可以在桌面上面建立一個(gè)指向eclipse的快捷鍵,或者到eclipse的目錄執(zhí)行 ./eclipse 即可啟動(dòng),我設(shè)置了eclipse的全局變量,因此只需要在終端中輸入eclipse即可啟動(dòng),如下圖
建議將工程目錄選擇在NFS共享的那個(gè)目錄,這樣方便從開發(fā)板加載驅(qū)動(dòng)或者執(zhí)行程序,我的就選擇在nfs6410這個(gè)共享目錄里面。
新建一個(gè)C項(xiàng)目,空項(xiàng)目
一直下一步,直到下圖位置,填好自己的arm-linux-gcc的路徑。
設(shè)置完成后點(diǎn)擊完成。
到這一步新建工程完成了,此時(shí)還有重要的一部要做,先別急著添加.c文件。在工程上面右鍵,屬性,到 C/C++常規(guī)---->Code Analysis---->路徑和符號(hào)
點(diǎn)擊下方ExportSettings,將設(shè)置導(dǎo)出為xml文件.我的道出到桌面上面了,這個(gè)大家隨意
點(diǎn)擊確定 應(yīng)用 退出即可。
到你剛才保存的位置處打開那個(gè).xml文件,用文本編輯器打開即可,我的在桌面上面,右鍵單擊,使用文本編輯器打開
現(xiàn)在我們還需要將autoconf.h中的宏定義加入到Eclipse中,執(zhí)行如下步驟
打開內(nèi)核的這個(gè)目錄 include/generated/ 這個(gè)按個(gè)人實(shí)際情況而定,我的是 cd /home/cfan/linux/linux-3.0.1/include/generated/ ,另外打開一個(gè)終端 cd到這個(gè)目錄
在剛剛這個(gè)終端中執(zhí)行
cat autoconf.h |grep define |awk '{print "" $2 " " $3 " "}' > symbol.xml
此時(shí)打開這個(gè)目錄/home/cfan/linux/linux-3.0.1/include/generated,會(huì)多了一個(gè)文件
此時(shí)文本編輯器里面打開了兩個(gè)xml文件了,將之前導(dǎo)出eclipse的那個(gè)xml文件打開,需要添加一行代碼,在這兩行代碼之間(如果有兩個(gè),是下面的那個(gè))
添加
__KERNEL__ 1
如下圖
添加后
再將剛剛那個(gè)symbol.xml文件里面的所有代碼復(fù)制到
__KERNEL__ 1
這行的下一行,如下圖
在上面的一個(gè)
添加
/home/cfan/linux/linux-3.0.1/include
/home/cfan/linux/linux-3.0.1/arch/arm/include
/home/cfan/linux/linux-3.0.1/arch/arm/plat-samsung/include
/home/cfan/linux/linux-3.0.1/arch/arm/mach-s3c64xx/include
這里面的路徑要看自己實(shí)際的內(nèi)核路徑進(jìn)行修改,也可以在工程屬性中一個(gè)一個(gè)的添加,就是添加一個(gè)linux有關(guān)的路徑而已。
保存退出即可。
再打開eclipse的剛剛那個(gè)導(dǎo)出的位置,現(xiàn)在將導(dǎo)出的文件導(dǎo)入即可
點(diǎn)擊完成即可,如果導(dǎo)入出問題了,仔細(xì)對(duì)照我的教程。應(yīng)用,退出即可,導(dǎo)入后會(huì)多了幾個(gè)路徑,內(nèi)核目錄里面的頭文件路徑
然后新建一個(gè).c文件
代碼是之前寫的
/****************************************************************************************************************
* 文件名稱 : led_drive.c
* 簡(jiǎn)介 : OK6410 LED驅(qū)動(dòng)
* 作者 : 異靈元(cp1300@139.com)
* 創(chuàng)建時(shí)間 : 2012/08/27 17:28
* 修改時(shí)間 : 2012/08/27
* 說明 : OK6410 開發(fā)板(S3C6410)LED(GPIO)驅(qū)動(dòng)
****************************************************************************************************************/
//系統(tǒng)頭文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//--------------------------//
#include
#include
#include
//--------------------------//
#include
#include
#include
///////////////////////////////////////////////
//驅(qū)動(dòng)模塊名稱
#define DEVICE_NAME "OK6410_LED"
//函數(shù)聲明
///////////////////////////////////////////////
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);
///////////////////////////////////////////////////
/* 這個(gè)結(jié)構(gòu)是字符設(shè)備驅(qū)動(dòng)的核心
* 當(dāng)應(yīng)用程序操作設(shè)備文件所提供的open,read,write等函數(shù),
* 最終會(huì)調(diào)用到這個(gè)結(jié)構(gòu)中的對(duì)應(yīng)函數(shù)
*/
static struct file_operations dev_fops = {
.owner = THIS_MODULE, //這是一個(gè)宏,指向編譯模塊時(shí)自動(dòng)創(chuàng)建的__this_module變量
.unlocked_ioctl = OK6410_LED_ioctl,
.read = OK6410_LED_read,
.write = OK6410_LED_write
};
//注冊(cè)驅(qū)動(dòng)所使用的相關(guān)信息
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME, //驅(qū)動(dòng)模塊名稱
.fops = &dev_fops,
};
//LED設(shè)備訪問信號(hào)量
struct semaphore led_sem;
/****************************************************************************************************************
*函數(shù)名 : static int __init OK6410_LED_init(void)
*功能 : LED模塊初始化函數(shù)
*參數(shù) : 無
*返回 : 0:成功;<0:失敗
*依賴 : linux底層宏定義
*作者 : 異靈元(cp1300@139.com)
*創(chuàng)建時(shí)間 : 2012/08/27 17:28
*最后修改時(shí)間: 2012/08/27 17:28
*說明 : 初始化LED硬件,注冊(cè)LED驅(qū)動(dòng)
****************************************************************************************************************/
static int __init OK6410_LED_init(void)
{
int ret;
unsigned int reg;
//GPIOM0-3 推挽輸出
reg = readl(S3C64XX_GPMCON); //獲取GPIOM寄存器數(shù)據(jù)
reg &= (~0xffff); //清除之前設(shè)置
reg |= 0x1111; //推挽輸出
writel(reg,S3C64XX_GPMCON); //配置IO模式
reg = readl(S3C64XX_GPMDAT); //讀取輸出寄存器之前數(shù)據(jù)
reg |= 0xf;
writel(reg,S3C64XX_GPMDAT); //寫入1,讓所有的燈都熄滅
ret = misc_register(&misc); //注冊(cè)驅(qū)動(dòng)
if(ret < 0)
{
printk(DEVICE_NAME " can't initialized LED!n");
return ret;
}
init_MUTEX(&led_sem); //注冊(cè)信號(hào)量
printk(DEVICE_NAME " initializedn");
return 0; //返回成功
}
/****************************************************************************************************************
*函數(shù)名 : static long OK6410_LED_ioctl(
struct file *file,
unsigned int cmd,
unsigned long arg)
*功能 : 發(fā)送命令給LED驅(qū)動(dòng)模塊,無實(shí)際作用,直接返回0
*參數(shù) : 無作用
*返回 : 0
*依賴 : 無
*作者 : 異靈元(cp1300@139.com)
*創(chuàng)建時(shí)間 : 2012/08/27 17:28
*最后修改時(shí)間: 2012/08/27 17:28
*說明 : 無
****************************************************************************************************************/
static long OK6410_LED_ioctl(
struct file *file,
unsigned int cmd,
unsigned long arg)
{
return 0;
}
/****************************************************************************************************************
*函數(shù)名 : static ssize_t OK6410_LED_write(
struct file *file,
const char __user *buff,
size_t size,
loff_t *loff)
*功能 : 寫數(shù)據(jù)到LED驅(qū)動(dòng)模塊,低電平燈亮
*參數(shù) : file:文件指針(無作用);buff:數(shù)據(jù)緩沖區(qū)指針;buff:數(shù)據(jù)數(shù)量;loff:無作用
*返回 : 0:成功;<0:失敗
*依賴 : linux底層宏
*作者 : 異靈元(cp1300@139.com)
*創(chuàng)建時(shí)間 : 2012/08/27 17:43
*最后修改時(shí)間: 2012/08/27 17:43
*說明 : 點(diǎn)燈函數(shù),低電平亮,0-3BIT有效;對(duì)應(yīng)4個(gè)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)) //獲取信號(hào)量
return -ERESTARTSYS;
reg = readl(S3C64XX_GPMDAT);
reg &= (~0xf);
reg |= buff[0] & 0xf;
writel(reg,S3C64XX_GPMDAT);
up(&led_sem); //釋放信號(hào)量
return 0;
}
/****************************************************************************************************************
*函數(shù)名 : static ssize_t OK6410_LED_read(
struct file *file,
char __user *buff,
size_t size,
loff_t *loff)
*功能 : 讀LED狀態(tài),低電平燈亮
*參數(shù) : file:文件指針(無作用);buff:數(shù)據(jù)緩沖區(qū)指針;buff:數(shù)據(jù)數(shù)量;loff:無作用
*返回 : 0:成功;<0:失敗
*依賴 : linux底層宏
*作者 : 異靈元(cp1300@139.com)
*創(chuàng)建時(shí)間 : 2012/08/27 17:48
*最后修改時(shí)間: 2012/08/27 17:48
*說明 : 讀取燈的狀態(tài),低電平燈亮,0-3bit有效;對(duì)應(yīng)4個(gè)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)) //獲取信號(hào)量
return -ERESTARTSYS;
reg = readl(S3C64XX_GPMDAT);
buff[0] = reg | 0xfffffff0;
up(&led_sem); //釋放信號(hào)量
return 0;
}
/****************************************************************************************************************
*函數(shù)名 : static void __exit OK6410_LED_exit(void)
*功能 : 卸載LED驅(qū)動(dòng)
*參數(shù) : 無
*返回 : 無
*依賴 : linux底層宏
*作者 : 異靈元(cp1300@139.com)
*創(chuàng)建時(shí)間 : 2012/08/27 17:50
*最后修改時(shí)間: 2012/08/27 17:50
*說明 : 卸載驅(qū)動(dòng)
****************************************************************************************************************/
static void __exit OK6410_LED_exit(void)
{
unsigned int reg;
//GPIOM0-3 輸入
reg = readl(S3C64XX_GPMCON); //獲取GPIOM寄存器數(shù)據(jù)
reg &= (~0xffff); //清除之前設(shè)置
writel(reg,S3C64XX_GPMCON); //配置IO模式
misc_deregister(&misc); //卸載驅(qū)動(dòng)
}
//動(dòng)態(tài)加載驅(qū)動(dòng)接口(必須)
module_init(OK6410_LED_init);
module_exit(OK6410_LED_exit);
//其它信息(非必需)
MODULE_AUTHOR("cp1300@139.com"); //驅(qū)動(dòng)程序作者
MODULE_DESCRIPTION("OK6410(S3C6410) LED Driver"); //一些描述信息
MODULE_LICENSE("GPL"); //遵循的協(xié)議
此時(shí)會(huì)有警告,不管他。
在工程屬性中將自動(dòng)生成makefile選項(xiàng)去掉
3.新建一個(gè)makefile文件修改makefile,編譯驅(qū)動(dòng)文件
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文件就是你的編譯文件的名稱,按照自己實(shí)際情況修開
保存后按 ctrl+B編譯工程。
完成后會(huì)發(fā)現(xiàn)目錄里面多了一個(gè)led.ko,這就是編譯好的LED驅(qū)動(dòng)模塊。
4.加載驅(qū)動(dòng)
在開發(fā)板上面加載驅(qū)動(dòng),沒有NFS的童鞋將led.ko復(fù)制到開發(fā)板中,不管是SD卡還是U盤,有NFS的就好辦了,在串口終端中CD到工程目錄
執(zhí)行 insmod led.ko 加載驅(qū)動(dòng),加載成功后會(huì)發(fā)現(xiàn)LED燈都滅了,后面會(huì)添加這個(gè)驅(qū)動(dòng)的測(cè)試程序
到這里使用eclipse編寫驅(qū)動(dòng)就完成了,新手肯定會(huì)覺得太繁瑣,其實(shí)一共就三步,只不過我寫的比較詳細(xì)而已,以后每次建立工程可以直接復(fù)制工程或者導(dǎo)入之前的那個(gè)xml文件皆可,麻煩也之麻煩這一次,希望對(duì)大家有所幫助。
5.附加,解決OK6410驅(qū)動(dòng)無法卸載問題。
?在嵌入式驅(qū)動(dòng)開發(fā)過程中需要頻繁的加載卸載驅(qū)動(dòng),但是使用rmmod的時(shí)候你會(huì)發(fā)現(xiàn),驅(qū)動(dòng)無法卸載,如 rmmod led,卸載的時(shí)候不需要.ko,直接是模塊名,我的寫錯(cuò)了。
這個(gè)可以看我的這篇文章:http://blog.csdn.net/cp1300/article/details/7994014
解決后就可以卸載驅(qū)動(dòng)了。
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? 《注意:轉(zhuǎn)載請(qǐng)標(biāo)明出處》