单片机
返回首页

Smart210学习记录-----linux定时器

2025-01-20 来源:cnblogs

1.内核定时器:


  Linux 内核所提供的用于操作定时器的数据结构和函数如下:


     (1) timer_list


  在 Linux 内核中,timer_list 结构体的一个实例对应一个定时器


  1 struct timer_list {

2          struct list_head entry; /* 定时器列表 */

3          unsigned long expires; /*定时器到期时间*/

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

5          unsigned long data; /* 作为参数被传入定时器处理函数 */

6          struct timer_base_s *base;

7   ...

8 };


当定时器期满后,其中第 5 行的 function()成员将被执行,而第 4 行的 data 成员则是传入其中的参数,第 3 行的 expires 则是定时器到期的时间(jiffies)。


如下代码定义一个名为 my_timer 的定时器:

struct timer_list my_timer;



  (2)初始化定时器


    void init_timer(struct timer_list * timer);


    上述 init_timer()函数初始化 timer_list 的 entry 的 next 为 NULL,并给 base 指针赋值


    TIMER_INITIALIZER(_function, _expires, _data)宏用于赋值定时器结构体的function、expires、

    data 和 base 成员


    DEFINE_TIMER(_name,  _function,  _expires,  _data)宏是定义并初始化定时器成员的“快捷方

    式”。


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

    也可用于初始化定时器并赋值其成员


  (3)增加定时器


    void add_timer(struct timer_list* timer);


   (4)删除定时器


    int  del_timer(struct timer_list* timer);


  del_timer_sync()是 del_timer()的同步版,在删除一个定时器时需等待其被处理完,因此该函数的调用不能发生在中断上下文。


   (5).修改定时器的 expire


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


  上述函数用于修改定时器的到期时间,在新的被传入的 expires 到来后才会执行定时器函数。


 


2.内核中延时的工作delayed_work


注意,对于这种周期性的任务,Linux 内核还提供了一套封装好的快捷机制,其本质利用工作队列

和定时器实现,这套快捷机制是就是delayed_work,delayed_work结构体的定义如代码清单10.11所示。

代码清单 10.11   delayed_work 结构体

1  struct delayed_work {

2                struct work_struct work;

3                struct timer_list timer;

4  };

5  struct work_struct {


6                atomic_long_t data;

7  #define WORK_STRUCT_PENDING 0 

8  #define WORK_STRUCT_FLAG_MASK (3UL)

9  #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)

10               struct list_head entry;

11               work_func_t func;

12 #ifdef CONFIG_LOCKDEP

13               struct lockdep_map lockdep_map;

14 #endif

15 };


我们可以通过如下函数调度一个 delayed_work 在指定的延时后执行:

int schedule_delayed_work(struct delayed_work *work, unsigned long delay);

当指定的 delay 到来时 delayed_work 结构体中 work 成员的 work_func_t 类型成员 func()会被

执行。work_func_t 类型定义为:

typedef void (*work_func_t)(struct work_struct *work);

其中 delay 参数的单位是 jiffies,因此一种常见的用法如下:

schedule_delayed_work(&work, msecs_to_jiffies(poll_interval));

其中的 msecs_to_jiffies()用于将毫秒转化为 jiffies。

如果要周期性的执行任务,通常会在 delayed_work 的工作函数中再次调用 schedule_delayed_

work(),周而复始。

如下函数用来取消 delayed_work:

int cancel_delayed_work(struct delayed_work *work);

int cancel_delayed_work_sync(struct delayed_work *work);


3.内核延时


Linux 内核中提供了如下 3 个函数分别进行纳秒、微秒和毫秒延迟:

void ndelay(unsigned long nsecs);

void udelay(unsigned long usecs);

void mdelay(unsigned long msecs);

上述延迟的实现原理本质上是忙等待,它根据 CPU 频率进行一定次数的循环。


毫秒时延(以及更大的秒时延)已经比较大了,在内核中,最好不要直接使用 mdelay()函数,这将无谓地耗费 CPU 资源,对于毫秒级以上时延,内核提供了下述函数:

void msleep(unsigned int millisecs);

unsigned long msleep_interruptible(unsigned int millisecs);

void ssleep(unsigned int seconds);

