OK6410按键中断方式的字符设备驱动编写

使用轮询的方法检测按键会得的程序占用CPU的使用率。因此使用中断的方式编写设备驱动。

本次编写字符驱动采用linux内核版本为3.0.1,板子为OK6410

 

在linux系统中不同设备对应不同的中断,不同中断则通过唯一的数字标志。这些中断值称为中断请求(IRQ)线。每个IRQ线都会关联一个数值量——比如经典的PC机上,IRQ 1是键盘中断。

申请中断

在编写驱动若需要使用中断,则必须注册一个中断处理程序。使用request_irq()函数注册,该函数在<linux/interrupt.h>头文件中。利用该函数分配并且激活一条的中断线,以处理中断。

int request_irq(unsigned int irq,
                irq_handler_t handler,
                unsigned long flags,
                const char *name,
                void *dev)

其中

irq:中断号。linux系统通过irq中分辨不同的中断IRQ线,在头文件中有相关的宏定义。外部中断宏定义为IRQ_EINT(x)。该宏定义位于arch/arm/mach-s3c64xx/include/mach/irqs.h中。

handler:中断处理程序。该函数的可能返回值有IRQ_NONE和IRQ_HANDLED,或者返回宏定义IRQ_RETVAL(val)。程序声明如下:

static irqreturn_irq_handler(int irq, void *dev)

flags:标志位,可以为零,也可以为多个标志的位掩码。定义在文件<linux/interrupt.h>中。可利用该标志位定义如按键中上升沿触发或者下降沿触发。

name:中断名,可随意设置

dev:dev是一个通用指针,该值具有唯一确定性,可以用于区分共享同一个中断处理程序的多个设备

释放中断处理程序

利用函数free_irq注销相应的中断处理程序,并且释放中断线。

void free_irq(unsigned int irq, void *dev)

 

 

 

字符驱动设备如下:

#include <linux/fs.h>  
#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/irq.h>
#include <linux/poll.h>  
#include <asm/irq.h>  
#include <asm/io.h>  
#include <linux/interrupt.h>  
#include <linux/cdev.h>  
#include <asm/uaccess.h>
#include <mach/gpio.h>
#include <linux/wait.h>
#include <linux/sched.h>  
#include <mach/hardware.h>  
#include <linux/platform_device.h>   
#include <plat/gpio-cfg.h>  

struct class *key_class;
struct class_device *key_class_device;
volatile unsigned long *gpmcon = NULL;
volatile unsigned long *gpmdat = NULL; 

struct cdev cdev;
dev_t dev;


static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;

unsigned int key_val;

struct pin_desc
{
	unsigned pin;
	unsigned key_val;
};

struct pin_desc pins_desc[4] = {
	{S3C64XX_GPN(0), 0x01},
	{S3C64XX_GPN(1), 0x02},
	{S3C64XX_GPN(2), 0x03},
	{S3C64XX_GPN(3), 0x04},	
};


static irqreturn_t button_irq(int irq, void *dev)
{
	unsigned int pval;
	struct pin_desc *pdesc = (struct pin_desc*)dev;

	pval = gpio_get_value(pdesc -> pin); //读取相应引脚的值
	if(pval)
	{
		key_val = 0x80 | pdesc -> key_val;
	}
	else
		key_val = pdesc -> key_val;
	
	ev_press = 1;
	wake_up_interruptible(&button_waitq); //当中断发生时该函数用于唤醒

	printk("press check\n");

	return IRQ_RETVAL(IRQ_HANDLED);
}



unsigned int key_poll(struct file *file, poll_table *wait)
{
	unsigned int mask = 0;
 
	poll_wait(file, &button_waitq, wait);//将button_waitq放入队列中。到这里还没有进入休眠
 
	if (ev_press)
		mask |= POLLIN | POLLWRNORM;//mask是返回应用程序poll的返回值,mask一定要和应用程序中的events相对应,不然还会进行超时
 
	return mask;
}

