7.自己寫中斷方式按鍵驅動程序(詳解)

request_irq()和free_irq()分析完畢後,接下來開始編寫上升沿中斷的按鍵驅動

如下圖,需要設置4個按鍵的EINT0, EINT2, EINT11, EINT19的模式為雙邊沿,且設置按鍵引腳為中斷引腳

7.自己寫中斷方式按鍵驅動程序(詳解)

這裡我們只需要使用request_irq函數就行了, 在request_irq函數里會初始chip->set_type(設置引腳和中斷模式)

1.首先添加頭文件

#include <linux> //要用到IRQ_EINT0和IRQT_RISING這些變量
/<linux>

2.在second_drv_open函數中,申請4箇中斷:

/* IRQ_EINT0: 中斷號, 定義在 asm/arch/irqs.h,被linux/irq.h調用
buttons_irq : 中斷服務函數,
IRQT_ BOTHEDGE: 雙邊沿中斷, 定義在 asm/irq.h,被linux/irq.h調用
“S1”: 保存文件到/proc/interrupt/S1,
1: dev_id,中斷函數的參數, 被用來釋放中斷服務函數,中斷時並會傳入中斷服務函數
*/
request_irq(IRQ_EINT0, buttons_irq,IRQT_BOTHEDGE, “S1”, 1);
request_irq(IRQ_EINT2, buttons_irq,IRQT_ BOTHEDGE, “S2”, 1);
request_irq(IRQ_EINT11, buttons_irq,IRQT_ BOTHEDGE, “S3”, 1);
request_irq(IRQ_EINT19, buttons_irq,IRQT_ BOTHEDGE, “S4”, 1);

3.在file_oprations結構體中添加.release成員函數,用來釋放中斷

static struct file_operations second_drv_fops={
.owner = THIS_MODULE,
.open = second_drv_open,
.read = second_drv_read,
.release=second_drv_class, //裡面添加free_irq函數,來釋放中斷服務函數

};

然後寫.release成員函數,釋放中斷:

int second_drv_class(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT0,1);
free_irq(IRQ_EINT2,1);
free_irq(IRQ_EINT11,1);
free_irq(IRQ_EINT19,1);
return 0;
}

4.寫action->handler中斷服務函數,在第2小節裡request_irq函數的中斷服務函數是buttons_irq

static irqreturn_t buttons_irq (int irq, void *dev_id) //irq:中斷號, void *:表示支持所有類型
{
printk(“irq=%d\n”);
return IRQ_HANDLED;
}

5.make後,然後放在開發板裡insmod,並掛載好了buttons設備節點,如下圖:

7.自己寫中斷方式按鍵驅動程序(詳解)

6.通過exec 5

如下圖,使用ps查看-sh進程為801,然後ls -l /proc/801/fd 找到描述符5指向/dev/buttons

7.自己寫中斷方式按鍵驅動程序(詳解)

如下圖,並申請中斷,當有按鍵按下時,就進入中斷服務函數buttons_irq()打印數據:

7.自己寫中斷方式按鍵驅動程序(詳解)

6.通過exec 5

會進入.release成員second_drv_class()函數釋放中斷,

然後cat /proc/interrupts會發現申請的中斷已經註銷掉了,在-sh進程fd文件裡也沒有文件描述符5

7.改進中斷按鍵驅動程序

使用等待隊列,讓read函數沒有中斷時,進入休眠狀態,降低CPU.

使用dev_id來獲取不同按鍵的狀態,是上升沿還是下降沿觸發?

7.1接下來要用到以下幾個函數:

s3c2410_gpio_getpin(unsigned int pin); //獲取引腳高低電平

pin: 引腳名稱,例如:S3C2410_GPA0,定義在

隊列3個函數(聲明隊列,喚醒隊列,等待隊列):

static DECLARE_WAIT_QUEUE_HEAD(qname); 

qname:就是中斷名字,被用來後面的喚醒中斷和等待中斷

wake_up_interruptible(*qname); 

喚醒一箇中斷,會將這個中斷重新添加到runqueue隊列(將中斷置為TASK_RUNNING狀態)

