第一個中斷驅動程式

來源:互聯網
上載者:User

這是一個按鍵中斷。linux的中斷機制和中斷原理的內容很多,不在這裡寫了。先寫一下關於第一個中斷的驅動程式。

中斷的驅動程式也是按照模組進行編寫的。

首先這是一個按鍵中斷,按鍵的驅動檔案不能寫,可以讀,可以開啟。這個程式中還使用了poll()對中斷進行了監控。

static int __init dev_init(void){int ret;ret = misc_register(&misc);printk (DEVICE_NAME"\tinitialized\n");return ret;}

這一部分是註冊函數。是一個混雜裝置的註冊。ret = misc_register(&misc)中的&misc是取混雜裝置結構體的地址。混雜裝置的結構體如下:

static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,};

在這個結構體中,MISC_DYNAMIC_MINOR是混雜裝置的次裝置號(主裝置號為10),DEVICE_NAME定義了混雜裝置的名稱,&dev_fops取混雜裝置file_operation結構體的地址。其中關於DEVICE_NAME使用的是宏定義:

#define DEVICE_NAME     "button"

關於dev_fops的定義如下:

static struct file_operations dev_fops = {    .owner   =   THIS_MODULE,    .open    =   s3c24xx_buttons_open,    .release =   s3c24xx_buttons_close,     .read    =   s3c24xx_buttons_read,    .poll    =   s3c24xx_buttons_poll,};

結構體的第一個成員是所有者。第二個成員是open函數,第三個是read函數,第四個是poll監控函數。

分別來介紹四個函數。

static int s3c24xx_buttons_open(struct inode *inode, struct file *file){    int i;    int err = 0;        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {continue;}        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,                           button_irqs[i].name, (void *)&button_irqs[i]);        if (err)            break;    }    if (err) {        i--;        for (; i >= 0; i--) {    if (button_irqs[i].irq < 0) {continue;    }    disable_irq(button_irqs[i].irq);            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);        }        return -EBUSY;    }    ev_press = 1;        return 0;}

首先實現了中斷的註冊request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, utton_irqs[i].name, (void *)&button_irqs[i]);這個函數嵌套在for迴圈中,button_irqs代表著定義的數組,數組的成員是6個。 每一個中斷對應的中斷處理函數是一樣的buttons_interrupt。 IRQ_TYPE_EDGE_BOTH對應的是中斷的處理屬性,上升沿和下降沿都要產生中斷。 button_irqs定義了裝置的名稱,從button_irqs數組傳遞進來六個裝置的名字。
 button_irqs涉及到共用中斷的時候開始使用,(void *)&button_irqs[i]選擇了地址,不同的資料對應的地址是不一樣的。這樣就確保了dev_id的不同。sizeof(button_irqs)/sizeof(button_irqs[0])整個數組的大小除以一個成員的大小,得到的是數群組成員的個數。

static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){    unsigned long err;    //預設的情況下是阻塞的。    if (!ev_press) {if (filp->f_flags & O_NONBLOCK)    return -EAGAIN;else    wait_event_interruptible(button_waitq, ev_press);    }    //執行到這裡的時候ev_press=1,這個實在中斷程式中實現的。    ev_press = 0;    err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));    return err ? -EFAULT : min(sizeof(key_values), count);}

在讀函數中,要注意的ev_press的變化和wait_event_interruptible(button_waitq, ev_press)阻塞。

這裡如果要想讓程式進行非阻塞,所以ev_press的值應該是1,但是這個1的實現是在中斷程式中實現的。ev_press只是一個標誌,並不是決定非阻塞的決定因素。

static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait){    unsigned int mask = 0;    poll_wait(file, &button_waitq, wait);    if (ev_press)        mask |= POLLIN | POLLRDNORM;    return mask;}

再說一下poll實現的步驟:

①poll_wait(file, &button_waitq, wait); 指明要使用的等待隊列是什麼,這裡是button_waitq。
② mask |= POLLIN | POLLRDNORM;    返回掩碼。不能向按鍵裡寫資料。

