加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 11.6  按鍵驅(qū)動程序?qū)嵗?/span>
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

嵌入式Linux設備驅(qū)動開發(fā)之:按鍵驅(qū)動程序?qū)嵗?/h1>

2013/09/13
1
閱讀需 46 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

?

11.6??按鍵驅(qū)動程序?qū)嵗?/h2>

11.6.1??按鍵工作原理

LED和蜂鳴器是最簡單的GPIO的應用,都不需要任何外部輸入或控制。按鍵同樣使用GPIO接口,但按鍵本身需要外部的輸入,即在驅(qū)動程序中要處理外部中斷。按鍵硬件驅(qū)動原理圖如圖11-7所示。在圖11-7的4×4矩陣按鍵(K1~K16)電路中,使用4個輸入/輸出端口(EINT0、EINT2、EINT11和EINT19)和4個輸出端口(KSCAN0~KSCAN3)。

圖11.7??按鍵驅(qū)動電路原理圖

按鍵驅(qū)動電路使用的端口和對應的寄存器如表11-18所示。

表11.18 按鍵電路的主要端口

管????腳

端????口

輸入/輸出

管????腳

端????口

輸入/輸出

KEYSCAN0

GPE11

輸出

EINT0

EINIT0/GPF0

輸入/輸出

KEYSCAN1

GPG6

輸出

EINT2

EINT2/GPF2

輸入/輸出

KEYSCAN2

GPE13

輸出

EINT11

EINT11/GPG3

輸入/輸出

KEYSCAN3

GPG2

輸出

EINT19

EINT19/GPG11

輸入/輸出

因為通常中斷端口是比較珍貴且有限的資源,所以在本電路設計中,16個按鍵復用了4個中斷線。那怎么樣才能及時而準確地對矩陣按鍵進行掃描呢?

某個中斷的產(chǎn)生表示,與它所對應的矩陣行的4個按鍵中,至少有一個按鍵被按住了。因此可以通過查看產(chǎn)生了哪個中斷,來確定在矩陣的哪一行中發(fā)生了按鍵操作(按住或釋放)。例如,如果產(chǎn)生了外部2號線中斷(EINT2變?yōu)榈?a class="article-link" target="_blank" href="/baike/1465710.html">電平),則表示K7、K8、K9和K15中至少有一個按鍵被按住了。這時候4個EINT端口應該通過GPIO配置寄存器被設置為外部中斷端口,而且4個KSCAN端口的輸出必須為低電平。

在確定按鍵操作所在行的位置之后,我們還得查看按鍵操作所在列的位置。此時要使用KSCAN端口組,同時將4個EINT端口配置為通用輸入端口(而不是中斷端口)。在4個KSCAN端口中,輪流將其中某一個端口的輸出置為低電平,其他3個端口的輸出置為高電平。這樣逐列進行掃描,直到按鍵所在列的KSCAN端口輸出為低電平,此時按鍵操作所在行的EINT管腳的輸入端口的值會變成低電平。例如,在確認產(chǎn)生了外部2號中斷之后,進行逐列掃描。若發(fā)現(xiàn)在KSCAN1為低電平時(其他端口輸出均為高電平),GPF2(EINT2管腳的輸入端口)變?yōu)榈碗娖剑瑒t可以斷定按鍵K8被按住了。

以上的討論都是在按鍵的理想狀態(tài)下進行的,但實際的按鍵動作會在短時間(幾毫秒至幾十毫秒)內(nèi)產(chǎn)生信號抖動。例如,當按鍵被按下時,其動作就像彈簧的若干次往復運動,將產(chǎn)生幾個脈沖信號。一次按鍵操作將會產(chǎn)生若干次按鍵中斷,從而會產(chǎn)生抖動現(xiàn)象。因此驅(qū)動程序中必須要解決去除抖動所產(chǎn)生的毛刺信號的問題。

11.6.2??按鍵驅(qū)動程序

首先按鍵設備相關(guān)的數(shù)據(jù)結(jié)構(gòu)的定義如下所示:

/*?butt_drv.h?*/

……

