?
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