【改进定时器】IMX257实现GPIO-IRQ定时器消抖驱动程序

发布时间:2024-08-16  

本文,我们还是在前面的按键驱动程序的基础上,引入定时器,来消除抖动。

一、内核定时器详解

1.timer_list结构体

在/include/linux/timer.h中。

struct timer_list {

   struct list_head entry;

    //定时器链表,用于存放软定时器,该链表根据定时器expirex字段的值将它们分组存放。

   unsigned long expires;

  //定时器的到期时间,到达expires时间后,定时器将调用其成员函数function,其中将data字段作为function的参数。该字段表示的时间是以时间节拍为单位。例如如果你想定时一秒,则expires=jiffies+HZ*1。

  void (*function)(unsigned long); //定时器到时处理函数

  unsigned long data; //function 的参数

  struct tvec_t_base_s *base;

  #ifdef CONFIG_TIMER_STATS

    void *start_site;

    char start_comm[16];

    int start_pid;

  #endif

};

时钟中断的发生频率设定为HZ,HZ是一个与体系结构无关的常数,在文件中定义,时钟中断对操作系统是非常重要的,当时钟中断发生时,将周期性地执行一些功能

 

2.初始化定时器

①init_timer

void init_timer(struct timer_list *timer)

{

  debug_timer_init(timer);

  __init_timer(timer);

}

 

static void __init_timer(struct timer_list *timer)

{

  timer->entry.next = NULL;

  timer->base = __raw_get_cpu_var(tvec_bases);

  #ifdef CONFIG_TIMER_STATS

    timer->start_site = NULL;

    timer->start_pid = -1;

    memset(timer->start_comm, 0, TASK_COMM_LEN);

  #endif

}

init_timer()函数被定义在kernel/timer.c中,实际上是将timer的entry的next指针置为NULL,为base字段赋值。

 

②. timer=TIMER_INITIALIZER(function,expires,data);

采用这种初始化方式,必须首先先写好定时器处理函数function. TIMER_INITIALIZER宏的定义如下:

#define TIMER_INITIALIZER(_function, _expires, _data) {

  .entry = { .prev = TIMER_ENTRY_STATIC },

  .function = (_function),

  .expires = (_expires),

  .data = (_data),

  .base = &boot_tvec_bases,

}

其中boot_tcec_bases是在kernel/timer中定义的一个全局的tvec_t_base_s类型的变量。

 

③.DEFINE_TIMER(timer,function,expires,data);

定义并初始化定时器timer,功能和前面差不多

#define DEFINE_TIMER(_name, _function, _expires, _data)

  struct timer_list _name =

  TIMER_INITIALIZER(_function, _expires, _data)

 

④. setup_timer(&timer);

等同于前面,不过对base字段的赋值是调用了init_timer()函数。setup_timer()原型为:

static inline void setup_timer(struct timer_list * timer,void (*function)(unsigned long),unsigned long data)

{

  timer->function = function;

  timer->data = data;

  init_timer(timer);

}

 

 

3.注册定时器

在定义并初始化了定时器之后,就要调用add_timer()函数来将该定时器注册到内核中,这样定时器才会工作。在注册之后,定时器就开始计时,在到达时间expires时,执行回调函数function(->data)。add_timer()函数的原型为:

static inline void add_timer(struct timer_list *timer)

{

  BUG_ON(timer_pending(timer));

  __mod_timer(timer, timer->expires);

}

 

4.删除定时器

int del_timer(struct timer_list *timer);

从内核中删除已经注册的定时器timer。如果该定时器是活动的,则返回1,否则返回0。

int del_timer(struct timer_list *timer)

{

  tvec_base_t *base;

  unsigned long flags;

  int ret = 0;

  timer_stats_timer_clear_start_info(timer);

  if (timer_pending(timer)) {

    base = lock_timer_base(timer, &flags);

    if (timer_pending(timer)) {

      detach_timer(timer, 1);

      ret = 1;

    }

    spin_unlock_irqrestore(&base->lock, flags);

  }

  return ret;

}

 

5.修改定时器时间

如果所给的要修改的时间等于定时器原来的时间并且定时器现在正处于活动状态,则不修改,返回1,否则修改定时器时间,返回0。mod_timer()是一个非有效的更新处于活动状态的定时器的时间的方法,如果定时器处于非活动状态,则会激活定时器。在功能上,mod_timer()等价于:

del_timer(timer);

timer->expires=expires;