typedef?struct?_st_key_info_matrix????????????/*?按鍵數(shù)據(jù)結(jié)構(gòu)?*/

{

????unsigned?char????key_id;????????????????????/*?按鍵ID?*/

????unsigned?int????irq_no;????????????????????/*?對應的中斷號?*/

????unsigned?int????irq_gpio_port;????????????/*?對應的中斷線的輸入端口地址*/

????unsigned?int????kscan_gpio_port;????????/*?對應的KSCAN端口地址?*/

}?st_key_info_matrix;

typedef?struct?_st_key_buffer????????????????/*?按鍵緩沖數(shù)據(jù)結(jié)構(gòu)?*/

{

????unsigned?long?jiffy[MAX_KEY_COUNT];????/*?按鍵時間,?5s以前的銨鍵作廢*/

????unsigned?char?buf[MAX_KEY_COUNT];????????????/*?按鍵緩沖區(qū)?*/

????unsigned?int?head,tail;????????????????????/*?按鍵緩沖區(qū)頭和尾?*/

}?st_key_buffer;

……

?

下面是矩陣按鍵數(shù)組的定義,數(shù)組元素的信息(一個按鍵信息)按照0行0列,0行1列,…,3行2列,3行3列的順序逐行排列。

static?st_key_info_matrix?key_info_matrix[MAX_COLUMN][MAX_ROW]?=

{

????{{10,????IRQ_EINT0,??S3C2410_GPF0,???S3C2410_GPE11},?????/*?0行0列?*/

????{11,????IRQ_EINT0,??S3C2410_GPF0,???S3C2410_GPG6},

????{12,????IRQ_EINT0,??S3C2410_GPF0,???S3C2410_GPE13},

????{16,????IRQ_EINT0,??S3C2410_GPF0,???S3C2410_GPG2}},

????{{7,????IRQ_EINT2,??S3C2410_GPF2,?S3C2410_GPE11},?????/*?1行0列?*/

????{8,?????IRQ_EINT2,??S3C2410_GPF2,??S3C2410_GPG6},

????{9,?????IRQ_EINT2,??S3C2410_GPF2,???S3C2410_GPE13},

????{15,????IRQ_EINT2,??S3C2410_GPF2,???S3C2410_GPG2}},

????{{4,????IRQ_EINT11,?S3C2410_GPG3,??S3C2410_GPE11},???????/*?2行0列?*/

????{5,?????IRQ_EINT11,?S3C2410_GPG3,??S3C2410_GPG6},

????{6,?????IRQ_EINT11,?S3C2410_GPG3,??S3C2410_GPE13},

????{14,????IRQ_EINT11,?S3C2410_GPG3,???S3C2410_GPG2}},

????{{1,????IRQ_EINT19,?S3C2410_GPG11,?S3C2410_GPE11},??????/*?3行0列?*/

????{2,?????IRQ_EINT19,?S3C2410_GPG11,?S3C2410_GPG6},

????{3,?????IRQ_EINT19,?S3C2410_GPG11,?S3C2410_GPE13},

????{13,????IRQ_EINT19,?S3C2410_GPG11,?S3C2410_GPG2}},

};

下面是與按鍵相關(guān)的端口的初始化函數(shù)。這些函數(shù)已經(jīng)在簡單的GPIO字符設備驅(qū)動程序里被使用過。此外,set_irq_type()函數(shù)用于設定中斷線的類型,在本實例中通過該函數(shù)將4個中斷線的類型配置為下降沿觸發(fā)式。

static?void?init_gpio(void)

