嵌入式核心及驅動開發之學習筆記(七) 非阻塞模式+中斷實現讀取資料
阿新 • • 發佈:2018-11-27
當中斷髮生時,驅動程式會跳轉到中斷處理的函式入口,實現了中斷的捕獲和處理,但這樣還不夠。要讓使用者能夠獲取到中斷分析的結果,我們將建立一個描述中斷事件的結構體物件。硬體產生中斷後,驅動程式碼將對中斷事件的分析結果儲存在結構體變數中,使用者需要的時候,直接通過介面函式獲取這個結構體的資料。
核心層:
硬體(中斷事件) --> 驅動程式(中斷處理) --> 結構體變數(新增到物件屬性)
應用層:
結構體(新增到物件屬性) --> API(核心層對應用層的介面) --> 使用者程式
實驗:根據之前學到的字元驅動步驟和程式碼規範,再進一步對之前的程式調整,便有了這個程式。對於核心層,當有中斷事件,去驅動程式跳到處理函式並將中斷資訊儲存在定義的結構體中;對於應用,使用while迴圈呼叫read函式 輪詢的方式,這算是一種非阻塞模型,比較耗費CPU的資源。
驅動程式
當按鍵按下(1->0)或者鬆開(0->1)時,產生一箇中斷事件,驅動程式進入中斷處理key_irq_handler,並將對按鍵的分析資料存放在key_event型別的結構體中。提供key_drv_read函式,實際上就是應用程式獲取這個結構體資料的介面函式
//key_drv.c #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/device.h> #include <asm/io.h> #include <asm/uaccess.h> irqreturn_t key_irq_handler(int irqno, void *devid); ssize_t key_drv_read (struct file *, char __user *, size_t, loff_t *); ssize_t key_drv_write (struct file *, const char __user *, size_t, loff_t *); int key_drv_open (struct inode *, struct file *); int key_drv_close (struct inode *, struct file *); #define GPXCON_REG 0x11000C20 #define KEY_ENTER 28 const struct file_operations key_fops = { .open = key_drv_open, .read = key_drv_read, .write = key_drv_write, .release = key_drv_close, }; struct key_event{ int code; // 按鍵的型別 int value; // 狀態 }; struct key_desc{ unsigned int dev_major; struct class *cls; struct device *dev; int irqno; void *reg_base; struct key_event event; }; struct key_desc *key_dev; //static int irqno; int get_irqno_from_node(void) { //從裝置樹路徑,查詢節點 struct device_node *np = of_find_node_by_path("/key_int_node"); if(np){ printk("find node ok\n"); }else{ printk("find node failed\n"); } int irqno = irq_of_parse_and_map(np, 0); printk("irqno = %d\n", irqno); return irqno; } static int __init key_drv_init(void) { int ret; //物件例項化 key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL); //申請主裝置號 key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops); //建立裝置結點 key_dev->cls = class_create(THIS_MODULE, "key_cls"); key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major,0), NULL, "key0"); //硬體初始化 key_dev->irqno = get_irqno_from_node(); ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key3_eint10", NULL); if(ret != 0) { printk("request_irq error\n"); return ret; } key_dev->reg_base = ioremap(GPXCON_REG, 8); return 0; } static void __exit key_drv_exit(void) { iounmap(key_dev->reg_base); //去對映 free_irq(key_dev->irqno, NULL); //釋放中斷資源 device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0)); // class_destroy(key_dev->cls); // unregister_chrdev(key_dev->dev_major, "key_drv"); //登出主裝置號 kfree(key_dev); //釋放結構體記憶體 } irqreturn_t key_irq_handler(int irqno, void *devid) { printk("-------%s-------------\n", __FUNCTION__); int value = readl(key_dev->reg_base + 4) & (1<<2); if(value){// 1 printk("key3 up\n"); key_dev->event.code = KEY_ENTER; key_dev->event.value = 0; }else{// 0 printk("key3 pressed\n"); key_dev->event.code = KEY_ENTER; key_dev->event.value = 1; } return IRQ_HANDLED; } ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos) { //printk("-------%s-------------\n", __FUNCTION__); int ret; ret = copy_to_user(buf, &key_dev->event, count); if(ret > 0) { printk("copy_to_user error\n"); return -EFAULT; } // 清除key_dev->event的資料記錄 memset(&key_dev->event, 0, sizeof(key_dev->event)); return count; } ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos) { printk("-------%s-------------\n", __FUNCTION__); return 0; } int key_drv_open(struct inode *inode, struct file *filp) { printk("-------%s-------------\n", __FUNCTION__); return 0; } int key_drv_close (struct inode *inode, struct file *filp) { printk("-------%s-------------\n", __FUNCTION__); return 0; } module_init(key_drv_init); module_exit(key_drv_exit); MODULE_LICENSE("GPL");
應用程式
在應用程式中,不斷read獲取按鍵的狀態,有按鍵觸發立即列印資料。
//key_test.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> struct key_event{ int code; // 按鍵的型別 int value; // 狀態 }; #define KEY_ENTER 28 int main(int argc, char *argv[]) { struct key_event event; int fd = open("/dev/key0", O_RDWR); if(fd < 0) { perror("open"); exit(1); } while(1) { read(fd, &event, sizeof(struct key_event)); if(event.code == KEY_ENTER) { if(event.value) { printf("APP__ key enter pressed\n"); }else{ printf("APP__ key enter up\n"); } } } close(fd); return 0; }
結果演示
編譯後在開發板上執行,檢視串列埠列印結果