qname:指向聲明的等待隊列類型中斷名字

wait_event_interruptible(qname, condition); 

等待事件中斷函數,用來將中斷放回等待隊列,

前提是condition要為0,然後將這個中斷從runqueue隊列中刪除(將中斷置為TASK_INTERRUPTIBLE狀態),然後會在函數里一直for(; ;)判斷condition為真才退出

注意:此時的中斷屬於殭屍進程(既不在等待隊列,也不在運行隊列),當需要這個進程時,需要使用wake_up_interruptible(*qname)來喚醒中斷

qname: (wait queue):為聲明的等待隊列的中斷名字

condition:狀態,等於0時就是中斷進入休眠, 1:退出休眠

7.2 驅動程序步驟

(1)定義引腳描述結構體數組,每個結構體都保存按鍵引腳和初始狀態,然後在中斷服務函數中通過s3c2410_gpio_getpin()來獲取按鍵是鬆開還是按下(因為中斷是雙邊沿觸發),並保存在key_val裡(它會在.read函數發送給用戶層)

 /*
*引腳描述結構體
*/
struct pin_desc{
unsigned int pin;
unsigned int pin_status;
};
/*
*key初始狀態(沒有按下): 0x01,0x02,0x03,0x04
*key狀態(按下): 0x81,0x82,0x83,0x84
*/
struct pin_desc pins_desc[4]={
{S3C2410_GPF0,0x01 },
{S3C2410_GPF2, 0x02 },
{S3C2410_GPG3, 0x03 },
{S3C2410_GPG11,0x04},} ;

(2)聲明等待隊列類型的中斷button_wait:

static DECLARE_WAIT_QUEUE_HEAD(button_ wait); //聲明等待隊列類型的中斷

(3)定義全局變量even _press,用於中斷事件標誌:

static volatile int even _press = 0;

(4)在.read函數里,將even _press置0放入等待事件中斷函數中,判斷even _press為真,才發送數據:

even_press = 0; 
wait_event_interruptible(button_ wait, even _press); //當even _press為真,表示有按鍵按下,退出等待隊列
copy_to_user(buf, &key_val, 1); //even _press為真,有數據了,發送給用戶層

(5)在中斷服務函數里,發生中斷時, 就將even _press置1,並喚醒中斷button_wait(.read函數里就會發送數據給用戶層):

even _press = 0; 
wake_up_interruptible(&button_wait); //喚醒中斷

7.3 更改測試程序second_interrupt_text.c

最終修改如下:

#include  //調用sys目錄下types.h文件
#include //stat.h獲取文件屬性
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
/*secondtext while一直獲取按鍵信息 */
int main(int argc,char **argv)
{
int fd,ret;
unsigned int val=0;
fd=open("/dev/buttons",O_RDWR);
if(fd<0)
{printf("can't open!!!\n");
return -1;}
while(1)
{
ret=read(fd,&val,1); //讀取一個值,(當在等待隊列時,本進程就會進入休眠狀態)
if(ret<0)
{
printf("read err!\n");
continue;
}
printf("key_val=0X%x\r\n",val);
}
return 0;
}
/<string.h>/<stdio.h>/<fcntl.h>

8.運行結果

insmod second_interrupt.ko //掛載驅動設備

./second_interrupt_text & //後臺運行測試程序

創建了4箇中斷,如下圖:

7.自己寫中斷方式按鍵驅動程序(詳解)

當沒有按鍵按下時,這個進程就處於靜止狀態staitc,如下圖所示:

7.自己寫中斷方式按鍵驅動程序(詳解)

在等待隊列(休眠狀態)下,該進程佔用了CPU0%資源,如下圖所示:

7.自己寫中斷方式按鍵驅動程序(詳解)

當有按鍵按下時,便打印數據,如下圖所示:

7.自己寫中斷方式按鍵驅動程序(詳解)

下節繼續改進按鍵程序—使用poll機制

本節驅動代碼如下:

#include <linux>
#include <linux>
#include <linux>
#include <linux>
#include <linux>
#include <linux>
#include
#include
#include
#include
#include
static struct class *seconddrv_class;
static struct class_device *seconddrv_class_devs;