{

????s3c2410_gpio_cfgpin(S3C2410_GPE11,?S3C2410_GPE11_OUTP);?/*?GPE11?*/

????s3c2410_gpio_setpin(S3C2410_GPE11,?0);

????s3c2410_gpio_cfgpin(S3C2410_GPE13,?S3C2410_GPE13_OUTP);?/*?GPE13?*/

????s3c2410_gpio_setpin(S3C2410_GPE13,?0);

????s3c2410_gpio_cfgpin(S3C2410_GPG2,?S3C2410_GPG2_OUTP);?/*?GPG2?*/

????s3c2410_gpio_setpin(S3C2410_GPG2,?0);

????s3c2410_gpio_cfgpin(S3C2410_GPG6,?S3C2410_GPG6_OUTP);?/*?GPG6?*/

????s3c2410_gpio_setpin(S3C2410_GPG6,?0);

????s3c2410_gpio_cfgpin(S3C2410_GPF0,?S3C2410_GPF0_EINT0);?/*?GPF0?*/

????s3c2410_gpio_cfgpin(S3C2410_GPF2,?S3C2410_GPF2_EINT2);?/*?GPF2?*/

????s3c2410_gpio_cfgpin(S3C2410_GPG3,?S3C2410_GPG3_EINT11);?/*?GPG3?*/

????s3c2410_gpio_cfgpin(S3C2410_GPG11,?S3C2410_GPG11_EINT19);?/*?GPG11?*/

????set_irq_type(IRQ_EINT0,?IRQT_FALLING);

????set_irq_type(IRQ_EINT2,?IRQT_FALLING);

????set_irq_type(IRQ_EINT11,?IRQT_FALLING);

????set_irq_type(IRQ_EINT19,?IRQT_FALLING);

}

下面講解按鍵驅(qū)動的主要接口,以下為驅(qū)動模塊的入口和卸載函數(shù)。

/*?初始化并添加struct?cdev結(jié)構(gòu)到系統(tǒng)之中?*/

static?void?button_setup_cdev(struct?cdev?*dev,?

????????????????????????int?minor,?struct?file_operations?*fops)

{

????int?err;

????int?devno?=?MKDEV(button_major,minor);

????cdev_init(dev,?fops);?/*?初始化結(jié)構(gòu)體struct?cdev?*/

????dev->owner?=?THIS_MODULE;

????dev->ops?=?fops;?/*?關(guān)聯(lián)到設備的file_operations結(jié)構(gòu)?*/

????err?=?cdev_add(dev,?devno,?1);?/*?將struct?cdev結(jié)構(gòu)添加到系統(tǒng)之中?*/

????if?(err)

????{

????????printk(KERN_INFO"Error?%d?adding?button?%dn",err,?minor);

????}

}

……

/*?驅(qū)動初始化?*/

static?int??button_init(void)

{

????int?ret;?

????/*?將主設備號和次設備號定義到一個dev_t數(shù)據(jù)類型的結(jié)構(gòu)體之中?*/

????dev_t?dev?=?MKDEV(button_major,?0);?

????if?(button_major)

????{/*?靜態(tài)注冊一個設備,設備號先前指定好,并設定設備名,用cat?/proc/devices來查看?*/

????????ret?=?register_chrdev_region(dev,?1,?BUTTONS_DEVICE_NAME);

????}

????else

????{?/*由系統(tǒng)動態(tài)分配主設備號?*/

????????ret?=?alloc_chrdev_region(&dev,?0,?1,?BUTTONS_DEVICE_NAME);

????????button_major?=?MAJOR(dev);?/*?獲得主設備號?*/

????}

????if?(ret?<?0)

????{

????????printk(KERN_WARNING"Button:unable?to?get?major?%dn",button_major);

???????return?ret;

????}

????/*?初始化和添加結(jié)構(gòu)體struct?cdev到系統(tǒng)之中?*/

????button_setup_cdev(&button_dev,?0,?&button_fops);?

????printk("Button?driver?initialized.n");

????return?0;

}

/*?驅(qū)動卸載?*/

static?void?__exit?button_exit(void)

{

????cdev_del(&button_dev);?/*?刪除結(jié)構(gòu)體struct?cdev?*/

????/*?卸載設備驅(qū)動所占有的資源?*/

????unregister_chrdev_region(MKDEV(button_major,?0),?1);?

????printk("Button?driver?uninstalledn");

}

module_init(button_init);?/*?初始化設備驅(qū)動程序的入口?*/

module_exit(button_exit);?/*?卸載設備驅(qū)動程序的入口?*/

MODULE_AUTHOR("David");

MODULE_LICENSE("Dual?BSD/GPL");

?

按鍵字符設備的file_operations結(jié)構(gòu)定義為:

