全志平台Tina PWM控制LED调试

2024-02-02 14:49:43 浏览数 (2)

基础操作:

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");

0 人点赞