rk3288模擬可調佔空比pwm波形

一、內核爲高精度定時器重新設計了一套軟件架構,它可以爲我們提供納秒級的定時精度,以滿足對精確時間有迫切需求的應用程序或內核驅動,以下學習使用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");


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章