Linux驱动学习day11(定时器)

定时器

定时器主要作用就是:设置超时时间,执行超时函数。

按键按下存在抖动,为了消除抖动可以设置定时器,如上图所示,按下一次按键会产生多次抖动,即会产生多次中断,在每次中断产生的时候,设置定时器,定时器时间是当前时间+超时时间,这样每次中断产生都会重新设置定时器时间,等到按键不抖动稳定的时候,就不会再改变定时器时间,这时候我们再记录按键值,就很稳定了。

内核中使用定时器的主要函数 

timer_setup(老版本setup_timer)

设置定时器,主要是初始化timer_list结构体,设置其中参数和函数

#define timer_setup(timer, callback, flags)			\__init_timer((timer), (callback), (flags))

add_timer

向内核添加定时器,timer->expires表示超时时间,时间到了内核会自动调用timer->function。

void add_timer(struct timer_list *timer)
{BUG_ON(timer_pending(timer));mod_timer(timer, timer->expires);
}

mod_timer

修改定时器超时时间

int mod_timer(struct timer_list *timer, unsigned long expires)
{return __mod_timer(timer, expires, 0);
}

del_timer

删除定时器

int del_timer(struct timer_list *timer)
{struct timer_base *base;unsigned long flags;int ret = 0;debug_assert_init(timer);if (timer_pending(timer)) {base = lock_timer_base(timer, &flags);ret = detach_if_pending(timer, base, true);raw_spin_unlock_irqrestore(&base->lock, flags);}return ret;
}

查看系统定时器时间:进入到内核目录,vi .config 搜索/CONFIG_HZ

3568系统tick如上图所示 3.33ms发生一次中断。每发生一次中断,全局变量jiffies会加1,所以定时器时间都是基于jiffies的。

修改时间有下面两种方法

/* add_timer之前 */
timer.expires = jiffies + xxx; /* xxx * 3.33ms */
timer.expires = jiffies + 2*HZ;/* jiffies 再加上 2 秒的时间 *//* add_timer之后 */
mod_timer(&timer , jiffies + xxx);
mod_timer(&timer , jiffies + 2*HZ);

 含定时器的驱动代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/gfp.h>
