7720f6ee8cf6c302fdeabb32111582951812250d
[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 max_led = 0;
49 static uint32_t nb_bytes = 0;
52 static void ws2812_bit_sender(void)
53 {
54         struct lpc_gpio* gpio_port = LPC_GPIO_REGS(ws2812_gpio.port);
55         uint32_t gpio_bit = (1 << ws2812_gpio.pin);
56         uint32_t byte_idx = 0;
57         uint8_t bit_idx = 7;
58         uint8_t bit = 0;
60         lpc_disable_irq();
62         /* Send data */
63         while (byte_idx < nb_bytes) {
64                 bit = (led_data[byte_idx] & (0x01 << bit_idx));
66                 if (bit == 0) {
67                         /* "high" time : 350ns */
68                         gpio_port->set = gpio_bit;
69                         nop();
70                         nop();
71                         nop();
72                         nop();
73                         nop();
74                         nop();
75                         nop();
76                         nop();
77                         /* "low" time : 800ns */
78                         gpio_port->clear = gpio_bit;
79                         nop();
80                         nop();
81                         nop();
82                         nop();
83                         nop();
84                         nop();
85                         nop();
86                         nop();
87                 } else {
88                         /* "high" time : 700ns */
89                         gpio_port->set = gpio_bit;
90                         nop();
91                         nop();
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                         /* "high" time : 600ns */
107                         gpio_port->clear = gpio_bit;
108                         nop();
109                         nop();
110                 }
112                 /* Move to the next bit */
113                 if (bit_idx == 0) {
114                         bit_idx = 7;
115                         byte_idx++;
116                 } else {
117                         bit_idx--;
118                 }
119         }
121         lpc_enable_irq();
124 /* Send led data from internal buffer (set leds to the selected color).
125  * If nb_leds is 0 then all led data set using ws2812_set_pixel() since the last call
126  *   to ws2812_clear_buffer(), ws2812_clear() or ws2812_stop() will be sent.
127  * Call to this function will disable interrupts due to timming restrictions during
128  *   the call to ws2812_bit_sender().
129  */
130 int ws2812_send_frame(uint16_t nb_leds)
132         if (nb_leds > NB_LEDS) {
133                 return -1;
134         }
135         if (nb_leds == 0) {
136                 nb_leds = max_led;
137         }
138         nb_bytes = (nb_leds + 1) * 3;
139         ws2812_bit_sender();
140         /* Reset delay */
141         usleep(60);
142         return 0;
145 /* Set a pixel (led) color in the data buffer */
146 int ws2812_set_pixel(uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue)
148         if (pixel_num >= NB_LEDS) {
149                 return -1;
150         }
151         led_data[ ((pixel_num * 3) + 0) ] = green;
152         led_data[ ((pixel_num * 3) + 1) ] = red;
153         led_data[ ((pixel_num * 3) + 2) ] = blue;
154         if (max_led < pixel_num) {
155                 max_led = pixel_num;
156         }
157         return 0;
159 /* Get a pixel (led) color from the data buffer */
160 int ws2812_get_pixel(uint16_t pixel_num, uint8_t* red, uint8_t* green, uint8_t* blue)
162         if (pixel_num >= NB_LEDS) {
163                 return -1;
164         }
165         *green = led_data[ ((pixel_num * 3) + 0) ];
166         *red = led_data[ ((pixel_num * 3) + 1) ];
167         *blue = led_data[ ((pixel_num * 3) + 2) ];
168         return 0;
171 /* Clear the internal data buffer. */
172 void ws2812_clear_buffer(void)
174         memset(led_data, 0, (NB_LEDS * 3));
175         max_led = 0;
178 /* Clear the internal data buffer and send it to the Leds, turning them all off */
179 void ws2812_clear(void)
181         /* Start at first led and send all leds off */
182         ws2812_clear_buffer();
183         ws2812_send_frame(NB_LEDS);
184         max_led = 0;
187 void ws2812_stop(void) __attribute__ ((alias ("ws2812_clear")));