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

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

嵌入式Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)之:按鍵驅(qū)動(dòng)程序?qū)嵗?/h1>

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

?

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

11.6.1??按鍵工作原理

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

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

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

表11.18 按鍵電路的主要端口

管????腳

端????口

輸入/輸出

管????腳

端????口

輸入/輸出

KEYSCAN0

GPE11

輸出

EINT0

EINIT0/GPF0

輸入/輸出

KEYSCAN1

GPG6

輸出

EINT2

EINT2/GPF2

輸入/輸出

KEYSCAN2

GPE13

輸出

EINT11

EINT11/GPG3

輸入/輸出

KEYSCAN3

GPG2

輸出

EINT19

EINT19/GPG11

輸入/輸出

因?yàn)橥ǔV袛喽丝谑潜容^珍貴且有限的資源,所以在本電路設(shè)計(jì)中,16個(gè)按鍵復(fù)用了4個(gè)中斷線。那怎么樣才能及時(shí)而準(zhǔn)確地對(duì)矩陣按鍵進(jìn)行掃描呢?

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

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

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

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

首先按鍵設(shè)備相關(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;????????????????????/*?對(duì)應(yīng)的中斷號(hào)?*/

????unsigned?int????irq_gpio_port;????????????/*?對(duì)應(yīng)的中斷線的輸入端口地址*/

????unsigned?int????kscan_gpio_port;????????/*?對(duì)應(yīng)的KSCAN端口地址?*/

}?st_key_info_matrix;

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

{

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

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

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

}?st_key_buffer;

……

?

下面是矩陣按鍵數(shù)組的定義,數(shù)組元素的信息(一個(gè)按鍵信息)按照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)在簡(jiǎn)單的GPIO字符設(shè)備驅(qū)動(dòng)程序里被使用過(guò)。此外,set_irq_type()函數(shù)用于設(shè)定中斷線的類型,在本實(shí)例中通過(guò)該函數(shù)將4個(gè)中斷線的類型配置為下降沿觸發(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ū)動(dòng)的主要接口,以下為驅(qū)動(dòng)模塊的入口和卸載函數(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)到設(shè)備的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ū)動(dòng)初始化?*/

static?int??button_init(void)

{

????int?ret;?

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

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

????if?(button_major)

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

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

????}

????else

????{?/*由系統(tǒng)動(dòng)態(tài)分配主設(shè)備號(hào)?*/

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

????????button_major?=?MAJOR(dev);?/*?獲得主設(shè)備號(hào)?*/

????}

????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ū)動(dòng)卸載?*/

static?void?__exit?button_exit(void)

{

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

????/*?卸載設(shè)備驅(qū)動(dòng)所占有的資源?*/

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

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

}

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

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

MODULE_AUTHOR("David");

MODULE_LICENSE("Dual?BSD/GPL");

?

按鍵字符設(shè)備的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ù)接口的實(shí)現(xiàn)。

/*?打開(kāi)文件,?申請(qǐng)中斷?*/

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();?????/*?申請(qǐng)4個(gè)中斷?*/

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

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

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

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);????/*?掃描按鍵,獲得進(jìn)行操作的按鍵的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端口,主要是為了恢復(fù)中斷端口配置?*/

????enable_irqs();??????/*?開(kāi)啟中斷?*/

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

}

/*?申請(qǐng)4個(gè)中斷?*/

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

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

?

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

/*?

**?進(jìn)入中斷后,?掃描銨鍵碼?

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

*/

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)

????{?/*?開(kāi)始逐列掃描,?掃描第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))

????????{?/*?觀察對(duì)應(yīng)的中斷線的輸入端口值?*/

????????????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ù)接口的實(shí)現(xiàn)。首先在按鍵緩沖中刪除已經(jīng)過(guò)時(shí)的按鍵操作信息,接下來(lái),從按鍵緩沖中讀取一條信息(按鍵ID)并傳遞給用戶層。

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

static?void?remove_timeoutkey(void)

{

????unsigned?long?tick;

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

????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();?/*?刪除過(guò)時(shí)的按鍵操作信息?*/

????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ū)動(dòng)程序中的主要內(nèi)容。

?

11.6.3??按鍵驅(qū)動(dòng)的測(cè)試程序

按鍵驅(qū)動(dòng)程序的測(cè)試程序所下所示。在測(cè)試程序中,首先打開(kāi)按鍵設(shè)備文件和gpio設(shè)備(包括4個(gè)LED和蜂鳴器)文件,接下來(lái),根據(jù)按鍵的輸入值(按鍵ID)的二進(jìn)制形式,LED?D9~D12發(fā)亮(例如,按下11號(hào)按鍵,則D9、D10和D12會(huì)發(fā)亮),而蜂鳴器當(dāng)每次按鍵時(shí)發(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);?/*?打開(kāi)按鈕設(shè)備?*/

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

????{

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

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

????}

????

????gpios_fd?=?open(GPIO_DEVICE_FILENAME,?O_RDWR);?/*?打開(kāi)GPIO設(shè)備?*/

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

????{

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

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

????}

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

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

????do

????{????

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

????????{

????????????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號(hào)鍵則退出?*/

????close(gpios_fd);?

????close(butt_fd);

????return?0;

}

首先編譯和加載按鍵驅(qū)動(dòng)程序,而且要?jiǎng)?chuàng)建設(shè)備文件節(jié)點(diǎn)。

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

$?insmod?butt_dev.ko?/*?加載buttons設(shè)備驅(qū)動(dòng)?*/

$?cat?/proc/devices??/*?通過(guò)這個(gè)命令可以查到buttons設(shè)備的主設(shè)備號(hào)?*/

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

接下來(lái),編譯和加載GPIO驅(qū)動(dòng)程序,而且要?jiǎng)?chuàng)建設(shè)備文件節(jié)點(diǎn)。

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

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

$?cat?/proc/devices?/*?通過(guò)這個(gè)命令可以查到GPIO設(shè)備的主設(shè)備號(hào)?*/

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

然后編譯并運(yùn)行驅(qū)動(dòng)測(cè)試程序。

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

$?./butt_test

相關(guān)推薦

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

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