内核定时器

定时器就是闹钟,到一定时间就做某些事。时间、做的事情==>超时时间、函数
在include\linux\timer.h中涉及这些函数:

//设置定时器,主要初始化timer_list结构体,设置其中的函数、参数
#define setup_timer(timer, fn, data)                    \__setup_timer((timer), (fn), (data), 0)
//向内和添加定时器timer->expires表示超时时间
extern void add_timer(struct timer_list *timer);
//修改定时器的超时时间,等同于del_timer(timer);timer->expires = expires; add_timer(timer);但更高效
extern int mod_timer(struct timer_list *timer, unsigned long expires);
//删除定时器
extern int del_timer(struct timer_list * timer);

定时器时间单位

在内核源码目录下的.config文件中定义了内核每秒钟发生的系统滴答(tick)中断次数,这是Linux系统的心跳。
每发生一次tcik中断,全局变量jiffies就会增加1.

CONFIG_HZ=100意味着每个滴答是10ms发生一次。
定时器的时间就是基于jiffies的,设置定时器的超时时间,一般使用这2中方法:

//1.在add_timer之前修改
timer.expires = jiffies + xxx;            //xxx表示多少个滴答后超时,也就是xxx*10ms
timer.expires = jiffies + 2*HZ;           //HZ表示CONFIG_HZ,2*HZ就相当于2秒
//2.在add_timer之前,使用mod_timer修改
mod_timer(&timer, jiffies + xxx);      //xxx表示多少个滴答后超时,也就是xxx*10ms
mod_timer(&timer, jiffies + 2*HZ);     //HZ表示CONFIG_HZ,2*HZ就相当于2秒

使用定时器处理按键防抖

一个按键在开关接触时,它的GPIO电平会反复变化,最后才稳定。一般是几十毫秒才会稳定。

  1. 在按键中断处理程序中,可以循环判断几十毫秒,发现电平稳定之后再上报。
  2. 使用定时器
    显然第1中方法太耗时,违背“中断尽快处理”的原则。但是如何使用定时器?
    核心在于:GPIO发生中断后,使用定时器计时10ms,如果期间又发生了中断就更新定时器,重新计时。
    直到定时器超时后,再记录按键信息。

分析