add_timer(timer);

 

int mod_timer(struct timer_list *timer, unsigned long expires)

{

  BUG_ON(!timer->function);

  timer_stats_timer_set_start_info(timer);

  if (timer->expires == expires && timer_pending(timer))

    return 1;

  return __mod_timer(timer, expires);

}

 

 

6.使用方法

1.创建定时器:struct timer_list my_timer;

2.初始化定时器:init_timer(&my_timer);

3.根据需要,设置定时器了:

my_timer.expires = jiffies + delay;

my_timer.data = 0;

my_timer.function = my_function;

4.激活定时器:add_timer(&my_timer);

 

7.实例解析

 

程序是在前面程序的基础上修改的,(博文地址:http://www.cnblogs.com/lihaiyan/p/4295961.html)

为了结构更简单,前面的程序使用了六个GPIO引脚,可能会相对复杂,为了更加坚定明了,此处简化为一个GPIO引脚了,思路和前面差不多,这里就是使用GPIO2_10这个引脚

① 定义定时器

②在打开函数中初始化定时器,并且申请GPIO2_10中断

③在中断中修改定时器,并且激活定时器,则没发生一次中断则激活一次定时器达到消除抖动的目的

此处的中断函数只有一个任务,就是激活定时器,然后将其他的功能均转移至定时器调用的函数中。

④可以发现,在定时器中,将前面中断中的功能全部实现了,一旦按键按下,接下来20ms内的抖动都无效,因为修改定时器无法修改已经激活的定时器,所以那20ms内高低电平的抖动都将无效,达到了消除抖动的功能

⑤编译测试。结果如下图所示:

附上驱动程序代码:


  1 /******************************

  2     linux key_query

  3  *****************************/

  4 #include

  5 #include

  6 #include

  7 #include

  8 #include

  9 #include

 10 #include

 11 #include

 12 #include

 13 #include

 14 #include

 15 #include

 16 #include //error: 'TASK_INTERRUPTIBLE' undeclared 

 17 #include

 18 #include

 19 

 20 #include "mx257_gpio.h"

 21 #include "mx25_pins.h"

 22 #include "iomux.h"

 23 

 24 #define Driver_NAME "key_interrupt"

 25 #define DEVICE_NAME "key_interrupt"

 26 

 27 #define GPIO2_21    MX25_PIN_CLKO

 28 #define GPIO3_15    MX25_PIN_EXT_ARMCLK

 29 #define GPIO2_10    MX25_PIN_A24

 30 #define GPIO2_11    MX25_PIN_A25

 31 #define GPIO2_8     MX25_PIN_A22

 32 #define GPIO2_9     MX25_PIN_A23

 33 #define GPIO2_6     MX25_PIN_A20

 34 #define GPIO2_7     MX25_PIN_A21

 35 //command

 36 #define key_input     0

 37 #define version        1

 38 //定义各个按键按下的键值

 39 struct pin_desc{

 40     unsigned int pin;

 41     unsigned int key_val;

 42 };

 43 //当按键按下时,键值分别为 以下值

 44 struct pin_desc pins_desc[8] = {

 45     {GPIO2_6,    0x01},

 46     {GPIO2_7,    0x02},

 47     {GPIO2_8,    0x03},

 48     {GPIO2_9,    0x04},

 49     {GPIO2_10,    0x05},

 50     {GPIO2_11,    0x06},

 51     {GPIO2_21,    0x07},

 52     {GPIO3_15,    0x08},

 53 };

 54 struct pin_desc * pindesc;

 55 static int flag=0;

 56 //定义一个全局变量,用于保存按下的键值

 57 static unsigned int key_val;

 58 

 59 struct timer_list my_timer;

 60 unsigned int cnt_timer = 0;

 61 #define TIMER_DELAY_20MS  HZ/50

 62 

 63 //interrupt head

 64 static DECLARE_WAIT_QUEUE_HEAD(key_interrupt_wait);

 65 static volatile unsigned char ev_press;  

 66 

 67 static int major=0;

 68 

 69 //auto to create device node

 70 static struct class *drv_class = NULL;

 71 static struct class_device *drv_class_dev = NULL;

 72 

 73 void timer_function(unsigned long data){

 74     int index = data;

 75     cnt_timer ++;

 76     printk("<0>enter timer_%d__%d",index,cnt_timer);

 77     if(flag){

 78         //获取按键键值

 79         gpio_direction_input(IOMUX_TO_GPIO(GPIO2_10));

 80         key_val = gpio_get_value(IOMUX_TO_GPIO(pindesc->pin));

 81         if(!key_val){

 82             key_val = pindesc->key_val;

 83             printk("<0>get key 0x%x",key_val);    

 84             printk("<0>KEY_DOWNn");

 85             flag = 0;

 86             ev_press = 1;

 87             wake_up_interruptible(&key_interrupt_wait);

 88         }

 89     }

 90 }

 91 /* 中断程序key_irq */

 92 static irqreturn_t key_irq(int irq, void *dev_id)

 93 {

 94     pindesc = (struct pin_desc *)dev_id;

 95     //发生了中断

 96     printk("<0>2_10function interrupt key_irq!nn");

 97     mod_timer(&my_timer, jiffies+HZ/50);

 98     flag = 1;

 99 

100     return IRQ_RETVAL(IRQ_HANDLED);

101 }

102 

103 

104 /* 应用程序对设备文件/dev/key_query执行open(...)时,

105  * 就会调用key_open函数*/

106 static int key_open(struct inode *inode, struct file *file)

107 {

108     printk("<0>function open 2_10! nn");

109 

110     init_timer(&my_timer);

111      my_timer.function = timer_function;

112     my_timer.expires = jiffies+HZ/50;

113       add_timer(&my_timer); //定时器应该在此时被启动

114 

115     request_irq(IOMUX_TO_IRQ(GPIO2_10), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_10", &pins_desc[4]);

116     return 0;

117 }

118 

119 

120 

121 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

122 {

123     int ret;

124     if(filp->f_flags & O_NONBLOCK){

125         if(!ev_press)

126             return -EAGAIN;

127     }else{

128     //如果按键没有按下,没有中断,休眠

129         wait_event_interruptible(key_interrupt_wait,ev_press);

130     }

131     ret = copy_to_user(buff,&key_val,sizeof(key_val));

132     if(ret){

133         ;

134     }

135     ev_press = 0;

136     return sizeof(key_val);

137 }

138 

139 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

140 {

141     printk("<0>function write!nn");

142     

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    e络盟社区启动555定时器疯狂设计挑战赛;挑战赛旨在庆祝首款555定时器诞生50周年 安富利旗下全球电子元器件产品与解决方案分销商e络盟通过其在线社区发起555定时器疯狂设计挑战赛,以庆祝首款555......
    基于555定时器和单片机的RC测量系统设计;  在电子仪器、仪表的制造及使用行业,有大量的印刷电路板需要调试、测量与维修,需要对电阻电容的数值进行测试。   本文介绍了一种基于AT89C51单片......
    电路项目分享|基于555定时器的DC-DC转换电路,附带......
    555定时器及50个经典设计电路;555定时器是美国Signetics公司1972年研制的用于取代机械式定时器的中规模集成电路,因输入端设计有三个5kΩ的电阻而得名。此电路后来竟风靡世界。目前,流行......
    plc控制器怎么用 plc控制器怎么修复;  plc控制器怎么用   PLC(Programmable Logic Controller)控制器的使用通常需要以下步骤:   了解......
    自动转换开关(2023-09-07)
    将介绍自动转换电路的原理、设计和操作,在该电路中,一系列 LED 等直流负载可由电池或交流-直流电源驱动。 电路原理: 该电路基于 555 定时器的双稳态工作原理。在该模式下,定时器输出为高电平或低电平,取决......
    关断状态。因此,晶体管 Q2 处于导通状态,从而通过绿色 LED 指示灯将负载切换至导通状态。 断电后恢复供电时,555 定时器 IC 变为低电平,从而触发 555 定时器 IC。555 定时器......
    利用随处可见的 555 定时器 取代 LED 驱动器的uP 控制;摘要本文详细介绍如何使用便宜的 555 定时器,在一些不需要 LED 驱动器全部功能的应用中,代替微处理器对专用 LED 驱动......
    构建一个基于555定时器的简单ESR测量装置;  电容器似乎一切都很好,直到您到达电源出现故障或拒绝以最佳方式运行的地步。如果问题是噪音,有一个简单的解决方法,你只需添加更多的电容器。但这......
    和 12V 电源。 2、带有 555 非稳态多谐振荡器的三角波发生器: 带有 555 非稳态多谐振荡器的三角波发生器 从下图可以看出,我们使用了一个带有 2.2K 电阻的 555 定时器......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>