ws2812 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/ws2812/pwm.h ADDED
@@ -0,0 +1,123 @@
1
+ /*
2
+ * pwm.h
3
+ *
4
+ * Copyright (c) 2014 Jeremy Garff <jer @ jers.net>
5
+ *
6
+ * All rights reserved.
7
+ *
8
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
9
+ * provided that the following conditions are met:
10
+ *
11
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
12
+ * conditions and the following disclaimer.
13
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
14
+ * of conditions and the following disclaimer in the documentation and/or other materials
15
+ * provided with the distribution.
16
+ * 3. Neither the name of the owner nor the names of its contributors may be used to endorse
17
+ * or promote products derived from this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
20
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
22
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ *
28
+ */
29
+
30
+ #ifndef __PWM_H__
31
+ #define __PWM_H__
32
+
33
+ #include <stdint.h>
34
+
35
+ /*
36
+ *
37
+ * Pin mappint of alternate pin configuration for PWM
38
+ *
39
+ * GPIO ALT PWM0 ALT PWM1
40
+ *
41
+ * 12 0
42
+ * 13 0
43
+ * 18 5
44
+ * 19 5
45
+ * 40 0
46
+ * 41 0
47
+ * 45 0
48
+ * 52 1
49
+ * 53 1
50
+ *
51
+ */
52
+
53
+
54
+ #define RPI_PWM_CHANNELS 2
55
+
56
+
57
+ typedef struct
58
+ {
59
+ uint32_t ctl;
60
+ #define RPI_PWM_CTL_MSEN2 (1 << 15)
61
+ #define RPI_PWM_CTL_USEF2 (1 << 13)
62
+ #define RPI_PWM_CTL_POLA2 (1 << 12)
63
+ #define RPI_PWM_CTL_SBIT2 (1 << 11)
64
+ #define RPI_PWM_CTL_RPTL2 (1 << 10)
65
+ #define RPI_PWM_CTL_MODE2 (1 << 9)
66
+ #define RPI_PWM_CTL_PWEN2 (1 << 8)
67
+ #define RPI_PWM_CTL_MSEN1 (1 << 7)
68
+ #define RPI_PWM_CTL_CLRF1 (1 << 6)
69
+ #define RPI_PWM_CTL_USEF1 (1 << 5)
70
+ #define RPI_PWM_CTL_POLA1 (1 << 4)
71
+ #define RPI_PWM_CTL_SBIT1 (1 << 3)
72
+ #define RPI_PWM_CTL_RPTL1 (1 << 2)
73
+ #define RPI_PWM_CTL_MODE1 (1 << 1)
74
+ #define RPI_PWM_CTL_PWEN1 (1 << 0)
75
+ uint32_t sta;
76
+ #define RPI_PWM_STA_STA4 (1 << 12)
77
+ #define RPI_PWM_STA_STA3 (1 << 11)
78
+ #define RPI_PWM_STA_STA2 (1 << 10)
79
+ #define RPI_PWM_STA_STA1 (1 << 9)
80
+ #define RPI_PWM_STA_BERR (1 << 8)
81
+ #define RPI_PWM_STA_GAP04 (1 << 7)
82
+ #define RPI_PWM_STA_GAP03 (1 << 6)
83
+ #define RPI_PWM_STA_GAP02 (1 << 5)
84
+ #define RPI_PWM_STA_GAP01 (1 << 4)
85
+ #define RPI_PWM_STA_RERR1 (1 << 3)
86
+ #define RPI_PWM_STA_WERR1 (1 << 2)
87
+ #define RPI_PWM_STA_EMPT1 (1 << 1)
88
+ #define RPI_PWM_STA_FULL1 (1 << 0)
89
+ uint32_t dmac;
90
+ #define RPI_PWM_DMAC_ENAB (1 << 31)
91
+ #define RPI_PWM_DMAC_PANIC(val) ((val & 0xff) << 8)
92
+ #define RPI_PWM_DMAC_DREQ(val) ((val & 0xff) << 0)
93
+ uint32_t resvd_0x0c;
94
+ uint32_t rng1;
95
+ uint32_t dat1;
96
+ uint32_t fif1;
97
+ uint32_t resvd_0x1c;
98
+ uint32_t rng2;
99
+ uint32_t dat2;
100
+ } __attribute__((packed)) pwm_t;
101
+
102
+
103
+ #define PWM_OFFSET (0x0020c000)
104
+ #define PWM_PERIPH_PHYS (0x7e20c000)
105
+
106
+
107
+ typedef struct
108
+ {
109
+ int pinnum;
110
+ int altnum;
111
+ } pwm_pin_table_t;
112
+
113
+ typedef struct
114
+ {
115
+ const int count;
116
+ const pwm_pin_table_t *pins;
117
+ } pwm_pin_tables_t;
118
+
119
+
120
+ int pwm_pin_alt(int chan, int pinnum);
121
+
122
+
123
+ #endif /* __PWM_H__ */
@@ -0,0 +1,685 @@
1
+ /*
2
+ * ws2811.c
3
+ *
4
+ * Copyright (c) 2014 Jeremy Garff <jer @ jers.net>
5
+ *
6
+ * All rights reserved.
7
+ *
8
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
9
+ * provided that the following conditions are met:
10
+ *
11
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
12
+ * conditions and the following disclaimer.
13
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
14
+ * of conditions and the following disclaimer in the documentation and/or other materials
15
+ * provided with the distribution.
16
+ * 3. Neither the name of the owner nor the names of its contributors may be used to endorse
17
+ * or promote products derived from this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
20
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
22
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ *
28
+ */
29
+
30
+
31
+ #include <stdint.h>
32
+ #include <stdio.h>
33
+ #include <stdlib.h>
34
+ #include <string.h>
35
+ #include <unistd.h>
36
+ #include <sys/types.h>
37
+ #include <sys/stat.h>
38
+ #include <fcntl.h>
39
+ #include <sys/mman.h>
40
+ #include <signal.h>
41
+
42
+ #include "board_info.h"
43
+ #include "mailbox.h"
44
+ #include "clk.h"
45
+ #include "gpio.h"
46
+ #include "dma.h"
47
+ #include "pwm.h"
48
+
49
+ #include "gamma.h"
50
+
51
+ #include "ws2811.h"
52
+
53
+ #define BUS_TO_PHYS(x) ((x)&~0xC0000000)
54
+
55
+ #define OSC_FREQ 19200000 // crystal frequency
56
+
57
+ /* 3 colors, 8 bits per byte, 3 symbols per bit + 55uS low for reset signal */
58
+ #define LED_RESET_uS 55
59
+ #define LED_BIT_COUNT(leds, freq) ((leds * 3 * 8 * 3) + ((LED_RESET_uS * \
60
+ (freq * 3)) / 1000000))
61
+
62
+ // Pad out to the nearest uint32 + 32-bits for idle low/high times the number of channels
63
+ #define PWM_BYTE_COUNT(leds, freq) (((((LED_BIT_COUNT(leds, freq) >> 3) & ~0x7) + 4) + 4) * \
64
+ RPI_PWM_CHANNELS)
65
+
66
+ #define SYMBOL_HIGH 0x6 // 1 1 0
67
+ #define SYMBOL_LOW 0x4 // 1 0 0
68
+
69
+ #define ARRAY_SIZE(stuff) (sizeof(stuff) / sizeof(stuff[0]))
70
+
71
+
72
+ typedef struct ws2811_device
73
+ {
74
+ volatile uint8_t *pwm_raw;
75
+ volatile dma_t *dma;
76
+ volatile pwm_t *pwm;
77
+ volatile dma_cb_t *dma_cb;
78
+ uint32_t dma_cb_addr;
79
+ volatile gpio_t *gpio;
80
+ volatile cm_pwm_t *cm_pwm;
81
+ int max_count;
82
+ } ws2811_device_t;
83
+
84
+ // We use the mailbox interface to request memory from the VideoCore.
85
+ // This lets us request one physically contiguous chunk, find its
86
+ // physical address, and map it 'uncached' so that writes from this
87
+ // code are immediately visible to the DMA controller. This struct
88
+ // holds data relevant to the mailbox interface.
89
+ // TODO: Should we embed this in ws2811_device_t really?
90
+ static struct {
91
+ int handle; /* From mbox_open() */
92
+ unsigned mem_ref; /* From mem_alloc() */
93
+ unsigned bus_addr; /* From mem_lock() */
94
+ unsigned size; /* Size of allocation */
95
+ uint8_t *virt_addr; /* From mapmem() */
96
+ } mbox;
97
+
98
+ /**
99
+ * Iterate through the channels and find the largest led count.
100
+ *
101
+ * @param ws2811 ws2811 instance pointer.
102
+ *
103
+ * @returns Maximum number of LEDs in all channels.
104
+ */
105
+ static int max_channel_led_count(ws2811_t *ws2811)
106
+ {
107
+ int chan, max = 0;
108
+
109
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
110
+ {
111
+ if (ws2811->channel[chan].count > max)
112
+ {
113
+ max = ws2811->channel[chan].count;
114
+ }
115
+ }
116
+
117
+ return max;
118
+ }
119
+
120
+ /**
121
+ * Map all devices into userspace memory.
122
+ *
123
+ * @param ws2811 ws2811 instance pointer.
124
+ *
125
+ * @returns 0 on success, -1 otherwise.
126
+ */
127
+ static int map_registers(ws2811_t *ws2811)
128
+ {
129
+ ws2811_device_t *device = ws2811->device;
130
+ uint32_t dma_addr = dmanum_to_phys(ws2811->dmanum);
131
+ uint32_t base = board_info_peripheral_base_addr();
132
+
133
+ if (!dma_addr)
134
+ {
135
+ return -1;
136
+ }
137
+
138
+ device->dma = mapmem(dma_addr, sizeof(dma_t));
139
+ if (!device->dma)
140
+ {
141
+ return -1;
142
+ }
143
+
144
+ device->pwm = mapmem(PWM_OFFSET + base, sizeof(pwm_t));
145
+ if (!device->pwm)
146
+ {
147
+ return -1;
148
+ }
149
+
150
+ device->gpio = mapmem(GPIO_OFFSET + base, sizeof(gpio_t));
151
+ if (!device->gpio)
152
+ {
153
+ return -1;
154
+ }
155
+
156
+ device->cm_pwm = mapmem(CM_PWM_OFFSET + base, sizeof(cm_pwm_t));
157
+ if (!device->cm_pwm)
158
+ {
159
+ return -1;
160
+ }
161
+
162
+ return 0;
163
+ }
164
+
165
+ /**
166
+ * Unmap all devices from virtual memory.
167
+ *
168
+ * @param ws2811 ws2811 instance pointer.
169
+ *
170
+ * @returns None
171
+ */
172
+ static void unmap_registers(ws2811_t *ws2811)
173
+ {
174
+ ws2811_device_t *device = ws2811->device;
175
+
176
+ if (device->dma)
177
+ {
178
+ unmapmem((void *)device->dma, sizeof(dma_t));
179
+ }
180
+
181
+ if (device->pwm)
182
+ {
183
+ unmapmem((void *)device->pwm, sizeof(pwm_t));
184
+ }
185
+
186
+ if (device->cm_pwm)
187
+ {
188
+ unmapmem((void *)device->cm_pwm, sizeof(cm_pwm_t));
189
+ }
190
+
191
+ if (device->gpio)
192
+ {
193
+ unmapmem((void *)device->gpio, sizeof(gpio_t));
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Given a userspace address pointer, return the matching bus address used by DMA.
199
+ * Note: The bus address is not the same as the CPU physical address.
200
+ *
201
+ * @param addr Userspace virtual address pointer.
202
+ *
203
+ * @returns Bus address for use by DMA.
204
+ */
205
+ static uint32_t addr_to_bus(const volatile void *virt)
206
+ {
207
+ uint32_t offset = (uint8_t *)virt - mbox.virt_addr;
208
+
209
+ return mbox.bus_addr + offset;
210
+ }
211
+
212
+ /**
213
+ * Stop the PWM controller.
214
+ *
215
+ * @param ws2811 ws2811 instance pointer.
216
+ *
217
+ * @returns None
218
+ */
219
+ static void stop_pwm(ws2811_t *ws2811)
220
+ {
221
+ ws2811_device_t *device = ws2811->device;
222
+ volatile pwm_t *pwm = device->pwm;
223
+ volatile cm_pwm_t *cm_pwm = device->cm_pwm;
224
+
225
+ // Turn off the PWM in case already running
226
+ pwm->ctl = 0;
227
+ usleep(10);
228
+
229
+ // Kill the clock if it was already running
230
+ cm_pwm->ctl = CM_PWM_CTL_PASSWD | CM_PWM_CTL_KILL;
231
+ usleep(10);
232
+ while (cm_pwm->ctl & CM_PWM_CTL_BUSY)
233
+ ;
234
+ }
235
+
236
+ /**
237
+ * Setup the PWM controller in serial mode on both channels using DMA to feed the PWM FIFO.
238
+ *
239
+ * @param ws2811 ws2811 instance pointer.
240
+ *
241
+ * @returns None
242
+ */
243
+ static int setup_pwm(ws2811_t *ws2811)
244
+ {
245
+ ws2811_device_t *device = ws2811->device;
246
+ volatile dma_t *dma = device->dma;
247
+ volatile dma_cb_t *dma_cb = device->dma_cb;
248
+ volatile pwm_t *pwm = device->pwm;
249
+ volatile cm_pwm_t *cm_pwm = device->cm_pwm;
250
+ int maxcount = max_channel_led_count(ws2811);
251
+ uint32_t freq = ws2811->freq;
252
+ int32_t byte_count;
253
+
254
+ stop_pwm(ws2811);
255
+
256
+ // Setup the PWM Clock - Use OSC @ 19.2Mhz w/ 3 clocks/tick
257
+ cm_pwm->div = CM_PWM_DIV_PASSWD | CM_PWM_DIV_DIVI(OSC_FREQ / (3 * freq));
258
+ cm_pwm->ctl = CM_PWM_CTL_PASSWD | CM_PWM_CTL_SRC_OSC;
259
+ cm_pwm->ctl = CM_PWM_CTL_PASSWD | CM_PWM_CTL_SRC_OSC | CM_PWM_CTL_ENAB;
260
+ usleep(10);
261
+ while (!(cm_pwm->ctl & CM_PWM_CTL_BUSY))
262
+ ;
263
+
264
+ // Setup the PWM, use delays as the block is rumored to lock up without them. Make
265
+ // sure to use a high enough priority to avoid any FIFO underruns, especially if
266
+ // the CPU is busy doing lots of memory accesses, or another DMA controller is
267
+ // busy. The FIFO will clock out data at a much slower rate (2.6Mhz max), so
268
+ // the odds of a DMA priority boost are extremely low.
269
+
270
+ pwm->rng1 = 32; // 32-bits per word to serialize
271
+ usleep(10);
272
+ pwm->ctl = RPI_PWM_CTL_CLRF1;
273
+ usleep(10);
274
+ pwm->dmac = RPI_PWM_DMAC_ENAB | RPI_PWM_DMAC_PANIC(7) | RPI_PWM_DMAC_DREQ(3);
275
+ usleep(10);
276
+ pwm->ctl = RPI_PWM_CTL_USEF1 | RPI_PWM_CTL_MODE1 |
277
+ RPI_PWM_CTL_USEF2 | RPI_PWM_CTL_MODE2;
278
+ usleep(10);
279
+ pwm->ctl |= RPI_PWM_CTL_PWEN1 | RPI_PWM_CTL_PWEN2;
280
+
281
+ // Initialize the DMA control block
282
+ byte_count = PWM_BYTE_COUNT(maxcount, freq);
283
+ dma_cb->ti = RPI_DMA_TI_NO_WIDE_BURSTS | // 32-bit transfers
284
+ RPI_DMA_TI_WAIT_RESP | // wait for write complete
285
+ RPI_DMA_TI_DEST_DREQ | // user peripheral flow control
286
+ RPI_DMA_TI_PERMAP(5) | // PWM peripheral
287
+ RPI_DMA_TI_SRC_INC; // Increment src addr
288
+
289
+ dma_cb->source_ad = addr_to_bus(device->pwm_raw);
290
+
291
+ dma_cb->dest_ad = (uint32_t)&((pwm_t *)PWM_PERIPH_PHYS)->fif1;
292
+ dma_cb->txfr_len = byte_count;
293
+ dma_cb->stride = 0;
294
+ dma_cb->nextconbk = 0;
295
+
296
+ dma->cs = 0;
297
+ dma->txfr_len = 0;
298
+
299
+ return 0;
300
+ }
301
+
302
+ /**
303
+ * Start the DMA feeding the PWM FIFO. This will stream the entire DMA buffer out of both
304
+ * PWM channels.
305
+ *
306
+ * @param ws2811 ws2811 instance pointer.
307
+ *
308
+ * @returns None
309
+ */
310
+ static void dma_start(ws2811_t *ws2811)
311
+ {
312
+ ws2811_device_t *device = ws2811->device;
313
+ volatile dma_t *dma = device->dma;
314
+ uint32_t dma_cb_addr = device->dma_cb_addr;
315
+
316
+ dma->cs = RPI_DMA_CS_RESET;
317
+ usleep(10);
318
+ dma->cs = RPI_DMA_CS_INT | RPI_DMA_CS_END;
319
+ dma->conblk_ad = dma_cb_addr;
320
+ dma->debug = 7; // clear debug error flags
321
+ dma->cs = RPI_DMA_CS_WAIT_OUTSTANDING_WRITES |
322
+ RPI_DMA_CS_PANIC_PRIORITY(15) |
323
+ RPI_DMA_CS_PRIORITY(15) |
324
+ RPI_DMA_CS_ACTIVE;
325
+ }
326
+
327
+ /**
328
+ * Initialize the application selected GPIO pins for PWM operation.
329
+ *
330
+ * @param ws2811 ws2811 instance pointer.
331
+ *
332
+ * @returns 0 on success, -1 on unsupported pin
333
+ */
334
+ static int gpio_init(ws2811_t *ws2811)
335
+ {
336
+ volatile gpio_t *gpio = ws2811->device->gpio;
337
+ int chan;
338
+
339
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
340
+ {
341
+ int pinnum = ws2811->channel[chan].gpionum;
342
+
343
+ if (pinnum)
344
+ {
345
+ int altnum = pwm_pin_alt(chan, pinnum);
346
+
347
+ if (altnum < 0)
348
+ {
349
+ return -1;
350
+ }
351
+
352
+ gpio_function_set(gpio, pinnum, altnum);
353
+ }
354
+ }
355
+
356
+ return 0;
357
+ }
358
+
359
+ /**
360
+ * Initialize the PWM DMA buffer with all zeros for non-inverted operation, or
361
+ * ones for inverted operation. The DMA buffer length is assumed to be a word
362
+ * multiple.
363
+ *
364
+ * @param ws2811 ws2811 instance pointer.
365
+ *
366
+ * @returns None
367
+ */
368
+ void pwm_raw_init(ws2811_t *ws2811)
369
+ {
370
+ volatile uint32_t *pwm_raw = (uint32_t *)ws2811->device->pwm_raw;
371
+ int maxcount = max_channel_led_count(ws2811);
372
+ int wordcount = (PWM_BYTE_COUNT(maxcount, ws2811->freq) / sizeof(uint32_t)) /
373
+ RPI_PWM_CHANNELS;
374
+ int chan;
375
+
376
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
377
+ {
378
+ ws2811_channel_t *channel = &ws2811->channel[chan];
379
+ int i, wordpos = chan;
380
+
381
+ for (i = 0; i < wordcount; i++)
382
+ {
383
+ if (channel->invert)
384
+ {
385
+ pwm_raw[wordpos] = ~0L;
386
+ }
387
+ else
388
+ {
389
+ pwm_raw[wordpos] = 0x0;
390
+ }
391
+
392
+ wordpos += 2;
393
+ }
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Cleanup previously allocated device memory and buffers.
399
+ *
400
+ * @param ws2811 ws2811 instance pointer.
401
+ *
402
+ * @returns None
403
+ */
404
+ void ws2811_cleanup(ws2811_t *ws2811)
405
+ {
406
+ int chan;
407
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
408
+ {
409
+ if (ws2811->channel[chan].leds)
410
+ {
411
+ free(ws2811->channel[chan].leds);
412
+ }
413
+ ws2811->channel[chan].leds = NULL;
414
+ }
415
+
416
+ if (mbox.virt_addr != NULL)
417
+ {
418
+ unmapmem(mbox.virt_addr, mbox.size);
419
+ mem_unlock(mbox.handle, mbox.mem_ref);
420
+ mem_free(mbox.handle, mbox.mem_ref);
421
+ if (mbox.handle >= 0)
422
+ mbox_close(mbox.handle);
423
+ memset(&mbox, 0, sizeof(mbox));
424
+ }
425
+
426
+ ws2811_device_t *device = ws2811->device;
427
+ if (device) {
428
+ free(device);
429
+ }
430
+ ws2811->device = NULL;
431
+ }
432
+
433
+
434
+ /*
435
+ *
436
+ * Application API Functions
437
+ *
438
+ */
439
+
440
+
441
+ /**
442
+ * Allocate and initialize memory, buffers, pages, PWM, DMA, and GPIO.
443
+ *
444
+ * @param ws2811 ws2811 instance pointer.
445
+ *
446
+ * @returns 0 on success, -1 otherwise.
447
+ */
448
+ int ws2811_init(ws2811_t *ws2811)
449
+ {
450
+ ws2811_device_t *device = NULL;
451
+ int chan;
452
+
453
+ // Zero mbox; non-zero values indicate action needed on cleanup
454
+ memset(&mbox, 0, sizeof(mbox));
455
+
456
+ ws2811->device = malloc(sizeof(*ws2811->device));
457
+ if (!ws2811->device)
458
+ {
459
+ return -1;
460
+ }
461
+ device = ws2811->device;
462
+
463
+ // Determine how much physical memory we need for DMA
464
+ mbox.size = PWM_BYTE_COUNT(max_channel_led_count(ws2811), ws2811->freq) +
465
+ + sizeof(dma_cb_t);
466
+ // Round up to page size multiple
467
+ mbox.size = (mbox.size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
468
+
469
+ // Use the mailbox interface to request memory from the VideoCore
470
+ // We specifiy (-1) for the handle rather than calling mbox_open()
471
+ // so multiple users can share the resource.
472
+ mbox.handle = -1; // mbox_open();
473
+ mbox.mem_ref = mem_alloc(mbox.handle, mbox.size, PAGE_SIZE,
474
+ board_info_sdram_address() == 0x40000000 ? 0xC : 0x4);
475
+ if (mbox.mem_ref < 0)
476
+ {
477
+ return -1;
478
+ }
479
+ mbox.bus_addr = mem_lock(mbox.handle, mbox.mem_ref);
480
+ if (mbox.bus_addr == ~0)
481
+ {
482
+ mem_free(mbox.handle, mbox.size);
483
+ return -1;
484
+ }
485
+ mbox.virt_addr = mapmem(BUS_TO_PHYS(mbox.bus_addr), mbox.size);
486
+
487
+ // Initialize all pointers to NULL. Any non-NULL pointers will be freed on cleanup.
488
+ device->pwm_raw = NULL;
489
+ device->dma_cb = NULL;
490
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
491
+ {
492
+ ws2811->channel[chan].leds = NULL;
493
+ }
494
+
495
+ // Allocate the LED buffers
496
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
497
+ {
498
+ ws2811_channel_t *channel = &ws2811->channel[chan];
499
+
500
+ channel->leds = malloc(sizeof(ws2811_led_t) * channel->count);
501
+ if (!channel->leds)
502
+ {
503
+ goto err;
504
+ }
505
+
506
+ memset(channel->leds, 0, sizeof(ws2811_led_t) * channel->count);
507
+ }
508
+
509
+ device->dma_cb = (dma_cb_t *)mbox.virt_addr;
510
+ device->pwm_raw = (uint8_t *)mbox.virt_addr + sizeof(dma_cb_t);
511
+
512
+ pwm_raw_init(ws2811);
513
+
514
+ memset((dma_cb_t *)device->dma_cb, 0, sizeof(dma_cb_t));
515
+
516
+ // Cache the DMA control block bus address
517
+ device->dma_cb_addr = addr_to_bus(device->dma_cb);
518
+
519
+ // Map the physical registers into userspace
520
+ if (map_registers(ws2811))
521
+ {
522
+ goto err;
523
+ }
524
+
525
+ // Initialize the GPIO pins
526
+ if (gpio_init(ws2811))
527
+ {
528
+ unmap_registers(ws2811);
529
+ goto err;
530
+ }
531
+
532
+ // Setup the PWM, clocks, and DMA
533
+ if (setup_pwm(ws2811))
534
+ {
535
+ unmap_registers(ws2811);
536
+ goto err;
537
+ }
538
+
539
+ return 0;
540
+
541
+ err:
542
+ ws2811_cleanup(ws2811);
543
+
544
+ return -1;
545
+ }
546
+
547
+ /**
548
+ * Shut down DMA, PWM, and cleanup memory.
549
+ *
550
+ * @param ws2811 ws2811 instance pointer.
551
+ *
552
+ * @returns None
553
+ */
554
+ void ws2811_fini(ws2811_t *ws2811)
555
+ {
556
+ ws2811_wait(ws2811);
557
+ stop_pwm(ws2811);
558
+
559
+ unmap_registers(ws2811);
560
+
561
+ ws2811_cleanup(ws2811);
562
+ }
563
+
564
+ /**
565
+ * Contains any errors from executing ws2811_wait
566
+ */
567
+ uint32_t ws2811_dma_error = 0;
568
+
569
+ /**
570
+ * Wait for any executing DMA operation to complete before returning.
571
+ *
572
+ * @param ws2811 ws2811 instance pointer.
573
+ *
574
+ * @returns 0 on success, -1 on DMA competion error
575
+ */
576
+ int ws2811_wait(ws2811_t *ws2811)
577
+ {
578
+ volatile dma_t *dma = ws2811->device->dma;
579
+
580
+ while ((dma->cs & RPI_DMA_CS_ACTIVE) &&
581
+ !(dma->cs & RPI_DMA_CS_ERROR))
582
+ {
583
+ usleep(10);
584
+ }
585
+
586
+ if (dma->cs & RPI_DMA_CS_ERROR)
587
+ {
588
+ // fprintf(stderr, "DMA Error: %08x\n", dma->debug);
589
+ ws2811_dma_error = dma->debug;
590
+ return -1;
591
+ }
592
+
593
+ return 0;
594
+ }
595
+
596
+ /**
597
+ * Setting to non-zero bypasses brightness
598
+ */
599
+ uint8_t ws2811_direct_colors = 0;
600
+
601
+ /**
602
+ * Render the PWM DMA buffer from the user supplied LED arrays and start the DMA
603
+ * controller. This will update all LEDs on both PWM channels.
604
+ *
605
+ * @param ws2811 ws2811 instance pointer.
606
+ *
607
+ * @returns None
608
+ */
609
+ int ws2811_render(ws2811_t *ws2811)
610
+ {
611
+ volatile uint8_t *pwm_raw = ws2811->device->pwm_raw;
612
+ int bitpos = 31;
613
+ int i, j, k, l, chan;
614
+
615
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++) // Channel
616
+ {
617
+ ws2811_channel_t *channel = &ws2811->channel[chan];
618
+ int wordpos = chan;
619
+ int scale = (channel->brightness & 0xff) + 1;
620
+
621
+ for (i = 0; i < channel->count; i++) // Led
622
+ {
623
+ uint8_t color[] = {0, 0, 0};
624
+
625
+ color[0] = (channel->leds[i] >> 8) & 0xff; // green
626
+ color[1] = (channel->leds[i] >> 16) & 0xff; // red
627
+ color[2] = (channel->leds[i] >> 0) & 0xff; // blue
628
+ if (ws2811_direct_colors == 0) {
629
+ // apply the gamma table
630
+ color[0] = (ws281x_gamma[color[0]] * scale) >> 8; // green
631
+ color[1] = (ws281x_gamma[color[1]] * scale) >> 8; // red
632
+ color[2] = (ws281x_gamma[color[2]] * scale) >> 8; // blue
633
+ }
634
+
635
+ for (j = 0; j < ARRAY_SIZE(color); j++) // Color
636
+ {
637
+ for (k = 7; k >= 0; k--) // Bit
638
+ {
639
+ uint8_t symbol = SYMBOL_LOW;
640
+
641
+ if (color[j] & (1 << k))
642
+ {
643
+ symbol = SYMBOL_HIGH;
644
+ }
645
+
646
+ if (channel->invert)
647
+ {
648
+ symbol = ~symbol & 0x7;
649
+ }
650
+
651
+ for (l = 2; l >= 0; l--) // Symbol
652
+ {
653
+ uint32_t *wordptr = &((uint32_t *)pwm_raw)[wordpos];
654
+
655
+ *wordptr &= ~(1 << bitpos);
656
+ if (symbol & (1 << l))
657
+ {
658
+ *wordptr |= (1 << bitpos);
659
+ }
660
+
661
+ bitpos--;
662
+ if (bitpos < 0)
663
+ {
664
+ // Every other word is on the same channel
665
+ wordpos += 2;
666
+
667
+ bitpos = 31;
668
+ }
669
+ }
670
+ }
671
+ }
672
+ }
673
+ }
674
+
675
+ // Wait for any previous DMA operation to complete.
676
+ if (ws2811_wait(ws2811))
677
+ {
678
+ return -1;
679
+ }
680
+
681
+ dma_start(ws2811);
682
+
683
+ return 0;
684
+ }
685
+