//TIMER_SOFTIRQ软中断的初始化代码
void __init init_timers(void)
{init_timer_cpus();init_timer_stats();timer_register_cpu_notifier();open_softirq(TIMER_SOFTIRQ, run_timer_softirq);         //注册了软中断的处理函数
}
//发生硬件中断后,处理完硬件中断,内核调用软件中断处理函数
static void run_timer_softirq(struct softirq_action *h)
{struct tvec_base *base = this_cpu_ptr(&tvec_bases);if (time_after_eq(jiffies, base->timer_jiffies))__run_timers(base);
}//add_timer会把timer放入内核某个链表中
//__run_timers会把链表中超时timer取出来,执行其中的函数
//判断超时就是jiffies大于等于timer->expires
static inline void __run_timers(struct tvec_base *base)
{struct timer_list *timer;spin_lock_irq(&base->lock);while (time_after_eq(jiffies, base->timer_jiffies)) {//....hlist_move_list(base->tv1.vec + index, head);while (!hlist_empty(head)) {void (*fn)(unsigned long);unsigned long data;bool irqsafe;timer = hlist_entry(head->first, struct timer_list, entry);fn = timer->function;data = timer->data;irqsafe = timer->flags & TIMER_IRQSAFE;timer_stats_account_timer(timer);base->running_timer = timer;detach_expired_timer(timer, base);if (irqsafe) {spin_unlock(&base->lock);call_timer_fn(timer, fn, data);spin_lock(&base->lock);} else {spin_unlock_irq(&base->lock);call_timer_fn(timer, fn, data);spin_lock_irq(&base->lock);}}}base->running_timer = NULL;spin_unlock_irq(&base->lock);
}

内核的timer,高效找到超时timer,可以参考这个文章

源码

button_timer.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/irqreturn.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/timer.h>struct gpio_key {int gpio;struct gpio_desc *gpiod;int flag;int irq;struct timer_list timer;
};
static struct gpio_key *myBtn_key;
static int button_major = 0;
static struct class *button_class;
static struct fasync_struct *btn_async;static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);#define MaxSize 128
struct QNode {int Data[MaxSize];int rear;int front;
};typedef struct QNode *Queue;int IsEmpty(Queue Q);
void AddQ(Queue PtrQ, int item);
int DeleteQ(Queue PtrQ);int IsEmpty(Queue Q)
{return (Q->rear == Q->front);   //1:empty 0:not empty
}void AddQ(Queue PtrQ, int item)
{if((PtrQ->rear+1)%MaxSize == PtrQ->front) {printk("%s,Queue full\n", __FUNCTION__);return;}PtrQ->rear = (PtrQ->rear+1)%MaxSize;PtrQ->Data[PtrQ->rear] = item;
}int DeleteQ(Queue PtrQ)
{if(PtrQ->front == PtrQ->rear) {printk("%s,Queue empty\n", __FUNCTION__);return -1;} else {PtrQ->front = (PtrQ->front+1)%MaxSize;return PtrQ->Data[PtrQ->front];}
}static Queue irqBuff;
static ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;int val;if(IsEmpty(irqBuff) && (file->f_flags & O_NONBLOCK)) {return -EAGAIN;}wait_event_interruptible(gpio_key_wait, !IsEmpty(irqBuff));val = DeleteQ(irqBuff);err = copy_to_user(buf, &val, 4);
//      if(err != 4) {//              return -1;
//      }return 4;
}static unsigned int button_poll(struct file *fp, poll_table * wait)
{printk("%s,button poll\n", __FUNCTION__);poll_wait(fp, &gpio_key_wait, wait);return IsEmpty(irqBuff) ? 0 : POLLIN | POLLRDNORM;
}int button_fasync(int fd, struct file *file, int on)
{if(fasync_helper(fd, file, on, &btn_async) >= 0)return 0;elsereturn -EIO;
}static struct file_operations button_ops = {.owner = THIS_MODULE,.read  = button_read,.poll  = button_poll,.fasync = button_fasync,
};static irqreturn_t myBtn_irq_request(int irq, void *dev_id)
{struct gpio_key *gpio_key = dev_id;printk(KERN_WARNING"myBtn_irq_request key %d irq happened\n", gpio_key->gpio);mod_timer(&gpio_key->timer, jiffies + HZ/50);return IRQ_HANDLED;
}static void myBtn_timer(unsigned long data)
{struct gpio_key *gpio_key = (struct gpio_key*)data;int val;val = gpiod_get_value(gpio_key->gpiod);printk(KERN_WARNING"key %d %d\n", gpio_key->gpio, val);val = (myBtn_key->gpio << 8)|val;AddQ(irqBuff, val);wake_up_interruptible(&gpio_key_wait);kill_fasync(&btn_async, SIGIO, POLLIN);
}static int my_button_probe(struct platform_device *pdev)
{struct device_node *node = pdev->dev.of_node;int count;enum of_gpio_flags flag;int i, err;count = of_gpio_count(node);if(!count) {printk("%s,there isn't any gpio availiable\n", __FUNCTION__);return -1;}myBtn_key = (struct gpio_key*)kzalloc(sizeof(struct gpio_key)*count, GFP_KERNEL);if(!myBtn_key) {printk("%s,kzalloc malloc failed\n", __FUNCTION__);return -1;}for(i=0;i<count;i++) {myBtn_key[i].gpio = of_get_gpio_flags(node, i, &flag);if(myBtn_key[i].gpio < 0) {printk("%s, of_get_gpio_flags failed\n", __FUNCTION__);return -1;}myBtn_key[i].gpiod = gpio_to_desc(myBtn_key[i].gpio);myBtn_key[i].flag  = flag & OF_GPIO_ACTIVE_LOW;myBtn_key[i].irq   = gpio_to_irq(myBtn_key[i].gpio);setup_timer(&myBtn_key[i].timer, myBtn_timer, (unsigned long)&myBtn_key[i]);myBtn_key[i].timer.expires = ~0;err = request_irq(myBtn_key[i].irq, myBtn_irq_request, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,"myBtn_key", &myBtn_key[i]);}button_major = register_chrdev(0, "mybutton", &button_ops);if (button_major < 0) {printk(KERN_ERR "button : couldn't get a major number.\n");return -1;}button_class = class_create(THIS_MODULE, "button_class");if(IS_ERR(button_class)) {printk(KERN_ERR "button class: create failed\n");unregister_chrdev(button_major, "mybutton");return -1;}device_create(button_class, NULL, MKDEV(button_major, 0), NULL, "mybutton%d", 0);return 0;
}static int my_button_remove(struct platform_device *pdev)
{struct device_node *node= pdev->dev.of_node;int count;int i;device_destroy(button_class, MKDEV(button_major, 0));class_destroy(button_class);unregister_chrdev(button_major, "mybutton");count = of_gpio_count(node);for(i=0;i<count;i++) {free_irq(myBtn_key[i].irq, &myBtn_key[i]);del_timer(&myBtn_key[i].timer);}kfree(myBtn_key);return 0;
}static struct of_device_id mybuttons[] = {{ .compatible = "mybtn,btn_drv" },{ },
};static struct platform_driver my_button_driver = {.probe  = my_button_probe,.remove = my_button_remove,.driver = {.name = "button_dirver",.of_match_table = mybuttons,},
};static int gpio_button_init(void)
{int err;irqBuff = (Queue)kzalloc(sizeof(struct QNode), GFP_KERNEL);err = platform_driver_register(&my_button_driver);printk(KERN_WARNING"my button dirver init\n");return 0;
}static void gpio_button_exit(void)
{platform_driver_unregister(&my_button_driver);kfree(irqBuff);printk(KERN_WARNING"my button dirver exit\n");
}module_init(gpio_button_init);
module_exit(gpio_button_exit);
MODULE_LICENSE("GPL");

