我是靠谱客的博主 微笑蜜粉,最近开发中收集的这篇文章主要介绍一、linux驱动开发-5.1-内核定时器一、时间管理和内核定时器二、驱动程序三、应用程序四、测试,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

一、时间管理和内核定时器

1.1、时间管理

 1.2、内核定时器

1.3、定时器API

1.3.1、init_timer

 1.3.2、add_timer

1.3.3、del_timer

1.3.4、del_timer_sync

1.3.5、mod_timer

1.3.6、内核短延时函数

二、驱动程序

三、应用程序

四、测试


一、时间管理和内核定时器

1.1、时间管理

       linux内核使用全局变量jiffies来记录系统从启动以来的系统节拍数,系统启动的时候会将jiffies初始化为0。

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

 1.2、内核定时器

       内核定时器并不是周期性运行的,超时以后会自动关闭,因此如果想要实现周期性定时,那么需要在定时处理函数里重新开启定时器。内核定义如下:

struct timer_list {
    struct list_head entry;
    unsigned long expires; /* 定时器超时时间,单位是节拍数 */
    struct tvec_base *base;
    void (*function)(unsigned long); /* 定时处理函数 */
    unsigned long data; /* 要传递给 function 函数的参数 */
    int slack;
};

       expires表示超时时间,单位为节拍数,比如现在需要定义一个周期为2秒的定时器,那么这个定时器超时时间为jiffies+(2*HZ),因此expires=jiffies+(2*HZ)。

1.3、定时器API

1.3.1、init_timer

       定义一个time_list变量后要用init_timer初始化一下。

/*
@Decription:初始化定时器
@timer:要初始化的定时器
*/
void init_timer(struct timer_list *timer)

 1.3.2、add_timer

       用于向内核注册一个定时器,注册成功以后,定时器就会开始运行。

/*
@Decription:注册定时器
@timer:要注册的定时器
*/
void add_timer(struct timer_list *timer)

1.3.3、del_timer

       用于删除定时器,不管定时器有没有被激活,都可以用来删除定时器

        在多处理器系统上,定时器可能会在其他的处理器上运行,因此在调用 del_timer 函数删除定时器之前要先等待其他处理器的定时处理器函数退出

/*
@Decription:删除定时器
@timer:要删除的定时器
@return:0,定时器还没被激活
         1,定时器已经激活
*/
int del_timer(struct timer_list *timer)

1.3.4、del_timer_sync

       del_timer 函数的同步版,会等待其他处理器使用完定时器再删除,del_timer_sync 不能使用在中断上下文中。

/*
@Decription:删除定时器
@timer:要删除的定时器
@return:0,定时器还没被激活
         1,定时器已经激活
*/
int del_timer_sync(struct timer_list *timer)

1.3.5、mod_timer

       用于修改定时值,如果定时器还没被激活,mod_timer会激活定时器!


@Decription:修改定时值
@timer:要修改定时值的定时器
@expires:修改后的超时时间
@return:0,调用mod_timer前定时器还没被激活
         1,调用mod_timer前定时器已经激活
int mod_timer(struct timer_list *timer, unsigned long expires)

使用示例:

//定义定时器
struct timer_list timer;

//定时器回调函数
void function(unsigned long arg)
{
    //user code

    //如果需要周期运行
    mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
}

//入口函数
void init(void)
{
    //初始化定时器
    init_timer(&timer);

    //设置超时处理函数
    timer.function = function;
    //设置超时时间
    timer.expires = jiffies + msecs_to_jiffies(2000);
    //传入设备结构体
    timer.data = (unsigned long)&dev;

    //启动定时器
    add_timer(&timer);
}

//出口函数
void exit(void)
{
    del_timer(&timer);
#if 0
    del_timer_sync(&timer);
#endif
}

1.3.6、内核短延时函数

二、驱动程序

        使用内核定时器周期性的闪烁LED灯,应用程序可以修改周期值,具体步骤如下:

        ①、timer设备中定义一个定时器

        ②、timer设备中定义一个定时周期,并定义一个自旋锁进行保护

        ③、打开定时器设备时候,只设置默认定时周期,默认开始定时器是关闭的

        ④、应用程序利用iotcl控制定时器设备,关闭调用del_timer_sync,打开就是取默认定时周期调用mod_timer,修改定时器就是接收应用程序传递的参数,调用mod_timer重新设置定时器周期并启动定时器。读取参数的时候用自旋锁进行保护

        ⑤、在定时回调函数中对LED状态取反,并调用mod_timer重新开启定时器

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>

#define TIMER_CNT     1     
#define TIMER_NAME    "timerdev"

#define CLOSE_CMD		1
#define OPEN_CMD		2
#define SETPERIOD_CMD	3

#define	timer_ON      1
#define timer_OFF     0

//设备结构体
struct timer_dev{
	dev_t devid;
	struct cdev cdev;
	struct class *class;
	struct device *device;
	int major;
	int minor;
	struct device_node *nd;		//设备节点
	int led_gpio;				//led所使用的GPIO编号
	struct timer_list timer;	//定时器
	unsigned long perpiod;		//定时器周期
	spinlock_t lock;    		//自旋锁
};

struct timer_dev timerdev;  

static int led_init(void)
{
	int ret = 0;
	//通过节点名字查找节点
	timerdev.nd = of_find_node_by_name(NULL, "gpioled");
	if (timerdev.nd == NULL)
	{
		printk("find %s failrn", "gpioled");
		return -EINVAL;
	}

	//获取GPIO编号
	timerdev.led_gpio = of_get_named_gpio(timerdev.nd, "gpio", 0);
	if (timerdev.led_gpio < 0)
	{
		printk("get gpio failrn");
		return -EINVAL;
	}

	//初始化led
	gpio_request(timerdev.led_gpio, "led");
	ret = gpio_direction_output(timerdev.led_gpio, 0);
	if (ret < 0)
	{
		printk("set led status failrn");
		return -EINVAL;
	}

	return 0;
}

static int timer_open(struct inode *inode, struct file *filp)
{
	led_init();

	filp->private_data = &timerdev;
	printk(" timer open!n");
	return 0;
}

static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct timer_dev *dev = filp->private_data;
	unsigned long perpiod = 0;
	unsigned long flags;
	switch(cmd)
	{
		case CLOSE_CMD:				//关闭定时器
			del_timer_sync(&dev->timer);
			break;
		case OPEN_CMD:				//打开定时器
			add_timer(&dev->timer);
			break;
		case SETPERIOD_CMD:			//设置定时器周期
			spin_lock_irqsave(&dev->lock, flags);
			perpiod = arg;
			dev->perpiod = arg;
			spin_unlock_irqrestore(&dev->lock, flags);
			mod_timer(&dev->timer, jiffies + msecs_to_jiffies(perpiod));
			break;
		default:
			break;
	}
	return 0;
}

static const struct file_operations timer_fops = {
	.owner = THIS_MODULE,
	.open  = timer_open,
	.unlocked_ioctl = timer_unlocked_ioctl,
};

//定时器回调函数
static void timer_function(unsigned long arg)
{
	static uint8_t sta = 0;
	struct timer_dev *dev = (struct timer_dev *)arg;

	sta = !sta;
	gpio_set_value(dev->led_gpio, sta);

	//重新启动定时器
	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->perpiod));
}

static int __init timer_init(void)
{
	int ret = 0;
	int val = 0;	

	//初始化自旋锁
	spin_lock_init(&timerdev.lock);

	//分配设备号
	if ( timerdev.major) {
		 timerdev.devid = MKDEV( timerdev.major, 0);
		register_chrdev_region( timerdev.devid,  TIMER_CNT,  TIMER_NAME);
	} else {
		alloc_chrdev_region(&timerdev.devid, 0,  TIMER_CNT,  TIMER_NAME);
		 timerdev.major = MAJOR(timerdev.devid);
		 timerdev.minor = MINOR(timerdev.devid);
	}

	//初始化cdev
	timerdev.cdev.owner = THIS_MODULE;
	cdev_init(&timerdev.cdev, &timer_fops);
	//添加cdev
	cdev_add(&timerdev.cdev, timerdev.devid,  TIMER_CNT);
	//创建类
	timerdev.class = class_create(THIS_MODULE,  TIMER_NAME);
	//创建设备
	timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL,  TIMER_NAME);

	//初始化定时器
	init_timer(&timerdev.timer);
	timerdev.timer.function = timer_function;
	timerdev.timer.expires = jiffies +msecs_to_jiffies(1000);
	timerdev.timer.data = (unsigned long)&timerdev;
	
	return 0;
}

static void __exit timer_exit(void)
{
	//删除定时器
	del_timer_sync(&timerdev.timer);

	//删除设备的类
	device_destroy(timerdev.class,  timerdev.devid);
	//删除类
	class_destroy(timerdev.class);
	//删除设备
	cdev_del(&timerdev.cdev);
	//注销设备号
	unregister_chrdev_region(timerdev.devid,  TIMER_CNT);
		
	printk(" dev exitn");
}

module_init( timer_init);
module_exit( timer_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZK");

三、应用程序

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

#define CLOSE_CMD		1
#define OPEN_CMD		2
#define SETPERIOD_CMD	3

int main(int argc, char *argv[])
{
    int fd, ret;
    unsigned int cmd;
    unsigned long arg;
    char *filename;
    unsigned char writebuf[1];

    if (argc != 2)
    {
        printf("Usage:n");
        printf("n");
        return -1;
    }
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("open file %s failn", filename);
    }

    while (1)
    {
        printf("please input cmd!rn");
        scanf("%d", &cmd);
        if (cmd == 1)
            cmd = CLOSE_CMD;
        else if (cmd == 2)
            cmd = OPEN_CMD;
        else if (cmd == 3)
        {
            cmd = SETPERIOD_CMD;
            printf("please input timer period:");
            scanf("%d", &arg);
        }

        ioctl(fd, cmd, arg);
    }

    close(fd);
}

四、测试

最后

以上就是微笑蜜粉为你收集整理的一、linux驱动开发-5.1-内核定时器一、时间管理和内核定时器二、驱动程序三、应用程序四、测试的全部内容,希望文章能够帮你解决一、linux驱动开发-5.1-内核定时器一、时间管理和内核定时器二、驱动程序三、应用程序四、测试所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部