Linux-2.6.32.2內核在mini2440上的移植(十六)---基於中斷的按鍵驅動移植

 

移植環境(紅色粗字體字修改後內容,藍色粗體字爲特別注意內容)

1,主機環境:VMare下CentOS 5.5 ,1G內存。

2,集成開發環境:Elipse IDE

3,編譯編譯環境:arm-linux-gcc v4.4.3,arm-none-linux-gnueabi-gcc v4.5.1。

4,開發板:mini2440,2M nor flash,128M nand flash。

5,u-boot版本:u-boot-2009.08

6,linux 版本:linux-2.6.32.2

7,參考文章:

嵌入式linux應用開發完全手冊,韋東山,編著。

Mini2440 之Linux 移植開發實戰指南

【1】硬件原理

Mini2440 具有6 個用戶測試按鍵,它們都是連接到CPU 的中斷引腳。如圖:

Linux-2.6.32.2內核在mini2440上的移植(十六)---基於中斷的按鍵驅動移植 - singleboy - singleboy的博客

 從圖中可以看出,6 個用戶按鍵分別對應如下CPU 資源引腳:

 按鍵   對應的端口寄存器  對應的中斷  對應的複用功能
 K1   GPG0  EINT8  僅有GPIO和中斷功能
 K2   GPG3  EINT11  nSS1
 K3   GPG5  EINT13  SPIMISO
 K4   GPG6  EINT14  SPIMOSI  
 K5   GPG7  EINT15  SPICLK
 K6   GPG11  EINT19  TCLK
   

 爲何如此安排這些按鍵資源呢?
首先,它們都具備中斷功能,因此可以直接做一些中斷相關的實驗,其次 GPG3,5,6,7這一組合可以形成一個全功能的SPI 接口,我們知道,有些全鍵盤就是通過SPI 接口擴展實現的,比如三星的公板SMDK2440 就帶有這種接口的鍵盤,只不過它需要添加一個SPI 接口的鍵盤芯片來實現。所以,我們不但在開發板上直接把這些引腳接到按鍵上,而且還特意增加了CON12 座以方便把這些按鍵引出到面板使用,或者作爲擴展全功能鍵盤的接口。這也是mini2440 精心設計的細節之一。

【2】驅動程序分析及編寫

在/linux-2.6.32.2/drivers/misc目錄下創建一個新的驅動程序文件mini2440_buttons.c,內容及詳細註釋如下:

#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 "buttons"
//設備名稱
/*定義中斷所用的結構體*/
struct button_irq_desc {
 int irq;
//按鍵對應的中斷號
 int pin; //按鍵所對應的GPIO 端口
 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"},
};
/*開發板上按鍵的狀態變量,注意這裏是’0’,對應的ASCII 碼爲30*/
static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};
/*因爲本驅動是基於中斷方式的,在此創建一個等待隊列,以配合中斷函數使用;當有按鍵按下並讀取到鍵
值時,將會喚醒此隊列,並設置中斷標誌,以便能通過 read 函數判斷和讀取鍵值傳遞到用戶態;當沒有按
鍵按下時,系統並不會輪詢按鍵狀態,以節省時鐘資源*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/*中斷標識變量,配合上面的隊列使用,中斷服務程序會把它設置爲1,read 函數會把它清零*/
static volatile int ev_press = 0;
/*本按鍵驅動的中斷服務程序*/
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);
 /*狀態改變,按鍵被按下,從這句可以看出,當按鍵沒有被按下的時候,寄存器的值爲1(上拉),但按
  鍵被按下的時候,寄存器對應的值爲0*/
 if (down != (key_values[button_irqs->number] & 1)) { // Changed
  
/*如果key1 被按下,則key_value[0]就變爲’1’,對應的ASCII 碼爲31*/
  key_values[button_irqs->number] = '0' + down;
  ev_press = 1;
/*設置中斷標誌爲1*/
  wake_up_interruptible(&button_waitq); /*喚醒等待隊列*/
 }
 return IRQ_RETVAL(IRQ_HANDLED);
}
/*
*在應用程序執行open(“/dev/buttons”,…)時會調用到此函數,在這裏,它的作用主要是註冊6 個按鍵的中斷。
*所用的中斷類型是IRQ_TYPE_EDGE_BOTH,也就是雙沿觸發,在上升沿和下降沿均會產生中斷,這樣做
是爲了更加有效地判斷按鍵狀態
*/
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;
 }
 /*註冊成功,則中斷隊列標記爲1,表示可以通過read 讀取*/
 ev_press = 1;
 /*正常返回*/
 return 0;
}
/*
*此函數對應應用程序的系統調用close(fd)函數,在此,它的主要作用是當關閉設備時釋放6 個按鍵的中斷*
處理函數
*/
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;
}
/*
*對應應用程序的read(fd,…)函數,主要用來向用戶空間傳遞鍵值
*/
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)
  
 /*當中斷標識爲0 時,並且該設備是以非阻塞方式打開時,返回*/
   return -EAGAIN;
  else
   
/*當中斷標識爲0 時,並且該設備是以阻塞方式打開時,進入休眠狀態,等待被喚醒*/
   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 或者select 的進程掛入隊列,以便被驅動程序喚醒*/
 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;
 
