单总线led驱动芯片WS2811在OpenWrt下的驱动

硬件平台:mt7688  
软件平台:原生sdk MediaTek_ApSoC_SDK_4300_20140916  linux-3.10.14内核
 
ws2811是单总线接口的led驱动芯片,通过单个GPIO模拟高低时序发出颜色值,芯片收到后控制输出对应PWM改变三路RGB灯的颜色。
 
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include "ralink_gpio.h"
 
 
static dev_t sys_led_dev;
static struct class* sys_led_class = 0;
static struct cdev* sys_led_cdev = 0;
#define SYS_LED_NAME        "sys_led"
 
union _rgb
{
    unsigned int color;
    struct 
    {
        unsigned char r;
        unsigned char g;
        unsigned char b;
    }u_s;
};
 
#define set_led_hight()        *(volatile u32*)(RALINK_REG_PIOSET1) = (1 << (44 - 32))
#define set_led_low()        *(volatile u32*)(RALINK_REG_PIORESET1) = (1 << (44 - 32))
 
inline void set_out()//设置GPIO为输出以便控制WS2811
{
    unsigned long tmp;
 
    tmp = *(volatile u32 *)(RALINK_REG_GPIOMODE2);
    tmp |= 1;
    *(volatile u32 *)(RALINK_REG_GPIOMODE2) = tmp;
 
    tmp = *(volatile u32*)(RALINK_REG_PIODIR1);
    tmp |= (1 << (44 - 32));
    *(volatile u32*)(RALINK_REG_PIODIR1) = tmp;
}
 
static void set_led_1(void)//发出二进制1
{
    set_led_hight();
    ndelay(678);
    set_led_low();
    ndelay(278);
}
 
static void set_led_0(void)//发出二进制0
{
    set_led_hight();
    ndelay(278);
    set_led_low();
    ndelay(678);
}
 
// >= 50us
static void reset_op(void)//发出Reset信号
{
    set_led_hight();//首先将总线拉高ndelay
    ndelay(300);//保持一段时间
    set_led_low();//拉低
    udelay(50);//至少保持50us,之后芯片等待接收新的数据
}
 
static int sys_led_open(struct inode* in, struct file* fi)
{
    set_out();
    return 0;
}
 
static int sys_led_ioctl(struct file* fi, unsigned int cmd, unsigned long data)
{
    union _rgb rgb;
    int i;
    unsigned int fl;
    switch (cmd)
    {
        case 0:
            if (copy_from_user((char*)&rgb, (char*)data, sizeof(rgb)))//从用户空间获得颜色值
            {
                return -1;
            }
            set_out();
            reset_op();
            local_irq_save(fl);//把当前中断状态保存到flags中,然后禁用当前处理器上的中断发送
            for (i = 0; i < 24; ++i)//发出24bit的颜色值
            {
                if (1 & rgb.color)
                    set_led_1();
                else
                    set_led_0();
                rgb.color >>= 1;
            }
            local_irq_restore(fl);//中断恢复
        break;
 
        case 1:
            reset_op();
        break;
    }
    return 0;
}
 
static ssize_t sys_led_write(struct file* fi, char* __user buf, size_t len, loff_t* ff)
{
    union _rgb rgb;
    unsigned int fl;
    int i;
    if (copy_from_user((char*)&rgb, (char*)buf, sizeof(rgb)))
    {
        return -1;
    }
    set_out();
    reset_op();
    local_irq_save(fl);
    for (i = 0; i < 24; ++i)
    {
        if (1 & rgb.color)
            set_led_1();
        else
            set_led_0();
        rgb.color >>= 1;
    }
    local_irq_restore(fl);
    return sizeof(union _rgb);
}
 
static struct file_operations led_file =
{
    .owner = THIS_MODULE,
    .open = sys_led_open,
    .unlocked_ioctl = sys_led_ioctl,
    .write = sys_led_write,
};
static void __exit sys_led_exit(void);
 
static int __init sys_led_init(void)
{
    int ret;
    sys_led_class = class_create(THIS_MODULE, "sys_led_class");
    if (IS_ERR(sys_led_class))
    {
        printk("error : sys led class create failed\n");
        return -1;
    }
 
    ret = alloc_chrdev_region(&sys_led_dev, 0, 1, SYS_LED_NAME);
    if (ret < 0)
    {
        printk("error : get dev number failed\n");
        goto error_exit;
    }
 
    device_create(sys_led_class, NULL, sys_led_dev, NULL, "%s", SYS_LED_NAME);
 
    sys_led_cdev = cdev_alloc();
 
    if (!sys_led_cdev)
    {
        printk("new memory cdev failed\n");
        goto error_exit;
    }
 
    cdev_init(sys_led_cdev, &led_file);
    sys_led_cdev->owner = THIS_MODULE;
    ret = cdev_add(sys_led_cdev, sys_led_dev, 1);
    if (ret < 0)
    {
        printk("sys led cdev add failed\n");
        goto error_exit;
    }
    return 0;
error_exit:
    sys_led_exit();
    return -1;
}
 
static void __exit sys_led_exit(void)
{
    if (sys_led_cdev)
    {
        cdev_del(sys_led_cdev);
    }
 
    if (sys_led_class)
    {
        device_destroy(sys_led_class, sys_led_dev);
        class_destroy(sys_led_class);
    }
 
    unregister_chrdev_region(sys_led_dev, 1);
}
 
 
module_init(sys_led_init);
module_exit(sys_led_exit);
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("system status led control");
MODULE_AUTHOR("system led");


本文章由作者:佐须之男 整理编辑,原文地址: 单总线led驱动芯片WS2811在OpenWrt下的驱动
本站的文章和资源来自互联网或者站长的原创,按照 CC BY -NC -SA 3.0 CN协议发布和共享,转载或引用本站文章应遵循相同协议。如果有侵犯版权的资 源请尽快联系站长,我们会在24h内删除有争议的资源。欢迎大家多多交流,期待共同学习进步。

相关推荐