Linux驱动学习记录-11.内核定时器

这章学习Linux内核定时器,介绍其API函数和用法。

一、时间管理

系统节拍默认100hz,即每秒100次,定义一个HZ=100
Linux内核使用全局变量jiffies来记录系统从启动以来的系统节拍数,系统启动时将jiffies初始化为0,定义在include/linux/jiffies.h中。
所以jiffies/HZ就是系统运行时间。

函数描述
time_after(unknow,know)unknown超过known返回真,否则返回假
time_before(unknow,known)没有超过返回真,否则返回假
time_after_eq(unknow,known)大于等于返回真,否则返回假
time_before_eq(unknown,known)小于等于返回真,否则返回假

unknown一般为jiffies,known是对比的值。
下面是jiffies和ms、us、ns之间的转换函数

函数描述
int jiffies_to_msecs(const unsigned long j)jiffies转换成毫秒
int jiffies_to_usecs(const unsigned long j)jiffies转换成微秒
u64 jiffies_to_nsecs(const unsigned long j)jiffies转换成纳秒
long msecs_to_jiffies(const unsigned int m)毫秒转换成jiffies
long usecs_to_jiffies(const unsigned int u)微妙转换成jiffes
unsigned long nsecs_to_jiffies(u64 n)纳秒转换成jiffies

二、内核定时器

提供超时时间和需要处理的函数即可。

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

比如定义一个周期2s的定时器,超时时间定义为expires = jiffies+(2*HZ)。
下面是定时器的API函数

/*初始化*/
void init_timer (struct timer_list *timer)
/*向内核注册定时器*/
void add_timer (struct timer_list *timer)
/*删除定时器,成功返回1*/
int del_timer (struct timer_list * timer)
/*等待其他处理器使用完定时器再删除*/
int del_timer_sync (struct timer_list *timer)
/*修改定时器的值*/
int mod_timer (struct timer_list *timer, unsigned long expires)

Linux内核短延迟函数

函数描述
void ndelay(unsigned long nsecs)纳秒延迟
void udelay(unsignd long usces)微秒延迟
void mdelay(unsigned long msecs)毫秒延迟

三、驱动程序

使用定时器让led闪,输入时间控制闪烁频率。

#include <linux/timer.h>
#define TIMER_CNT 1
#define KEY_NAME  "timer"

struct timer_device {
    struct device_node *nd;
    int timer_gpio;
    struct timer_list timer;
    int period;
}
struct timer_device timer;
static int timer_open(struct inode *inode, struct file *filp)
{
    filp->private = &timer;
    timer.period = 1000;
}
/*
filp:要打开的设备文件
cmd:应用程序发送的命令
arg:参数
*/
static long timer_unlocked_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct timer_device *dev = filp->private;
    int ret;
    switch(cmd) {
        case CLOSE:
        ret = del_timer_sync(&dev->timer);
        break;
        case OPEN:
        mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->period));
        break;
        case SET:
        dev->period = arg;
        mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->period));
        break;
        default:
        break;
    }
    return 0;
}
static struct file_operations timer_fops = {
    .owner = THIS_MODULE,
    .open = timer_open,
    .unlocked_ioctl = timer_unlocked_ioctl,
};
void function(unsigned long arg)
{
    struct timer_device *t = (struct timer_device *)arg;
    static int temp = 0;
    if(temp % 2)
        gpio_set_value(t->timer_gpio, 1);
    else
        gpio_set_value(t->timer_gpio, 0);

    temp++;
    mod_timer (&t->limer, jiffies + msecs_to_jiffies(t->period));
}
static int __init timer_init(void)
{
    init_timer(&timer.timer);
    timer.timer.function = function;
    timer.timer.expires = jiffies + msecs_to_jiffies(1000);
    timer.timer.data = (unsigned long) &timer;

    return 0;

}
static void __exit timer_exit(void)
{
    del_timer_sync(&timer.timer);

    cdev_del(&timer.cdev);
    unreginser_chrdev_region(timer.devid, TIMER_CNT);
    device_destroy(timer.class, timer.devid);
    class_destroy(timer.class);
}

重复代码略写
这fops函数加了unlocked_ioctl函数,用于传递参数。
在del定时器后,可以不必初始化-注册定时器,因为只是从内核删除,本身还存在,所以再激活,只要add或者mod函数激活既可以使用。
定时器fun函数输入参数是dev结构体的地址(unsigned long),因此可以在fun函数继续使用结构体内的变量。
最后exit函数,记得删除定时器,删除建议使用sync函数。

四、测试

1.测试App

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

	fd = open(filename, O_RDWR);
	while (1) {
		printf("Input CMD:");
		ret = scanf("%d", &cmd);
		if (ret != 1) {				/* 参数输入错误 */
			gets(str);				/* 防止卡死 */
		}

		if(cmd == 1)				/* 关闭LED灯 */
			cmd = CLOSE;
		else if(cmd == 2)			/* 打开LED灯 */
			cmd = OPEN;
		else if(cmd == 3) {
			cmd = SET;	/* 设置周期值 */
			printf("Input Timer Period:");
			ret = scanf("%d", &arg);
			if (ret != 1) {			/* 参数输入错误 */
				gets(str);			/* 防止卡死 */
			}
		}
		ioctl(fd, cmd, arg);		/* 控制定时器的打开和关闭 */	
	}
	close(fd);
}

2.编译

和前面一样

3.测试

参考App代码吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值