static?struct?file_operations?button_fops?=

{

????.owner?=?THIS_MODULE,

????.ioctl?=?button_ioctl,

????.open?=?button_open,

????.read?=?button_read,

????.release?=?button_release,

};

以下為open和release函數(shù)接口的實現(xiàn)。

/*?打開文件,?申請中斷?*/

static?int?button_open(struct?inode?*inode,struct?file?*filp)?

{

????int?ret?=?nonseekable_open(inode,?filp);

????if?(ret?<?0)?

????{

????????return?ret;

????}

????init_gpio();????????????????/*?相關(guān)GPIO端口的初始化*/

????ret?=?request_irqs();?????/*?申請4個中斷?*/

????if?(ret?<?0)?

????{

????????return?ret;

????}

????init_keybuffer();????????????/*?初始化按鍵緩沖數(shù)據(jù)結(jié)構(gòu)?*/

????return?ret;

}

/*?關(guān)閉文件,?屏蔽中斷?*/

static?int?button_release(struct?inode?*inode,struct?file?*filp)

{

????free_irqs();????????????????/*?屏蔽中斷?*/

????return?0;

}

在open函數(shù)接口中,進行了GPIO端口的初始化、申請硬件中斷以及按鍵緩沖的初始化等工作。在以前的章節(jié)中提過,中斷端口是比較寶貴而且數(shù)量有限的資源。因此需要注意,最好要在第一次打開設備時申請(調(diào)用request_irq函數(shù))中斷端口,而不是在驅(qū)動模塊加載的時候申請。如果已加載的設備驅(qū)動占用而在一定時間段內(nèi)不使用某些中斷資源,則這些資源不會被其他驅(qū)動所使用,只能白白浪費掉。而在打開設備的時候(調(diào)用open函數(shù)接口)申請中斷,則不同的設備驅(qū)動可以共享這些寶貴的中斷資源。

以下為中斷申請和釋放的部分以及中斷處理函數(shù)。

/*?中斷處理函數(shù),其中irq為中斷號?*/

static?irqreturn_t?button_irq(int?irq,?void?*dev_id,?struct?pt_regs?*regs)

{

????unsigned?char?ucKey?=?0;

????disable_irqs();????????/*?屏蔽中斷?*/

????/*?延遲50ms,?屏蔽按鍵毛刺?*/

????udelay(50000);

????ucKey?=?button_scan(irq);????/*?掃描按鍵,獲得進行操作的按鍵的ID?*/

????if?((ucKey?>=?1)?&&?(ucKey?<=?16))

????????{

????????/*?如果緩沖區(qū)已滿,?則不添加?*/

????????if?(((key_buffer.head?+?1)?&?(MAX_KEY_COUNT?-?1))?!=?key_buffer.tail)

????????{

????????????spin_lock_irq(&buffer_lock);

????????????key_buffer.buf[key_buffer.tail]?=?ucKey;

???? ????????key_buffer.jiffy[key_buffer.tail]?=?get_tick_count();

????????????key_buffer.tail?++;

????????????key_buffer.tail?&=?(MAX_KEY_COUNT?-1);

????????????spin_unlock_irq(&buffer_lock);

????????}

????}

????init_gpio();????????/*?初始化GPIO端口,主要是為了恢復中斷端口配置?*/

????enable_irqs();??????/*?開啟中斷?*/

????return?IRQ_HANDLED;/*?2.6內(nèi)核返回值一般是這個宏?*/

}

/*?申請4個中斷?*/

static??int?request_irqs(void)

{

????int?ret,?i,?j;

????for?(i?=?0;?i?<?MAX_COLUMN;?i++)

????{

????????ret?=?request_irq(key_info_matrix[i][0].irq_no,?

button_irq,?SA_INTERRUPT,?BUTTONS_DEVICE_NAME,?NULL);

????????if?(ret?<?0)

????????{

????????????for?(j?=?0;?j?<?i;?j++)

????????????{

????????????????free_irq(key_info_matrix[j][0].irq_no,?NULL);?

????????????}

????????????return?-EFAULT;

????????}

????}

????return?0;

}

