linux驱动中断接收数据read,linux设备驱动的中断与并发请求事件

设备驱动的中断事件处理如下图所示,他与普通驱动的不同之处在于多了个数据缓冲区,驱动程序对上层提供的read/write方法并不直接完成硬件的数据操作,中断处理程序也是相对独立,他们通过缓冲区交换数据。而数据缓冲区的数据可通过FIFO进行读和写的操作,但每次只能进行读或者写的操作,两者不可同时进行,这就又涉及到了并发请求。并发请求类似信号量中的PV操作,对数据缓冲区的读写就相当于之前的生产者和消费者到仓库的存取。

37638064_1.png

中断事件涉及的函数

头文件

#include

申请中断函数,成功返回0

int request_irq(unsigned int irq,

void(*handler)(int,void*,struct pt_regs*),

unsigned long irqflag,

const char *devname, void *dev_id);

//irq:中断号

// irqflag:

//SA_INTERRUPT,快速中断,执行handler函数时不能被打断

//SA_SHIRQ,共享中断,,执行handler函数时能被打断

//SA_SAMPLE_RANDOM,中断可能被用来产生随机数

// dev_id:在共享中断时用于区分不同的中断处理程序

// devname:中断设备名,在中断下运行cat /dev/interrupt可查看到

释放中断函数

void free_irq(unsigned int irq, void *dev_id);

#include

使能中断函数

void enable_irq(unsigned int irq);

禁止中断函数

void disable_irq(unsigned int irq);

S3C2410使能GPIO的外部中断功能

int set_external_irq(int irq, int edge, int pullup);

// edge:

//EXT_LOWLEVEL,

//EXT_HIGHLEVEL,

//EXT_FALLING_EDGE,

//EXT_RISING_EDGE,

//EXT_BOTH_EDGES

中断处理程序示例

void handler(int irq, void *dev_id, struct pt_regs *regs)

{

//中断处理

}

在初始化时申请并初始化中断

set_external_irq(IRQ_EINT0, EXT_FALLING_EDGE, 0);

request_irq(IRQ_EINT0,handler,SA_INTERRUPT,"KEY",NULL);

注意:要把使能外部中断函数set_external_irq放在中断请求函数request_irq之前,否则,会有小麻烦。

并发请求涉及的函数

使用信号量控制并发请求

#include

定义信号量变量

struct semaphore bufflock;

初始化信号量

void sema_init(struct semaphore *sem, int);

获取(等待)信号量(不可打断)(相当于V操作)

void down(struct semaphore *sem);

获取(等待)信号量(可被打断)

int down_interruptible(struct semaphore *sem);

释放信号量(相当于P操作)

void up(struct semaphore *sem);

FIFO队列

由于队列具有先进先出的功能,所以它能符合充当数据缓冲区的功能。我们可以自己写一个FIFO函数实现读和写的功能,也可调用/kernel/kernel/fifo.c的函数。如果要调用/kernel/kernel/fifo.c,编译时要注意,可用命令进行:/usr/local/arm/2.95.3/bin/arm-linux-gcc -c -o kfifo.o kfifo.c -I/home/su/kernel/include -D__KERNEL__ -DMODULE -DEXPORT_SYMTAB –DMODVERSIONS。当我们完成编译后,要先加载kfifo.o,再加载我们自己写的驱动,否则会出错。以下以调用/kernel/kernel/fifo.c为例。

#include

首先需要定义一个自旋锁,该锁由kfifo维护,我们只需要定义它即可

static spinlock_t buffer_lock = SPIN_LOCK_UNLOCKED;

然后定义一个kfifo指针,注意,这里定义指针即可,不用分配空间

static struct kfifo *buffer;

使用kfifo_alloc可以创建一个BUFFER_SIZE大小的fifo,所有空间由kfifo自动分配

#define BUFFER_SIZE256

buffer = kfifo_alloc(BUFFER_SIZE, GFP_KERNEL, &buffer_lock);

使用kfifo_put可以将数据放入kfifo内

kfifo_put(buffer, &key, sizeof(key));

使用kfifo_len可以检查fifo内的可用数据

if(kfifo_len(buffer) >= sizeof(key))

使用kfifo_get可以从fifo内取出数据

kfifo_get(buffer, &key, sizeof(key));

最后退出时,释放buffer的内存空间

kfifo_free(buffer);

附:基于凌阳SPCE3200实验箱的4x4键盘的驱动(中断法实现)源代码

这里有两个需要注意的地方:

1、头文件的摆放顺序:#include 要放在#include 之后,否则编译出错,因为asm/irq.h里面定义了一些linux/kfifo.h要用到的宏,若先写#include ,加载驱动时,编译器会把里头的宏当成函数处理,但又找不到相应的函数,所以出错。这样的头文件出错,出错非常隐蔽,解决的办法唯有一步一步跟错查找。

2、该驱动的等待信号量函数应使用可打断类型的int down_interruptible(struct semaphore *sem),否则运行应用程序时,按下ctrl+c程序没法正常退出,还需按下实验箱上的任意一个按键。因为该中断是可被打断类型的,所以能接受响应外部的另外的中断,即能退出程序。

#ifndef __KERNEL__

#define __KERNEL__

#endif

#ifndef MODULE

#define MODULE

#endif

#include

#include

#include /* printk() */

#include /* __init __exit */

#include /* size_t */

#include /* file_operation */

//#include /* Error number */

#include /* udelay */

#include

#include /* copy_to_user, copy_from_user */

#include

#include

#include

#include

#include

#define DRIVER_NAME"key"

#ifdef DEBUG

#define PRINTK(fmt, arg...)printk(KERN_NOTICE fmt, ##arg)

#else

#define PRINTK(fmt, arg...)

#endif

#define BUFFER_SIZE256

static int keyDriver_Major = 0;/* Driver Major Number */

static int keyNum;

struct semaphore bufflock;

static spinlock_t buffer_lock = SPIN_LOCK_UNLOCKED;

static struct kfifo *buffer;

void handler(int irq, void *dev_id, struct pt_regs *regs)

{

int c_key,r_key,key;

disable_irq(IRQ_EINT0);

disable_irq(IRQ_EINT1);

disable_irq(IRQ_EINT2);

disable_irq(IRQ_EINT3);

GPFCON = 0x0055;

GPFDAT = 0xf0;

mdelay(1);

c_key = GPFDAT & 0xf0;//获得行标低四位

GPFCON = 0x5500;

GPFDAT = 0x0f;

udelay(30);

r_key = GPFDAT & 0x0f; //获得列标高四位

key = ~( c_key | r_key) & 0xff;

//PRINTK("key = 0x%x/n", key);

switch (key)

{

case 0x88:

PRINTK("key16 /n");

keyNum = 16;

break;

case 0x84:

PRINTK("key15 /n");

keyNum = 15;

break;

case 0x82:

PRINTK("key14 /n");

keyNum = 14;

break;

case 0x81:

PRINTK("key13 /n");

keyNum = 13;

break;

case 0x48:

PRINTK("key12 /n");

keyNum = 12;

break;

case 0x44:

PRINTK("key11 /n");

keyNum = 11;

break;

case 0x42:

PRINTK("key10 /n");

keyNum = 10;

break;

case 0x41:

PRINTK("key9 /n");

keyNum = 9;

break;

case 0x28:

PRINTK("key8 /n");

keyNum = 8;

break;

case 0x24:

PRINTK("key7 /n");

keyNum = 7;

break;

case 0x22:

PRINTK("key6 /n");

keyNum = 6;

break;

case 0x21:

PRINTK("key5 /n");

keyNum = 5;

break;

case 0x18:

PRINTK("key4 /n");

keyNum = 4;

break;

case 0x14:

PRINTK("key3 /n");

keyNum = 3;

break;

case 0x12:

PRINTK("key2 /n");

keyNum = 2;

break;

case 0x11:

PRINTK("key1 /n");

keyNum = 1;

break;

default:

break;

}

if(keyNum > 0)

{

kfifo_put(buffer, &keyNum, sizeof(keyNum));

up(&bufflock);

}

// PRINTK("keyNum = %d/n", keyNum);

//PRINTK("GPFCON = 0x%x GPFUP = 0x%x GPGDAT = 0x%x/n", GPFCON, GPFUP, GPFDAT);

//GPFCON = (GPFCON | 0xffff) & 0x55AA;

GPFCON = 0x55AA;

GPFUP=0x00;

GPFDAT = 0x00;

enable_irq(IRQ_EINT0);

enable_irq(IRQ_EINT1);

enable_irq(IRQ_EINT2);

enable_irq(IRQ_EINT3);

}

/* Driver Operation Functions */

static int keyDriver_open(struct inode *inode, struct file *filp)

{

MOD_INC_USE_COUNT;

PRINTK("keyDriver open called!/n");

enable_irq(IRQ_EINT0);

enable_irq(IRQ_EINT1);

enable_irq(IRQ_EINT2);

enable_irq(IRQ_EINT3);

sema_init(&bufflock, 0);

buffer = kfifo_alloc(BUFFER_SIZE, GFP_KERNEL, &buffer_lock);

return 0;

}

static int keyDriver_release(struct inode *inode, struct file *filp)

{

MOD_DEC_USE_COUNT;

PRINTK("keyDriver release called!/n");

disable_irq(IRQ_EINT0);

disable_irq(IRQ_EINT1);

disable_irq(IRQ_EINT2);

disable_irq(IRQ_EINT3);

kfifo_free(buffer);

return 0;

}

static ssize_t keyDriver_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)

{

size_t read_size = count;

PRINTK("myDriver read called!/n");

PRINTK("/tcount=%d, pos=%d/n", count, (int)*f_pos);

down_interruptible(&bufflock); //若这里使用down的话,运行应用程序时会出点小问题

kfifo_get(buffer, &keyNum, sizeof(keyNum));

copy_to_user(buf, &keyNum, read_size);

*f_pos += read_size;

return read_size;

}

static struct file_operations keyDriver_fops = {

owner:THIS_MODULE,

//write:keyDriver_write,

read:keyDriver_read,

//ioctl:keyDriver_ioctl,

open:keyDriver_open,

release:keyDriver_release,

};

#ifdef CONFIG_DEVFS_FS

devfs_handle_t devfs_keyDriver_dir;

devfs_handle_t devfs_keyDriver_raw;

#endif

static int __init myModule_init(void)

{

GPFCON = 0x5500;//GPF4,5,6,7设置为输出

GPFUP = 0x00;//GPF4、5、6、7上拉使能

GPFDAT = 0x00;//GPF4,5,6,7输出低电平

set_external_irq(IRQ_EINT0, EXT_FALLING_EDGE, 0);

request_irq(IRQ_EINT0, handler, SA_INTERRUPT, "KEY1", NULL);

set_external_irq(IRQ_EINT1, EXT_FALLING_EDGE, 0);

request_irq(IRQ_EINT1, handler, SA_INTERRUPT, "KEY2", NULL);

set_external_irq(IRQ_EINT2, EXT_FALLING_EDGE, 0);

request_irq(IRQ_EINT2, handler, SA_INTERRUPT, "KEY3", NULL);

set_external_irq(IRQ_EINT3, EXT_FALLING_EDGE, 0);

request_irq(IRQ_EINT3, handler, SA_INTERRUPT, "KEY4", NULL);

/* Module init code */

PRINTK("myModule_init/n");

/* Driver register */

keyDriver_Major = register_chrdev(0, DRIVER_NAME, &keyDriver_fops);

if(keyDriver_Major < 0)

{

PRINTK("register char device fail!/n");

return keyDriver_Major;

}

PRINTK("register keyDriver OK! Major = %d/n", keyDriver_Major);

#ifdef CONFIG_DEVFS_FS

devfs_keyDriver_dir = devfs_mk_dir(NULL, "keyDriver", NULL);

devfs_keyDriver_raw = devfs_register(devfs_keyDriver_dir, "raw0", DEVFS_FL_DEFAULT, keyDriver_Major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &keyDriver_fops, NULL);

PRINTK("add dev file to devfs OK!/n");

#endif

return 0;

}

static void __exit myModule_exit(void)

{

/* Module exit code */

PRINTK("myModule_exit/n");

free_irq(IRQ_EINT0, NULL);

free_irq(IRQ_EINT1, NULL);

free_irq(IRQ_EINT2, NULL);

free_irq(IRQ_EINT3, NULL);

/* Driver unregister */

if(keyDriver_Major > 0)

{

#ifdef CONFIG_DEVFS_FS

devfs_unregister(devfs_keyDriver_raw);

devfs_unregister(devfs_keyDriver_dir);

#endif

unregister_chrdev(keyDriver_Major, DRIVER_NAME);

}

return;

}

MODULE_AUTHOR("SXZ");

MODULE_LICENSE("Dual BSD/GPL");

module_init(myModule_init);

module_exit(myModule_exit);

应用程序源代码:

#include

#include

#include

int main(int argc, char *argv[])

{

int fd;

unsigned char key;

if((fd = open("/dev/keyDriver/raw0", O_RDONLY)) < 0)

{

printf("can't open keyDriver/n");

exit(1);

}

while(1)

{

read(fd, &key, 1);

printf("program_key = %d/n", key);

}

close(fd);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值