static irqreturn_t buttons_interrupt(int irq, void *dev_id){    struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;    int down;    // udelay(0);    down = !s3c2410_gpio_getpin(button_irqs->pin); //讀取GPIO寄存器的資料寄存器的值。               //這裡的!是因為down為1的時候表示被按下了。            //s3c2410_gpio_getpin(button_irqs->pin);表示沒有按下的時候等於1。    if (down != (key_values[button_irqs->number] & 1)) { // Changed   key_values[button_irqs->number] = '0' + down;  //防止下次再按下的時候中斷產生操作。  cyx = key_values[button_irqs->number];  printk ("hh"); printk ("%d",down); printk ("%d\n",cyx);  ev_press = 1;    wake_up_interruptible(&button_waitq);    }        return IRQ_RETVAL(IRQ_HANDLED);}

這裡的是中斷的處理常式。其中的cyx = key_values[button_irqs->number];是我自己加的,為了列印一下這個值。

這裡整體出現思維混亂,首先是down的值在執行之後是1,0的變化。這裡的down是通過函數s3c2410_gpio_getpin(button_irqs->pin);取資料寄存器的值再取反得到的,因為按鍵被按下時所對應的的寄存器是0,所以被按下時down的值是1。down的值解釋到這裡。再說說cyx,cyx的值在程式執行以後是49和48變化,瞬間不理解了。後來發現它的玄機在這裡:key_values[button_irqs->number] = '0' + down;這裡的‘0’是一個常量值48!!!所以48+0(1)出現了49和48的變化。你可能會問,那你列印hh的目的是什嗎?因為自己是菜鳥,所以想徹底搞清楚到底在哪裡或者說怎麼執行的中斷。最後要注意的是:

down = !s3c2410_gpio_getpin(button_irqs->pin); //讀取GPIO寄存器的資料寄存器的值。   //這裡的!是因為down為1的時候表示被按下了。//s3c2410_gpio_getpin(button_irqs->pin);表示沒有按下的時候等於1。    if (down != (key_values[button_irqs->number] & 1)) { // Changedkey_values[button_irqs->number] = '0' + down;  //防止下次再按下的時候中斷產生操作。

這個一個演算法技巧,我覺得要反覆看看的地方。

static int s3c24xx_buttons_close(struct inode *inode, struct file *file){    int i;        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {    continue;}free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);    }    return 0;}

這裡是中斷登出函數,對應的open函數。這裡首先是通過for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)for迴圈實現了六個按鍵寄存器的便利,然後free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);釋放了中斷。其中sizeof(button_irqs)/sizeof(button_irqs[0]);實現了結構體成員數量的實現。其中button_irqs定義如下:

static struct button_irq_desc button_irqs [] = {    {IRQ_EINT8 , S3C2410_GPG0 ,  S3C2410_GPG0_EINT8  , 0, "KEY0"},    {IRQ_EINT11, S3C2410_GPG3 ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},    {IRQ_EINT13, S3C2410_GPG5 ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},    {IRQ_EINT14, S3C2410_GPG6 ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},    {IRQ_EINT15, S3C2410_GPG7 ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},    {IRQ_EINT19, S3C2410_GPG11,  S3C2410_GPG11_EINT19, 5, "KEY5"},};

裝置的登出函數:

static void __exit dev_exit(void){misc_deregister(&misc);}

misc_deregister,混雜裝置的登出函數。

下面附上完整的驅動程式代碼(由於老謝使用的是linux2.6.29的核心版本,而自己的手上只有linux2.6.32的驅動程式,所以兩套程式會有一點不同。)所有代碼親測可用。

