主題:中斷系統的實際應用
阿新 • • 發佈:2018-12-31
編譯系統 :ubuntu 16.04
內 核 :linux-2.6.22.6
硬體平臺 :jz2240
交叉編譯器:arm-linux-gcc 3.4.5
上一篇具體的分析了linux中斷框架,這裡貼出來一個具體事例
驅動程式如下:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/wait.h> #include <asm/irq.h> #include <asm/uaccess.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #include <asm/io.h> /* 這是一個按鍵驅動 * 如果沒有按鍵按下,就進行休眠 * 為什麼要休眠,因為應用程式會一直在讀或者等到按鍵被按下來, * 如果說一直沒有按下按鍵,那麼久需要讓使用者空間進行休眠,不然會一直處於讀阻塞 */ static struct class *g_buttons_class; static struct class_device *g_my_buttons_classdevice; struct buttondesc { unsigned int key_gpio; //引腳 unsigned int irq ; //中斷號 unsigned int flag; //觸發條件 const char *name; //名字 char keyval;//自定義鍵值 }; static struct buttondesc g_buttons[4] = { {S3C2410_GPF0, IRQ_EINT0, IRQT_FALLING, "S2",0x11}, {S3C2410_GPF2, IRQ_EINT2, IRQT_FALLING, "S3",0x12}, {S3C2410_GPG3, IRQ_EINT11, IRQT_FALLING, "S4",0x13}, {S3C2410_GPG11, IRQ_EINT19, IRQT_FALLING, "S5",0x14}, }; static char g_keyval;//用於返回給使用者空間 /* ------------------------------------------------------------------------- */ /* ################## 休眠相關變數 ################## */ #define SLEEP 1 #define NO_SLEEP 0 static DECLARE_WAIT_QUEUE_HEAD(g_buttons_waitq); static char g_ev_press = SLEEP;//用於標誌中斷休眠 /* ------------------------------------------------------------------------- */ /* ################## 原子操作相關變數 ################## */ static atomic_t g_atomic = ATOMIC_INIT(1);//定義一個原子,並且初始化為1 //實際的中斷處理函式 static irq_handler_t buttons_handler(int irq, void *dev_id) { struct buttondesc *temp_desc = (struct buttondesc *)dev_id;//根據傳遞引數可以獲得對應中斷號陣列 unsigned int temp_key = s3c2410_gpio_getpin(temp_desc->key_gpio);//讀取gpio引腳狀態 if(!temp_key)//如果有按鍵按下就是0 g_keyval = 0x80 | temp_desc->keyval; else g_keyval = temp_desc->keyval; //訪問全域性變數這裡要進行原子操作 if(!atomic_dec_and_test(&g_atomic))//測試其是否為0,為 0 則返回 true,否則返回 false { //已經開啟,不能再次開啟 atomic_inc(&g_atomic); return -1; } g_ev_press = NO_SLEEP;//喚醒休眠 atomic_inc(&g_atomic);//恢復原子值 wake_up_interruptible(&g_buttons_waitq); return 0; } //驅動讀函式 static ssize_t buttons_read(struct file *fp, char __user *buf, size_t size, loff_t *fops) { wait_event_interruptible(g_buttons_waitq, (g_ev_press == NO_SLEEP));//當g_ev_press == NO_SLEEP條件符合是 會被喚醒,繼續執行 if(copy_to_user(buf,&g_keyval, 1))//返回到使用者空間 printk("copy_to_user not complete\n"); //訪問全域性變數這裡要進行原子操作 if(!atomic_dec_and_test(&g_atomic))//測試其是否為0,為 0 則返回 true,否則返回 false { //已經開啟,不能再次開啟 atomic_inc(&g_atomic); return -1; } g_ev_press = SLEEP;//重新進行休眠 atomic_inc(&g_atomic);//恢復原子值 return 0; } //驅動開啟函式 static int buttons_open(struct inode *inodep, struct file *fp) { int i; //註冊中斷 for(i = 0; i < sizeof(g_buttons)/sizeof(struct buttondesc); i++) request_irq(g_buttons[i].irq,buttons_handler,g_buttons[i].flag,(const char *)g_buttons[i].name,(void *)(&g_buttons[i])); //訪問全域性變數這裡要進行原子操作 if(!atomic_dec_and_test(&g_atomic))//測試其是否為0,為 0 則返回 true,否則返回 false { //已經開啟,不能再次開啟 atomic_inc(&g_atomic); return -1; } g_ev_press = SLEEP;//進行休眠 atomic_inc(&g_atomic);//恢復原子值 printk(KERN_INFO "open buttons success\n"); return 0; } //關閉 static int buttons_close(struct inode *inodep, struct file *fp) { int i; for(i = 0; i < sizeof(g_buttons)/sizeof(struct buttondesc); i++) free_irq(g_buttons[i].irq,&g_buttons[i]); return 0; } //操作函式集 static struct file_operations g_buttons_op = { .owner = THIS_MODULE, .open = buttons_open, .read = buttons_read, .release = buttons_close, }; int g_buttons_major; static int __init buttons_init(void) { g_buttons_major = register_chrdev(0, "my_buttons", &g_buttons_op); //註冊裝置,在/proc/device下可以看到 g_buttons_class = class_create(THIS_MODULE, "HBUT_class"); //建立類 g_my_buttons_classdevice = class_device_create(g_buttons_class, NULL, MKDEV(g_buttons_major, 0), NULL, "buttons"); // 可以看到/dev/button return 0; } static void __exit buttons_exit(void) { unregister_chrdev(g_buttons_major,"my_buttons"); class_device_destroy(g_buttons_class,MKDEV(g_buttons_major, 0)); class_destroy(g_buttons_class); } MODULE_AUTHOR("大白菜"); MODULE_DESCRIPTION("buttons drive with sleep and interrupt"); MODULE_LICENSE("GPL"); module_init(buttons_init); module_exit(buttons_exit);
使用者程式如下:
#include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <error.h> #include <stdio.h> #include <string.h> #include <signal.h> /* ifconfig eth0 192.168.0.11 mount -t nfs -o nolock,vers=2 192.168.0.104:/home/book/wangruo /mnt cd /mnt/led */ int g_fd;//檔案描述符 void my_handler(int sign_num) { printf("\n ---------press Ctrl + c ---------\n"); close(g_fd); exit(0); } int main(int argc,int **argv) { char keyval = 0; int count=0; g_fd = open("/dev/buttons",O_RDWR); if(g_fd < 0) { perror("open /dev/buttons"); return -1; } //接收到訊號 signal(SIGINT,my_handler); while(1) { read(g_fd,&keyval,sizeof(char));//沒有中斷時,休眠 printf("keyval = 0x%x count = %d\n",keyval,++count); } close(g_fd); return 0; }
應用程式執行結果: