概述
目录
一、时间管理和内核定时器
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-内核定时器一、时间管理和内核定时器二、驱动程序三、应用程序四、测试所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复