#include <linux/of_irq.h>
#include <linux/poll.h>
#include <linux/timer.h>#define BUF_LEN 128
#define NEXT_POS(x) ((x + 1) % BUF_LEN)static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
static struct fasync_struct *button_fasync;static struct class *mybutton_class;
static struct gpio_inf *gpio_if;
static int major;
static int g_key[BUF_LEN];
static int r, w;struct gpio_inf {int gpio;int irq;struct gpio_desc *gpiod;enum of_gpio_flags flag;struct timer_list my_button_timer;
};static int is_key_buf_empty(void) {return (r == w);
}static int is_key_buf_full(void) {return (r == NEXT_POS(w));
}static void put_key(int key) {if (!is_key_buf_full()) {g_key[w] = key;w = NEXT_POS(w);}
}static int get_key(void) {int key = 0;if (!is_key_buf_empty()) {key = g_key[r];r = NEXT_POS(r);}return key;
}static void mybutton_keys_timer(struct timer_list *t)
{int val, key;struct gpio_inf *gf = from_timer(gf, t, my_button_timer);if(!gf)return;val = gpio_get_value(gf->gpio);printk("mybutton_keys_timer key %d value%d\n", gf->gpio, val);key = (gf->gpio << 8) | val;put_key(key);wake_up_interruptible(&gpio_key_wait);kill_fasync(&button_fasync, SIGIO, POLL_IN);return;
}static irqreturn_t my_key_handler(int irq, void *dev_id) 
{struct gpio_inf * inf = (struct gpio_inf *)dev_id;printk("my_key_handler %s %s %d key:%d\n", __FILE__, __FUNCTION__, __LINE__ , inf->gpio);mod_timer(&inf->my_button_timer, jiffies + HZ/50);return IRQ_HANDLED;
}static ssize_t gpio_button_read(struct file *file, char __user *buf, size_t size, loff_t *off) {int err, key;//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);if(is_key_buf_empty() && (file->f_flags & O_NONBLOCK))return -EAGAIN;wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());key = get_key();err = copy_to_user(buf, &key, 4);return 4;
}static unsigned int gpio_button_poll(struct file *fp, poll_table *wait) {//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);poll_wait(fp, &gpio_key_wait, wait);return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}static int gpio_button_fasync(int fd, struct file *file, int on)
{//printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);if(fasync_helper(fd, file, on , &button_fasync) >= 0)return 0;elsereturn -EIO;
}static struct file_operations button_opr = {.owner  = THIS_MODULE,.read   = gpio_button_read,.poll   = gpio_button_poll,.fasync = gpio_button_fasync,
};static const struct of_device_id my_key[] = {{ .compatible = "my,mybutton" },{},
};MODULE_DEVICE_TABLE(of, my_key);static int chip_demo_gpio_probe(struct platform_device *pdev) {int count, i, err;struct device_node *node;enum of_gpio_flags flag;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);node = pdev->dev.of_node;count = of_gpio_count(node);if (count <= 0) {dev_err(&pdev->dev, "Invalid GPIO count: %d\n", count);return -EINVAL;}gpio_if = kzalloc(count * sizeof(struct gpio_inf), GFP_KERNEL);if (!gpio_if) {dev_err(&pdev->dev, "Failed to allocate memory\n");return -ENOMEM;}for (i = 0; i < count; i++) {gpio_if[i].gpio  = of_get_gpio_flags(node, i, &flag);gpio_if[i].irq   = gpio_to_irq(gpio_if[i].gpio);gpio_if[i].gpiod = gpio_to_desc(gpio_if[i].gpio);gpio_if[i].flag  = flag & OF_GPIO_ACTIVE_LOW;err = request_irq(gpio_if[i].irq, my_key_handler,IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,"my_key", &gpio_if[i]);if (err) {printk("request_irq %d failed\n", gpio_if[i].irq);}timer_setup(&gpio_if[i].my_button_timer, mybutton_keys_timer , 0);gpio_if[i].my_button_timer.expires = ~0; /* 最大超时时间 */add_timer(&gpio_if[i].my_button_timer);}return 0;
}static int chip_demo_gpio_remove(struct platform_device *pdev) {int count, i;struct device_node *node = pdev->dev.of_node;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);count = of_gpio_count(node);for (i = 0; i < count; i++) {del_timer(&gpio_if[i].my_button_timer);free_irq(gpio_if[i].irq, &gpio_if[i]);}kfree(gpio_if);return 0;
}static struct platform_driver my_key_drv = {.probe = chip_demo_gpio_probe,.remove = chip_demo_gpio_remove,.driver = {.name = "my_key_drv",.of_match_table = my_key,}
};static int __init gpio_key_drv_init(void) {int err;// 注册字符设备major = register_chrdev(0, "my_button", &button_opr);if (major < 0) {printk("register_chrdev failed: %d\n", major);return major;}mybutton_class = class_create(THIS_MODULE, "mybutton_class");if (IS_ERR(mybutton_class)) {unregister_chrdev(major, "my_button");printk("class_create failed\n");return PTR_ERR(mybutton_class);}device_create(mybutton_class, NULL, MKDEV(major, 0), NULL, "my_button");printk("char device /dev/my_button created, major=%d\n", major);// 注册 platform 驱动err = platform_driver_register(&my_key_drv);if (err) {device_destroy(mybutton_class, MKDEV(major, 0));class_destroy(mybutton_class);unregister_chrdev(major, "my_button");return err;}return 0;
}static void __exit gpio_key_drv_exit(void) {platform_driver_unregister(&my_key_drv);device_destroy(mybutton_class, MKDEV(major, 0));class_destroy(mybutton_class);unregister_chrdev(major, "my_button");printk("char device /dev/my_button removed\n");
}module_init(gpio_key_drv_init);
module_exit(gpio_key_drv_exit);
MODULE_LICENSE("GPL");

 tasklet

