基础操作:
LED:红灯与蓝灯,红灯---亮/灭,蓝灯---亮/灭/呼吸 节点:/sys/class/misc/misc_dev/pwm_leds/ blue_func: cat 读取状态 echo 写入:0:低亮;1:中亮;2:高亮;4:灭;6:呼吸灯 red_func cat 读取状态 echo 写入:1:中亮;4:灭;
可调节led占空比,输入任意101-199的数字,其值减去100即为输入占空比,如:
想输入占空比为95,则输入:
echo 195 > /sys/class/misc/misc_dev/pwm_leds/blue_func
想输入占空比为4,则输入:
echo 104 > /sys/class/misc/misc_dev/pwm_leds/blue_func
效果展示:
pwm led驱动源码
代码语言:javascript复制#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/ktime.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/syslog.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/pwm.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/*red led gpio PE1*/
#define RLED_NUM 129
#define ENABLE 1
#define DISABLE 0
#define MAX 100
#define LED_DEFAULT_STATE 9
#define LED_DEFAULT_DELAY (HZ / 20)
//#define DEBUG_PWM
#ifdef DEBUG_PWM
#define pwm_printf pr_info
#else
#define pwm_printf pr_debug
#endif
static unsigned int duty_ns = 8000000; /*占空,高电平 u32*/
static unsigned long long period_ns = 10000000; /*周期__u64*/
static struct mutex pwm_muter;
static struct delayed_work blue_led_delay_work;
static struct delayed_work green_led_delay_work;
struct pwm_green_led {
struct pwm_device *pwm0_dev;
int pwm_id; /*0:green;1:blue*/
unsigned long green_status;
int time_delay;
};
struct pwm_blue_led {
struct pwm_device *pwm1_dev;
int pwm_id; /*0:green;1:blue*/
unsigned long blue_status;
int time_delay;
};
struct pwm_red_led {
unsigned long red_status;
u32 rled_gpio;
};
static struct pwm_green_led *t_pwm_green_led;
static struct pwm_blue_led *t_pwm_blue_led;
static struct pwm_red_led *t_pwm_red_led;
static void set_pwm(int pwm_id, int level)
{
unsigned int ret = 0;
unsigned duty_ns, period_ns ;
mutex_lock(&pwm_muter);
period_ns = 100000; /*1000000000/10000 freq=10KHz, 1s=1000000000ns*/
duty_ns = (period_ns * level)/MAX ;
if (pwm_id == 0) {
ret = pwm_config(t_pwm_green_led->pwm0_dev, duty_ns, period_ns);
ret = pwm_enable(t_pwm_green_led->pwm0_dev);
} else if (pwm_id == 1) {
ret = pwm_config(t_pwm_blue_led->pwm1_dev, duty_ns, period_ns);
ret = pwm_enable(t_pwm_blue_led->pwm1_dev);
}
pwm_printf("[pwm_leds] pwm_config pwm%d:<%d | %d>, ret = %dn",
pwm_id, duty_ns, period_ns, ret);
mutex_unlock(&pwm_muter);
}
static ssize_t get_blue_func(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%dn", (int)t_pwm_blue_led->blue_status);
}
static ssize_t set_blue_func(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
ret = kstrtoul(buf, 10, &t_pwm_blue_led->blue_status);
if (t_pwm_blue_led->time_delay != 0)
cancel_delayed_work_sync(&blue_led_delay_work);
pwm_printf("id = %d, status= %dn",
t_pwm_blue_led->pwm_id, t_pwm_blue_led->blue_status);
switch (t_pwm_blue_led->blue_status) {
case 0: /*LOW_BRIGHT*/
t_pwm_blue_led->time_delay = 0;
set_pwm(t_pwm_blue_led->pwm_id, 90);
break;
case 1: /*MIDIUM_BRIGHT*/
t_pwm_blue_led->time_delay = 0;
set_pwm(t_pwm_blue_led->pwm_id, 50);
break;
case 2: /*HIGHT_BRIGHT*/
t_pwm_blue_led->time_delay = 0;
set_pwm(t_pwm_blue_led->pwm_id, 10);
break;
case 4: /*DISABLE*/
t_pwm_blue_led->time_delay = 0;
pwm_disable(t_pwm_blue_led->pwm1_dev);
break;
case 6: /*ADJEST_BRGHT*/
t_pwm_blue_led->time_delay = HZ / 2;
break;
case 7: /*test*/
t_pwm_blue_led->time_delay = 0;
set_pwm(t_pwm_blue_led->pwm_id, 99);
break;
case 8: /*test*/
t_pwm_blue_led->time_delay = 0;
set_pwm(t_pwm_blue_led->pwm_id, 50);
break;
case 9: /*test*/
t_pwm_blue_led->time_delay = 0;
set_pwm(t_pwm_blue_led->pwm_id, 4);
break;
default:
if(t_pwm_blue_led->blue_status > 100 || t_pwm_blue_led->blue_status < 200){
t_pwm_blue_led->time_delay = 0;
set_pwm(t_pwm_blue_led->pwm_id, (t_pwm_blue_led->blue_status-100));
}else{
t_pwm_blue_led->time_delay = 0;
pr_err("inval cmdn");
}
break;
}
if (t_pwm_blue_led->time_delay)
schedule_delayed_work(&blue_led_delay_work,
t_pwm_blue_led->time_delay);
return size;
}
static ssize_t get_green_func(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%dn", (int)t_pwm_green_led->green_status);
}
static ssize_t set_green_func(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
ret = kstrtoul(buf, 10, &t_pwm_green_led->green_status);
if (t_pwm_green_led->time_delay != 0)
cancel_delayed_work_sync(&green_led_delay_work);
pwm_printf("pwm_id = %d, status= %dn",
t_pwm_green_led->pwm_id,
t_pwm_green_led->green_status);
switch (t_pwm_green_led->green_status) {
case 0: /*LOW_BRIGHT*/
t_pwm_green_led->time_delay = 0;
set_pwm(t_pwm_green_led->pwm_id, 90);
break;
case 1: /*MIDIUM_BRIGHT*/
t_pwm_green_led->time_delay = 0;
set_pwm(t_pwm_green_led->pwm_id, 50);
break;
case 2: /*HIGHT_BRIGHT*/
t_pwm_green_led->time_delay = 0;
set_pwm(t_pwm_green_led->pwm_id, 10);
break;
case 4: /*DISABLE*/
t_pwm_green_led->time_delay = 0;
pwm_disable(t_pwm_green_led->pwm0_dev);
break;
case 6: /*ADJEST_BRGHT*/
t_pwm_green_led->time_delay = HZ / 20;
break;
default:
if(t_pwm_green_led->green_status > 100 || t_pwm_green_led->green_status < 200){
t_pwm_green_led->time_delay = 0;
set_pwm(t_pwm_green_led->pwm_id, (t_pwm_green_led->green_status-100));
}else{
t_pwm_green_led->time_delay = 0;
pr_err("inval cmdn");
}
break;
}
if (t_pwm_green_led->time_delay)
schedule_delayed_work(&green_led_delay_work,
t_pwm_green_led->time_delay);
#if 0
/*int pwm_id = 0;*/
switch (green_status) {
case 1: /*MIDIUM_BRIGHT*/
set_pwm0(0, 30);
break;
case 4: /*DISABLE*/
pwm_disable(pwm0_dev);
break;
default:
pr_err("inval cmdn");
break;
}
#endif
return size;
}
static ssize_t set_red_func(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
ret = kstrtoul(buf, 10, &t_pwm_red_led->red_status);
pwm_printf("red_status= %dn", t_pwm_red_led->red_status);
switch (t_pwm_red_led->red_status) {
case 0:
case 1: /*MIDIUM_BRIGHT*/
case 2:
gpio_set_value(t_pwm_red_led->rled_gpio, 1);
break;
case 4: /*DISABLE*/
gpio_set_value(t_pwm_red_led->rled_gpio, 0);
break;
default:
pr_err("inval cmdn");
break;
}
return size;
}
static ssize_t get_red_func(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%dn", (int)t_pwm_red_led->red_status);
}
static DEVICE_ATTR(blue_func, 0664,
get_blue_func, set_blue_func);
static DEVICE_ATTR(green_func, 0664,
get_green_func, set_green_func);
static DEVICE_ATTR(red_func, 0664,
get_red_func, set_red_func);
static struct attribute *sunxi_pwmleds_attributes[] = {
&dev_attr_blue_func.attr,
&dev_attr_green_func.attr,
&dev_attr_red_func.attr,
NULL
};
static struct attribute_group sunxi_pwmleds_attribute_group = {
.name = "pwm_leds",
.attrs = sunxi_pwmleds_attributes
};
static struct miscdevice sunxi_pwmleds_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_dev",
};
static void pwm_blue_led_delay_work(struct work_struct *work)
{
static int level;
static int pol;
pwm_printf(">>>led_timer_func level= %d, pol=%d n", level, pol);
int pwm_id = t_pwm_blue_led->pwm_id;
switch (level) {
#if 1
case 0:
set_pwm(pwm_id, 99);
break;
case 1:
set_pwm(pwm_id, 96);
break;
case 2:
set_pwm(pwm_id, 92);
break;
case 3:
set_pwm(pwm_id, 88);
break;
case 4:
set_pwm(pwm_id, 84);
break;
case 5:
set_pwm(pwm_id, 80);
break;
case 6:
set_pwm(pwm_id, 76);
break;
case 7:
set_pwm(pwm_id, 72);
break;
case 8:
set_pwm(pwm_id, 68);
break;
case 9:
set_pwm(pwm_id, 64);
break;
case 10:
set_pwm(pwm_id, 60);
break;
case 11:
set_pwm(pwm_id, 56);
break;
case 12:
set_pwm(pwm_id, 52);
break;
case 13:
set_pwm(pwm_id, 48);
break;
case 14:
set_pwm(pwm_id, 44);
break;
case 15:
set_pwm(pwm_id, 40);
break;
case 16:
set_pwm(pwm_id, 36);
break;
case 17:
set_pwm(pwm_id, 32);
break;
case 18:
set_pwm(pwm_id, 28);
break;
case 19:
set_pwm(pwm_id, 24);
break;
case 20:
set_pwm(pwm_id, 20);
break;
case 21:
set_pwm(pwm_id, 16);
break;
case 22:
set_pwm(pwm_id, 12);
break;
case 23:
set_pwm(pwm_id, 8);
break;
case 24:
set_pwm(pwm_id, 4);
break;
#else
case 0:
set_pwm(pwm_id, 100);
break;
/* case 1: set_pwm(pwm_id, 96); break;*/
case 2:
set_pwm(pwm_id, 92);
break;
/* case 3: set_pwm(pwm_id, 88); break;*/
case 4:
set_pwm(pwm_id, 84);
break;
/* case 5: set_pwm(pwm_id, 80); break;*/
case 6:
set_pwm(pwm_id, 76);
break;
/* case 7: set_pwm(pwm_id, 72); break;*/
case 8:
set_pwm(pwm_id, 68);
break;
/* case 9: set_pwm(pwm_id, 64); break;*/
case 10:
set_pwm(pwm_id, 60);
break;
/* case 11: set_pwm(pwm_id, 56); break;*/
case 12:
set_pwm(pwm_id, 52);
break;
/* case 13: set_pwm(pwm_id, 48); break;*/
case 14:
set_pwm(pwm_id, 44);
break;
/* case 15: set_pwm(pwm_id, 40); break;*/
case 16:
set_pwm(pwm_id, 36);
break;
/* case 17: set_pwm(pwm_id, 32); break;*/
case 18:
set_pwm(pwm_id, 28);
break;
/* case 19: set_pwm(pwm_id, 24); break;*/
case 20:
set_pwm(pwm_id, 20);
break;
/* case 21: set_pwm(pwm_id, 16); break;*/
case 22:
set_pwm(pwm_id, 12);
break;
/* case 23: set_pwm(pwm_id, 8); break;*/
case 24:
set_pwm(pwm_id, 4);
break;
#endif
default:
break;
}
if (pol == 1) {
level = level 1;
if (level >= 24) {
level = 24;
pol = 0;
}
} else {
level = level - 1;
if (level <= 0) {
level = 0;
pol = 1;
}
}
if (level == 24)
schedule_delayed_work(&blue_led_delay_work, HZ/1);
else if (level == 0)
schedule_delayed_work(&blue_led_delay_work, HZ/2);
else
schedule_delayed_work(&blue_led_delay_work, HZ/(level 4));
}
static void pwm_green_led_delay_work(struct work_struct *work)
{
static int level;
static int pol;
int pwm_id = t_pwm_green_led->pwm_id;
pwm_printf(">>>led_timer_func level= %d, pol=%d n", level, pol);
switch (level) {
#if 1
case 0:
set_pwm(pwm_id, 99);
break;
case 1:
set_pwm(pwm_id, 96);
break;
case 2:
set_pwm(pwm_id, 92);
break;
case 3:
set_pwm(pwm_id, 88);
break;
case 4:
set_pwm(pwm_id, 84);
break;
case 5:
set_pwm(pwm_id, 80);
break;
case 6:
set_pwm(pwm_id, 76);
break;
case 7:
set_pwm(pwm_id, 72);
break;
case 8:
set_pwm(pwm_id, 68);
break;
case 9:
set_pwm(pwm_id, 64);
break;
case 10:
set_pwm(pwm_id, 60);
break;
case 11:
set_pwm(pwm_id, 56);
break;
case 12:
set_pwm(pwm_id, 52);
break;
case 13:
set_pwm(pwm_id, 48);
break;
case 14:
set_pwm(pwm_id, 44);
break;
case 15:
set_pwm(pwm_id, 40);
break;
case 16:
set_pwm(pwm_id, 36);
break;
case 17:
set_pwm(pwm_id, 32);
break;
case 18:
set_pwm(pwm_id, 28);
break;
case 19:
set_pwm(pwm_id, 24);
break;
case 20:
set_pwm(pwm_id, 20);
break;
case 21:
set_pwm(pwm_id, 16);
break;
case 22:
set_pwm(pwm_id, 12);
break;
case 23:
set_pwm(pwm_id, 8);
break;
case 24:
set_pwm(pwm_id, 4);
break;
#else
case 0:
set_pwm(pwm_id, 100);
break;
/* case 1: set_pwm(pwm_id, 96); break;*/
case 2:
set_pwm(pwm_id, 92);
break;
/* case 3: set_pwm(pwm_id, 88); break;*/
case 4:
set_pwm(pwm_id, 84);
break;
/* case 5: set_pwm(pwm_id, 80); break;*/
case 6:
set_pwm(pwm_id, 76);
break;
/* case 7: set_pwm(pwm_id, 72); break;*/
case 8:
set_pwm(pwm_id, 68);
break;
/* case 9: set_pwm(pwm_id, 64); break;*/
case 10:
set_pwm(pwm_id, 60);
break;
/* case 11: set_pwm(pwm_id, 56); break;*/
case 12:
set_pwm(pwm_id, 52);
break;
/* case 13: set_pwm(pwm_id, 48); break;*/
case 14:
set_pwm(pwm_id, 44);
break;
/* case 15: set_pwm(pwm_id, 40); break;*/
case 16:
set_pwm(pwm_id, 36);
break;
/* case 17: set_pwm(pwm_id, 32); break;*/
case 18:
set_pwm(pwm_id, 28);
break;
/* case 19: set_pwm(pwm_id, 24); break;*/
case 20:
set_pwm(pwm_id, 20);
break;
/* case 21: set_pwm(pwm_id, 16); break;*/
case 22:
set_pwm(pwm_id, 12);
break;
/* case 23: set_pwm(pwm_id, 8); break;*/
case 24:
set_pwm(pwm_id, 4);
break;
#endif
default:
break;
}
if (pol == 1) {
level = level 1;
if (level >= 24) {
level = 24;
pol = 0;
}
} else {
level = level - 1;
if (level <= 0) {
level = 0;
pol = 1;
}
}
if (level == 24)
schedule_delayed_work(&green_led_delay_work, HZ/1);
else if (level == 0)
schedule_delayed_work(&green_led_delay_work, HZ/2);
else
schedule_delayed_work(&green_led_delay_work, HZ/(level 4));
}
static int pwm_leds_ctrl_init(void)
{
int ret = 0;
int pwm_id = 0; /*pwm0_para used*/
pwm_printf("[pwm_leds]: %s: %d===inton", __func__, __LINE__);
t_pwm_green_led = kzalloc(sizeof(struct pwm_green_led), GFP_KERNEL);
t_pwm_blue_led = kzalloc(sizeof(struct pwm_blue_led), GFP_KERNEL);
t_pwm_red_led = kzalloc(sizeof(struct pwm_red_led), GFP_KERNEL);
if (!t_pwm_green_led || !t_pwm_blue_led || !t_pwm_red_led) {
pr_err("[pwm_leds] request memory fail!n");
return -1;
}
t_pwm_green_led->pwm0_dev = pwm_request(0, "led_green");
if (t_pwm_green_led->pwm0_dev == NULL ||
IS_ERR(t_pwm_green_led->pwm0_dev)) {
pr_err("[pwm_leds] pwm_request pwm0 fail!n");
return -1;
}
t_pwm_green_led->green_status = LED_DEFAULT_STATE;
t_pwm_green_led->pwm_id = 0;
t_pwm_green_led->time_delay = LED_DEFAULT_DELAY;
pwm_printf("[pwm_leds] pwm_request pwm0 success!n");
t_pwm_blue_led->pwm1_dev = pwm_request(1, "led_blue");
if (t_pwm_blue_led->pwm1_dev == NULL ||
IS_ERR(t_pwm_blue_led->pwm1_dev)) {
pr_err("[pwm_leds] pwm_request pwm1 fail!n");
return -1;
}
t_pwm_blue_led->blue_status = LED_DEFAULT_STATE;
t_pwm_blue_led->pwm_id = 1;
t_pwm_blue_led->time_delay = LED_DEFAULT_DELAY;
pwm_printf("[pwm_leds] pwm_request pwm1 success!n");
t_pwm_red_led->rled_gpio = RLED_NUM;
t_pwm_red_led->red_status = LED_DEFAULT_STATE;
if (!gpio_is_valid(t_pwm_red_led->rled_gpio)) {
pr_err("failed to get gpion");
return -EINVAL;
}
ret = gpio_request(t_pwm_red_led->rled_gpio, "rled_gpio");
if (ret) {
pr_err("failed to request rled gpion");
return -EINVAL;
}
gpio_direction_output(t_pwm_red_led->rled_gpio, 1);
gpio_set_value(t_pwm_red_led->rled_gpio, 0);
mutex_init(&pwm_muter);
INIT_DELAYED_WORK(&blue_led_delay_work, pwm_blue_led_delay_work);
INIT_DELAYED_WORK(&green_led_delay_work, pwm_green_led_delay_work);
return ret;
}
static int __init pwm_leds_init(void)
{
int ret, err, pwmleds_used;
pwm_printf("pwmleds debug gpio driver initn");
ret = pwm_leds_ctrl_init();
if (ret == 0)
pwm_printf("pwm_leds_init is ok==============n");
else
pwm_printf("pwm_leds_init is err==============n");
err = misc_register(&sunxi_pwmleds_dev);
if (err) {
pwm_printf("%s led register as misc device errorn", __func__);
goto exit;
}
err = sysfs_create_group(&sunxi_pwmleds_dev.this_device->kobj,
&sunxi_pwmleds_attribute_group);
if (err)
pwm_printf("%s sysfs_create_group errorn", __func__);
return 0;
exit:
return -1;
}
static void __exit pwm_leds_exit(void)
{
pwm_printf("Bye, pwm_leds exitn");
gpio_set_value(t_pwm_red_led->rled_gpio, 0);
gpio_free(t_pwm_red_led->rled_gpio);
pwm_disable(t_pwm_green_led->pwm0_dev);
pwm_disable(t_pwm_blue_led->pwm1_dev);
cancel_delayed_work_sync(&blue_led_delay_work);
cancel_delayed_work_sync(&green_led_delay_work);
pwm_free(t_pwm_green_led->pwm0_dev);
pwm_free(t_pwm_blue_led->pwm1_dev);
kfree(t_pwm_green_led);
kfree(t_pwm_blue_led);
kfree(t_pwm_red_led);
sysfs_remove_group(&sunxi_pwmleds_dev.this_device->kobj,
&sunxi_pwmleds_attribute_group);
misc_deregister(&sunxi_pwmleds_dev);
}
module_init(pwm_leds_init);
module_exit(pwm_leds_exit);
MODULE_DESCRIPTION("a simple pwmleds driver");
MODULE_AUTHOR("yanu");
MODULE_LICENSE("GPL");