1. 程式人生 > >嵌入式核心及驅動開發之學習筆記(七) 非阻塞模式+中斷實現讀取資料

嵌入式核心及驅動開發之學習筆記(七) 非阻塞模式+中斷實現讀取資料

當中斷髮生時,驅動程式會跳轉到中斷處理的函式入口,實現了中斷的捕獲和處理,但這樣還不夠。要讓使用者能夠獲取到中斷分析的結果,我們將建立一個描述中斷事件的結構體物件。硬體產生中斷後,驅動程式碼將對中斷事件的分析結果儲存在結構體變數中,使用者需要的時候,直接通過介面函式獲取這個結構體的資料。

核心層:

硬體(中斷事件) -->  驅動程式(中斷處理) --> 結構體變數(新增到物件屬性)

應用層:

結構體(新增到物件屬性) --> 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;

}



 

結果演示  

編譯後在開發板上執行,檢視串列埠列印結果