/*把按鍵設備註冊爲misc 設備,其設備號是自動分配的*/
 ret = misc_register(&misc);

 if(ret < 0)
  {
        printk(DEVICE_NAME "register falid!\n");
        return ret;
   }
 printk (DEVICE_NAME"\tinitialized\n");
 return 0;
}
/*註銷設備*/
static void __exit dev_exit(void)
{
 misc_deregister(&misc);
}
module_init(dev_init);
//模塊初始化,僅當使用insmod/podprobe 命令加載時有用,如果設備不是通過模塊方式加載,此處將不會被調用
module_exit(dev_exit); //卸載模塊,當該設備通過模塊方式加載後,可以通過rmmod 命令卸載,將調用此函數
MODULE_LICENSE("GPL"); //版權信息
MODULE_AUTHOR("singleboy.");
//作者名字

【3】爲內核添加按鍵設備的內核配置選項

把按鍵驅動加入到內核中,打開 linux-2.6.32.2/drivers/misc/Kconfig 文件,定位到16行附近,加入如下紅色部分內容:

if MISC_DEVICES

 config MINI2440_BUTTONS
  tristate "Buttons driver for FriendlyARM Mini2440 development boards"
  depends on MACH_MINI2440
  default y if MACH_MINI2440
  help
   this is buttons driver for FriendlyARM Mini2440 development boards

config LEDS_MINI2440
 tristate "LED Support for Mini2440 GPIO LEDs"
 depends on MACH_MINI2440
 default y if MACH_MINI2440
 help
  This option enables support for LEDs connected to GPIO lines
  on Mini2440 boards.

【4】對應的驅動目標文件加入內核

打開linux-2.6.32.2/drivers/misc/Makefile 文件,添加如下紅色部分內容:

obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
obj-$(CONFIG_C2PORT)  += c2port/
obj-$(CONFIG_MINI2440_BUTTONS) += mini2440_buttons.o
obj-$(CONFIG_LEDS_MINI2440) += mini2440_leds.o
obj-$(CONFIG_MINI2440_ADC) += mini2440_adc.o
obj-y    += eeprom/
obj-y    += cb710/

【5】確認內核配置

接上面的步驟,在內核源代碼目錄下執行:make menuconfig 重新配置內核,依次選擇進入如下子菜單項:

Device Drivers --->
    [*] Misc devices  ---> 

        <*>   Buttons driver for FriendlyARM Mini2440 development boards //選項默認是選中的,若沒有選中,則按空格鍵選中它。

退出並保存內核配置。

然後退出保存所選配置, 在命令行執行: make uImage , 將會生成arch/arm/boot/uImage,然後將其複製到/nfsboot目錄下後啓動開發板。可以在看到串口終端中啓動信息:

... ...

brd: module loaded
buttons     initialized!
S3C24XX NAND Driver, (c) 2004 Simtec Electronics

... ...

說明leds設備加載成功。

【6】buttons測試

爲了測試該驅動程序,我們還需要編寫一個簡單的測試程序,在友善官方提供的光盤中已經提供了該測試程序的源代碼,它位於\linux 示例代碼\examples\buttons目錄中,文件名爲:buttons_test.c。將其複製到主機/root/linux-test/codetest目錄下,下面是其中的代碼:

#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>

int main(void)
{
 int buttons_fd;
 char buttons[6] = {'0', '0', '0', '0', '0', '0'}; //定義按鍵值變量,對於驅動函數中的key_values 數組

 buttons_fd = open("/dev/buttons", 0); /*打開按鍵設備/dev/buttons*/
 if (buttons_fd < 0) {
  perror("open device buttons"); /*打開失敗則退出*/
  exit(1);
 }

 for (;;) { /*永讀按鍵並打印鍵值和狀態*/
  char current_buttons[6];
  int count_of_changed_key;
  int i;

/*使用read 函數讀取一組按鍵值(6 個)*/
  if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) {
   perror("read buttons:");
   exit(1);
  }

/*逐個分析讀取到的按鍵值*/

  for (i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++) {
   if (buttons[i] != current_buttons[i]) {
    buttons[i] = current_buttons[i];

/*打印按鍵值,並標明按鍵按下/擡起的狀態*/
    printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down");
    count_of_changed_key++;
   }
  }
  if (count_of_changed_key) {
   printf("\n");
  }
 }

 close(buttons_fd); /*關閉按鍵設備文件*/
 return 0;
}

在終端中進入到codetest目錄,然後執行:
[root@localhost codetest]# ls
adc_test    adc_test.c~     backlight_test.c  i2c  led.c   tstest.c
adc_test.c  backlight_test  buttons_test.c    led  tstest
[root@localhost codetest]# arm-linux-gcc -o buttons_test buttons_test.c
[root@localhost codetest]# cp buttons_test /nfsboot/nfs
[root@localhost codetest]#

將生成可執行目標文件buttons_test,複製到與開發板共享的nfsboot/nfs中,在開發板的命令行終端執行:

[root@mini2440 /]#ls -l /dev/buttons
crw-rw----    1 root     root      10,  63 Jan  1 00:00 /dev/buttons
[root@mini2440 /]#ls -l /dev/leds
crw-rw----    1 root     root      10,  62 Jan  1 00:00 /dev/leds
[root@mini2440 /]#cd /mnt/nfs
[root@mini2440 nfs]#ls
adc_test        buttons_test    test1.wav
backlight_test  i2c             tstest
bigworld.wav    led             yesterday.mp3
[root@mini2440 nfs]#./buttons_test
key 6 is down
key 6 is up
key 4 is down
key 4 is up
key 5 is down
key 5 is up
key 3 is down
key 3 is up
key 2 is down
key 2 is up
key 1 is down
key 1 is up
可以看到已經產生的動作。

接下來,將進行pwm驅動的移植。

發佈了1 篇原創文章 · 獲贊 8 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章