Adding ws2812fx lib and its single-led version ws2812sfx
[lpc1224] / extdrv / ws2812.c
1 /****************************************************************************
2  *   extdrv/ws2812.c
3  *
4  *
5  * Copyright 2013 Nathael Pajani <nathael.pajani@ed3l.fr>
6  *
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  *************************************************************************** */
23 /*
24  * Support for the WS2812 and WS2812B Chainable RGB Leds
25  *
26  * WS2812 protocol can be found here : https://www.adafruit.com/datasheets/WS2812.pdf
27  *
28  */
30 #include "core/system.h"
31 #include "core/pio.h"
32 #include "drivers/gpio.h"
33 #include "lib/string.h"
34 #include "extdrv/ws2812.h"
37 static struct pio ws2812_gpio = LPC_GPIO_0_0;
39 /* Configure Selected GPIO as output. */
40 void ws2812_config(const struct pio* gpio)
41 {
42         config_pio(gpio, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL));
43         pio_copy(&ws2812_gpio, gpio);
44         gpio_dir_out(ws2812_gpio);
45 }
47 static uint8_t led_data[NB_LEDS * 3];
48 static uint16_t brightness = 256;
49 static uint16_t max_led = 0;
50 static uint32_t nb_bytes = 0;
53 static void ws2812_bit_sender(void)
54 {
55         struct lpc_gpio* gpio_port = LPC_GPIO_REGS(ws2812_gpio.port);
56         uint32_t gpio_bit = (1 << ws2812_gpio.pin);
57         uint32_t byte_idx = 0;
58         uint8_t bit_idx = 7;
59         uint8_t bit = 0;
60         uint8_t data = (led_data[0] * brightness) >> 8;
62         lpc_disable_irq();
64         /* Send data */
65         while (byte_idx < nb_bytes) {
66                 bit = (data & (0x01 << bit_idx));
68                 if (bit == 0) {
69                         /* "high" time : 350ns */
70                         gpio_port->set = gpio_bit;
71                         nop();
72                         nop();
73                         nop();
74                         nop();
75                         nop();
76                         nop();
77                         nop();
78                         nop();
79                         /* "low" time : 800ns */
80                         gpio_port->clear = gpio_bit;
81                         nop();
82                         nop();
83                         nop();
84                         nop();
85                         nop();
86                         nop();
87                         nop();
88                         nop();
89                 } else {
90                         /* "high" time : 700ns */
91                         gpio_port->set = gpio_bit;
92                         nop();
93                         nop();
94                         nop();
95                         nop();
96                         nop();
97                         nop();
98                         nop();
99                         nop();
100                         nop();
101                         nop();
102                         nop();
103                         nop();
104                         nop();
105                         nop();
106                         nop();
107                         nop();
108                         /* "high" time : 600ns */
109                         gpio_port->clear = gpio_bit;
110                         nop();
111                         nop();
112                 }
114                 /* Move to the next bit */
115                 if (bit_idx == 0) {
116                         bit_idx = 7;
117                         byte_idx++;
118                         data = (led_data[byte_idx] * brightness) >> 8;
119                 } else {
120                         bit_idx--;
121                 }
122         }
124         lpc_enable_irq();
127 /* Send led data from internal buffer (set leds to the selected color).
128  * If nb_leds is 0 then all led data set using ws2812_set_pixel() since the last call
129  *   to ws2812_clear_buffer(), ws2812_clear() or ws2812_stop() will be sent.
130  * Call to this function will disable interrupts due to timming restrictions during
131  *   the call to ws2812_bit_sender().
132  */
133 int ws2812_send_frame(uint16_t nb_leds)
135         if (nb_leds > NB_LEDS) {
136                 return -1;
137         }
138         if (nb_leds == 0) {
139                 nb_leds = max_led;
140         }
141         nb_bytes = (nb_leds + 1) * 3;
142         ws2812_bit_sender();
143         /* Reset delay */
144         usleep(60);
145         return 0;
148 void ws2812_set_brightness(uint8_t b)
150         brightness = b + 1;
153 /* Set a pixel (led) color in the data buffer */
154 int ws2812_set_pixel(uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue)
156         if (pixel_num >= NB_LEDS) {
157                 return -1;
158         }
159         led_data[ ((pixel_num * 3) + 0) ] = green;
160         led_data[ ((pixel_num * 3) + 1) ] = red;
161         led_data[ ((pixel_num * 3) + 2) ] = blue;
162         if (max_led < pixel_num) {
163                 max_led = pixel_num;
164         }
165         return 0;
167 /* Get a pixel (led) color from the data buffer */
168 int ws2812_get_pixel(uint16_t pixel_num, uint8_t* red, uint8_t* green, uint8_t* blue)
170         if (pixel_num >= NB_LEDS) {
171                 return -1;
172         }
173         *green = led_data[ ((pixel_num * 3) + 0) ];
174         *red = led_data[ ((pixel_num * 3) + 1) ];
175         *blue = led_data[ ((pixel_num * 3) + 2) ];
176         return 0;
179 /* Clear the internal data buffer. */
180 void ws2812_clear_buffer(void)
182         memset(led_data, 0, (NB_LEDS * 3));
183         max_led = 0;
186 /* Clear the internal data buffer and send it to the Leds, turning them all off */
187 void ws2812_clear(void)
189         /* Start at first led and send all leds off */
190         ws2812_clear_buffer();
191         ws2812_send_frame(NB_LEDS);
192         max_led = 0;
195 void ws2812_stop(void) __attribute__ ((alias ("ws2812_clear")));