#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/irq.h>#include <asm/irq.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <linux/platform_device.h>#include <linux/cdev.h>#include <linux/miscdevice.h>#include <linux/sched.h>#include <linux/gpio.h>#define DEVICE_NAME     "button"struct button_irq_desc {    int irq;    int pin;    int pin_setting;    int number;    char *name;};static struct button_irq_desc button_irqs [] = {    {IRQ_EINT8 , S3C2410_GPG(0) ,  S3C2410_GPG0_EINT8  , 0, "KEY0"},    {IRQ_EINT11, S3C2410_GPG(3) ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},    {IRQ_EINT13, S3C2410_GPG(5) ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},    {IRQ_EINT14, S3C2410_GPG(6) ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},    {IRQ_EINT15, S3C2410_GPG(7) ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},    {IRQ_EINT19, S3C2410_GPG(11),  S3C2410_GPG11_EINT19, 5, "KEY5"},};static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};static DECLARE_WAIT_QUEUE_HEAD(button_waitq);static volatile int ev_press = 0;static int cyx;static irqreturn_t buttons_interrupt(int irq, void *dev_id){    struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;    int down;    // udelay(0);    down = !s3c2410_gpio_getpin(button_irqs->pin);    if (down != (key_values[button_irqs->number] & 1)) { // Changedkey_values[button_irqs->number] = '0' + down;cyx = key_values[button_irqs->number];        ev_press = 1;        wake_up_interruptible(&button_waitq);printk ("%d\n",cyx);printk ("hh");printk ("%d\n",down);    }        return IRQ_RETVAL(IRQ_HANDLED);}static int s3c24xx_buttons_open(struct inode *inode, struct file *file){    int i;    int err = 0;        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {continue;}        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,                           button_irqs[i].name, (void *)&button_irqs[i]);        if (err)            break;    }    if (err) {        i--;        for (; i >= 0; i--) {    if (button_irqs[i].irq < 0) {continue;    }    disable_irq(button_irqs[i].irq);            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);        }        return -EBUSY;    }    ev_press = 1;        return 0;}static int s3c24xx_buttons_close(struct inode *inode, struct file *file){    int i;        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {    continue;}free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);    }    return 0;}static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){    unsigned long err;    if (!ev_press) {if (filp->f_flags & O_NONBLOCK)    return -EAGAIN;else    wait_event_interruptible(button_waitq, ev_press);    }        ev_press = 0;    err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));    return err ? -EFAULT : min(sizeof(key_values), count);}static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait){    unsigned int mask = 0;    poll_wait(file, &button_waitq, wait);    if (ev_press)        mask |= POLLIN | POLLRDNORM;    return mask;}static struct file_operations dev_fops = {    .owner   =   THIS_MODULE,    .open    =   s3c24xx_buttons_open,    .release =   s3c24xx_buttons_close,     .read    =   s3c24xx_buttons_read,    .poll    =   s3c24xx_buttons_poll,};static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,};static int __init dev_init(void){int ret;ret = misc_register(&misc);printk (DEVICE_NAME"\tinitialized\n");return ret;}static void __exit dev_exit(void){misc_deregister(&misc);}module_init(dev_init);module_exit(dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("FriendlyARM Inc.");

下面是應用程式的代碼:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/select.h>#include <sys/time.h>#include <errno.h>#include <linux/input.h>int main(void){int buttons_fd;int key_value,i=0,count;struct input_event ev_key;buttons_fd = open("/dev/button", O_RDWR);if (buttons_fd < 0) {perror("open device buttons");exit(1);}for (;;) {count = read(buttons_fd,&ev_key,sizeof(struct input_event));for(i=0; i<(int)count/sizeof(struct input_event); i++)if(EV_KEY==ev_key.type)printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code,ev_key.value);if(EV_SYN==ev_key.type)printf("syn event\n\n");}close(buttons_fd);return 0;}

下面是驅動程式的Makefile

ifneq ($(KERNELRELEASE),)obj-m := button.oelseKDIR := /home/guoqian/4-3-1/linux-2.6.29all:make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-clean:rm -f *.ko *.o *.mod.o *.mod.c *.symvers  modul*endif

最後附上驅動程式的執行。

最後的執行效果是每按一下按鍵會出現兩次列印,這是因為open函數中的這段代碼的原因:

err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,                           button_irqs[i].name, (void *)&button_irqs[i]);

IRQ_TYPE_EDGE_BOTH這個參數定義了GPIO的上升沿和下降沿都要產生中斷。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.