/*?釋放中斷?*/

static?__inline?void?free_irqs(void)

{

????int?i;

????for?(i?=?0;?i?<?MAX_COLUMN;?i++)

????{

????????free_irq(key_info_matrix[i][0].irq_no,?NULL);

????}

}

中斷處理函數(shù)在每次中斷產(chǎn)生的時候會被調(diào)用,因此它的執(zhí)行時間要盡可能得短。通常中斷處理函數(shù)只是簡單地喚醒等待資源的任務,而復雜且耗時的工作則讓這個任務去完成。中斷處理函數(shù)不能向用戶空間發(fā)送數(shù)據(jù)或者接收數(shù)據(jù),不能做任何可能發(fā)生睡眠的操作,而且不能調(diào)用schedule()函數(shù)。

為了簡單起見,而且考慮到按鍵操作的時間比較長,在本實例中的中斷處理函數(shù)button_irq()里,通過調(diào)用睡眠函數(shù)來消除毛刺信號。讀者可以根據(jù)以上介紹的對中斷處理函數(shù)的要求改進該部分代碼。

?

按鍵掃描函數(shù)如下所示。首先根據(jù)中斷號確定操作按鍵所在行的位置,然后采用逐列掃描法最終確定操作按鍵所在的位置。

/*?

**?進入中斷后,?掃描銨鍵碼?

**?返回:?按鍵碼(1~16),?0xff表示錯誤?

*/

static?__inline?unsigned?char?button_scan(int?irq)

{

????unsigned?char?key_id?=?0xff;

????unsigned?char?column?=?0xff,?row?=?0xff;????

????

????s3c2410_gpio_cfgpin(S3C2410_GPF0,?S3C2410_GPF0_INP);?/*?GPF0?*/

????s3c2410_gpio_cfgpin(S3C2410_GPF2,?S3C2410_GPF2_INP);?/*?GPF2?*/

????s3c2410_gpio_cfgpin(S3C2410_GPG3,?S3C2410_GPG3_INP);?/*?GPG3?*/

????s3c2410_gpio_cfgpin(S3C2410_GPG11,?S3C2410_GPG11_INP);?/*?GPG11?*/

????

????switch?(irq)

????{?/*?根據(jù)irq值確定操作按鍵所在行的位置*/

????????case?IRQ_EINT0:

????????{

????????????column?=?0;

????????}

????????break;

????????case?IRQ_EINT2:

????????{

????????????column?=?1;

????????}

????????break;

????????case?IRQ_EINT11:

????????{

????????????column?=?2;

????????}

????????break;

????????case?IRQ_EINT19:

????????{

????????????column?=?3;

????????}

????????break;

????}????

????if?(column?!=?0xff)

????{?/*?開始逐列掃描,?掃描第0列?*/

????????s3c2410_gpio_setpin(S3C2410_GPE11,?0);?/*?將KSCAN0置為低電平?*/

????????s3c2410_gpio_setpin(S3C2410_GPG6,?1);

????????s3c2410_gpio_setpin(S3C2410_GPE13,?1);

????????s3c2410_gpio_setpin(S3C2410_GPG2,?1);

???????if(!s3c2410_gpio_getpin(key_info_matrix[column][0].irq_gpio_port))

????????{?/*?觀察對應的中斷線的輸入端口值?*/

????????????key_id?=?key_info_matrix[column][0].key_id;

????????????return?key_id;

????????}

????????/*?掃描第1列*/

????????s3c2410_gpio_setpin(S3C2410_GPE11,?1);

????????s3c2410_gpio_setpin(S3C2410_GPG6,?0);?/*?將KSCAN1置為低電平?*/

????????s3c2410_gpio_setpin(S3C2410_GPE13,?1);

????????s3c2410_gpio_setpin(S3C2410_GPG2,?1);

????????if(!s3c2410_gpio_getpin(key_info_matrix[column][1].irq_gpio_port))

????????{

????????????key_id?=?key_info_matrix[column][1].key_id;

????????????return?key_id;

????????}

????????/*?掃描第2列*/

????????s3c2410_gpio_setpin(S3C2410_GPE11,?1);

????????s3c2410_gpio_setpin(S3C2410_GPG6,?1);

????????s3c2410_gpio_setpin(S3C2410_GPE13,?0);?/*?將KSCAN2置為低電平?*/

????????s3c2410_gpio_setpin(S3C2410_GPG2,?1);

????????if(!s3c2410_gpio_getpin(key_info_matrix[column][2].irq_gpio_port))

????????{

????????????key_id?=?key_info_matrix[column][2].key_id;

????????????return?key_id;

????????}

????????/*?掃描第3列*/

????????s3c2410_gpio_setpin(S3C2410_GPE11,?1);

????????s3c2410_gpio_setpin(S3C2410_GPG6,?1);

????????s3c2410_gpio_setpin(S3C2410_GPE13,?1);

????????s3c2410_gpio_setpin(S3C2410_GPG2,?0);?/*?將KSCAN3置為低電平?*/

????????if(!s3c2410_gpio_getpin(key_info_matrix[column][3].irq_gpio_port))

????????{

????????????key_id?=?key_info_matrix[column][3].key_id;

????????????return?key_id;

????????}

????}?

????return?key_id;

}

