单片机
返回首页

mini2440 按键驱动POLL机制实验

2022-10-13 来源:csdn

Makefile


KERN_DIR = /home/grh/kernel_source_code/linux-2.6.32.2

all : 

make -C $(KERN_DIR) M=`pwd` modules

arm-linux-gcc key_interrupt_app.c -o key_interrupt_app

clean :

make -C $(KERN_DIR) M=`pwd` modules clean

rm -rf modules.order

obj-m += test_driver.o

obj-m += key_poll.o

obj-m += key_interrupt.o

copy : 

cp key_interrupt.ko key_interrupt_app /nfs


驱动源代码,就只是添加了Poll相关的部分

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#define GRH_MODULE_NAME 'key_interrupt'

 

static int major;

static struct class *key_interrupt_class;

static struct class_device *key_interrupt_device; 

static int key_value;

 

//wait_event_interruptible函数需要的两个变量

static DECLARE_WAIT_QUEUE_HEAD(grh_wait_interrupt); //休眠的进程队列头

static volatile int sleep_for_interrupt; //这个变量为0的时候read函数会休眠,中断里面将其置1,read函数末尾将其设置为0

 

 

 

//pin_desc是对每一个按键中断的描述,不仅仅可以是整数,也可以是更复杂到的字段,这里用简单的按键值就行了

int pin_desc[6] = {

1, 2, 3, 4, 5, 6

};

 

 

//中断处理函数

static irqreturn_t grh_handle_key_eint(int irq, void *dev_id){

int *p;

p = dev_id;

 

//printk(KERN_EMERG'key pressed! key=%dn', *p);

key_value = *p;

 

//唤醒休眠的进程

sleep_for_interrupt = 1;

wake_up_interruptible(&grh_wait_interrupt);

 

return IRQ_HANDLED;

}

 

static void init_key(void){

//注册irq中断处理函数,将按键值和中断号绑定,所有清中断操作以及初始化中断相关寄存器的操作全部交给

//内核自动完成了,不再需要像裸机程序一样显式地对寄存器进行读写了,中断发生后会自动跳到grh_handle_key_eint

request_irq(IRQ_EINT8, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, 'key1', pin_desc);

request_irq(IRQ_EINT11, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, 'key2', pin_desc+1);

request_irq(IRQ_EINT13, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, 'key3', pin_desc+2);

request_irq(IRQ_EINT14, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, 'key4', pin_desc+3);

request_irq(IRQ_EINT15, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, 'key5', pin_desc+4);

request_irq(IRQ_EINT19, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, 'key6', pin_desc+5);

}

 

static int key_interrupt_open(struct inode *inode, struct file *file){

printk(KERN_EMERG'DRIVER: OPENn');

sleep_for_interrupt = 0;

init_key();

return 0;

}

 

static ssize_t key_interrupt_write(struct inode *inode, const char __user *buf, size_t count, loff_t *ppos){

printk(KERN_EMERG'DRIVER: WRITEn');

return 0;

}

 

static ssize_t key_interrupt_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){

printk(KERN_EMERG'DRIVER: READn');

//根据sleep_for_interrupt的数值决定是否将驱动进程加进休眠队列grh_wait_interrupt中,立即休眠进程

wait_event_interruptible(grh_wait_interrupt, sleep_for_interrupt);

copy_to_user(buf, &key_value, 4);

 

//下一次进入read的时候继续休眠等待中断发生

sleep_for_interrupt = 0;

return 0;

}

 

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

//注销中断

free_irq(IRQ_EINT8, pin_desc);

free_irq(IRQ_EINT11, pin_desc+1);

free_irq(IRQ_EINT13, pin_desc+2);

free_irq(IRQ_EINT14, pin_desc+3);

free_irq(IRQ_EINT15, pin_desc+4);

free_irq(IRQ_EINT19, pin_desc+5);

printk(KERN_EMERG'DRIVER: RELEASEn');

return 0;

}

 

//sys_poll会反复在死循环里面调用key_interrupt_poll

