• linux驱动之内核定时器


    linux内核定时器如何使用?

    内核定时器不是周期性的, 一次定时时间到了以后就会关闭, 除非重新打开

    关键函数
    头文件 include\linux\timer.h

    定时器结构体:

    struct timer_list {
    	/*
    	 * All fields that change during normal runtime grouped to the
    	 * same cacheline
    	 */
    	struct hlist_node	entry;
    	unsigned long		expires;//超时时间
    	void			(*function)(unsigned long);//定时器超时函数
    	unsigned long		data;//传入超时函数的参数
    	u32			flags;
    
    #ifdef CONFIG_TIMER_STATS
    	int			start_pid;
    	void			*start_site;
    	char			start_comm[16];
    #endif
    #ifdef CONFIG_LOCKDEP
    	struct lockdep_map	lockdep_map;
    #endif
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述
    通过宏 from_timer 可以获取包含定时器结构体的首地址

    ①setup_timer(timer, fn, data);
    设置定时器,主要是初始化timer_list结构体,设置其中的函数、参数。
    *② void add_timer(struct timer_list timer):
    向内核添加定时器。timer->expires表示超时时间。
    当超时时间到达,内核就会调用这个函数:timer->function(timer->data)。
    *③ int mod_timer(struct timer_list timer, unsigned long expires):
    修改定时器的超时时间,
    它等同于:del_timer(timer); timer->expires = expires; add_timer(timer);
    但是更加高效。
    *④ int del_timer(struct timer_list timer):
    删除定时器。
    **⑤int del_timer_sync(struct timer_list *timer) **
    del_timer_sync函数是 del_timer函数的同步版,会等待其他处理器使用完定时器再删除,
    del_timer_sync不能使用在中断上下文中。

    int del_timer_sync(struct timer_list *timer) 
    
    • 1

    定时器时间单位

    编译内核时,可以在内核源码根目录下用“ls -a”看到一个隐藏文件 .config, 打开后可以看到如下这项:
    CONFIG_HZ=100
    这表示内核每秒中会发生100次系统滴答中断
    每发生一次tick中断,全局变量jiffies就会累加1。
    这个在内核配置是可以修改的
    menuconfig的

    -> Kernel Features                                                                                                            
       -> Timer frequency (<choice> [=y])  
    
    • 1
    • 2

    全局变量 jiffies
    既然是变量那么就有溢出的风险, 内核提供了宏来避免溢出造成的错误
    time_after(unkown, known) unkown > known?
    time_before(unkown, known) unkown < known?
    time_after_eq(unkown, known) unkown >= known?
    time_before_eq(unkown, known) unkown <= known?

    unkown通常为 jiffies,known通常是需要对比的值。

    Linux内核提供了几个 jiffies和 ms、us、ns之间的转换函数

    //将 jiffies类型的参数 j分别转换为对应的毫秒、微秒、纳秒。
    int jiffies_to_msecs(const unsigned long j)  
    int jiffies_to_usecs(const unsigned long j) 
    u64 jiffies_to_nsecs(const unsigned long j) 
    
    // 将毫秒、微秒、纳秒转换为 jiffies类型。 
    long msecs_to_jiffies(const unsigned int m) 
    long usecs_to_jiffies(const unsigned int u) 
    unsigned long nsecs_to_jiffies(u64 n)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
      unsigned long timeout; 
      timeout = jiffies + (2 * HZ);    /* 超时的时间点 */ 
       
      /************************************* 
        具体的代码 
       ************************************/ 
        
      /* 判断有没有超时 */ 
      if(time_before(jiffies, timeout)) { 
       /* 超时未发生 */ 
     } else { 
       /* 超时发生 */ 
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    定时器的时间就是基于jiffies的,我们修改超时时间时,一般使用这2种方法:
    ① 在add_timer之前,直接修改:
    timer.expires = jiffies + xxx; // xxx表示多少个滴答后超时,也就是xxx10ms
    timer.expires = jiffies + 2
    HZ; // HZ等于CONFIG_HZ,2HZ就相当于2秒
    **!宏 HZ 表示一秒的节拍数, 即频率 **
    ② 在add_timer之后,使用mod_timer修改:
    mod_timer(&timer, jiffies + xxx); // xxx表示多少个滴答后超时,也就是xxx
    10ms
    mod_timer(&timer, jiffies + 2HZ); // HZ等于CONFIG_HZ,2HZ就相当于2秒

    编码
    核心代码:

    定时器超时函数申明:

    static void key_timer_expire(unsigned long data)
    {
    	/* data ==> gpio */
    	struct gpio_dev *dev = data;//地址强转
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    初始化定时器:

    
    struct gpio_dev{
    	int gpio;
    	......
    	struct timer_list key_timer;//声明一个定时器
    } ;
    static struct gpio_dev dev;
    
    //初始化定时器
    setup_timer(&dev.key_timer, key_timer_expire, &dev);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    向内核添加定时器:

    注意: 这里add_timer添加之后 如果超时时间是0, 那么超时函数会被马上调用, 所以在刚开始时,要先设成最大值
    dev.key_timer.expires = ~0; //~0 变成最大值
    add_timer(&gpio_keys_100ask[i].key_timer);
    
    • 1
    • 2
    • 3

    设置定时器超时时间:
    如果定时器还没有激活的话,mod_timer函数会激活定时

    //HZ表示1000毫秒, HZ/50表示20毫秒
    mod_timer(&dev->key_timer, jiffies + HZ/50);//也可以使用宏jffies + msecs_to_jiffies(20);
    
    
    • 1
    • 2
    • 3

    内核短延迟函数

     纳秒、微秒和毫秒延时函数。
    void ndelay(unsigned long nsecs) 
     void udelay(unsigned long usecs) 
    void mdelay(unsigned long mseces)
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    DBA-现在应该刚刚入门吧
    JDBC和数据库连接池。
    Elasticsearch7 添加密码验证、并且使用postman访问带密码的es
    【Mysql系列】(一)MySQL语句执行流程
    css布局总体
    不添加端口号访问非80网站
    youtubeDNN模型实现2-网络模型结构
    【跟小嘉学 Rust 编程】三十、Rust 使用 Slint UI
    SRTT-110VDC-4H-C时间继电器
    python os.system( 没有那个文件或目录
  • 原文地址:https://blog.csdn.net/qq_40684669/article/details/127780312