ws2812 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+