1. 程式人生 > >主題:中斷系統的實際應用

主題:中斷系統的實際應用

編譯系統   :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;
}


應用程式執行結果: