新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 一個相當詳細的MINI2440按鍵驅動詳解

        一個相當詳細的MINI2440按鍵驅動詳解

        作者: 時間:2016-11-11 來源:網絡 收藏
        /*mini2440_buttons_my.c*/

        /*后面加了_my*/

        本文引用地址:http://www.104case.com/article/201611/316663.htm

        /*按鍵驅動程序*/

        /*mini2440所用到的按鍵資源*/
        /**************************************************/
        /* 按鍵 對應的IO寄存器 對應的中斷引腳*/
        /* K1 GPG0 EINT8 */
        /* K2 GPG3 EINT11 */
        /* K3 GPG5 EINT13 */
        /* K4 GPG6 EINT14 */
        /* K5 GPG7 EINT15 */
        /* K6 GPG11 EINT19 */
        /**************************************************/

        /*要搞清楚誰是輸入*/
        /*在這里,按鍵控制對應的中斷引腳,從而控制對應的IO寄存器*/
        /*相當于信息從外面輸入*/
        /*我們要做的是根據對應的輸入信息,來采取相應的響應動作*/
        /*這就達到了中斷響應的目的*/
        /*其核心就是要檢測*/
        /*那么,該如何去檢測呢?*/
        /*通過什么來檢測呢?*/

        /*如何得知一個設備究竟用到哪些資源呢?*/
        /*這是個非常重要的問題*/
        /*我想應該看具體的電路原理圖*/
        /*只有看圖,才能了解具體的電路連接情況*/
        /*從而得知設備所需的硬件資源*/
        /*廠商的原理圖通常給的都比較詳細*/

        /*引用的頭文件*/

        #include /*模塊有關的*/

        #include /*內核有關的*/

        #include /*文件系統有關的*/

        #include /*init*/

        #include /*delay*/

        #include /*poll*/

        #include /*中斷*/

        #include interrupt.h> /*linux中斷*/

        #include /*uaccess*/

        #include /*寄存器設置*/

        #include /*hardware*/

        /*定義宏*/

        #define BUTTON_MAJOR 221 /*主設備號,本來是232,我改為221*/

        #define DEVICE_NAME "buttons_my" /*設備名,本來是buttons,我加上了_my*/

        /*定義按鈕中斷的描述結構體*/
        /*由它把按鈕中斷的信息綜合起來*/
        /*各個成員表示什么意思?*/

        struct button_irq_desc
        {
        int irq; /*中斷號*/
        /*中斷號唯一表示一個中斷*/

        int pin; /*中斷控制的寄存器*/
        /*該寄存器的值由中斷引腳設置*/
        /*我們希望從該寄存器讀出控制信息*/

        int pin_setting; /*中斷的引腳*/
        /*該引腳的電平由按鍵來控制*/
        /*從而最終我們由按鍵控制了寄存器的值*/

        int number; /*編號*/
        char *name; /*名稱*/
        };

        /*指定6個按鍵的信息*/

        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*/
        }

        /*這樣,資源就組織起來了*/
        /*事實上,在這里我們不僅組織起了硬件資源*/
        /*我們也把一定的軟件資源也糅合進去了*/
        /*像中斷號*/


        /*key_values數組*/
        /*存放各個按鍵在發生中斷情況下的值*/
        /*volatile是什么意思呢?*/
        /*這個數組是我們存放按鍵操作結果的,因此非常重要*/

        static volatile int key_values [] = {0,0,0,0,0,0};


        /*宏DECLARE_WAIT_QUEUE_HEAD(),是干什么的呢?*/
        /*該宏應該是創建了一個等待隊列*/
        /*等待隊列,是進程調度的一種重要方法*/
        /*等待隊列也很有意思,button_waitq,表示按鍵等待的隊列*/
        /*就是說,按鍵一按下,就會激活其等待隊列里的進程,來做相應的處理*/
        /*因此,按鍵的等待隊列,或者說中斷所設置的等待隊列,*/
        /*是中斷處理中非常重要的資源,它大大擴展了中斷處理的能力*/


        static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*button_waitq是什么呢?*/
        /*應該是等待隊列的名稱*/


        /*key_values數組中是否有數據的標志,0表示無數據可讀,1表示有數據可讀*/

        static volatile int ev_press = 0; /*初始為0*/

        /*中斷服務程序buttons_interrupt()的申明*/
        /*即當檢測到有中斷時,就會執行該中斷服務程序*/
        /*那么如何檢測到有中斷發生呢?*/
        /*并且中斷發生了,知道發生了什么樣的中斷呢?*/
        /*中斷有很多種,該中斷服務程序究竟該服務于哪一個中斷呢?*/
        /*顯然,要把中斷號與中斷服務程序聯結起來,構成一個整體*/
        /*這個工作可以在open函數里做*/

        /*參數irq---中斷號*/
        /*中斷服務程序應該是與中斷號一一對應的*/
        /*對應于某個中斷號的中斷一發生,就會調用該中斷號對應的服務程序*/
        /*那么,檢測中斷的發生,就成了先決條件*/
        /*參數dev_id ---具體是哪一個按鈕*/

        static irqreturn_t buttons_interrupt(int irq,void *dev_id);

        /*mini2440_buttons_open()函數申明*/
        /*驅動函數open調用的具體函數*/
        /*由open函數具體實現硬件的初始化工作*/
        /*以及軟件的初始化工作*/
        /*為我們的鍵盤設備的運行創造好環境*/

        static int mini2440_buttons_open(struct inode *inode,struct file *file);

        /*mini2440_buttons_close()函數的申明*/
        /*release調用的具體函數*/
        /*設備軟件環境的拆卸*/
        /*具體就是中斷的釋放工作*/
        /*因為中斷資源,也是系統寶貴的資源,所以不用的時候,要釋放*/

        static int mini2440_buttons_close(struct inode *inode,struct file *file);

        /*mini2440_buttons_read()函數的申明*/
        /*read調用的具體函數*/
        /*由它讀取鍵盤輸入的結果*/
        /*實質上就是讀取key_values數組的值*/
        /*它完成了鍵盤作為輸入設備的核心功能*/
        /*數組是否可讀,要根據標志位ev_press來判斷*/
        /*如果數組可讀,則讀取數據到用戶buffer中*/
        /*如果數組不可讀,則進程進入等待隊列,等待到數組可讀為止*/
        /*等待隊列機制,是中斷管理中常用到的機制*/
        /*因為有些進程經常需要等待某一事件的發生*/

        static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp);
        /*注意__user,指的是用戶空間*/
        /*即要把鍵盤的輸入結果讀取到用戶空間去*/

        /*mini2440_buttons_poll()函數的申明*/
        /*poll調用的具體函數*/
        /*poll實質上是select的調用函數*/
        /*如果有按鍵數據,則select會立刻返回*/
        /*如果沒有按鍵數據,則等待*/
        /*實質上這是鍵盤等待輸入的機制*/

        static unsigned int mini2440_buttons_poll(struct file *file,struct poll_table_struct *wait);


        /*file_operations結構體*/
        /*驅動函數的設置*/
        /*分別將前面的驅動函數設置進來*/

        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調用的具體函數*/
        /*模塊創建時的初始化函數*/
        /*主要做的工作是注冊設備和創建設備*/
        /*而具體的硬件初始化工作,它可以不做*/
        /*而把它留給fops里的函數來做*/

        static int __init mini2440_buttons_init(void);

        /*mini2440_buttons_exit()函數的申明*/
        /*模塊卸載時的掃尾工作*/
        /*主要是設備的卸載工作*/

        static void __exit mini2440_buttons_exit(void);

        /*模塊創建時的入口點*/

        module_init(mini2440_buttons_init);


        /*模塊卸載時的入口點*/

        module_exit(mini2440_buttons_exit);

        /*驅動程序的一些信息*/

        MODULE_AUTHOR("http://www.arm9.net"); /*驅動程序的作者*/

        MODULE_DESCRIPTION("S3C2410/S3C2440 BUTTON Driver"); /*描述信息*/

        MODULE_LICENSE("GPL"); /*遵循的協議*/

        /********************************************************************/
        /*********************下面是前面申明函數的實現***********************/
        /********************************************************************/


        /**********************mini2440_buttons_init()***********************/

        static int __init mini2440_buttons_init(void)
        {
        int ret; /*設備注冊的返回值*/

        /*注冊設備驅動程序*/
        /*設備號,設備名,和驅動函數*/

        ret = register_chrdev(BUTTON_MAJOR,DEVICE_NAME,&mini2440_buttons_fops);

        /*對注冊失敗的處理*/

        if(ret < 0)
        {
        printk(DEVICE_NAME " cant register major numbern");
        return ret;
        }

        /*創建設備*/
        /*devfs_mk_cdev()函數是內核態的設備創建函數*/
        /*而mknod是用戶態的設備創建函數*/

        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);

        /*注消設備驅動*/

        unregister_chrdev(BUTTON_MAJOR,DEVICE_NAME);
        }


        /*****************mini2440_buttons_open()******************************/

        static int mini2440_buttons_open(struct inode *inode,struct file *file)
        {
        int i; /*循環變量,因為有6個按鈕*/

        int err; /*中斷注冊函數的返回值*/

        /*對每個按鈕分別處理,用for循環來做*/
        /*具體地是要聯結寄存器和相應的引腳*/
        /*聯結中斷號和相應的中斷服務程序*/
        /*這一步類似于前面所說的驅動的注冊*/
        /*我們可以成功稱作中斷的注冊*/

        for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i++)
        {
        /*寄存器與中斷引腳的聯結*/

        s3c2410_gpio_cfgpin(button_irqs[i].pin,button_irqs[i].pin_setting);

        /*中斷的注冊*/
        /*request_irq()函數*/
        /*要注意其輸入參數*/
        /*&button_irqs[i]是該中斷享有的資源*/
        /*會被傳入buttons_interrupt,進行處理*/

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

        /*中斷類型的設置*/
        /*set_irq_type()函數*/
        /*IRQT_BOTHEDGE的中斷類型代表什么樣的中斷呢?*/

        /*有幾個非常重要的問題*/
        /*中斷注冊后,并設置好其中斷類型之后,當有中斷發生時,*/
        /*即按下某個按鈕時,系統能夠自動檢測到有中斷發生嗎?*/
        /*檢測到有中斷發生,它能夠自動辨別是幾號中斷嗎?*/
        /*知道了是幾號中斷,那么它能自動調用其中斷服務程序嗎?*/
        /*對這幾個問題的解答,夠成了linux系統中斷處理機制的核心*/

        set_irq_type(button_irqs[i].irq,IRQT_BOTHEDGE);

        /*注冊失敗的處理*/

        if(err)
        break; /*跳出循環*/
        }

        /*若有一個按鈕中斷注冊失敗*/
        /*則還需把前面注冊成功的中斷給拆了*/

        if(err)
        {
        i--; /*回到前面一個按鈕的處理*/

        for(;i >=0; i--) /*依此拆除*/
        {
        /*使中斷不起作用*/

        disable_irq(button_irqs[i].irq);

        /*釋放中斷資源*/

        free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
        }

        return -EBUSY; /*中斷注冊沒成功的最終的返回值*/
        }

        return 0; /*正常返回*/
        }


        /**************************buttons_interrupt()*****************************/
        /*此中斷服務程序,在每中斷一次,就要對key_values數組設一下值*/
        /*并對數組可讀標志位ev_press設一下值*/
        /*并喚醒在等待隊列里的進程*/
        /*這是中斷處理經常要做的事情*/
        /*在這里,等待隊列button_waitq里經常等待的進程是數組的讀取進程*/
        /*就是說,讀取進程在沒有讀到數據的時候就一直在等待,等待按鍵的輸入*/
        /*讀取進程在等待,并不代表所有進程在等待,其它進程該干啥干啥去*/

        static irqreturn_t buttons_interrupt(int irq,void *dev_id)
        {
        /*button_irq_desc結構體變量*/
        /*對傳入的資源進行處理*/

        struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

        /*獲取寄存器的值*/
        /*這一步至關重要*/
        /*s3c2410_gpio_getpin()函數直接獲取寄存器的值*/

        /*要注意,按一下按鈕,會發生兩次中斷*/

        /*即按下是一次中斷,放開又是一次中斷*/

        int up = s3c2410_gpio_getpin(button_irqs->pin);

        /*通過電路原理圖,可以知道沒按下的時候,中斷引腳應該是高電平*/
        /*從而寄存器的值應該是1*/
        /*變量取up也是有意義的,表示默認狀態是彈起的狀態*/
        /*當按下按鈕的狀態下,寄存器的值就應該是0*/

        /*下面對up的值進行處理*/
        /*即是要把數據經過一定的變換存入key_values數組中*/

        if(up) /*如果是彈起的狀態*/
        /*那么就要在key_values數組的相應位存入很大的一個值*/
        /*同時又要能從值里辨別出是哪個按鍵*/

        key_values[button_irqs->number] = (button_irqs->number + 1) + 0x80;
        /*比如K1鍵開啟的狀態下,key_values[0]被置為(0+1)+0x80,即為129*/

        else /*如果按鍵是閉合的狀態*/
        /*那么就要在key_values數組的相應位存入一個很小的數*/
        /*同時又要能從值中辨別出是哪個鍵*/

        key_values[button_irqs->number] = (button_irqs->number + 1);
        /*比如K1鍵閉合,則key_values[0]被置為(0+1),即為1*/

        /*對數組可讀標志位進行設置*/

        ev_press = 1; /*表示數組已經可讀了*/

        /*喚醒休眠的進程?*/
        /*button_waitq隊列里存放有相應的處理進程*/
        /*如讀取數組的值的進程*/
        /*要注意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; /*循環變量,要操作好幾個按鍵*/

        /*for循環,對各個按鍵依此釋放中斷*/

        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函數,只讀取一次中斷的值,而不是連續地讀入*/
        /*要做到連續地讀入,則需要做一個循環,不斷地調用該read函數,但那不是驅動程序里該做的事情*/

        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 數組里沒有值,則會此進程會休眠*/
        /*一直到中斷來臨之后,中斷服務程序會喚醒此休眠進程從而繼續讀取值*/
        /*key_values數組里有沒有值,是靠ev_press標志位來判斷的*/
        /*有值,就是1,無值,就是0*/

        /*進程等待隊列的機制,是進程調度的一種方法*/

        if(!ev_press) /*標志位為0,即無數據時*/
        {
        if(filp->f_flags & O_NONBLOCK) /*??*/
        return -EAGAIN;
        else /*進程休眠,放進button_waitq等待隊列*/
        /*這里,把ev_press標志位設成了休眠進程的標志位了?*/
        /*這是為了便于利用poll_wait函數*/
        /*也就是利于select函數*/
        wait_event_interruptible(button_waitq,ev_press);
        /*在中斷處理函數中,此進程會被喚醒*/
        /*喚醒前,ev_press 已被置1了*/
        /*喚醒后的執行點從這里開始*/
        }

        /*下面就是標志位為1,即有數據可讀的的處理情況*/

        /*那就開始往用戶空間讀數據唄*/

        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*/
        /*表示讀取過了*/

        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()函數*/
        /*會監測進程隊列button_waitq里的進程*/
        /*例如,如果mini2440_button_read所在的進程的標志位ev_press置為1了*/
        /*那么就不會再等待了*/
        /*這實質上就是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; /*鍵盤輸出時用到的循環變量*/

        int buttons_fd; /*buttons設備號*/

        int key_value[4]; /*四個按鍵的取值*/

        /*打開鍵盤設備文件*/

        buttons_fd = open("/dev/buttons",0); /*以0方式打開*/

        /*打開出錯處理*/

        if(buttons_fd < 0) /*打開出錯就會返回一個負值*/
        {
        perror("open device buttons"); /*perror函數?*/

        exit(1); /*返回1*/
        }

        /*for無限循環,等待用戶輸入*/
        /*這是很典型的程序執行方式*/

        for(;;)
        {
        fd_set rds; /*fd_set是types.h中定義的類型,實質上是int型*/
        /*rds用來存儲設備號*/

        int ret; /*for循環內定義的局部變量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)) /*??*/
        {
        /*讀取鍵盤驅動發出的數據*/
        /*key_value和鍵盤驅動中定義一致*/

        int ret = read(buttons_fd,key_value,sizeof(key_value)); /*注意此處的ret和前面的ret有何不同*/
        /*注意鍵盤設備讀取的特點*/

        /*對ret的處理*/
        if(ret != sizeof(key_value)) /*沒有接收夠*/
        {
        if(errno != EAGAIN) /*???*/
        perror("read buttonsn");
        continue;
        }
        else /*正確接收,則打印到標準終端*/
        {
        for(i = 0;i < 4;i++) /*最開始定義的循環變量i*/
        printf("K%d %s, key value = 0x%02xn",i,(key_value[i] & 0x80) ? "released" : key_value[i] ? "pressed down" : "",key_value[i]);
        /*這一連串的輸出,要注意格式*/
        }
        }
        }

        /*關閉設備*/

        close(buttons_fd);

        return 0; /*主函數返回*/

        END!!



        關鍵詞: MINI2440按鍵驅

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 会昌县| 榆林市| 伊宁县| 建昌县| 乌鲁木齐县| 阳城县| 云和县| 石渠县| 崇信县| 托里县| 馆陶县| 繁峙县| 长治县| 新乐市| 马公市| 习水县| 重庆市| 雅江县| 华容县| 本溪市| 大港区| 兴海县| 上饶县| 七台河市| 永泰县| 新民市| 乡城县| 依安县| 榆社县| 通渭县| 塔河县| 巴东县| 工布江达县| 当涂县| 开江县| 贵州省| 黄大仙区| 闽清县| 新泰市| 鄂州市| 金阳县|