当硬件中断发生时,系统首先调用对应的硬件中断处理函数(ISR),该函数完成紧急任务后迅速返回。随后,系统会处理软中断(softirq),内核维护了一个软中断处理函数数组 softirq_vec[],其中包含了用于执行延后任务的函数。作为软中断的一种实现,tasklet被安排在软中断中执行;当中断处理函数通过 tasklet_schedule() 调度tasklet时,该tasklet被加入执行链表。软中断触发时,系统调用 tasklet_action() 遍历tasklet链表,依次执行每个tasklet的处理函数,从而完成硬件中断的后续工作。

根据上述流程,可以得出:

1、为每个按键添加tasklet。tasklet_init()

2、写软中断执行函数

2、在request_irq的中断处理函数中,调度tasklet。tasklet_schedule()。将tasklet加入软中断执行链表。

驱动程序代码

这里我在结构体里面添加了last_val,为了判断按键按下是否发生变化,变化则记录其值,没变化就不记录,这是因为正点原子RK3568中使用GPIO引脚电平来模拟按键按下和松开,我的板子在这块贼不稳定,一会就跳出一大堆信息,如下图所示:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/gfp.h>
#include <linux/of_irq.h>
#include <linux/poll.h>
#include <linux/timer.h>#define BUF_LEN 128
#define NEXT_POS(x) ((x + 1) % BUF_LEN)static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
static struct fasync_struct *button_fasync;static struct class *mybutton_class;
static struct gpio_inf *gpio_if;
static int major;
static int g_key[BUF_LEN];
static int r, w;struct gpio_inf {int gpio;int irq;int last_val;struct gpio_desc *gpiod;enum of_gpio_flags flag;struct timer_list my_button_timer;struct tasklet_struct task;
};static int is_key_buf_empty(void) {return (r == w);
}static int is_key_buf_full(void) {return (r == NEXT_POS(w));
}static void put_key(int key) {if (!is_key_buf_full()) {g_key[w] = key;w = NEXT_POS(w);}
}static int get_key(void) {int key = 0;if (!is_key_buf_empty()) {key = g_key[r];r = NEXT_POS(r);}return key;
}static void mybutton_keys_timer(struct timer_list *t)
{int val, key;struct gpio_inf *gf = from_timer(gf, t, my_button_timer);if(!gf)return;val = gpio_get_value(gf->gpio);if (val != gf->last_val) {gf->last_val = val;  // 更新记录值return;  // 状态不稳定,忽略这次}printk("mybutton_keys_timer key %d value%d\n", gf->gpio, val);key = (gf->gpio << 8) | val;put_key(key);wake_up_interruptible(&gpio_key_wait);kill_fasync(&button_fasync, SIGIO, POLL_IN);return;
}static void my_button_tasklet(unsigned long data)
{int val, key;struct gpio_inf *gf = (struct gpio_inf *)data;if(!gf)return;val = gpio_get_value(gf->gpio);if (val != gf->last_val) {gf->last_val = val;  return;  }printk("my_button_tasklet key %d value%d\n", gf->gpio, val);key = (gf->gpio << 8) | val;put_key(key);wake_up_interruptible(&gpio_key_wait);kill_fasync(&button_fasync, SIGIO, POLL_IN);return;
}static irqreturn_t my_key_handler(int irq, void *dev_id) 
{struct gpio_inf * inf = (struct gpio_inf *)dev_id;//printk("my_key_handler %s %s %d key:%d\n", __FILE__, __FUNCTION__, __LINE__ , inf->gpio);tasklet_schedule(&inf->task);mod_timer(&inf->my_button_timer, jiffies + HZ/50);return IRQ_HANDLED;
}static ssize_t gpio_button_read(struct file *file, char __user *buf, size_t size, loff_t *off) {int err, key;//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);if(is_key_buf_empty() && (file->f_flags & O_NONBLOCK))return -EAGAIN;wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());key = get_key();err = copy_to_user(buf, &key, 4);return 4;
}static unsigned int gpio_button_poll(struct file *fp, poll_table *wait) {//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);poll_wait(fp, &gpio_key_wait, wait);return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}static int gpio_button_fasync(int fd, struct file *file, int on)
{//printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);if(fasync_helper(fd, file, on , &button_fasync) >= 0)return 0;elsereturn -EIO;
}static struct file_operations button_opr = {.owner  = THIS_MODULE,.read   = gpio_button_read,.poll   = gpio_button_poll,.fasync = gpio_button_fasync,
};static const struct of_device_id my_key[] = {{ .compatible = "my,mybutton" },{},
};MODULE_DEVICE_TABLE(of, my_key);static int chip_demo_gpio_probe(struct platform_device *pdev) {int count, i, err;struct device_node *node;enum of_gpio_flags flag;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);node = pdev->dev.of_node;count = of_gpio_count(node);if (count <= 0) {dev_err(&pdev->dev, "Invalid GPIO count: %d\n", count);return -EINVAL;}gpio_if = kzalloc(count * sizeof(struct gpio_inf), GFP_KERNEL);if (!gpio_if) {dev_err(&pdev->dev, "Failed to allocate memory\n");return -ENOMEM;}for (i = 0; i < count; i++) {gpio_if[i].gpio  = of_get_gpio_flags(node, i, &flag);gpio_if[i].irq   = gpio_to_irq(gpio_if[i].gpio);gpio_if[i].gpiod = gpio_to_desc(gpio_if[i].gpio);gpio_if[i].flag  = flag & OF_GPIO_ACTIVE_LOW;gpio_if[i].last_val = gpio_get_value(gpio_if[i].gpio);err = request_irq(gpio_if[i].irq, my_key_handler,IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,"my_key", &gpio_if[i]);if (err) {printk("request_irq %d failed\n", gpio_if[i].irq);}timer_setup(&gpio_if[i].my_button_timer, mybutton_keys_timer , 0);gpio_if[i].my_button_timer.expires = ~0;add_timer(&gpio_if[i].my_button_timer);tasklet_init(&gpio_if[i].task, my_button_tasklet, (unsigned long)&gpio_if[i]);}return 0;
}static int chip_demo_gpio_remove(struct platform_device *pdev) {int count, i;struct device_node *node = pdev->dev.of_node;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);count = of_gpio_count(node);for (i = 0; i < count; i++) {del_timer(&gpio_if[i].my_button_timer);free_irq(gpio_if[i].irq, &gpio_if[i]);tasklet_kill(&gpio_if[i].task);}kfree(gpio_if);return 0;
}static struct platform_driver my_key_drv = {.probe = chip_demo_gpio_probe,.remove = chip_demo_gpio_remove,.driver = {.name = "my_key_drv",.of_match_table = my_key,}
};static int __init gpio_key_drv_init(void) {int err;// 注册字符设备major = register_chrdev(0, "my_button", &button_opr);if (major < 0) {printk("register_chrdev failed: %d\n", major);return major;}mybutton_class = class_create(THIS_MODULE, "mybutton_class");if (IS_ERR(mybutton_class)) {unregister_chrdev(major, "my_button");printk("class_create failed\n");return PTR_ERR(mybutton_class);}device_create(mybutton_class, NULL, MKDEV(major, 0), NULL, "my_button");printk("char device /dev/my_button created, major=%d\n", major);// 注册 platform 驱动err = platform_driver_register(&my_key_drv);if (err) {device_destroy(mybutton_class, MKDEV(major, 0));class_destroy(mybutton_class);unregister_chrdev(major, "my_button");return err;}return 0;
}static void __exit gpio_key_drv_exit(void) {platform_driver_unregister(&my_key_drv);device_destroy(mybutton_class, MKDEV(major, 0));class_destroy(mybutton_class);unregister_chrdev(major, "my_button");printk("char device /dev/my_button removed\n");
}module_init(gpio_key_drv_init);
module_exit(gpio_key_drv_exit);
MODULE_LICENSE("GPL");

