From: Nathael Pajani Date: Thu, 13 Aug 2015 23:03:15 +0000 (+0200) Subject: Add WS2812 chainable leds support X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=6361905a50753d421680a9285e4a3a8a546f5c9f;p=soft%2Flpc122x%2Fcore Add WS2812 chainable leds support --- diff --git a/README b/README index b2a1628..d940868 100644 --- a/README +++ b/README @@ -92,6 +92,7 @@ SUPPORTED FEATURES and INTERFACES - CC1101 Sub 1GHz RF Transceiver - Status led - Epaper display + - WS2812 chainable leds - Other - 8x8 font for use with Epaper display diff --git a/extdrv/ws2812.c b/extdrv/ws2812.c new file mode 100644 index 0000000..c379c16 --- /dev/null +++ b/extdrv/ws2812.c @@ -0,0 +1,180 @@ +/**************************************************************************** + * extdrv/ws2812.c + * + * + * Copyright 2013 Nathael Pajani + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + +/* + * Support for the WS2812 Chainable RGB Leds + * + * WS2812 protocol can be found here : https://www.adafruit.com/datasheets/WS2812.pdf + * + */ + +#include +#include "core/lpc_regs_12xx.h" +#include "core/lpc_core_cm0.h" +#include "core/system.h" +#include "core/pio.h" +#include "drivers/gpio.h" +#include "lib/string.h" +#include "extdrv/ws2812.h" + + +static struct pio ws2812_gpio = LPC_GPIO_0_0; + +/* Configure Selected GPIO as output. */ +void ws2812_config(const struct pio* gpio) +{ + config_pio(gpio, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL)); + pio_copy(&ws2812_gpio, gpio); + gpio_dir_out(ws2812_gpio); +} + +static uint8_t led_data[NB_LEDS * 3]; +static uint16_t max_led = 0; +static uint32_t nb_bytes = 0; + + +static void ws2812_bit_sender(void) +{ + struct lpc_gpio* gpio_port = LPC_GPIO_REGS(ws2812_gpio.port); + uint32_t gpio_bit = (1 << ws2812_gpio.pin); + uint32_t byte_idx = 0; + uint8_t bit_idx = 7; + uint8_t bit = 0; + + lpc_disable_irq(); + + /* Send data */ + while (byte_idx < nb_bytes) { + bit = (led_data[byte_idx] & (0x01 << bit_idx)); + + if (bit == 0) { + /* "high" time : 350ns */ + gpio_port->set = gpio_bit; + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + /* "low" time : 800ns */ + gpio_port->clear = gpio_bit; + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + } else { + /* "high" time : 700ns */ + gpio_port->set = gpio_bit; + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + /* "high" time : 600ns */ + gpio_port->clear = gpio_bit; + nop(); + nop(); + } + + /* Move to the next bit */ + if (bit_idx == 0) { + bit_idx = 7; + byte_idx++; + } else { + bit_idx--; + } + } + + lpc_enable_irq(); +} + +/* Send led data from internal buffer (set leds to the selected color). + * If nb_leds is 0 then all led data set using ws2812_set_pixel() since the last call + * to ws2812_clear_buffer(), ws2812_clear() or ws2812_stop() will be sent. + * Call to this function will disable interrupts due to timming restrictions during + * the call to ws2812_bit_sender(). + */ +int ws2812_send_frame(uint16_t nb_leds) +{ + if (nb_leds > NB_LEDS) { + return -1; + } + if (nb_leds == 0) { + nb_leds = max_led; + } + nb_bytes = (nb_leds + 1) * 3; + ws2812_bit_sender(); + /* Reset delay */ + usleep(60); + return 0; +} + +/* Set a pixel (led) color in the data buffer */ +int ws2812_set_pixel(uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue) +{ + if (pixel_num >= NB_LEDS) { + return -1; + } + led_data[ ((pixel_num * 3) + 0) ] = green; + led_data[ ((pixel_num * 3) + 1) ] = red; + led_data[ ((pixel_num * 3) + 2) ] = blue; + if (max_led < pixel_num) { + max_led = pixel_num; + } + return 0; +} + +/* Clear the internal data buffer. */ +void ws2812_clear_buffer(void) +{ + memset(led_data, 0, (NB_LEDS * 3)); + max_led = 0; +} + +/* Clear the internal data buffer and send it to the Leds, turning them all off */ +void ws2812_clear(void) +{ + /* Start at first led and send all leds off */ + ws2812_clear_buffer(); + ws2812_send_frame(NB_LEDS); + max_led = 0; +} + +void ws2812_stop(void) __attribute__ ((alias ("ws2812_clear"))); + diff --git a/include/extdrv/ws2812.h b/include/extdrv/ws2812.h new file mode 100644 index 0000000..1026e99 --- /dev/null +++ b/include/extdrv/ws2812.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * extdrv/ws2812.h + * + * + * Copyright 2013 Nathael Pajani + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + +/* + * Support for the WS2812 Chainable RGB Leds + * + * WS2812 protocol can be found here : https://www.adafruit.com/datasheets/WS2812.pdf + * + */ + +/* + * Preliminary notice : + * This driver will only function with a clock frequency of 48MHz (or close to) due to + * the use of nop() to get the right timmings. + * + * Internal data buffer : + * This driver uses an internal buffer for NB_LEDS leds or "pixels". The buffer + * size is (3 * NB_LEDS) bytes. + * The buffer is set pixel per pixel using the ws2812_set_pixel() function. + * The buffer is then sent to the led strip using ws2812_send_frame(). + * + * Driving several led strips : + * It is possible to drive several led strips using this driver by calling the config + * function ws2812_config() between each ws2812_send_frame() call, and setting the led + * data again using the ws2812_set_pixel() function for each new led. + * This solution is not the most adapted for several led strips, and the driver should be + * modified to use an external buffer which address is either passed to each function or + * set using a modified version of the ws2812_config() function. + * In this case, the timmings should be checked and updated as access to the data may + * require more instructions. + * + * Note : ws2812_send_frame() will call lpc_disable_irq() to disable all interrupts + * when entering the timming critical section. + * Use of timers and interrupts have been tried but timmings are too short even + * whith the micro-controller running at 48MHz and direct access to the timer and + * GPIO registers. + * Instead the function uses a few nop() to get the right timmings. + * + */ + +#include +#include "core/pio.h" + + +/* Size of the internal buffer. + * Change the value to the number of leds of your led strip. + */ +#define NB_LEDS 60 + + +/* Configure the pin for the led data signal. */ +void ws2812_config(const struct pio* gpio); + +/* Send led data from internal buffer using the configured GPIO pin. (Set leds to the + * selected color). + * If no pin have been configured, GPIO_0_0 will be used. + * If nb_leds is 0 then all led data set using ws2812_set_pixel() since the last call + * to ws2812_clear_buffer(), ws2812_clear() or ws2812_stop() will be sent. + * Call to this function will disable interrupts due to timming restrictions. + * Return -1 on error (nb_leds above NB_LEDS), or 0 on success. + */ +int ws2812_send_frame(uint16_t nb_leds); + +/* Set a pixel (led) color in the data buffer (frame) + * The pixel number 'pixel_num' is the led offset in the led strip. + * 'red', 'green' and 'blue' are the color values of the pixel. A value of 0 is off, + * while a value of 0xFF (255) is full brightness. + * Return -1 on error (pixel_num above NB_LEDS), or 0 on success. + */ +int ws2812_set_pixel(uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue); + +/* Clear the internal data buffer. */ +void ws2812_clear_buffer(void); + +/* Clear the internal data buffer and send it to the Leds, turning them all off */ +void ws2812_clear(void); +/* Alias for ws2812_clear */ +void ws2812_stop(void); +