一、內核爲高精度定時器重新設計了一套軟件架構,它可以爲我們提供納秒級的定時精度,以滿足對精確時間有迫切需求的應用程序或內核驅動,以下學習使用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 <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/hrtimer.h>
#include <linux/jiffies.h>
#include <linux/miscdevice.h>
#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<duty_cycle<100
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");