int key_open(struct inode *inode,struct file *file)
{

	request_irq(IRQ_EINT(0), button_irq, IRQF_SAMPLE_RANDOM | IRQ_TYPE_EDGE_BOTH |IRQF_SHARED, "s0",&pins_desc[0]);  /* 这里的irq指的不是中断源 */
	request_irq(IRQ_EINT(1), button_irq, IRQF_SAMPLE_RANDOM | IRQ_TYPE_EDGE_BOTH |IRQF_SHARED, "s1",&pins_desc[1]);
	request_irq(IRQ_EINT(2), button_irq, IRQF_SAMPLE_RANDOM | IRQ_TYPE_EDGE_BOTH |IRQF_SHARED, "s2",&pins_desc[2]);
	request_irq(IRQ_EINT(3), button_irq, IRQF_SAMPLE_RANDOM | IRQ_TYPE_EDGE_BOTH |IRQF_SHARED, "s3",&pins_desc[3]);
	
	
	printk("open!\n");
	return 0;
}

ssize_t key_release(struct inode *inode,struct file *file)
{
	free_irq(IRQ_EINT(0),&pins_desc[0]);
	free_irq(IRQ_EINT(1),&pins_desc[1]);
	free_irq(IRQ_EINT(2),&pins_desc[2]);
	free_irq(IRQ_EINT(3),&pins_desc[3]);
	return 0;
}


ssize_t key_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    
	if(count != 1)
	return -EINVAL;

	printk("$$$$$$$$$$key_read$$$$$$$$$\n");
        wait_event_interruptible(button_waitq, ev_press);
 /* 当ev_press为1且wake_up_interruptible()函数执行后read函数才能继续进行,否则堵塞在此处*/
        copy_to_user(buf,&key_val,1);
	ev_press = 0;
	return count;
 
}

ssize_t key_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	char mbuf[10];
	printk("write!\n");
	copy_from_user(mbuf,buf,count);
	    switch(mbuf[0])
    {
        case 1:
            writel(0x01, gpmdat);
            break;
 
        case 0:
            writel(0x0F,gpmdat);
            break;
		default:
			break;

    }
		return count;
}



struct file_operations key = {
	.owner   = THIS_MODULE,
	.open    = key_open,
	.read    = key_read,
	.write   = key_write,
	.release = key_release,
	.poll    = key_poll,
};


int mykey_init(void)
{
    cdev_init(&cdev,&key);

	alloc_chrdev_region(&dev,0,1,"int_key_chrdev");

	cdev_add(&cdev,dev,1);

	key_class = class_create(THIS_MODULE,"int_key");
	/*if(IS_ERR(led_class))
	return PTR_ERR(led_class);*/

	key_class_device = device_create(key_class,NULL,dev,NULL,"int_key1");
	/* if(unlikely(IS_ERR(led_class)))
	return PTR_ERR(led_class); */
   
	return 0;
}

void key_exit(void)
{
	cdev_del(&cdev);
	unregister_chrdev_region(dev,1);
	device_unregister(key_class_device);
	class_destroy(key_class);
}

MODULE_LICENSE("GPL");
module_init(mykey_init);
module_exit(key_exit);

其中模块的init函数不能命名为key_init()。因为在相关文件中有宏定义,因此这里命名为mykey_init。

另外飞凌官方资料给的linux系统中似乎已经使用了按键中断线,因此在request_irq()中flag要加上IRQF_SHARED,该宏定义允许表示多个设备共用一个中断线,否则会报错。

测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>


int main(int argc, char **argv)
{
    int fd;
    char val;

    fd = open("/dev/int_key1",O_RDWR);
    
    if(fd < 0)
    { 
        printf("Can't open\n");
    }

    while(1)
{   
    read(fd,&val,1);
    printf("key_val = 0x%x",val); 
    }
    close(fd);
    return 0;
}

Makefile

KERDIR := /home/wade/arm/cha_driver/linux-3.0.1.new