工作队列

中断下半部(timer,tasklet)都是在中断上下文中执行的,无法休眠,如果处理复杂事情的时候,无法休眠,会将CPU资源占满,无法执行用户程序,这样就会使得系统卡顿。为了解决该问题,可以使用线程处理复杂事情。线程可以休眠。(在内核中,使用工作队列,内核会自动创建内核线程)

缺点:当工作队列中前一个work比较耗时,这样就会影响到之后的work工作。

驱动要做的部分:

1、构造work,.func

2、将work放入队列,wake_up唤醒--->schedule_work();

如果处理的事情非常复杂,就不直接使用系统的内核线程,自己创建一个内核线程单独处理。

container_of() 可以获得结构体的地址,主要采用反推。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/gfp.h>
#include <linux/of_irq.h>
#include <linux/poll.h>
#include <linux/timer.h>
#include <linux/workqueue.h>#define BUF_LEN 128
#define NEXT_POS(x) ((x + 1) % BUF_LEN)static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
static struct fasync_struct *button_fasync;static struct class *mybutton_class;
static struct gpio_inf *gpio_if;
static int major;
static int g_key[BUF_LEN];
static int r, w;struct gpio_inf {int gpio;int irq;int last_val;struct gpio_desc *gpiod;enum of_gpio_flags flag;struct timer_list my_button_timer;struct tasklet_struct task;struct work_struct work;
};static int is_key_buf_empty(void) {return (r == w);
}static int is_key_buf_full(void) {return (r == NEXT_POS(w));
}static void put_key(int key) {if (!is_key_buf_full()) {g_key[w] = key;w = NEXT_POS(w);}
}static int get_key(void) {int key = 0;if (!is_key_buf_empty()) {key = g_key[r];r = NEXT_POS(r);}return key;
}static void mybutton_keys_timer(struct timer_list *t)
{int val, key;struct gpio_inf *gf = from_timer(gf, t, my_button_timer);if(!gf)return;val = gpio_get_value(gf->gpio);if (val != gf->last_val) {gf->last_val = val;  // 更新记录值return;  // 状态不稳定,忽略这次}printk("mybutton_keys_timer key %d value%d\n", gf->gpio, val);key = (gf->gpio << 8) | val;put_key(key);wake_up_interruptible(&gpio_key_wait);kill_fasync(&button_fasync, SIGIO, POLL_IN);return;
}static void my_button_tasklet(unsigned long data)
{int val, key;struct gpio_inf *gf = (struct gpio_inf *)data;if(!gf)return;val = gpio_get_value(gf->gpio);if (val != gf->last_val) {gf->last_val = val;  return;  }printk("my_button_tasklet key %d value%d\n", gf->gpio, val);key = (gf->gpio << 8) | val;put_key(key);wake_up_interruptible(&gpio_key_wait);kill_fasync(&button_fasync, SIGIO, POLL_IN);return;
}static void my_button_work_func(struct work_struct *work)
{int val, key;struct gpio_inf *gf = container_of(work, struct gpio_inf, work);if(!gf)return;val = gpio_get_value(gf->gpio);if (val != gf->last_val) {gf->last_val = val;  return;  }printk("my_button_work_func key %d value%d\n", gf->gpio, val);key = (gf->gpio << 8) | val;put_key(key);return;}static irqreturn_t my_key_handler(int irq, void *dev_id) 
{struct gpio_inf * inf = (struct gpio_inf *)dev_id;//printk("my_key_handler %s %s %d key:%d\n", __FILE__, __FUNCTION__, __LINE__ , inf->gpio);tasklet_schedule(&inf->task);mod_timer(&inf->my_button_timer, jiffies + HZ/50);schedule_work(&inf->work);return IRQ_HANDLED;
}static ssize_t gpio_button_read(struct file *file, char __user *buf, size_t size, loff_t *off) {int err, key;//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);if(is_key_buf_empty() && (file->f_flags & O_NONBLOCK))return -EAGAIN;wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());key = get_key();err = copy_to_user(buf, &key, 4);return 4;
}static unsigned int gpio_button_poll(struct file *fp, poll_table *wait) {//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);poll_wait(fp, &gpio_key_wait, wait);return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}static int gpio_button_fasync(int fd, struct file *file, int on)
{//printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);if(fasync_helper(fd, file, on , &button_fasync) >= 0)return 0;elsereturn -EIO;
}static struct file_operations button_opr = {.owner  = THIS_MODULE,.read   = gpio_button_read,.poll   = gpio_button_poll,.fasync = gpio_button_fasync,
};static const struct of_device_id my_key[] = {{ .compatible = "my,mybutton" },{},
};MODULE_DEVICE_TABLE(of, my_key);static int chip_demo_gpio_probe(struct platform_device *pdev) {int count, i, err;struct device_node *node;enum of_gpio_flags flag;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);node = pdev->dev.of_node;count = of_gpio_count(node);if (count <= 0) {dev_err(&pdev->dev, "Invalid GPIO count: %d\n", count);return -EINVAL;}gpio_if = kzalloc(count * sizeof(struct gpio_inf), GFP_KERNEL);if (!gpio_if) {dev_err(&pdev->dev, "Failed to allocate memory\n");return -ENOMEM;}for (i = 0; i < count; i++) {gpio_if[i].gpio  = of_get_gpio_flags(node, i, &flag);gpio_if[i].irq   = gpio_to_irq(gpio_if[i].gpio);gpio_if[i].gpiod = gpio_to_desc(gpio_if[i].gpio);gpio_if[i].flag  = flag & OF_GPIO_ACTIVE_LOW;gpio_if[i].last_val = gpio_get_value(gpio_if[i].gpio);err = request_irq(gpio_if[i].irq, my_key_handler,IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,"my_key", &gpio_if[i]);if (err) {printk("request_irq %d failed\n", gpio_if[i].irq);}timer_setup(&gpio_if[i].my_button_timer, mybutton_keys_timer , 0);gpio_if[i].my_button_timer.expires = ~0;add_timer(&gpio_if[i].my_button_timer);tasklet_init(&gpio_if[i].task, my_button_tasklet, (unsigned long)&gpio_if[i]);INIT_WORK(&gpio_if[i].work, my_button_work_func);}return 0;
}static int chip_demo_gpio_remove(struct platform_device *pdev) {int count, i;struct device_node *node = pdev->dev.of_node;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);count = of_gpio_count(node);for (i = 0; i < count; i++) {del_timer(&gpio_if[i].my_button_timer);free_irq(gpio_if[i].irq, &gpio_if[i]);tasklet_kill(&gpio_if[i].task);}kfree(gpio_if);return 0;
}static struct platform_driver my_key_drv = {.probe = chip_demo_gpio_probe,.remove = chip_demo_gpio_remove,.driver = {.name = "my_key_drv",.of_match_table = my_key,}
};static int __init gpio_key_drv_init(void) {int err;// 注册字符设备major = register_chrdev(0, "my_button", &button_opr);if (major < 0) {printk("register_chrdev failed: %d\n", major);return major;}mybutton_class = class_create(THIS_MODULE, "mybutton_class");if (IS_ERR(mybutton_class)) {unregister_chrdev(major, "my_button");printk("class_create failed\n");return PTR_ERR(mybutton_class);}device_create(mybutton_class, NULL, MKDEV(major, 0), NULL, "my_button");printk("char device /dev/my_button created, major=%d\n", major);// 注册 platform 驱动err = platform_driver_register(&my_key_drv);if (err) {device_destroy(mybutton_class, MKDEV(major, 0));class_destroy(mybutton_class);unregister_chrdev(major, "my_button");return err;}return 0;
}static void __exit gpio_key_drv_exit(void) {platform_driver_unregister(&my_key_drv);device_destroy(mybutton_class, MKDEV(major, 0));class_destroy(mybutton_class);unregister_chrdev(major, "my_button");printk("char device /dev/my_button removed\n");
}module_init(gpio_key_drv_init);
module_exit(gpio_key_drv_exit);
MODULE_LICENSE("GPL");

