NXP

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

2019-07-12 13:36发布

硬件平台:mt7688 软件平台:原生sdk MediaTek_ApSoC_SDK_4300_20140916 linux-3.10.14内核 ws2811是单总线接口的led驱动芯片,通过单个GPIO模拟高低时序发出颜 {MOD}值,芯片收到后控制输出对应PWM改变三路RGB灯的颜 {MOD}。 #include #include #include #include #include #include #include #include #include #include #include #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)))//从用户空间获得颜 {MOD}值 { return -1; } set_out(); reset_op(); local_irq_save(fl);//把当前中断状态保存到flags中,然后禁用当前处理器上的中断发送 for (i = 0; i < 24; ++i)//发出24bit的颜 {MOD}值 { 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 "); return -1; } ret = alloc_chrdev_region(&sys_led_dev, 0, 1, SYS_LED_NAME); if (ret < 0) { printk("error : get dev number failed "); 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 "); 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 "); 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");