以下是read函數(shù)接口的實現(xiàn)。首先在按鍵緩沖中刪除已經(jīng)過時的按鍵操作信息,接下來,從按鍵緩沖中讀取一條信息(按鍵ID)并傳遞給用戶層。

/*?從緩沖刪除過時數(shù)據(jù)(5s前的按鍵值)?*/

static?void?remove_timeoutkey(void)

{

????unsigned?long?tick;

????spin_lock_irq(&buffer_lock);?/*?獲得一個自旋鎖?*/

????while(key_buffer.head?!=?key_buffer.tail)

????{

????????tick?=?get_tick_count()?-?key_buffer.jiffy[key_buffer.head];

????????if?(tick??<?5000)????/*?5s?*/

????????????break;

????????key_buffer.buf[key_buffer.head]?=?0;

????????key_buffer.jiffy[key_buffer.head]?=?0;

????????key_buffer.head?++;

????????key_buffer.head?&=?(MAX_KEY_COUNT?-1);

????}

????spin_unlock_irq(&buffer_lock);?/*?釋放自旋鎖?*/

}

/*?讀鍵盤?*/

static?ssize_t?button_read(struct?file?*filp,?

????????????????????????????char?*buffer,?size_t?count,?loff_t?*f_pos)

{

????ssize_t?ret?=?0;

????remove_timeoutkey();?/*?刪除過時的按鍵操作信息?*/

????spin_lock_irq(&buffer_lock);

????while((key_buffer.head?!=?key_buffer.tail)?&&?(((size_t)ret)?<?count))

????{

????????put_user((char)(key_buffer.buf[key_buffer.head]),?&buffer[ret]);

????????key_buffer.buf[key_buffer.head]?=?0;

????????key_buffer.jiffy[key_buffer.head]?=?0;

????????key_buffer.head?++;

????????key_buffer.head?&=?(MAX_KEY_COUNT?-1);

????????ret?++;

????}

????spin_unlock_irq(&buffer_lock);

????return?ret;

}

以上介紹了按鍵驅(qū)動程序中的主要內(nèi)容。

?

11.6.3??按鍵驅(qū)動的測試程序

按鍵驅(qū)動程序的測試程序所下所示。在測試程序中,首先打開按鍵設備文件和gpio設備(包括4個LED和蜂鳴器)文件,接下來,根據(jù)按鍵的輸入值(按鍵ID)的二進制形式,LED?D9~D12發(fā)亮(例如,按下11號按鍵,則D9、D10和D12會發(fā)亮),而蜂鳴器當每次按鍵時發(fā)出聲響。

/*?butt_test.c?*/

#include?<sys/stat.h>

#include?<fcntl.h>

#include?<stdio.h>

#include?<sys/time.h>

#include?<sys/types.h>

#include?<unistd.h>

#include?<asm/delay.h>

#include?"butt_drv.h"

#include?"gpio_drv.h"

main()