/* 聲明等待隊列類型中斷 button_wait */
static DECLARE_WAIT_QUEUE_HEAD(button_wait);

/*
* 定義中斷事件標誌
* 0:進入等待隊列 1:退出等待隊列
*/
static int even_press=0;
/*
* 定義全局變量key_val,保存key狀態
*/
static int key_val=0;
/*
*引腳描述結構體
*/
struct pin_desc{
unsigned int pin;

unsigned int pin_status;
};

/*
*key初始狀態(沒有按下): 0x01,0x02,0x03,0x04
*key狀態(按下): 0x81,0x82,0x83,0x84
*/
struct pin_desc pins_desc[4]={
{S3C2410_GPF0,0x01 },
{S3C2410_GPF2, 0x02 },
{S3C2410_GPG3, 0x03 },
{S3C2410_GPG11,0x04},} ;
int second_drv_class(struct inode *inode, struct file *file) //卸載中斷
{
free_irq(IRQ_EINT0,&pins_desc[0]);
free_irq(IRQ_EINT2,&pins_desc[1]);
free_irq(IRQ_EINT11,&pins_desc[2]);
free_irq(IRQ_EINT19,&pins_desc[3]);
return 0;
}
/* 確定是上升沿還是下降沿 */
static irqreturn_t buttons_irq (int irq, void *dev_id) //中斷服務函數
{
struct pin_desc *pindesc=(struct pin_desc *)dev_id; //獲取引腳描述結構體
unsigned int pin_val=0;
pin_val=s3c2410_gpio_getpin(pindesc->pin);
if(pin_val)
{
/*沒有按下 (下降沿),清除0x80*/
key_val=pindesc->pin_status&0xef;
}
else
{
/*按下(上升沿),加上0x80*/
key_val=pindesc->pin_status|0x80;
}
even_press=1; //退出等待隊列
wake_up_interruptible(&button_wait); //喚醒 中斷

return IRQ_HANDLED;
}
static int second_drv_open(struct inode *inode, struct file *file)
{
request_irq(IRQ_EINT0,buttons_irq,IRQT_BOTHEDGE,"S1",&pins_desc[0]);

request_irq(IRQ_EINT2, buttons_irq,IRQT_BOTHEDGE, "S2", &pins_desc[1]);
request_irq(IRQ_EINT11, buttons_irq,IRQT_BOTHEDGE, "S3", &pins_desc[2]);
request_irq(IRQ_EINT19, buttons_irq,IRQT_BOTHEDGE, "S4", &pins_desc[3]);
return 0;
}

static int second_drv_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{/*將中斷 進入等待隊列(休眠狀態)*/
wait_event_interruptible(button_wait, even_press);
/*有按鍵按下,退出等待隊列,上傳key_val 給用戶層*/
if(copy_to_user(buf,&key_val,sizeof(key_val)))
return EFAULT;
     even_press=0; //數據發完後,立馬設為休眠狀態,避免誤操作
return 0;
}

static struct file_operations second_drv_fops={
.owner = THIS_MODULE,
.open = second_drv_open,
.read = second_drv_read,
.release=second_drv_class, //裡面添加free_irq函數,來釋放中斷服務函數
};

volatile int second_major;
static int second_drv_init(void)
{
second_major=register_chrdev(0,"second_drv",&second_drv_fops); //創建驅動
seconddrv_class=class_create(THIS_MODULE,"second_dev"); //創建類名
seconddrv_class_devs=class_device_create(seconddrv_class, NULL, MKDEV(second_major,0), NULL,"buttons");
return 0;
}
static int second_drv_exit(void)
{
unregister_chrdev(second_major,"second_drv"); //卸載驅動
class_device_unregister(seconddrv_class_devs); //卸載類設備
class_destroy(seconddrv_class); //卸載類
return 0;
}
module_init(second_drv_init);
module_exit(second_drv_exit);
MODULE_LICENSE("GPL v2");
/<linux>/<linux>/<linux>/<linux>/<linux>/<linux>


分享到:


相關文章: