歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

TQ2440按鍵點亮LED驅動程序

一,硬件分析:

1.打開TQ2440的底板原理圖找到按鍵測試的模塊,如下圖所示:

從圖我們知道,控制按鍵k1 k2 k3 k4 的管腳為EINT1 EINT4 EINT2 EINT0 ,當按鍵按下時,管腳輸出低電平,當按鍵沒有被按下時,管腳輸出高電平。

2.打開TQ2440核心板原理圖找到EINT1  EINT4 EINT2 EINT0所對應的cpu控制引腳,如下圖所示:

從圖我們可以知道,EINT1  EINT4 EINT2 EINT0 對應的cpu控制引腳為GPF1 GPF4 GPF2 GPF0 。

3.打開s3c2410的芯片手冊查看引腳GPF1 GPF4 GPF2 GPF0 ,如下圖所示 :

如圖我們知道如何將cpu引腳 GPF1 GPF4 GPF2 GPF0 設置成中斷模式,比如我要將GPF0設置成為中斷模式,我只需要將其寄存器二進制的第1位和第0位設置成為1和0即可,但是我們只需要知道原理即可,具體將引腳設置成為中斷模式,我們可以利用linux內核中的一些宏來實現。

二,按鍵點亮LED驅動程序源碼Button6.c :

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/irq.h> //定義 IRQ_TYPE_EDGE_BOTH
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/wait.h>
 
#define DEVICE_NAME  "button6"  //設備名稱         
#define DEVICE_MAJOR  2898 //主設備號 
#define LED_ON 1  //定義LED亮為1
#define LED_OFF 0  //定義LED暗為0
 
//定義按鍵中斷結構體 
struct button_irq

    int irq; //中斷號
    int pin; //中斷控制引腳   
    int pin_setting; //中斷控制引腳的設置(設置控制引腳為中斷模式)     
    int number; //按鍵編號
    char *name; //按鍵名稱   
};
 
//按鍵數組,包含4個按鍵的信息
//S3C2410_GPF0_EINT0在Regs-gpio.h中     
static struct button_irq button_irqs [] =
{
    {IRQ_EINT1, S3C2410_GPF1, S3C2410_GPF1_EINT1, 0, "KEY1"}, //按鍵K1
    {IRQ_EINT4, S3C2410_GPF4, S3C2410_GPF4_EINT4, 1, "KEY2"}, //按鍵K2
    {IRQ_EINT2, S3C2410_GPF2, S3C2410_GPF2_EINT2, 2, "KEY3"}, //按鍵K3
    {IRQ_EINT0, S3C2410_GPF0, S3C2410_GPF0_EINT0, 3, "KEY4"}, //按鍵K4
};
 
//保存按鍵值的數組
//volatile關鍵字意義:每次讀和寫都是用內存中的值而不是CPU寄存器的值
static volatile int key_values [] = {0, 0, 0, 0}; 
 
static unsigned long led_table [] ={ S3C2410_GPB5, S3C2410_GPB6, S3C2410_GPB7, S3C2410_GPB8,};//LED控制引腳 
//LED控制引腳的設置(設置引腳為輸出模式)
static unsigned int led_cfg_table [] ={ S3C2410_GPB5_OUTP, S3C2410_GPB6_OUTP, S3C2410_GPB7_OUTP, S3C2410_GPB8_OUTP,};
 
//等待隊列:
//等待隊列頭,當應用程序讀取按鍵時,如果此時沒有按鍵按下,程序就休眠
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); //定義並初始化等待隊列           
static volatile int ev_press = 0;  //按鍵是否被按的標志(按下和松開都算)     
 
//中斷處理函數 
static irqreturn_t buttons_interrupt(int irq, void *dev_id)

    struct button_irq *button_irqs = (struct button_irq*)dev_id; //獲取request_irq注冊中斷時關聯的參數信息
    int up = s3c2410_gpio_getpin(button_irqs->pin); //注冊中斷
    if (up)
        key_values[button_irqs->number] = (button_irqs->number + 1) + 0x80;
    else
        key_values[button_irqs->number] = (button_irqs->number + 1);  //根據中斷注冊情況設置按鍵的值
    ev_press = 1;                  //表示中斷發生了
    wake_up_interruptible(&button_waitq);  //喚醒休眠的進程 
    return IRQ_RETVAL(IRQ_HANDLED);//返回中斷信息
}
 
//定義open方法 
static int buttons_open(struct inode *inode, struct file *file)

    int i;
    int err;
    for (i = 0; i < 4; i++)
    {
        s3c2410_gpio_cfgpin(button_irqs[i].pin,button_irqs[i].pin_setting); //設置中斷控制引腳為中斷模式   
        err = request_irq(button_irqs[i].irq, buttons_interrupt, NULL, button_irqs[i].name, (void *)&button_irqs[i]); //申請中斷 
        if (err) //返回值為非零值表示申請中斷不成功 
            break;
    }
    if (err) //申請中斷錯誤處理 
    {
      for (; i >= 0; i--)
      {
          //釋放已經注冊的中斷
          disable_irq(button_irqs[i].irq);
          free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
      }
      return -EBUSY; //返回值表示中斷已被占用且不能共享       
  }
  //配置LED引腳為輸出模式   
  for (i = 0; i < 4; i++)
  {   
      s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
  }
  return 0;
}
 
//定義close方法 
static int buttons_close(struct inode *inode, struct file *file)

    int i;
    for (i = 0; i < 4; i++)
    {
        // 釋放已經注冊的中斷
        disable_irq(button_irqs[i].irq);
        free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
    }
    return 0;
}
 
//定義read方法 
static int buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
  unsigned long err;
  //if (!ev_press) //如果沒有按鍵按下
  while(!ev_press) //如果沒有按鍵按下         
  {
      if (filp->f_flags & O_NONBLOCK) //如果是非阻塞讀取設備,那麼程序就直接返回 -EAGAIN
          return -EAGAIN; //不阻塞,下次再來read 
      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));
  memset((void *)key_values, 0, sizeof(key_values));//清零
  return err ? -EFAULT : min(sizeof(key_values), count);
}
 
//select方法 
static unsigned int buttons_poll( struct file *file, struct poll_table_struct *wait)

    unsigned int mask = 0;
    poll_wait(file, &button_waitq, wait); //將等待隊列添加到poll_table中 
    if (ev_press)
        mask |= POLLIN | POLLRDNORM; //設備可讀的掩碼
    return mask; //返回設備可讀的掩碼 
}
 
//定義ioctl方法 
static int leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{   
    if(arg > 4)
    { 
      return -EINVAL;
    }
    switch(cmd)
    { 
        case LED_ON: //如果是點亮
            s3c2410_gpio_setpin(led_table[arg], 0);//LED控制引腳輸出低電平,此時燈亮
            return 0; 
        case LED_OFF://如果是熄滅
            s3c2410_gpio_setpin(led_table[arg], 1);//LED控制引腳輸出高電平,此時燈滅
            return 0; 
        default: 
        return -EINVAL;
    }
    return 0;
}
 
//定義file_operations方法 
static struct file_operations buttons_fops =
{
    .owner = THIS_MODULE, 
    .open = buttons_open,
    .release = buttons_close,
    .read = buttons_read,
    .poll = buttons_poll,
    .ioctl = leds_ioctl,
};
 
//聲明自動創建設備文件的類     
static struct class *button_class;
 
//驅動程序加載函數的實現
static int __init buttons_init(void)

    int ret;
    int i;
    printk("TQ2440/SKY2440 LEDS!\n"); //輸出初始化信息 
    ret = register_chrdev(DEVICE_MAJOR, DEVICE_NAME, &buttons_fops); //注冊設備
    if (ret)
    {
        printk(DEVICE_NAME  " can't register major number\n");   
        return ret;
    }         
    //設備節點文件自動創建的實現
    button_class = class_create(THIS_MODULE, DEVICE_NAME);//注冊一個類,使mdev可以在"/dev/"目錄下面建立設備節點
    if(IS_ERR(button_class))
    {
        printk("Err: failed in Button_leds class. \n");
        return -1;
    }
    device_create(button_class, NULL, MKDEV(DEVICE_MAJOR, 0), NULL, DEVICE_NAME);//創建一個設備節點,節點名為DEVICE_NAME
    for(i=0;i<4;++i) //熄滅4盞LED燈
    {
      s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]); //配置LED的控制引腳為輸出模式
      s3c2410_gpio_setpin(led_table[i],1); //使LED控制引腳輸出高電平,燈滅 
    }
    printk(DEVICE_NAME " initialized\n");//打印信息,內核中的打印用printk函數
    return 0;
}
 
//驅動程序卸載函數的實現
static void __exit buttons_exit(void)

    unregister_chrdev(DEVICE_MAJOR, DEVICE_NAME);//注銷設備 
    device_destroy(button_class, MKDEV(DEVICE_MAJOR, 0)); //刪掉設備節點
    class_destroy(button_class); //注銷類
}
 
module_init(buttons_init);//驅動模塊加載聲明,執行“insmod  tope-buttons.ko”命令時調用的函數
module_exit(buttons_exit); //驅動模塊加載聲明,執行“rmmod  tope-buttons”命令時調用的函數
MODULE_LICENSE("GPL");//遵循的協議

源碼分析:

1.程序的一開始我們定義了一個按鍵中斷的結構體,我們可以將這個結構體看做是本程序要處理的按鍵中斷的數據類型。然後我們分別對四個按鍵進行初始化。這裡要說明下實現cpu引腳工作在中斷模式下的宏S3C2410_GPF1_EINT1,S3C2410_GPF4_EINT4 ,S3C2410_GPF2_EINT2 和S3C2410_GPF0_EINT0 ,他們定義在在Regs-gpio.h中,源碼路徑:arch/arm/mach-s3c2410/include/mach/regs-gpio.h ,我們來看下宏S3C2410_GPF1_EINT1:#define S3C2410_GPF1_EINT1  (0x02 << 2) ,我們分析可以知道這裡實際上就是將GPF1的寄存器二進制位的第3位和第2位設置成為1和0,從而使得GPF1工作在中斷模式下。接著定義LED燈的控制引腳的數組和將LED控制引腳設置成為輸出模式的宏,這裡不做解釋,因為前面我寫過一篇TQ2440LED燈的驅動程序的文章,不懂請看那篇文章。

2.設置中斷處理函數,當發生中斷時就調用這個中斷處理函數來處理中斷,這之後是定義設備的open方法,對於本文的程序open方法主要的職責是:設置cpu的中斷控制引腳工作在中斷模式下並且申請中斷;設置LED的cpu控制引腳工作在輸出模式下。

3.定義設備的close方法,close方法的主要職責:釋放系統的中斷,如果申請的中斷沒有釋放會占用系統的中斷資源,導致某些程序無法運行,比如我一開始由於中斷的釋放這裡沒有處理好,導致的問題是當我將驅動模塊加載進系統內核,然後運行我的驅動測試程序,這些都沒有問題,但是當我關掉測試程序再次運行時,本人發現測試程序打不開設備了,不能運行了,提示錯誤設備正忙,原因就是系統的中斷資源沒有及時釋放。

4.定義設備的read方法,read方法的主要職責是:當我上層應用程序需要讀我的設備的時候,當我設備沒有數據可讀的時候,這個時候該如何處理的問題。

5.定義設備的poll方法,poll設備方法負責完成:

①使用poll_wait將等待隊列添加到poll_table中。

②返回描述設備是否可讀或可寫的掩碼。

位掩碼:

POLLIN  設備可讀

POLLRDNORM 數據可讀

POLLOUT 設備可寫

POLLWRNORM 數據可寫

設備可讀通常返回 (POLLIN|POLLRDNORM)

設備可寫通常返回 (POLLOUT|POLLWRNORM)

6.定義ioctl方法,這個設備方法不做解釋,不懂的話請看我前面寫的那篇TQ2440LED驅動程序的文章。

7.定義file_operations 。file_operations結構中的每一個成員的名字都對應著一個上層應用程序的調用。

8.定義模塊加載函數:這裡需要特別說明的是,我們要在驅動模塊加載函數裡面要加入熄滅4盞LED燈的代碼,以便等下測試的時候能夠很好的看到按鍵點亮LED燈的效果。

9.定義模塊卸載函數:略。

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-10/107954p2.htm

Copyright © Linux教程網 All Rights Reserved