在Linux驅動程式開發之三----按鍵驅動(Tiny6410)博文中讨論了使用中斷來實作按鍵驅動,毫無疑問,中斷方式效率相當高,但是在此之前,CPU要想獲知按鍵的狀态都是通過查詢方式來實作的,查詢方式就是CPU不停的檢查按鍵的狀态,如果有變化則立即輸出,而中斷是外設向CPU傳遞消息,告訴CPU自己狀态改變了,要求CPU進行處理,而其他時間CPU可以做自己想做的事情,和微機原理以及作業系統課程中講的一樣,代碼實作如下:
首先編寫驅動架構,然後配置KEY值的GPIO管腳為輸入,然後實作read函數,在read函數中讀取KEY值,并拷貝到使用者空間,使用者程式則不停的讀取按鍵的狀态,如果變低則輸出一條消息,否則不輸出。Tiny6410中按鍵的電路圖如下:

當未按下時,K1~K8為高電平,當按下後為低電平。
KEY | GPIO管腳 |
KEY1 | GPN0 |
KEY2 | GPN1 |
KEY3 | GPN2 |
KEY4 | GPN3 |
KEY5 | GPN4 |
KEY6 | GPN5 |
KEY7 | GPL11 |
KEY8 | GPL12 |
詳細代碼如下:
[cpp] view plain copy print ?
- #include <linux/module.h>//MODULE_LICENSE,MODULE_AUTHOR
- #include <linux/init.h>//module_init/module_exit
- #include <linux/fs.h>//file_operations
- #include <asm/io.h>//ioread32,iowrite32
- #include <linux/cdev.h>//cdev
- #include <mach/map.h>//定義了S3C64XX_VA_GPIO
- #include <mach/regs-gpio.h>//定義了gpio-bank-n中使用的S3C64XX_GPN_BASE
- #include <mach/gpio-bank-n.h>//定義了GPNCON
- #include <mach/gpio-bank-l.h>//定義了GPNCON
- #include <linux/wait.h>//wait_event_interruptible(wait_queue_head_t q,int condition);
- //wake_up_interruptible(struct wait_queue **q)
- #include <linux/sched.h>//request_irq,free_irq
- #include <asm/uaccess.h>//copy_to_user
- #include <linux/irq.h>//IRQ_TYPE_EDGE_FALLING
- #include <linux/interrupt.h>//request_irq,free_irq
- #include <linux/device.h>//class device
- MODULE_AUTHOR("jefby");
- MODULE_LICENSE("Dual BSD/GPL");
- MODULE_DESCRIPTION("Tiny 6410 buttons with search");
- #define GPNCON 0x7F008830
- #define GPLCON0 0x7F008810
- static volatile unsigned int * gpncon = NULL;
- static volatile unsigned int * gpndat = NULL;
- static volatile unsigned int * gplcon = NULL;
- static volatile unsigned int * gpldat = NULL;
- //按鍵裝置的主裝置号
- static int buttons_major = 0;
- //裝置号
- dev_t dev;
- //字元裝置
- struct cdev * buttons_cdev;
- static struct class * tiny6410_buttons_class = NULL;
- static struct device * tiny6410_buttons_device = NULL;
- //裝置打開操作,主要完成BUTTONS所對應的GPIO的初始化,注冊使用者中斷處理函數
- int buttons_open(struct inode *inode,struct file *filp)
- {
- unsigned val;
- gpncon = (volatile unsigned int*)ioremap(GPNCON,16);
- gpndat = gpncon + 1;
- val = ioread32(gpncon);//讀取GPNCON的值
- val = (val & ~(0xFFF));//設定GPIO 0~5為輸入
- iowrite32(val,gpncon);
- //設定KEY7,KEY8為輸入,gpl11,gpl12
- gplcon = (volatile unsigned int*)ioremap(GPLCON0,16);
- gpldat = gplcon + 2;//gpldat
- val = ioread32(gplcon+1);//讀取GPNCON1的值
- val = (val & ~(0xFF<<12));//設定GPL11和12為輸入
- iowrite32(val,gplcon+1);
- printk("buttons open.\n");
- return 0;
- }
- //按鍵讀若沒有鍵被按下,則使程序休眠;若有按鍵被按下,則拷貝資料到使用者空間,然後清零
- int buttons_read(struct file *filp, char __user *buf, size_t len, loff_t * pos)
- {
- unsigned char keyval[8]={0};
- unsigned int temp=0;
- int i=0;
- if(len != 8)
- return -1;
- temp=ioread32(gpndat);
- //讀取KEY1~KEY6的值
- for(i=0;i<6;++i){
- keyval[i] = (temp&(0x1<<i))?1 : 0;
- }
- temp = ioread32(gpldat);
- //讀取KEY7和KEY8的值
- keyval[6]=(temp&(0x1<<11))?1:0;
- keyval[7]=(temp&(0x1<<12))?1:0;
- copy_to_user(buf,keyval,sizeof(keyval));
- return 0;
- }
- //主要是解除安裝使用者中斷處理程式
- int buttons_close(struct inode *inode,struct file *filp)
- {
- printk("buttons close.\n");
- return 0;
- }
- static struct file_operations buttons_fops = {
- .owner = THIS_MODULE,
- .read = buttons_read,
- .release = buttons_close,
- .open = buttons_open,
- };
- static int module_buttons_init(void)
- {
- int err=0;
- int result=0;
- printk("Tiny6410 buttons module init.\n");
- if(buttons_major){
- dev = MKDEV(buttons_major,0);
- result = register_chrdev_region(dev,1,"buttons");
- }else{
- result = alloc_chrdev_region(&dev,0,1,"buttons");
- buttons_major = MAJOR(dev);
- }
- if(result < 0){
- printk(KERN_WARNING "buttons : can't get major %d\n",buttons_major);
- }
- printk("buttons major is %d",buttons_major);
- buttons_cdev = cdev_alloc();
- buttons_cdev ->ops = &buttons_fops;
- cdev_init(buttons_cdev,&buttons_fops);
- cdev_add(buttons_cdev,dev,1);
- tiny6410_buttons_class = class_create(THIS_MODULE, "tiny6410buttons");
- if (IS_ERR(tiny6410_buttons_class)) {
- err = PTR_ERR(tiny6410_buttons_class);
- printk("create class error.\n");
- }
- tiny6410_buttons_device = device_create(tiny6410_buttons_class, NULL, MKDEV(buttons_major, 0), NULL,
- "buttons");
- printk("buttons add ok.\n");
- return 0;
- }
- static void module_buttons_exit(void)
- {
- iounmap(gpncon);
- device_destroy(tiny6410_buttons_class, MKDEV(buttons_major, 0));
- class_destroy(tiny6410_buttons_class);
- cdev_del(buttons_cdev);
- unregister_chrdev_region(dev,1);
- printk("Tiny6410 buttons module exit");
- }
- module_init(module_buttons_init);
- module_exit(module_buttons_exit);
使用者程式的代碼如下:
[cpp] view plain copy print ?
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- int main(int argc,char **argv)
- {
- int i;
- int ret;
- int fd;
- unsigned char keyval[8]={1,1,1,1,1,1,1,1};
- static int cnt = 0;
- //打開裝置
- fd = open("/dev/buttons",0);
- if(fd < 0){
- printf("can't open /dev/buttons\n");
- return -1;
- }
- while(1){
- for(i=0;i<8;++i){
- ret = read(fd,keyval,sizeof(keyval));
- if(ret < 0){
- printf("read err.\n");
- return -1;
- }
- if(keyval[i] == 0){
- printf("%d:KEY%d entered.\n",cnt++,i+1);
- }
- }//for
- }//while(1)
- close(fd);
- return 0;
- }
運作截圖如下:
可以看出每按下一次就會列印出一串消息,說明那個按鍵被按下。使用top指令檢視目前的CPU使用率,截圖如下:可以看出,查詢方式下确實效率太低了,CPU被占用了90%多以上