在上述的驅動系列博客中,咱們已經瞭解了關於阻塞和非阻塞、異步通知、輪詢、內存和I/O口訪問、併發控制等知識,按鍵設備驅動相對來講是比較簡單的,本章內容能夠加深咱們對字符設備驅動架構、阻塞與非阻塞、中判定時器等相關知識的理解。在嵌入式的系統中,按鍵的硬件原理簡單,就是經過一個上拉電阻將處理器的外部中斷引腳拉高,電阻的另外一端接按鈕並接地就能夠實現。html
1.按鍵的確認流程以下node
2 按鍵驅動中的有關數據結構緩存
2.1 按鍵設備結構體以及定時器數據結構
#define MAX KEY BUF 16 // 鍵緩衝區大小 typedef unsigned char KEY RET; //設備結構體: typedef struct { unsigned int keyStatus[KEY NUM]; //4個 鍵的 鍵狀態 KEY RET buf[MAX KEY BUF]; // 鍵緩衝區 unsigned int head, tail; // 鍵緩衝區頭和尾 wait queue head t wq; //等待隊列 struct cdev cdev; //cdev 結構體 } KEY DEV; static struct timer list key timer[KEY NUM];//4個 鍵去抖定時器
2.2 按鍵硬件資源、鍵值信息結構體架構
static struct key info { int irq no; //中斷號 unsigned int gpio port; //GPIO端口 int key no; //鍵值 } key info tab [4] = { /* 鍵所使用的CPU 資源*/ { IRQ EINT10, GPIO G2, 1 } , { IRQ EINT13, GPIO G5, 2 } , { IRQ EINT14, GPIO G6, 3 } , { IRQ EINT15, GPIO G7, 4 } , };
2.3 按鍵設備驅動文件操做結構體併發
static struct file operations s3c2410 key fops = { owner: THIS MODULE, open: s3c2410 key open, //啓動設備 release: s3c2410 key release, //關閉設備 read: s3c2410 key read, //讀取 鍵的鍵值 };
3 按鍵設備的模塊加載和卸載函數異步
3.1 加載函數函數
static int init s3c2410 key init (void) { ...//申請設備號,添加cdev request irqs(); //註冊中斷函數 keydev .head = keydev .tail = 0; //初始化結構體 for (i = 0; i < KEY NUM; i++) keydev.keyStatus[i] = KEYSTATUS UP; init waitqueue head (&(keydev .wq)); //等待隊列 //初始化定時器,實現軟件的去抖動 for (i = 0; i < KEY NUM; i++) setup timer (&key timer[i], key timer handler, i); //把 鍵的序號做爲傳入定時器處理函數的參數 }
3.2 卸載函數ui
static void exit s3c2410 key exit (void) { free irqs(); //註銷中斷 ...//釋放設備號,刪除cdev }
3.3 中斷申請函數指針
/*申請系統中斷,中斷方式爲降低沿觸發*/ static int request irqs(void) { struct key info *k; int i; for (i= 0; i < sizeof(key info tab) / sizeof(key info tab [1]); i++) { k = key info tab + i; set external irq (k->irq no, EXT LOWLEVEL, GPIO PULLUP DIS); //設置低電平觸發 if (request irq (k->irq no, &buttons irq, SA INTERRUPT, DEVICE NAME, i)) //申請中斷,將 鍵序號做爲參數傳入中斷服務程序 { return - 1; } } return 0; }
3.4 中斷釋放函數
/*釋放中斷*/ static void free irqs(void) { struct key info *k; int i; for (i= 0; i < sizeof(key info tab) / sizeof(key info tab [1]); i++) { k = key info tab + i; free irq (k->irq no, buttons irq); //釋放中斷 } }
4 按鍵設備驅動中斷和定時器處理程序
在按鍵按下以後,將發生中斷,在中斷處理程序中,應該先關閉中斷進去查詢模式,延時以消抖以下中斷處理過程只有頂半部,沒有底半部。
4.1 中斷處理程序
static void s3c2410 eint key (int irq, void *dev id, struct pt regs *reg) { int key = dev id; disable irq (key info tab [key].irq no); //關中斷,轉入查詢 式 keydev.keyStatus[key] = KEYSTATUS DOWNX;//狀態爲按下 _ key timer [key].expires == jiffies + KEY TIMER DELAY1;//延遲 add timer (&key timer[key]); //啓動定時器 }
4.2 定時器處理流程
按鍵按下時,該按鍵將記錄字啊緩衝區,同時定時器啓動延時,每次記錄新的鍵值時,等待隊列被喚醒,其代碼以下。
//按鍵設備驅動的定時器處理函數 static void key timer handler (unsigned long data) { int key = data; if (ISKEY DOWN (key)) { if (keydev.keyStatus[key] == KEYSTATUS DOWNX) //從中斷進入 { keydev .keyStatus[key] = KEYSTATUS DOWN; key timer[key].expires == jiffies + KEY TIMER DELAY; //延遲 keyEvent (); //記錄鍵值,喚醒等待隊列 add timer(&key timer [key]); } else { key timer[key].expires == jiffies + KEY TIMER DELAY; //延遲 add timer(&key timer [key]); } } else //鍵已擡起 { keydev.keyStatus[key] = KEYSTATUS UP; enable irq (key info tab [key].irq no); }
5 打開和釋放函數
這裏主要是設置keydev.head和keydev.tail還有按鍵事件函數指針keyEvent的值,按鍵設備驅動的打開、釋放函數以下:
static int s3c2410 key open (struct inode *inode, struct file *filp) { keydev .head = keydev .tail = 0; //清空 鍵動做緩衝區 keyEvent = keyEvent raw; //函數指針指向 鍵處理函數keyEvent raw return 0; } static int s3c2410 key release (struct inode *inode, struct file *filp) { keyEvent = keyEvent dummy; //函數指針指向空函數 return 0; }
6 讀函數
讀函數主要是提供對按鍵設備結構體緩衝區的讀並複製到用戶空間,當keydev.head != keydev.tail時,說明緩衝區有數據,使用copy_to_user()函數拷貝到用戶空間,反之根據用戶空間是阻塞仍是非阻塞讀分爲如下兩種狀況:
//按鍵設備驅動的讀函數 static ssize t s3c2410 key read (struct file *filp,char *buf,ssize t count, loff t*ppos) { retry: if (keydev.head != keydev .tail) //當前循環隊列中有數據 { key ret = keyRead (); //讀取按鍵 copy to user(..); //把數據從內核空間傳送到用戶空間 } else { if (filp->f flags &O NONBLOCK) //若用戶採用非阻塞方式讀取 { return - EAGAIN; } interruptible sleep on (&(keydev .wq)); //用戶採用阻塞方式讀取,調用該函數使進程睡眠 goto retry; } return 0; }
版權全部,轉載請註明轉載地址:http://www.cnblogs.com/lihuidashen/p/4498025.html