一、内核为高精度定时器重新设计了一套软件架构,它可以为我们提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动,以下学习使用hrtimer(high resolution timer)高精度定时器。
二、hrtimer_init函数初始化定时器工作模式。which_clock可以是CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_BOOTTIME中的一种,mode则可以是相对时间HRTIMER_MODE_REL,也可以是绝对时间HRTIMER_MODE_ABS。
void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,
enum hrtimer_mode mode);
三、设定超时回调函数。
timer.function = hr_callback;
四、使用hrtimer_start激活该定时器。根据time和mode参数的值计算hrtimer的超时时间,并设置到timer->expire域。 expire设置的是绝对时间,所以如果参数mode的值为HRTIMER_MODE_REL(即参数tim的值为相对时间),那么需要将tim的值修正为绝对时间:expire = tim + timer->base->get_time(),调用enqueue_hrtimer,将hrtimer加入到红黑树中。
int hrtimer_start(struct hrtimer *timer, ktime_t tim,
const enum hrtimer_mode mode);
五、使用hrtimer_cancel取消一个hrtimer。
int hrtimer_cancel(struct hrtimer *timer);
六、定时器一旦到期,function字段指定的回调函数会被调用,该函数的返回值为一个枚举值,它决定了该hrtimer是否需要被重新激活。
enum hrtimer_restart {
HRTIMER_NORESTART, /* Timer is not restarted /
HRTIMER_RESTART, / Timer must be restarted */
};
七、把hrtimer的到期时间推进一个tick周期,返回HRTIMER_RESTART表明该hrtimer需要再次启动,以便产生下一个tick事件。
hrtimer_forward(timer, now, tick_period);
return HRTIMER_RESTART;
示例为模拟pwm波
形控制占空比调节风扇的速度
/*
* linux/drivers/video/backlight/dc_fan.c
* direct current fan driver
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GPIO_HIGH 1
#define GPIO_LOW 0
int FAN_OUT_GPIO;
int FAN_EN_GPIO;
int fan_freq=1000; //1000-1Khz
int duty_cycle=40;
int temp;
static struct hrtimer dc_timer;
ktime_t kt;
ssize_t dcfan_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int ret;
char *data;
char *first,*second;
printk(KERN_EMERG "%s,line:%d\n",__FUNCTION__,__LINE__);
if(!copy_from_user((char *)data, buf, count))
{
printk(KERN_EMERG "%s,1,data:%s,count=%d\n",__FUNCTION__,data,count);
first = strsep(&data,",");
second = data;
ret = kstrtoint(first,10,&temp);
if(ret){
printk(KERN_EMERG "%s,line:%d,kstrtoint-err\n",__FUNCTION__,__LINE__);
return -1;
}
if(temp<100) duty_cycle = temp; //0
ret = kstrtoint(second,10,&temp);
if(ret){
printk(KERN_EMERG "%s,line:%d,kstrtoint-err\n",__FUNCTION__,__LINE__);
return -1;
}
if(temp<10000) fan_freq = 1000000/temp; //fan_freq<10000Hz
printk(KERN_EMERG "App write duty_cycle=%d,fan_freq=%d\n",duty_cycle,fan_freq);
return count;
}
else{
return -1;
}
}
ssize_t dcfan_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
size_t cnt;
char data[10]={0};
printk(KERN_EMERG "%s-line:%d: enter\n",__FUNCTION__,__LINE__);
sprintf(data,"%d,%d",duty_cycle,1000000/fan_freq);
printk(KERN_EMERG "%s,duty_cycle=%d,fan_freq=%dHz,data=%s\n",\
__FUNCTION__,duty_cycle,fan_freq,data);
cnt = strlen(data);
if(!copy_to_user((char *)buf, data, cnt))
return 0;
else
return -1;
}
static int dcfan_open(struct inode *inode, struct file *file)
{
printk(KERN_EMERG "%s-line:%d: enter\n",__FUNCTION__,__LINE__);
return 0;
}
static struct file_operations dcfan_ops = {
.owner = THIS_MODULE,
.open = dcfan_open,
.read = dcfan_read,
.write = dcfan_write,
};
enum hrtimer_restart hrtimer_hander(struct hrtimer *timer)
{
unsigned long valid_value=0;
static int value=0;
//printk(KERN_EMERG "%s-line:%d\n",__FUNCTION__,__LINE__);
value++;
if(value%2){
valid_value = 1000*duty_cycle*fan_freq/100;
gpio_set_value(FAN_OUT_GPIO,GPIO_HIGH);
}
else{
valid_value =1000*(100-duty_cycle) * fan_freq/100;
gpio_set_value(FAN_OUT_GPIO,GPIO_LOW);
}
kt = ktime_set(0,valid_value);
hrtimer_forward(timer,timer->base->get_time(),kt);
return HRTIMER_RESTART;
}
static int get_dts_gpio(struct platform_device *pdev)
{
int ret ;
enum of_gpio_flags flag;
struct device_node *fan_node = pdev->dev.of_node;
/********FAN_OUT_GPIO********/
FAN_OUT_GPIO = of_get_named_gpio_flags(fan_node,"dcfan-out-gpios",0,&flag);
if (!gpio_is_valid(FAN_OUT_GPIO)){
printk(KERN_INFO "hello: invalid gpio : %d\n",FAN_OUT_GPIO);
return -1;
}
ret = gpio_request(FAN_OUT_GPIO, "fan_out_gpio");
if (ret) {
gpio_free(FAN_OUT_GPIO);
return -EIO;
}
gpio_direction_output(FAN_OUT_GPIO, GPIO_HIGH);
/********FAN_EN_GPIO********/
FAN_EN_GPIO = of_get_named_gpio_flags(fan_node,"enable-gpios",0,&flag);
if (!gpio_is_valid(FAN_EN_GPIO)){
printk(KERN_INFO "hello: invalid gpio : %d\n",FAN_EN_GPIO);
return -1;
}
ret = gpio_request(FAN_EN_GPIO, "fan_en_gpio");
if (ret) {
gpio_free(FAN_EN_GPIO);
return -EIO;
}
gpio_direction_output(FAN_EN_GPIO, GPIO_HIGH);
return 0;
}
struct miscdevice dc_fan_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "dc-fan",
.fops = &dcfan_ops,
};
static int dc_fan_probe(struct platform_device *pdev)
{
int ret;
dev_info(&pdev->dev, "%s,line:%d: enter\n",__FUNCTION__,__LINE__);
get_dts_gpio(pdev);
ret = misc_register(&dc_fan_dev);
kt = ktime_set(0,500);
hrtimer_init(&dc_timer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
hrtimer_start(&dc_timer,kt,HRTIMER_MODE_REL);
dc_timer.function = hrtimer_hander;
printk(KERN_INFO "%s-line:%d: exit\n",__FUNCTION__,__LINE__);
return 0;
}
static int dc_fan_remove(struct platform_device *pdev)
{
printk(KERN_INFO "Enter %s\n", __FUNCTION__);
gpio_free(FAN_OUT_GPIO);
gpio_free(FAN_EN_GPIO);
misc_deregister(&dc_fan_dev);
hrtimer_cancel(&dc_timer);
return 0;
}
static void dc_fan_shutdown(struct platform_device *pdev)
{
dc_fan_remove(pdev);
}
static const struct of_device_id of_dc_fan_match[] = {
{ .compatible = "dc-fan", },
{},
};
static struct platform_driver dc_fan_driver = {
.probe = dc_fan_probe,
.remove = dc_fan_remove,
.shutdown = dc_fan_shutdown,
.driver = {
.name = "dc-fan",
//.pm = GPIO_FAN_PM,
#ifdef CONFIG_OF_GPIO
.of_match_table = of_match_ptr(of_dc_fan_match),
#endif
},
};
module_platform_driver(dc_fan_driver);
MODULE_DESCRIPTION("DC FAN Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:gpio-fan");