1.初始化定时器
void hrtimer_init(struct hrtimer *timer,
clockid_t which_clock,
enum hrtimer_mode mode);
参数说明
timer:描述高精度定时器的结构体;
struct hrtimer {
struct timerqueue_node node;
ktime_t _softexpires;
enum hrtimer_restart (*function)(struct hrtimer *);
struct hrtimer_clock_base *base;
unsigned long state;
.
.
.
};
which_clock:使用的时间基准系统
enum hrtimer_base_type {
HRTIMER_BASE_MONOTONIC, // 单调递增的monotonic时间,不包含休眠时间,不受系统影响
HRTIMER_BASE_REALTIME, // 墙上时间,如果系统时间变了,定时器也会变
HRTIMER_BASE_BOOTTIME, // 单调递增的boottime,包含休眠时间
HRTIMER_MAX_CLOCK_BASES, // 用于后续数组的定义
};
mode:模式,可以有以下几种模式
HRTIMER_MODE_ABS 绝对模式
HRTIMER_MODE_REL 相对模式
HRTIMER_MODE_PINNED 和CPU绑定
HRTIMER_MODE_ABS_PINNED 第一种和第三种的结合
HRTIMER_MODE_REL_PINNED 第二种和第三种的结合
使用如:
hrtimer_init(&polling_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
2.设置超时时间
方式一
poll_delay = ktime_set(0,2000000); // 0s+1000000ns = 2ms
方式二
poll_delay = ns_to_ktime(2 * NSEC_PER_MSEC); //2ms
3.设置超时回掉函数
polling_timer.function = led_timer_func;
led_timer_func定义
static enum hrtimer_restart led_timer_func(struct hrtimer *timer)
{
/* 这里做一些不可描述的操作 */
hrtimer_forward(timer, timer->base->get_time(), poll_delay);//把hrtimer的到期时间推进一个tick周期
return HRTIMER_RESTART;
}
简单说明
返回HRTIMER_RESTART表示该hrtimer需要再次启动,
返回HRTIMER_NORESTART表示该hrtimer不继续启动。
4.开始定时器
hrtimer_start(&polling_timer, poll_delay, HRTIMER_MODE_REL);
5.取消定时器
int hrtimer_cancel(struct hrtimer *timer);
6.模拟PWM控制灯光
–>dts配置
led_light:led_light{
compatible = "allwinner,led_light";
led_gpio = <&r_pio PL 11 1 1 0xffffffff 0xffffffff>;
status = "okay";
};
–>驱动代码
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/hrtimer.h>
//#define PER (HZ/1000)
#define LED_DEVICE_NAME "led_light"
static int led_gpio;
static struct hrtimer polling_timer;
static ktime_t poll_delay;
static uint8_t is_run = 0,light = 5;
static dev_t gpio_dev_t;
static struct cdev *char_dev;
static struct class *cls;
static struct device *dev;
static const struct of_device_id led_light_of_match[] = {
{.compatible = "allwinner,led_light",},
{ /* sentinel */ }
};
static enum hrtimer_restart led_timer_func(struct hrtimer *timer)
{
static uint8_t count = 0;
if(light == 0){
gpio_set_value(led_gpio,0);
is_run = 0;
return HRTIMER_NORESTART;
}
if(count <= light){
gpio_set_value(led_gpio,1);
} else {
gpio_set_value(led_gpio,0);
}
count++;
count %= 11;
hrtimer_forward(timer, timer->base->get_time(), poll_delay);
is_run = 1;
return HRTIMER_RESTART;
}
static int led_open (struct inode *inode, struct file *filp){
return 0;
}
ssize_t led_read (struct file *file, char __user *buf,size_t count, loff_t *ppos) {
copy_to_user(buf, &light, sizeof(light));
return sizeof(light);
}
ssize_t led_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
char vbuff[5] = {'\0','\0','\0','\0','\0'};
int value = 0;
int err = -1;
if(count > 4) {
printk(KERN_ERR"%s:too large to write !!!\n",__func__);
return count;
}
copy_from_user(vbuff, buf, count);
if(count == 1){
value = vbuff[0];
printk(KERN_ERR"%s:value= %d\n",__func__,value);
if(value>=0 && value<=10){
light = (uint8_t)value;
} else if(value>=0x30 && value<=(10+0x30)){
light = (uint8_t)value - 0x30;
}
if(light>0 && is_run == 0){
hrtimer_start(&polling_timer, poll_delay, HRTIMER_MODE_REL);
}
return count;
}
//printk(KERN_ERR"%s: vbuff = %s\n",__func__,vbuff);
err = sscanf(vbuff,"%d",&value);
if(err < 0) {
printk(KERN_ERR"%s: value is invalid!!!\n",__func__);
return count;
}
printk(KERN_ERR"%s:value= %d\n",__func__,value);
if(value>=0 && value<=10){
light = (uint8_t)value;
}
if(light>0 && is_run == 0){
hrtimer_start(&polling_timer, poll_delay, HRTIMER_MODE_REL);
}
return count;
}
static int led_close (struct inode *inode, struct file *filp){
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_close,
};
static int led_light_probe(struct platform_device *pdev) {
struct device_node *np = pdev->dev.of_node;
int ret = -1;
printk(KERN_ERR"------%s------\n",__func__);
if (!np) {
dev_err(&pdev->dev, "laser: Device Tree np missing\n");
return -EINVAL;
}
led_gpio = of_get_named_gpio_flags(np, "led_gpio", 0, NULL);
if (!gpio_is_valid(led_gpio)) {
pr_err("%s: led_gpio is invalid. \n", __func__);
return -1;
}
ret = gpio_request(led_gpio, "led-light");
if(ret < 0){
pr_err("%s: can't request led_gpio. \n", __func__);
return -1;
}
gpio_direction_output(led_gpio,0);
gpio_set_value(led_gpio,0);
/* 初始化定时器 */
hrtimer_init(&polling_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
poll_delay = ns_to_ktime(2 * NSEC_PER_MSEC);
polling_timer.function = led_timer_func;
//hrtimer_start(&polling_timer, poll_delay, HRTIMER_MODE_REL);
is_run = 0;
/* 申请设备号 */
ret = alloc_chrdev_region(&gpio_dev_t,0,1,LED_DEVICE_NAME);
if(ret < 0){
printk(KERN_ERR"dev_t request error!\n");
goto chrdev_alloc_error;
}
/* 分配cdev */
char_dev = cdev_alloc();
if (NULL == char_dev || IS_ERR(char_dev)) {
printk(KERN_ERR"char_dev fail!\n");
goto cdev_alloc_error;
}
/* 初始化cdev */
cdev_init(char_dev, &fops);
/* 注册cdev */
ret = cdev_add(char_dev, gpio_dev_t, 1);
if(ret < 0){
printk(KERN_ERR"cdev add error!\n");
goto cdev_add_error;
}
/* 创建设备节点 */
cls = class_create(THIS_MODULE, LED_DEVICE_NAME);
dev = device_create(cls, NULL, gpio_dev_t, NULL, LED_DEVICE_NAME);
if (NULL == dev || IS_ERR(dev)) {
printk(KERN_ERR"device_create fail!\n");
goto device_creat_error;
}
return 0;
/* 异常处理 */
device_creat_error:
class_destroy(cls);
cdev_del(char_dev);
cdev_add_error:
// kfree(char_dev);
cdev_alloc_error:
unregister_chrdev_region(gpio_dev_t,1);
chrdev_alloc_error:
gpio_set_value(led_gpio, 0);
gpio_free(led_gpio);
return -1;
}
static int led_light_remove(struct platform_device *pdev) {
device_del(dev);
class_destroy(cls);
cdev_del(char_dev);
// kfree(char_dev);
unregister_chrdev_region(gpio_dev_t,1);
hrtimer_cancel(&polling_timer);
gpio_set_value(led_gpio, 0);
gpio_free(led_gpio);
return 0;
}
#ifdef CONFIG_PM
static int led_light_suspend(struct device *dev) {
gpio_set_value(led_gpio,0);
return 0;
}
static int led_light_resume(struct device *dev) {
return 0;
}
static const struct dev_pm_ops led_light_pm = {
.suspend = led_light_suspend,
.resume = led_light_resume,
};
#define led_light_pm_ops (&led_light_pm)
#else /* CONFIG_PM */
#define led_light_pm_ops NULL
#endif /* CONFIG_PM */
static struct platform_driver led_light_driver = {
.driver = {
.name = "led_light",
.of_match_table = of_match_ptr(led_light_of_match),
.pm = led_light_pm_ops,
},
.probe = led_light_probe,
.remove = led_light_remove,
};
module_platform_driver(led_light_driver);
MODULE_LICENSE("GPL v2");