unsigned int key_interrupt_poll(struct file *file, struct poll_table_struct *wait){

unsigned int mask;

printk(KERN_EMERG'DRIVER POLLn');

poll_wait(file, &grh_wait_interrupt, wait); //把当前进程挂到休眠队列里面,但是不立即休眠

mask = 0;

if(sleep_for_interrupt){ //中断发生了

mask |= POLLIN | POLLRDNORM; //把有可读数据的标志位设置为1,用户层可以得到这个mask

}

 

return mask;

/*

如果返回的mask是0那么进程直接进入定时休眠,如果在定时休眠过程中中断发生了,sys_poll里面的

定时休眠结束,sys_poll又会循环调用key_interrupt_poll,但这个时候mask一定返回非零数值了,这时

sys_poll中的休眠结束,进程继续运行。如果定时休眠过程中中断一直没有发生,那么定时休眠超时之后,

sys_poll再调用一次key_interrupt_poll,然会判断是否超时,如果超时直接结束进程的休眠。这是

Linux内核的poll机制的大概原理,这样在用户层调用一次poll函数,进程最多休眠时间就是传进来的wait参数,

中断一发生,定时休眠立刻结束,否则进程就休眠到一次定时休眠结束为止。

*/

}

 

 

static struct file_operations key_interrupt_fops = {

.owner = THIS_MODULE,

.open = key_interrupt_open,

.write = key_interrupt_write,

.read = key_interrupt_read,

.release = key_interrupt_release,

.poll = key_interrupt_poll

};

 

int key_interrupt_module_init(void){

printk(KERN_EMERG'INIT MODULE!n');

 

//register the driver with the device

major = register_chrdev(0, GRH_MODULE_NAME, &key_interrupt_fops);

 

//create my own device class

key_interrupt_class = class_create(THIS_MODULE, 'key_interrupt_class');

//create my device of my own class

key_interrupt_device = device_create(key_interrupt_class, NULL, MKDEV(major,0), NULL, 'key_interrupt_device');

 

return 0;

}

 

void key_interrupt_module_exit(void){

unregister_chrdev(major, GRH_MODULE_NAME);

device_unregister(key_interrupt_device);

class_destroy(key_interrupt_class);

printk(KERN_EMERG'EXIT MODULE!n');

}

 

module_init(key_interrupt_module_init);

module_exit(key_interrupt_module_exit);

 

MODULE_AUTHOR('GRH');

MODULE_VERSION('1.0');

MODULE_DESCRIPTION('KEY POLL DRIVER');

MODULE_LICENSE('GPL');



用户空间测试程序,每次在Poll之后判断是不是发生了中断,如果发生了中断就打印按键值,如果超时了就直接退出程序

#include

#include

#include

#include

#include

#include

#include

 

int main(void){

struct pollfd fds[1];

int i, sum, ret;

int key_value;

int fd;

 

fd = open('/dev/key_interrupt_device', O_RDWR);

if(-1 == fd){

printf('open key device error!n');

return -1;

}

fds[0].fd = fd;

fds[0].events = POLLIN;

 

while(1){

ret = poll(fds, 1, 5000); //只监控一个设备,这里不使用poll的多路复用功能,休眠延时是5000ms

if(-1 == ret){

printf('poll error!n');

break;

}

else if(0 == ret){

printf('poll time out!n');

break;

}

else{

//读操作结束的时候自然驱动程序里面的中断发生的标志sleep_for_interrupt

//就清空了,实际上read的时候该标志比为1,进程已经不可能进入休眠了,因为poll

//返回正值的时候,才会进入read

read(fd, &key_value, 4); 

printf('key pressed : %dn', key_value);

}

}

close(fd);

return 0;

}



测试结果(经过测试在timeout情况下,驱动里面poll会调用两次,这和Linux内核中poll机制的实现是吻合的,内核里面实现Poll机制的源代码这里就不分析了,有兴趣的自己去读一下吧,这里只是我自己对实验代码做一个记录而已):

进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章