all:
	make -C $(KERDIR) M=`pwd` modules

clean:
	make -C $(KERDIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m  += int_drv.o

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编写字符设备驱动程序需要以下步骤: 1. 创建设备文件: 在/dev目录下创建设备文件,使用mknod命令或者调用系统函数makedev()来创建设备文件。 2. 定义设备结构体: 定义一个结构体来描述设备的属性,包括设备号、设备名称、设备操作函数等。 3. 初始化设备结构体: 在设备结构体中初始化设备的属性,例如设备名称、设备号、设备操作函数等。 4. 实现设备操作函数: 设备操作函数包括打开设备、关闭设备、读取设备和写入设备等。 5. 注册字符设备驱动: 通过调用系统函数register_chrdev()来注册字符设备驱动。 6. 卸载字符设备驱动: 通过调用系统函数unregister_chrdev()来卸载字符设备驱动。 以下是一个简单的字符设备驱动程序示例: ``` #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/cdev.h> #include <asm/uaccess.h> #define DEVICE_NAME "my_device" #define BUFFER_SIZE 1024 static char device_buffer[BUFFER_SIZE]; static dev_t dev_num; static struct cdev *driver_object; static struct class *device_class; static int driver_open(struct inode *gerbe, struct file *filep) { printk(KERN_INFO "Driver: open()\n"); return 0; } static int driver_close(struct inode *gerbe, struct file *filep) { printk(KERN_INFO "Driver: close()\n"); return 0; } static ssize_t driver_read(struct file *filep, char *buffer, size_t len, loff_t *offset) { int bytes_read = 0; printk(KERN_INFO "Driver: read()\n"); if (*offset >= BUFFER_SIZE) return 0; if (*offset + len > BUFFER_SIZE) len = BUFFER_SIZE - *offset; bytes_read = copy_to_user(buffer, device_buffer + *offset, len); *offset += len - bytes_read; return len - bytes_read; } static ssize_t driver_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) { int bytes_write = 0; printk(KERN_INFO "Driver: write()\n"); if (*offset >= BUFFER_SIZE) return -ENOSPC; if (*offset + len > BUFFER_SIZE) len = BUFFER_SIZE - *offset; bytes_write = copy_from_user(device_buffer + *offset, buffer, len); *offset += len - bytes_write; return len - bytes_write; } static struct file_operations fops = { .owner = THIS_MODULE, .open = driver_open, .release = driver_close, .read = driver_read, .write = driver_write, }; static int __init driver_init(void) { int ret; printk(KERN_INFO "Driver: initialization started\n"); ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME); if (ret < 0) { printk(KERN_ALERT "Driver: failed to allocate major number\n"); return ret; } driver_object = cdev_alloc(); if (driver_object == NULL) { printk(KERN_ALERT "Driver: failed to allocate cdev object\n"); unregister_chrdev_region(dev_num, 1); return -ENOMEM; } driver_object->ops = &fops; ret = cdev_add(driver_object, dev_num, 1); if (ret < 0) { printk(KERN_ALERT "Driver: failed to add character device\n"); cdev_del(driver_object); unregister_chrdev_region(dev_num, 1); return ret; } device_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(device_class)) { printk(KERN_ALERT "Driver: failed to create device class\n"); cdev_del(driver_object); unregister_chrdev_region(dev_num, 1); return PTR_ERR(device_class); } device_create(device_class, NULL, dev_num, NULL, DEVICE_NAME); printk(KERN_INFO "Driver: initialization completed\n"); return 0; } static void __exit driver_exit(void) { printk(KERN_INFO "Driver: exiting\n"); device_destroy(device_class, dev_num); class_destroy(device_class); cdev_del(driver_object); unregister_chrdev_region(dev_num, 1); } module_init(driver_init); module_exit(driver_exit); MODULE_LICENSE("GPL"); ``` 这是一个非常简单的字符设备驱动程序示例,它创建一个名为“my_device”的设备,并实现了打开、关闭、读取和写入设备操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值