内核线程

 中断的线程化处理

主要程序代码

/* 注册irq时,使用request_threaded_irq */
err = request_threaded_irq(gpio_if[i].irq , my_key_handler , my_key_threaded_func , IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT , "my_key" , &gpio_if[i]);static irqreturn_t my_key_threaded_func(int irq, void *data)
{int val, key;struct gpio_inf *gf = (struct gpio_inf *)data ;val = gpio_get_value(gf->gpio);printk("my_key_threaded_func key %d value%d\n", gf->gpio, val);printk("my_key_threaded_func: the process is %s pid %d\n" , current->comm , current->pid);key = (gf->gpio << 8) | val;put_key(key);return IRQ_HANDLED;
}

结果如图所示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87062.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87062.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 编程之观察者模式详解

一、什么是观察者模式&#xff1f; 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;用于对象之间的一对多依赖关系&#xff1a;当被观察对象&#xff08;Subject&#xff09;状态发生变化时&#xff0c;所有依赖它的观察者&#xff08;O…

【C++】经典string类问题

目录 1. 浅拷贝 2. 深拷贝 3. string类传统写法 4. string类现代版写法 5. 自定义类实现swap成员函数 6. 标准库swap函数的调用 7. 引用计数和写时拷贝 1. 浅拷贝 若string类没有显示定义拷贝构造函数与赋值运算符重载&#xff0c;编译器会自动生成默认的&#xff0c…

kotlin中object:的用法

在Kotlin中&#xff0c;object: 用于声明匿名对象&#xff08;Anonymous Object&#xff09;&#xff0c;这是实现接口或继承类的轻量级方式&#xff0c;无需显式定义具名类。以下是核心用法和场景&#xff1a; 1. 基本语法 val obj object : SomeInterface { // 实现接口ov…

js代码04

题目 非常好。我们刚刚看到了回调函数在处理多个异步操作时会变得多么混乱&#xff08;回调地狱&#xff09;。为了解决这个问题&#xff0c;现代 JavaScript 提供了一个更强大、更优雅的工具&#xff1a;Promise。 Promise&#xff0c;正如其名&#xff0c;是一个“承诺”。…

Jenkins初探-通过Docker部署Jenkins并安装插件

简介 本文介绍了使用Docker安装Jenkins并进行初始配置的完整流程。主要内容包括&#xff1a; (1)通过docker pull命令获取Jenkins镜像&#xff1b;(2)使用docker run命令启动容器并映射端口&#xff1b;(3)访问Jenkins界面获取初始管理员密码&#xff1b;(4)安装推荐插件并创…

嵌入式开发:GPIO、UART、SPI、I2C 驱动开发详解与实战案例

&#x1f4cd; 本文为嵌入式学习系列第二篇&#xff0c;基于 GitHub 开源项目&#xff1a;0voice/EmbeddedSoftwareLearn &#x1f4ac; 作者&#xff1a;0voice &#x1f440; 适合对象&#xff1a;嵌入式初学者、STM32学习者、想搞明白外设驱动开发的C语言学习者 一、驱动是什…