上述函数将使得调用它的进程睡眠参数指定的时间,msleep()、ssleep()不能被打断,而msleep_interruptible()则可以被打断。


秒设备驱动程序:


#include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include

 #include


static unsigned char timermajor = 0;

#define   TIMERNAME    'mytimer'


static struct class *timer_class;

static struct device *timer_device;


struct timer_dev {

    struct cdev cdev;

    atomic_t counter;

    struct timer_list  mytimer;

};


struct timer_dev *my_timer_dev;


static void my_timer_fun(unsigned int arg)

{

    mod_timer(&my_timer_dev->mytimer, jiffies + HZ);

    atomic_inc(&my_timer_dev->counter);

    printk(KERN_NOTICE 'current jiffies is %ldn', jiffies); 

}



static int my_timer_open(struct inode * inode, struct file * file)

{

    init_timer( &my_timer_dev->mytimer);

    my_timer_dev->mytimer.expires = jiffies+ HZ;

    my_timer_dev->mytimer.function = & my_timer_fun;


    add_timer(&my_timer_dev->mytimer);

    atomic_set(&my_timer_dev->counter, 0);

    return 0;

}


static ssize_t my_timer_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)

{

    int counter;


    counter = atomic_read(&my_timer_dev->counter);


    if(copy_to_user(buf, &counter, sizeof(int)))

            printk('copy_to_user errorn');


    return 0;

}


static int my_timer_close(struct inode *inode, struct file *file)

{

    del_timer( &my_timer_dev->mytimer);

    return 0;

}


struct file_operations timer_fops = {

    .owner    =    THIS_MODULE,

    .open     =    my_timer_open,

    .read     =    my_timer_read,

    .release  =    my_timer_close,

};



static void timer_setup_cdev(struct timer_dev *dev, int minor )

{

    unsigned char err;

    dev_t deno;

    deno = MKDEV(timermajor, minor);

    cdev_init(&dev->cdev, &timer_fops);

    dev->cdev.owner = THIS_MODULE;

    dev->cdev.ops   = &timer_fops;

    err = cdev_add(&dev->cdev, deno, 1);

    if(err)

        printk(KERN_ALERT'cdev_addd errorn');

}


static int __init my_timer_init(void)

{

    int err = 0;

    dev_t deno;

    if(timermajor) {

        register_chrdev_region(deno, 1, TIMERNAME);

    } else {

        err = alloc_chrdev_region(&deno, 0, 1, TIMERNAME);

        timermajor = MAJOR(deno);

    }

    if(err)

        printk(KERN_ALERT'alloc_chrdev_region errorn');

    

    printk(KERN_ALERT'timermajor is %dn', timermajor);

    

    my_timer_dev = kmalloc(sizeof(struct timer_dev), GFP_KERNEL);

    if(!my_timer_dev) {

        printk(KERN_ALERT'kmalloc errorn');

        return -ENOMEM;

    }

    memset(my_timer_dev, 0, sizeof(struct timer_dev) );

    

    timer_setup_cdev(my_timer_dev, 0);


    timer_class = class_create(THIS_MODULE, TIMERNAME);

    if(IS_ERR(timer_class)) {

        printk(KERN_ALERT'class_create errorn');

        return -EBUSY;

    }


    timer_device = device_create(timer_class, NULL, deno, NULL, TIMERNAME);

    if(IS_ERR(timer_device)) {

        printk(KERN_ALERT'device_create errorn');

        return -EBUSY;

    }

    

    return 0;

}


static void __exit my_key_exit(void)

{

    cdev_del(&my_timer_dev->cdev);

    unregister_chrdev_region(MKDEV(timermajor, 0), 1);

   kfree(my_timer_dev);

}


MODULE_LICENSE('GPL');

MODULE_AUTHOR('qigaohua');

module_init(my_timer_init);

module_exit(my_key_exit);


测试程序:


#include


#include


#include


#include


int main()

{

    int fd;

    int counter = 0;

    int old_counter = 0;

    fd = open('/dev/mytimer', O_RDWR);

    if(fd < 0) {

        printf('open /dev/mytimer errorn');

        return 0;

    }


    while(1) {

        read(fd, &counter, sizeof(int));

        if(counter != old_counter) {

            old_counter = counter;

            printf('counter is %dn', counter);

        }

    }

}


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章