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
英文地址,請注明出處:http://en.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…