常用 Linux 命令和 shell 脚本语言整理

目录 一、Linux 命令大全 1、文件和目录操作 &#xff08;1&#xff09;ls 列出目录内容 &#xff08;2&#xff09;pwd 查看当前目录 &#xff08;3&#xff09;cd 切换目录 &#xff08;4&#xff09;mkdir 创建目录 &#xff08;5&#xff09;cp 复制文件或目录 &…

YOLOv12_ultralytics-8.3.145_2025_5_27部分代码阅读笔记-autobackend.py

autobackend.py ultralytics\nn\autobackend.py 目录 autobackend.py 1.所需的库和模块 2.def check_class_names(names: Union[List, Dict]) -> Dict[int, str]: 3.def default_class_names(data: Optional[Union[str, Path]] None) -> Dict[int, str]: 4.cla…

【MySQL基础】MySQL索引全面解析:从原理到实践

MySQL学习&#xff1a; https://blog.csdn.net/2301_80220607/category_12971838.html?spm1001.2014.3001.5482 前言&#xff1a; 在前面我们基本上已经把MySQL的基础知识都进行了学习&#xff0c;但是我们之前处理的数据都是十分少的&#xff0c;但是如果当我们的数据量很大…

第三十五章 I2S——音频传输接口

第三十五章 I2S——音频传输接口 目录 第三十五章 I2S——音频传输接口 1 I2S概述 1.1 简介 1.2 功能特点 1.3 工作原理 1.4 利用DMA通信的I2S 1.4.1 I2S配合DMA通信工作原理 1.4.2 配置要点 2 应用场景 2.1 消费类音频设备 2.2 专业音频设备 2.3 通信设备 2.4 汽车电子 2.5 嵌…

