4. 例-内核高精度定时器

本文详细介绍了Linux内核中的高精度定时器hrtimer的初始化、设置超时时间、回调函数、启动和取消操作。通过实例展示了如何使用hrtimer实现模拟PWM控制灯光,包括设备树配置、驱动代码编写等步骤,涉及到了GPIO和中断处理。
摘要由CSDN通过智能技术生成

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");
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值