{

????int?butt_fd,?gpios_fd,?i;

????unsigned?char?key?=?0x0;

????butt_fd?=?open(BUTTONS_DEVICE_FILENAME,?O_RDWR);?/*?打開按鈕設備?*/

????if?(butt_fd?==?-1)

????{

????????printf("Open?button?device?button?errr!n");

????????return?0;

????}

????

????gpios_fd?=?open(GPIO_DEVICE_FILENAME,?O_RDWR);?/*?打開GPIO設備?*/

????if?(gpios_fd?==?-1)

????{

????????printf("Open?button?device?button?errr!n");

????????return?0;

????}

????ioctl(butt_fd,?0);????/*?清空鍵盤緩沖區(qū),?后面參數(shù)沒有意義?*/

????printf("Press?No.16?key?to?exitn");?

????do

????{????

????????if?(read(butt_fd,?&key,?1)?<=?0)?/*?讀鍵盤設備,得到相應的鍵值?*/

????????{

????????????continue;

????????}

????

????????printf("Key?Value?=?%dn",?key);

????????for?(i?=?0;?i?<?LED_NUM;?i++)

????????{

????????????if?((key?&?(1?<<?i))?!=?0)

????????????{

????????????????ioctl(gpios_fd,?LED_D09_SWT?+?i,?LED_SWT_ON);?/*?LED發(fā)亮*/

????????????}

????????}

????????ioctl(gpios_fd,?BEEP_SWT,?BEEP_SWT_ON);?/*?發(fā)聲*/

????????sleep(1);

????????for?(i?=?0;?i?<?LED_NUM;?i++)

????????{

????????????ioctl(gpios_fd,?LED_D09_SWT?+?i,?LED_SWT_OFF);????/*?LED熄滅*/

????????}

????????ioctl(gpios_fd,?BEEP_SWT,?BEEP_SWT_OFF);

????}?while(key?!=?16);?/*?按16號鍵則退出?*/

????close(gpios_fd);?

????close(butt_fd);

????return?0;

}

首先編譯和加載按鍵驅(qū)動程序,而且要創(chuàng)建設備文件節(jié)點。

$?make?clean;make????/*?驅(qū)動程序的編譯*/

$?insmod?butt_dev.ko?/*?加載buttons設備驅(qū)動?*/

$?cat?/proc/devices??/*?通過這個命令可以查到buttons設備的主設備號?*/

$?mknod?/dev/buttons??c??252??0??/*?假設主設備號為252,?創(chuàng)建設備文件節(jié)點*/

接下來,編譯和加載GPIO驅(qū)動程序,而且要創(chuàng)建設備文件節(jié)點。

$?make?clean;make?/*?驅(qū)動程序的編譯*/

$?insmod?gpio_drv.ko?/*?加載GPIO驅(qū)動?*/

$?cat?/proc/devices?/*?通過這個命令可以查到GPIO設備的主設備號?*/

$?mknod?/dev/gpio??c??251??0??/*?假設主設備號為251,?創(chuàng)建設備文件節(jié)點*/

然后編譯并運行驅(qū)動測試程序。

$?arm-linux-gcc?–o?butt_test??butt_test.c

$?./butt_test

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

華清遠見(www.farsight.com.cn)是國內(nèi)領(lǐng)先嵌入師培訓機構(gòu),2004年注冊于中國北京海淀高科技園區(qū),除北京總部外,上海、深圳、成都、南京、武漢、西安、廣州均有直營分公司。華清遠見除提供嵌入式相關(guān)的長期就業(yè)培訓、短期高端培訓、師資培訓及企業(yè)員工內(nèi)訓等業(yè)務外,其下屬研發(fā)中心還負責嵌入式、Android及物聯(lián)網(wǎng)方向的教學實驗平臺的研發(fā)及培訓教材的出版,截止目前為止已公開出版70余本嵌入式/移動開發(fā)/物聯(lián)網(wǎng)相關(guān)圖書。企業(yè)理念:專業(yè)始于專注 卓識源于遠見。企業(yè)價值觀:做良心教育、做專業(yè)教育,更要做受人尊敬的職業(yè)教育。