产品-Figma(英文版),图像的布尔类型图例说明

文章目录 Union SelectionSubtract SelectionIntersect SelectionExclude SelectionFlatten Selection Union Selection 把多个形状合并成一个新的完整形状&#xff0c;保留所有外部轮廓&#xff0c;内部不被切割。由于红色的长方形在外面的一层&#xff0c;所以切割后&#x…

Windows CMD命令分类大全

⚙️ ‌一、系统与磁盘管理‌ ‌系统信息‌ systeminfo&#xff1a;查看详细硬件及系统配置&#xff08;版本/内存/补丁&#xff09;211 winver&#xff1a;快速检查Windows版本11 msinfo32&#xff1a;图形化系统信息面板811‌磁盘工具‌ chkdsk /f&#xff1a;修复磁盘错误&…

【Dify系列】【Dify1.4.2 升级到Dify1.5.0】

1. 升级前准备工作 1.1 数据备份&#xff1a; 进入原安装包 docker 目录&#xff0c;备份“volumes”文件夹&#xff0c;此文件夹包含了 Dify 数据库数据&#xff1a; rootjoe:/usr/local/dify/docker/volumes# pwd /usr/local/dify/docker/volumesrootjoe:/usr/local/dify/…

DeepSeek网页版随机点名器

用DeepSeek帮我们生成了一个基于html5的随机点名器&#xff0c;效果非常棒&#xff0c;如果需要加入名字&#xff0c;请在代码中按照对应的格式添加即可。 提示词prompt 帮我生成一个随机点名的HTML5页面 生成真实一点的名字数据 点击随机按钮开始随机选择 要有闪动的效果 &…

前后端分离实战2----后端

戳我抵达前端 项目描述&#xff1a;用Vscode创建Spring Bootmybatis项目&#xff0c;用maven进行管理。创建一个User表&#xff0c;对其内容进行表的基本操作&#xff08;增删改查&#xff09;&#xff0c;显示在前端。 项目地址&#xff1a;戳我一键下载项目 运行效果如下&…

深入 ARM-Linux 的系统调用世界

1、引言 本篇文章以 ARM 架构为例&#xff0c;进行讲解。需要读者有一定的 ARM 架构基础 在操作系统的世界中&#xff0c;系统调用&#xff08;System Call&#xff09;是用户空间与内核空间沟通的桥梁。用户态程序如 ls、cp 或你的 C 程序&#xff0c;无权直接操作硬件、访问文…

LabVIEW键盘鼠标监测控制

通过Input Device Control VIs&#xff0c;实现对键盘和鼠标活动的监测。通过AcquireInput Data VI 在循环中持续获取输入数据&#xff0c;InitializeKeyboard与InitializeMouse VIs 先获取设备ID 引用&#xff0c;用于循环内监测操作&#xff1b;运行时可输出按键信息&#xf…

Linux 系统管理:自动化运维与容器化部署

在现代 IT 基础设施中&#xff0c;自动化运维和容器化部署是提高系统管理效率和可维护性的关键。Linux 系统因其稳定性和灵活性而被广泛应用于服务器和数据中心。本文将深入探讨 Linux 系统管理中的自动化运维和容器化部署技术&#xff0c;帮助系统管理员实现高效运维和快速部署…

直播 APP 开发需要多少成本

直播行业的火爆催生了大量直播 APP 开发需求&#xff0c;而开发成本是开发者最关注的问题之一。其成本构成复杂&#xff0c;受功能需求、开发方式、技术难度等多种因素影响。​ 基础功能开发是成本的重要组成部分。用户注册登录、直播间创建与管理、视频播放、聊天互动等功能开…

Reactor操作符的共享与复用

在 Reactor 中&#xff0c;transform 和 transformDeferred 是两个用于代码复用和操作符链封装的高级操作符。它们允许你将一组操作符封装成一个函数&#xff0c;并在适当的时候应用到响应式流中。以下是它们的详细总结&#xff1a; 1. transform 操作符 作用&#xff1a;tran…