rk3288 定时器使用相关推荐

  1. 一文吃透JAVA定时器格式

    JAVA 定时器时间格式 ​ 基本格式: [秒]  [分]  [小时]  [日]  [月]  [周]   [年] 序号 说明 是否必填 允许填写的值 允许使用的通配符 1 秒 是 0-59 ,   - ...

  2. Timer定时器开发

    Timer定时器开发 定时器的作用是不占线程的等待一个确定时间,同样通过callback来通知定时器到期. 参考:https://github.com/sogou/workflow 定时器的创建 同样 ...

  3. linux定时器(crontab)实例

    linux实验示例----实现每2分钟将"/etc"下面的文件打包存储到"/usr/lobal"目录下 ·Step1:编辑当前用户的crontab并保存 终端输 ...

  4. c语言程序设计分段定时器,单片机C语言编程定时器的几种表达方式

    原标题:单片机C语言编程定时器的几种表达方式 吴鉴鹰单片机开发板地址 店铺:[吴鉴鹰的小铺] 地址:[https://item.taobao.com/item.htm?_u=ukgdp5a7629&a ...

  5. linux 内核按键抖动,Tiny4412 Linux驱动之按键(定时器防抖动) | 技术部落

    按键抖动示意图 我们平常所用的按键为机械弹性开关,由于触点的弹性作用,按键在闭合时不会马上稳定的接通,而是有一段时间的抖动,在断开时也不会立即断开.抖动时间由按键的机械特性所决定,一般为5ms~10m ...

  6. mysql 定时器不能持续循环执行_定时器,不循环执行指定方法?如何解决?

    ⁽⁽ଘ你管我管你疯啊ଓ⁾⁾ 01-22 加粗 标红 插入代码 插入链接 插入图片 上传视频 请 登录 后发表内容 关闭 新增或编辑超链接 链接地址 关闭 插入视频 视频链接 messageScroll ...

  7. oracle定时器怎么开启,Oracle的定时器使用示例

    下面是编程之家 jb51.cc 通过网络收集整理的代码片段. 编程之家小编现在分享给大家,也给大家做个参考. --Oracle定时job --数据库执行此脚本时时,请确保只执行一次,否则会引发错误! ...

  8. java 定时器获得外部参数_JMeter定时器使用小结

    一.定时器的作用域 1.无论定时器位置在Sampler之前还是下面,定时器是在每个sampler(采样器)之前执行的,而不是之后: 2.当执行一个Sampler之前时,所有当前作用域内的定时器都会被执 ...

  9. PHP 毫秒级定时器,实现php毫秒定时器方法详解

    描述 PHP编程语言是一种快速.简洁的服务端脚本编程语言,可以制作强大的交互性展现.在编程界PHP是完全免费的语言,在程序员身上使用非常的广泛,在编程中是大家高效的选择. PHP能实现的功能 1.可以 ...

最新文章

  1. 编译py-faster-rcnn的问题汇总及解决方法
  2. tacotron2 注意力机制 self-attention学习
  3. arma模型matlab代码_DCC GARCH模型
  4. 基于libmemcached为php扩展memcached服务
  5. eclipse svn 分支合并到主干
  6. RDKit | 基于RDKit 的化合物预处理
  7. 骑士CMS文件包含+getshell漏洞复现(python自动化验证扫描漏洞)
  8. 网络操作系统和分布式系统区别简介
  9. C++PrimerPlus 第七章 函数-C++的编程模块-7.3 函数和数组
  10. 重磅来袭!这几款手机软件绝对的深入人心!
  11. 应用NMOS和晶体三极管作为基本开关管的相关知识
  12. Win-mac版 AE 2018安装附教程
  13. Automatic White Balance(AWB)/Color Constancy(CC)系列论文
  14. RTP-RTCP协议分析
  15. 西电-机器学习-逻辑回归
  16. 数据库课程设计-职工工资管理系统
  17. pr cpu100%_PR插件proDAD4.0.487.1安装教程
  18. 月收入15k在上海的生活
  19. 基于精英反向学习和Lévy飞行的鲸鱼优化算法
  20. ftp协议及简单配置

热门文章

  1. 拿到一个网站,怎么判断该网站是否存在sql注入漏洞?
  2. initramfs详解-----初识initramfs
  3. 基于51单片机的遥控小车
  4. java抽奖_JAVA实现用户抽奖功能(附完整代码)
  5. 数学上的一些小技巧韩信点兵的问题
  6. beautifulsoup的用法
  7. python解方程代码_Python解方程的技巧介绍(代码示例)
  8. 详解AUTOSAR:Green Hills Software(GHS)编译下载瑞萨RH850程序(环境配置篇—2)
  9. Spark教程——(10)Spark SQL读取Phoenix数据本地执行计算
  10. 网络隧道Tunnel技术