一個(gè)相當詳細的MINI2440按鍵驅動(dòng)詳解
/*后面加了_my*/
本文引用地址:http://dyxdggzs.com/article/201611/316663.htm/*按鍵驅動(dòng)程序*/
/*mini2440所用到的按鍵資源*/
/**************************************************/
/* 按鍵 對應的IO寄存器 對應的中斷引腳*/
/* K1 GPG0 EINT8 */
/* K2 GPG3 EINT11 */
/* K3 GPG5 EINT13 */
/* K4 GPG6 EINT14 */
/* K5 GPG7 EINT15 */
/* K6 GPG11 EINT19 */
/**************************************************/
/*要搞清楚誰(shuí)是輸入*/
/*在這里,按鍵控制對應的中斷引腳,從而控制對應的IO寄存器*/
/*相當于信息從外面輸入*/
/*我們要做的是根據對應的輸入信息,來(lái)采取相應的響應動(dòng)作*/
/*這就達到了中斷響應的目的*/
/*其核心就是要檢測*/
/*那么,該如何去檢測呢?*/
/*通過(guò)什么來(lái)檢測呢?*/
/*如何得知一個(gè)設備究竟用到哪些資源呢?*/
/*這是個(gè)非常重要的問(wèn)題*/
/*我想應該看具體的電路原理圖*/
/*只有看圖,才能了解具體的電路連接情況*/
/*從而得知設備所需的硬件資源*/
/*廠(chǎng)商的原理圖通常給的都比較詳細*/
/*引用的頭文件*/
#include
#include
#include
#include
#include
#include
#include /*中斷*/
#include
#include /*uaccess*/
#include /*寄存器設置*/
#include /*hardware*/
/*定義宏*/
#define BUTTON_MAJOR 221 /*主設備號,本來(lái)是232,我改為221*/
#define DEVICE_NAME "buttons_my" /*設備名,本來(lái)是buttons,我加上了_my*/
/*定義按鈕中斷的描述結構體*/
/*由它把按鈕中斷的信息綜合起來(lái)*/
/*各個(gè)成員表示什么意思?*/
struct button_irq_desc
{
int irq; /*中斷號*/
/*中斷號唯一表示一個(gè)中斷*/
int pin; /*中斷控制的寄存器*/
/*該寄存器的值由中斷引腳設置*/
/*我們希望從該寄存器讀出控制信息*/
int pin_setting; /*中斷的引腳*/
/*該引腳的電平由按鍵來(lái)控制*/
/*從而最終我們由按鍵控制了寄存器的值*/
int number; /*編號*/
char *name; /*名稱(chēng)*/
};
/*指定6個(gè)按鍵的信息*/
static struct button_irq_desc button_irqs [] =
{
{IRQ_EINT8,S3C2410_GPG0,S3C2410_GPG0_EINT8,0,"KEY1"}, /*K1*/
{IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG3_EINT11,1,"KEY2"}, /*K2*/
{IRQ_EINT13,S3C2410_GPG5,S3C2410_GPG5_EINT13,2,"KEY3"}, /*K3*/
{IRQ_EINT14,S3C2410_GPG6,S3C2410_GPG6_EINT14,3,"KEY4"}, /*K4*/
{IRQ_EINT15,S3C2410_GPG7,S3C2410_GPG7_EINT15,4,"KEY5"}, /*K5*/
{IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG11_EINT19,5,"KEY6"}, /*K6*/
}
/*這樣,資源就組織起來(lái)了*/
/*事實(shí)上,在這里我們不僅組織起了硬件資源*/
/*我們也把一定的軟件資源也糅合進(jìn)去了*/
/*像中斷號*/
/*key_values數組*/
/*存放各個(gè)按鍵在發(fā)生中斷情況下的值*/
/*volatile是什么意思呢?*/
/*這個(gè)數組是我們存放按鍵操作結果的,因此非常重要*/
static volatile int key_values [] = {0,0,0,0,0,0};
/*宏DECLARE_WAIT_QUEUE_HEAD(),是干什么的呢?*/
/*該宏應該是創(chuàng )建了一個(gè)等待隊列*/
/*等待隊列,是進(jìn)程調度的一種重要方法*/
/*等待隊列也很有意思,button_waitq,表示按鍵等待的隊列*/
/*就是說(shuō),按鍵一按下,就會(huì )激活其等待隊列里的進(jìn)程,來(lái)做相應的處理*/
/*因此,按鍵的等待隊列,或者說(shuō)中斷所設置的等待隊列,*/
/*是中斷處理中非常重要的資源,它大大擴展了中斷處理的能力*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*button_waitq是什么呢?*/
/*應該是等待隊列的名稱(chēng)*/
/*key_values數組中是否有數據的標志,0表示無(wú)數據可讀,1表示有數據可讀*/
static volatile int ev_press = 0; /*初始為0*/
/*中斷服務(wù)程序buttons_interrupt()的申明*/
/*即當檢測到有中斷時(shí),就會(huì )執行該中斷服務(wù)程序*/
/*那么如何檢測到有中斷發(fā)生呢?*/
/*并且中斷發(fā)生了,知道發(fā)生了什么樣的中斷呢?*/
/*中斷有很多種,該中斷服務(wù)程序究竟該服務(wù)于哪一個(gè)中斷呢?*/
/*顯然,要把中斷號與中斷服務(wù)程序聯(lián)結起來(lái),構成一個(gè)整體*/
/*這個(gè)工作可以在open函數里做*/
/*參數irq---中斷號*/
/*中斷服務(wù)程序應該是與中斷號一一對應的*/
/*對應于某個(gè)中斷號的中斷一發(fā)生,就會(huì )調用該中斷號對應的服務(wù)程序*/
/*那么,檢測中斷的發(fā)生,就成了先決條件*/
/*參數dev_id ---具體是哪一個(gè)按鈕*/
static irqreturn_t buttons_interrupt(int irq,void *dev_id);
/*mini2440_buttons_open()函數申明*/
/*驅動(dòng)函數open調用的具體函數*/
/*由open函數具體實(shí)現硬件的初始化工作*/
/*以及軟件的初始化工作*/
/*為我們的鍵盤(pán)設備的運行創(chuàng )造好環(huán)境*/
static int mini2440_buttons_open(struct inode *inode,struct file *file);
/*mini2440_buttons_close()函數的申明*/
/*release調用的具體函數*/
/*設備軟件環(huán)境的拆卸*/
/*具體就是中斷的釋放工作*/
/*因為中斷資源,也是系統寶貴的資源,所以不用的時(shí)候,要釋放*/
static int mini2440_buttons_close(struct inode *inode,struct file *file);
/*mini2440_buttons_read()函數的申明*/
/*read調用的具體函數*/
/*由它讀取鍵盤(pán)輸入的結果*/
/*實(shí)質(zhì)上就是讀取key_values數組的值*/
/*它完成了鍵盤(pán)作為輸入設備的核心功能*/
/*數組是否可讀,要根據標志位ev_press來(lái)判斷*/
/*如果數組可讀,則讀取數據到用戶(hù)buffer中*/
/*如果數組不可讀,則進(jìn)程進(jìn)入等待隊列,等待到數組可讀為止*/
/*等待隊列機制,是中斷管理中常用到的機制*/
/*因為有些進(jìn)程經(jīng)常需要等待某一事件的發(fā)生*/
static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp);
/*注意__user,指的是用戶(hù)空間*/
/*即要把鍵盤(pán)的輸入結果讀取到用戶(hù)空間去*/
/*mini2440_buttons_poll()函數的申明*/
/*poll調用的具體函數*/
/*poll實(shí)質(zhì)上是select的調用函數*/
/*如果有按鍵數據,則select會(huì )立刻返回*/
/*如果沒(méi)有按鍵數據,則等待*/
/*實(shí)質(zhì)上這是鍵盤(pán)等待輸入的機制*/
static unsigned int mini2440_buttons_poll(struct file *file,struct poll_table_struct *wait);
/*file_operations結構體*/
/*驅動(dòng)函數的設置*/
/*分別將前面的驅動(dòng)函數設置進(jìn)來(lái)*/
static struct file_operations mini2440_buttons_fops =
{
.owner = THIS_MODULE,
.open = mini2440_buttons_open, /*open()*/
.release = mini2440_buttons_close, /*release()*/
.read = mini2440_buttons_read, /*read()*/
.poll = mini2440_buttons_poll /*poll()*/
};
/*mini2440_buttons_init()函數的申明*/
/*module_init調用的具體函數*/
/*模塊創(chuàng )建時(shí)的初始化函數*/
/*主要做的工作是注冊設備和創(chuàng )建設備*/
/*而具體的硬件初始化工作,它可以不做*/
/*而把它留給fops里的函數來(lái)做*/
static int __init mini2440_buttons_init(void);
/*mini2440_buttons_exit()函數的申明*/
/*模塊卸載時(shí)的掃尾工作*/
/*主要是設備的卸載工作*/
static void __exit mini2440_buttons_exit(void);
/*模塊創(chuàng )建時(shí)的入口點(diǎn)*/
module_init(mini2440_buttons_init);
/*模塊卸載時(shí)的入口點(diǎn)*/
module_exit(mini2440_buttons_exit);
/*驅動(dòng)程序的一些信息*/
MODULE_AUTHOR("http://www.arm9.net"); /*驅動(dòng)程序的作者*/
MODULE_DESCRIPTION("S3C2410/S3C2440 BUTTON Driver"); /*描述信息*/
MODULE_LICENSE("GPL"); /*遵循的協(xié)議*/
/********************************************************************/
/*********************下面是前面申明函數的實(shí)現***********************/
/********************************************************************/
/**********************mini2440_buttons_init()***********************/
static int __init mini2440_buttons_init(void)
{
int ret; /*設備注冊的返回值*/
/*注冊設備驅動(dòng)程序*/
/*設備號,設備名,和驅動(dòng)函數*/
ret = register_chrdev(BUTTON_MAJOR,DEVICE_NAME,&mini2440_buttons_fops);
/*對注冊失敗的處理*/
if(ret < 0)
{
printk(DEVICE_NAME " cant register major numbern");
return ret;
}
/*創(chuàng )建設備*/
/*devfs_mk_cdev()函數是內核態(tài)的設備創(chuàng )建函數*/
/*而mknod是用戶(hù)態(tài)的設備創(chuàng )建函數*/
devfs_mk_cdev(MKDEV(BUTTON_MAJOR,0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
printk(DEVICE_NAME " initializedn");
return 0;
}
/******************mini2440_buttons_exit()****************************/
static void __exit mini2440_buttons_exit(void)
{
/*移除設備*/
devfs_remove(DEVICE_NAME);
/*注消設備驅動(dòng)*/
unregister_chrdev(BUTTON_MAJOR,DEVICE_NAME);
}
/*****************mini2440_buttons_open()******************************/
static int mini2440_buttons_open(struct inode *inode,struct file *file)
{
int i; /*循環(huán)變量,因為有6個(gè)按鈕*/
int err; /*中斷注冊函數的返回值*/
/*對每個(gè)按鈕分別處理,用for循環(huán)來(lái)做*/
/*具體地是要聯(lián)結寄存器和相應的引腳*/
/*聯(lián)結中斷號和相應的中斷服務(wù)程序*/
/*這一步類(lèi)似于前面所說(shuō)的驅動(dòng)的注冊*/
/*我們可以成功稱(chēng)作中斷的注冊*/
for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i++)
{
/*寄存器與中斷引腳的聯(lián)結*/
s3c2410_gpio_cfgpin(button_irqs[i].pin,button_irqs[i].pin_setting);
/*中斷的注冊*/
/*request_irq()函數*/
/*要注意其輸入參數*/
/*&button_irqs[i]是該中斷享有的資源*/
/*會(huì )被傳入buttons_interrupt,進(jìn)行處理*/
err = request_irq(button_irqs[i].irq,buttons_interrupt,NULL,button_irqs[i].name,(void *)&button_irqs[i]);
/*中斷類(lèi)型的設置*/
/*set_irq_type()函數*/
/*IRQT_BOTHEDGE的中斷類(lèi)型代表什么樣的中斷呢?*/
/*有幾個(gè)非常重要的問(wèn)題*/
/*中斷注冊后,并設置好其中斷類(lèi)型之后,當有中斷發(fā)生時(shí),*/
/*即按下某個(gè)按鈕時(shí),系統能夠自動(dòng)檢測到有中斷發(fā)生嗎?*/
/*檢測到有中斷發(fā)生,它能夠自動(dòng)辨別是幾號中斷嗎?*/
/*知道了是幾號中斷,那么它能自動(dòng)調用其中斷服務(wù)程序嗎?*/
/*對這幾個(gè)問(wèn)題的解答,夠成了linux系統中斷處理機制的核心*/
set_irq_type(button_irqs[i].irq,IRQT_BOTHEDGE);
/*注冊失敗的處理*/
if(err)
break; /*跳出循環(huán)*/
}
/*若有一個(gè)按鈕中斷注冊失敗*/
/*則還需把前面注冊成功的中斷給拆了*/
if(err)
{
i--; /*回到前面一個(gè)按鈕的處理*/
for(;i >=0; i--) /*依此拆除*/
{
/*使中斷不起作用*/
disable_irq(button_irqs[i].irq);
/*釋放中斷資源*/
free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
}
return -EBUSY; /*中斷注冊沒(méi)成功的最終的返回值*/
}
return 0; /*正常返回*/
}
/**************************buttons_interrupt()*****************************/
/*此中斷服務(wù)程序,在每中斷一次,就要對key_values數組設一下值*/
/*并對數組可讀標志位ev_press設一下值*/
/*并喚醒在等待隊列里的進(jìn)程*/
/*這是中斷處理經(jīng)常要做的事情*/
/*在這里,等待隊列button_waitq里經(jīng)常等待的進(jìn)程是數組的讀取進(jìn)程*/
/*就是說(shuō),讀取進(jìn)程在沒(méi)有讀到數據的時(shí)候就一直在等待,等待按鍵的輸入*/
/*讀取進(jìn)程在等待,并不代表所有進(jìn)程在等待,其它進(jìn)程該干啥干啥去*/
static irqreturn_t buttons_interrupt(int irq,void *dev_id)
{
/*button_irq_desc結構體變量*/
/*對傳入的資源進(jìn)行處理*/
struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
/*獲取寄存器的值*/
/*這一步至關(guān)重要*/
/*s3c2410_gpio_getpin()函數直接獲取寄存器的值*/
/*要注意,按一下按鈕,會(huì )發(fā)生兩次中斷*/
/*即按下是一次中斷,放開(kāi)又是一次中斷*/
int up = s3c2410_gpio_getpin(button_irqs->pin);
/*通過(guò)電路原理圖,可以知道沒(méi)按下的時(shí)候,中斷引腳應該是高電平*/
/*從而寄存器的值應該是1*/
/*變量取up也是有意義的,表示默認狀態(tài)是彈起的狀態(tài)*/
/*當按下按鈕的狀態(tài)下,寄存器的值就應該是0*/
/*下面對up的值進(jìn)行處理*/
/*即是要把數據經(jīng)過(guò)一定的變換存入key_values數組中*/
if(up) /*如果是彈起的狀態(tài)*/
/*那么就要在key_values數組的相應位存入很大的一個(gè)值*/
/*同時(shí)又要能從值里辨別出是哪個(gè)按鍵*/
key_values[button_irqs->number] = (button_irqs->number + 1) + 0x80;
/*比如K1鍵開(kāi)啟的狀態(tài)下,key_values[0]被置為(0+1)+0x80,即為129*/
else /*如果按鍵是閉合的狀態(tài)*/
/*那么就要在key_values數組的相應位存入一個(gè)很小的數*/
/*同時(shí)又要能從值中辨別出是哪個(gè)鍵*/
key_values[button_irqs->number] = (button_irqs->number + 1);
/*比如K1鍵閉合,則key_values[0]被置為(0+1),即為1*/
/*對數組可讀標志位進(jìn)行設置*/
ev_press = 1; /*表示數組已經(jīng)可讀了*/
/*喚醒休眠的進(jìn)程?*/
/*button_waitq隊列里存放有相應的處理進(jìn)程*/
/*如讀取數組的值的進(jìn)程*/
/*要注意wake_up_interruptible()這些函數的用法*/
wake_up_interruptible(&button_waitq);
/*返回*/
return IRQ_RETVAL(IRQ_HANDLED); /*?*/
}
/**********************mini2440_buttons_close()*****************************/
static int mini2440_buttons_close(struct inode *inode,struct file *file)
{
int i; /*循環(huán)變量,要操作好幾個(gè)按鍵*/
/*for循環(huán),對各個(gè)按鍵依此釋放中斷*/
for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i++)
{
/*使中斷失效*/
disable_irq(button_irqs[i].irq);
/*釋放資源*/
free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
}
/*返回*/
return 0;
}
/**********************mini2440_buttons_read()***************************/
/*要注意,該read函數,只讀取一次中斷的值,而不是連續地讀入*/
/*要做到連續地讀入,則需要做一個(gè)循環(huán),不斷地調用該read函數,但那不是驅動(dòng)程序里該做的事情*/
static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
{
unsigned long err; /*copy_to_user()函數的返回值*/
/*如果key_values 數組里沒(méi)有值,則會(huì )此進(jìn)程會(huì )休眠*/
/*一直到中斷來(lái)臨之后,中斷服務(wù)程序會(huì )喚醒此休眠進(jìn)程從而繼續讀取值*/
/*key_values數組里有沒(méi)有值,是靠ev_press標志位來(lái)判斷的*/
/*有值,就是1,無(wú)值,就是0*/
/*進(jìn)程等待隊列的機制,是進(jìn)程調度的一種方法*/
if(!ev_press) /*標志位為0,即無(wú)數據時(shí)*/
{
if(filp->f_flags & O_NONBLOCK) /*??*/
return -EAGAIN;
else /*進(jìn)程休眠,放進(jìn)button_waitq等待隊列*/
/*這里,把ev_press標志位設成了休眠進(jìn)程的標志位了?*/
/*這是為了便于利用poll_wait函數*/
/*也就是利于select函數*/
wait_event_interruptible(button_waitq,ev_press);
/*在中斷處理函數中,此進(jìn)程會(huì )被喚醒*/
/*喚醒前,ev_press 已被置1了*/
/*喚醒后的執行點(diǎn)從這里開(kāi)始*/
}
/*下面就是標志位為1,即有數據可讀的的處理情況*/
/*那就開(kāi)始往用戶(hù)空間讀數據唄*/
err = copy_to_user(buff,(const void *)key_values,min(sizeof(key_values),count));
/*copy_to_user()函數的使用*/
/*對key_values數組清零*/
memset((void *)key_values,0,sizeof(key_values));
/*對標志位置0*/
/*表示讀取過(guò)了*/
ev_press = 0;
/*對err的處理*/
if(err) /*讀取錯誤*/
return -EFAULT;
else /*讀取正確*/
/*則返回讀取到的字節數*/
return min(sizeof(key_values),count);
}
/************************mini2440_buttons_poll()***********************/
static unsigned int mini2440_buttons_poll(struct file *file,struct poll_table_struct *wait)
{
unsigned int mask = 0; /* */
/*poll_wait()函數*/
/*會(huì )監測進(jìn)程隊列button_waitq里的進(jìn)程*/
/*例如,如果mini2440_button_read所在的進(jìn)程的標志位ev_press置為1了*/
/*那么就不會(huì )再等待了*/
/*這實(shí)質(zhì)上就是select函數的運行機制*/
poll_wait(file,&button_waitq,wait);
if(ev_press)
mask |= POLLIN | POLLRDNORM; /*??*/
return mask;
}
/*按鍵測試程序*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*主函數入口*/
int main(void)
{
int i; /*鍵盤(pán)輸出時(shí)用到的循環(huán)變量*/
int buttons_fd; /*buttons設備號*/
int key_value[4]; /*四個(gè)按鍵的取值*/
/*打開(kāi)鍵盤(pán)設備文件*/
buttons_fd = open("/dev/buttons",0); /*以0方式打開(kāi)*/
/*打開(kāi)出錯處理*/
if(buttons_fd < 0) /*打開(kāi)出錯就會(huì )返回一個(gè)負值*/
{
perror("open device buttons"); /*perror函數?*/
exit(1); /*返回1*/
}
/*for無(wú)限循環(huán),等待用戶(hù)輸入*/
/*這是很典型的程序執行方式*/
for(;;)
{
fd_set rds; /*fd_set是types.h中定義的類(lèi)型,實(shí)質(zhì)上是int型*/
/*rds用來(lái)存儲設備號*/
int ret; /*for循環(huán)內定義的局部變量ret*/
FD_ZERO(&rds); /*rds初始化*/
/*FD_ZERO是哪里定義的呢?*/
FD_SET(buttons_fd,&rds); /*將buttons設備號賦給rds*/
/*FD_SET是哪里定義的呢?*/
/*使用系統調用select檢查是否能夠從/dev/buttons設備讀取數據*/
/*select函數是干什么的呢?*/
ret = select(buttons_fd + 1,&rds,NULL,NULL,NULL);
/*返回值ret*/
/*返回值的具體意義是什么呢?*/
/*對ret的處理*/
if(ret < 0) /*當ret小于0*/
{
perror("select");
exit(1);
}
if(ret == 0) /*當ret等于0*/
{
printf("Timeout.n");
}
else /*能夠讀到數據*/
if(FD_ISSET(buttons_fd,&rds)) /*??*/
{
/*讀取鍵盤(pán)驅動(dòng)發(fā)出的數據*/
/*key_value和鍵盤(pán)驅動(dòng)中定義一致*/
int ret = read(buttons_fd,key_value,sizeof(key_value)); /*注意此處的ret和前面的ret有何不同*/
/*注意鍵盤(pán)設備讀取的特點(diǎn)*/
/*對ret的處理*/
if(ret != sizeof(key_value)) /*沒(méi)有接收夠*/
{
if(errno != EAGAIN) /*???*/
perror("read buttonsn");
continue;
}
else /*正確接收,則打印到標準終端*/
{
for(i = 0;i < 4;i++) /*最開(kāi)始定義的循環(huán)變量i*/
printf("K%d %s, key value = 0x%02xn",i,(key_value[i] & 0x80) ? "released" : key_value[i] ? "pressed down" : "",key_value[i]);
/*這一連串的輸出,要注意格式*/
}
}
}
/*關(guān)閉設備*/
close(buttons_fd);
return 0; /*主函數返回*/
}
END??!
評論