pixel_pi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,757 @@
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 "clk.h"
43
+ #include "gpio.h"
44
+ #include "dma.h"
45
+ #include "pwm.h"
46
+
47
+ #include "ws2811.h"
48
+
49
+
50
+ #define OSC_FREQ 19200000 // crystal frequency
51
+
52
+ /* 3 colors, 8 bits per byte, 3 symbols per bit + 55uS low for reset signal */
53
+ #define LED_RESET_uS 55
54
+ #define LED_BIT_COUNT(leds, freq) ((leds * 3 * 8 * 3) + ((LED_RESET_uS * \
55
+ (freq * 3)) / 1000000))
56
+
57
+ // Pad out to the nearest uint32 + 32-bits for idle low/high times the number of channels
58
+ #define PWM_BYTE_COUNT(leds, freq) (((((LED_BIT_COUNT(leds, freq) >> 3) & ~0x7) + 4) + 4) * \
59
+ RPI_PWM_CHANNELS)
60
+
61
+ #define SYMBOL_HIGH 0x6 // 1 1 0
62
+ #define SYMBOL_LOW 0x4 // 1 0 0
63
+
64
+ #define ARRAY_SIZE(stuff) (sizeof(stuff) / sizeof(stuff[0]))
65
+
66
+
67
+ typedef struct ws2811_device
68
+ {
69
+ volatile uint8_t *pwm_raw;
70
+ volatile dma_t *dma;
71
+ volatile pwm_t *pwm;
72
+ volatile dma_cb_t *dma_cb;
73
+ uint32_t dma_cb_addr;
74
+ dma_page_t page_head;
75
+ volatile gpio_t *gpio;
76
+ volatile cm_pwm_t *cm_pwm;
77
+ int max_count;
78
+ } ws2811_device_t;
79
+
80
+
81
+ // ARM gcc built-in function, fortunately works when root w/virtual addrs
82
+ void __clear_cache(char *begin, char *end);
83
+
84
+
85
+ /**
86
+ * Iterate through the channels and find the largest led count.
87
+ *
88
+ * @param ws2811 ws2811 instance pointer.
89
+ *
90
+ * @returns Maximum number of LEDs in all channels.
91
+ */
92
+ static int max_channel_led_count(ws2811_t *ws2811)
93
+ {
94
+ int chan, max = 0;
95
+
96
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
97
+ {
98
+ if (ws2811->channel[chan].count > max)
99
+ {
100
+ max = ws2811->channel[chan].count;
101
+ }
102
+ }
103
+
104
+ return max;
105
+ }
106
+
107
+ /**
108
+ * Map a physical address and length into userspace virtual memory.
109
+ *
110
+ * @param phys Physical 32-bit address of device registers.
111
+ * @param len Length of mapped region.
112
+ *
113
+ * @returns Virtual address pointer to physical memory region, NULL on error.
114
+ */
115
+ static void *map_device(const uint32_t phys, const uint32_t len)
116
+ {
117
+ uint32_t start_page_addr = phys & PAGE_MASK;
118
+ uint32_t end_page_addr = (phys + len) & PAGE_MASK;
119
+ uint32_t pages = end_page_addr - start_page_addr + 1;
120
+ int fd = open("/dev/mem", O_RDWR | O_SYNC);
121
+ void *virt;
122
+
123
+ if (fd < 0)
124
+ {
125
+ perror("Can't open /dev/mem");
126
+ close(fd);
127
+ return NULL;
128
+ }
129
+
130
+ virt = mmap(NULL, PAGE_SIZE * pages, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
131
+ start_page_addr);
132
+ if (virt == MAP_FAILED)
133
+ {
134
+ perror("map_device() mmap() failed");
135
+ close(fd);
136
+ return NULL;
137
+ }
138
+
139
+ close(fd);
140
+
141
+ return (void *)(((uint8_t *)virt) + PAGE_OFFSET(phys));
142
+ }
143
+
144
+ /**
145
+ * Unmap a physical address and length from virtual memory.
146
+ *
147
+ * @param addr Virtual address pointer of device registers.
148
+ * @param len Length of mapped region.
149
+ *
150
+ * @returns None
151
+ */
152
+ static void unmap_device(volatile void *addr, const uint32_t len)
153
+ {
154
+ uint32_t virt = (uint32_t)addr;
155
+ uint32_t start_page_addr = virt & PAGE_MASK;
156
+ uint32_t end_page_addr = (virt + len) & PAGE_MASK;
157
+ uint32_t pages = end_page_addr - start_page_addr + 1;
158
+
159
+ munmap((void *)addr, PAGE_SIZE * pages);
160
+ }
161
+
162
+ /**
163
+ * Map all devices into userspace memory.
164
+ *
165
+ * @param ws2811 ws2811 instance pointer.
166
+ *
167
+ * @returns 0 on success, -1 otherwise.
168
+ */
169
+ static int map_registers(ws2811_t *ws2811)
170
+ {
171
+ ws2811_device_t *device = ws2811->device;
172
+ uint32_t dma_addr = dmanum_to_phys(ws2811->dmanum);
173
+
174
+ if (!dma_addr)
175
+ {
176
+ return -1;
177
+ }
178
+
179
+ device->dma = map_device(dma_addr, sizeof(dma_t));
180
+ if (!device->dma)
181
+ {
182
+ return -1;
183
+ }
184
+
185
+ device->pwm = map_device(PWM, sizeof(pwm_t));
186
+ if (!device->pwm)
187
+ {
188
+ return -1;
189
+ }
190
+
191
+ device->gpio = map_device(GPIO, sizeof(gpio_t));
192
+ if (!device->gpio)
193
+ {
194
+ return -1;
195
+ }
196
+
197
+ device->cm_pwm = map_device(CM_PWM, sizeof(cm_pwm_t));
198
+ if (!device->cm_pwm)
199
+ {
200
+ return -1;
201
+ }
202
+
203
+ return 0;
204
+ }
205
+
206
+ /**
207
+ * Unmap all devices from virtual memory.
208
+ *
209
+ * @param ws2811 ws2811 instance pointer.
210
+ *
211
+ * @returns None
212
+ */
213
+ static void unmap_registers(ws2811_t *ws2811)
214
+ {
215
+ ws2811_device_t *device = ws2811->device;
216
+
217
+ if (device->dma)
218
+ {
219
+ unmap_device(device->dma, sizeof(dma_t));
220
+ }
221
+
222
+ if (device->pwm)
223
+ {
224
+ unmap_device(device->pwm, sizeof(pwm_t));
225
+ }
226
+
227
+ if (device->cm_pwm)
228
+ {
229
+ unmap_device(device->cm_pwm, sizeof(cm_pwm_t));
230
+ }
231
+
232
+ if (device->gpio)
233
+ {
234
+ unmap_device(device->gpio, sizeof(gpio_t));
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Given a userspace address pointer, return the matching bus address used by DMA.
240
+ * Note: The bus address is not the same as the CPU physical address.
241
+ *
242
+ * @param addr Userspace virtual address pointer.
243
+ *
244
+ * @returns Bus address for use by DMA.
245
+ */
246
+ static uint32_t addr_to_bus(const volatile void *addr)
247
+ {
248
+ char filename[40];
249
+ uint64_t pfn;
250
+ int fd;
251
+
252
+ sprintf(filename, "/proc/%d/pagemap", getpid());
253
+ fd = open(filename, O_RDONLY);
254
+ if (fd < 0)
255
+ {
256
+ perror("addr_to_bus() can't open pagemap");
257
+ return ~0UL;
258
+ }
259
+
260
+ if (lseek(fd, (uint32_t)addr >> 9, SEEK_SET) !=
261
+ (uint32_t)addr >> 9)
262
+ {
263
+ perror("addr_to_bus() lseek() failed");
264
+ close(fd);
265
+ return ~0UL;
266
+ }
267
+
268
+ if (read(fd, &pfn, sizeof(pfn)) != sizeof(pfn))
269
+ {
270
+ perror("addr_to_bus() read() failed");
271
+ close(fd);
272
+ return ~0UL;
273
+ }
274
+
275
+ close(fd);
276
+
277
+ return ((uint32_t)pfn << 12) | 0x40000000 | ((uint32_t)addr & 0xfff);
278
+ }
279
+
280
+ /**
281
+ * Stop the PWM controller.
282
+ *
283
+ * @param ws2811 ws2811 instance pointer.
284
+ *
285
+ * @returns None
286
+ */
287
+ static void stop_pwm(ws2811_t *ws2811)
288
+ {
289
+ ws2811_device_t *device = ws2811->device;
290
+ volatile pwm_t *pwm = device->pwm;
291
+ volatile cm_pwm_t *cm_pwm = device->cm_pwm;
292
+
293
+ // Turn off the PWM in case already running
294
+ pwm->ctl = 0;
295
+ usleep(10);
296
+
297
+ // Kill the clock if it was already running
298
+ cm_pwm->ctl = CM_PWM_CTL_PASSWD | CM_PWM_CTL_KILL;
299
+ usleep(10);
300
+ while (cm_pwm->ctl & CM_PWM_CTL_BUSY)
301
+ ;
302
+ }
303
+
304
+ /**
305
+ * Setup the PWM controller in serial mode on both channels using DMA to feed the PWM FIFO.
306
+ *
307
+ * @param ws2811 ws2811 instance pointer.
308
+ *
309
+ * @returns None
310
+ */
311
+ static int setup_pwm(ws2811_t *ws2811)
312
+ {
313
+ ws2811_device_t *device = ws2811->device;
314
+ volatile dma_t *dma = device->dma;
315
+ volatile dma_cb_t *dma_cb = device->dma_cb;
316
+ volatile pwm_t *pwm = device->pwm;
317
+ volatile cm_pwm_t *cm_pwm = device->cm_pwm;
318
+ int maxcount = max_channel_led_count(ws2811);
319
+ uint32_t freq = ws2811->freq;
320
+ dma_page_t *page;
321
+ int32_t byte_count;
322
+
323
+ stop_pwm(ws2811);
324
+
325
+ // Setup the PWM Clock - Use OSC @ 19.2Mhz w/ 3 clocks/tick
326
+ cm_pwm->div = CM_PWM_DIV_PASSWD | CM_PWM_DIV_DIVI(OSC_FREQ / (3 * freq));
327
+ cm_pwm->ctl = CM_PWM_CTL_PASSWD | CM_PWM_CTL_SRC_OSC;
328
+ cm_pwm->ctl = CM_PWM_CTL_PASSWD | CM_PWM_CTL_SRC_OSC | CM_PWM_CTL_ENAB;
329
+ usleep(10);
330
+ while (!(cm_pwm->ctl & CM_PWM_CTL_BUSY))
331
+ ;
332
+
333
+ // Setup the PWM, use delays as the block is rumored to lock up without them. Make
334
+ // sure to use a high enough priority to avoid any FIFO underruns, especially if
335
+ // the CPU is busy doing lots of memory accesses, or another DMA controller is
336
+ // busy. The FIFO will clock out data at a much slower rate (2.6Mhz max), so
337
+ // the odds of a DMA priority boost are extremely low.
338
+
339
+ pwm->rng1 = 32; // 32-bits per word to serialize
340
+ usleep(10);
341
+ pwm->ctl = RPI_PWM_CTL_CLRF1;
342
+ usleep(10);
343
+ pwm->dmac = RPI_PWM_DMAC_ENAB | RPI_PWM_DMAC_PANIC(7) | RPI_PWM_DMAC_DREQ(3);
344
+ usleep(10);
345
+ pwm->ctl = RPI_PWM_CTL_USEF1 | RPI_PWM_CTL_MODE1 |
346
+ RPI_PWM_CTL_USEF2 | RPI_PWM_CTL_MODE2;
347
+ usleep(10);
348
+ pwm->ctl |= RPI_PWM_CTL_PWEN1 | RPI_PWM_CTL_PWEN2;
349
+
350
+ // Initialize the DMA control blocks to chain together all the DMA pages
351
+ page = &device->page_head;
352
+ byte_count = PWM_BYTE_COUNT(maxcount, freq);
353
+ while ((page = dma_page_next(&device->page_head, page)) &&
354
+ byte_count)
355
+ {
356
+ int32_t page_bytes = PAGE_SIZE < byte_count ? PAGE_SIZE : byte_count;
357
+
358
+ dma_cb->ti = RPI_DMA_TI_NO_WIDE_BURSTS | // 32-bit transfers
359
+ RPI_DMA_TI_WAIT_RESP | // wait for write complete
360
+ RPI_DMA_TI_DEST_DREQ | // user peripheral flow control
361
+ RPI_DMA_TI_PERMAP(5) | // PWM peripheral
362
+ RPI_DMA_TI_SRC_INC; // Increment src addr
363
+
364
+ dma_cb->source_ad = addr_to_bus(page->addr);
365
+ if (dma_cb->source_ad == ~0L)
366
+ {
367
+ return -1;
368
+ }
369
+
370
+ dma_cb->dest_ad = (uint32_t)&((pwm_t *)PWM_PERIPH)->fif1;
371
+ dma_cb->txfr_len = page_bytes;
372
+ dma_cb->stride = 0;
373
+ dma_cb->nextconbk = addr_to_bus(dma_cb + 1);
374
+
375
+ byte_count -= page_bytes;
376
+ if (!dma_page_next(&device->page_head, page))
377
+ {
378
+ break;
379
+ }
380
+
381
+ dma_cb++;
382
+ }
383
+
384
+ // Terminate the final control block to stop DMA
385
+ dma_cb->nextconbk = 0;
386
+
387
+ dma->cs = 0;
388
+ dma->txfr_len = 0;
389
+
390
+ return 0;
391
+ }
392
+
393
+ /**
394
+ * Start the DMA feeding the PWM FIFO. This will stream the entire DMA buffer out of both
395
+ * PWM channels.
396
+ *
397
+ * @param ws2811 ws2811 instance pointer.
398
+ *
399
+ * @returns None
400
+ */
401
+ static void dma_start(ws2811_t *ws2811)
402
+ {
403
+ ws2811_device_t *device = ws2811->device;
404
+ volatile dma_t *dma = device->dma;
405
+ uint32_t dma_cb_addr = device->dma_cb_addr;
406
+
407
+ dma->conblk_ad = dma_cb_addr;
408
+ dma->cs = RPI_DMA_CS_WAIT_OUTSTANDING_WRITES |
409
+ RPI_DMA_CS_PANIC_PRIORITY(15) |
410
+ RPI_DMA_CS_PRIORITY(15) |
411
+ RPI_DMA_CS_ACTIVE;
412
+ }
413
+
414
+ /**
415
+ * Initialize the application selected GPIO pins for PWM operation.
416
+ *
417
+ * @param ws2811 ws2811 instance pointer.
418
+ *
419
+ * @returns 0 on success, -1 on unsupported pin
420
+ */
421
+ static int gpio_init(ws2811_t *ws2811)
422
+ {
423
+ volatile gpio_t *gpio = ws2811->device->gpio;
424
+ int chan;
425
+
426
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
427
+ {
428
+ int pinnum = ws2811->channel[chan].gpionum;
429
+
430
+ if (pinnum)
431
+ {
432
+ int altnum = pwm_pin_alt(chan, pinnum);
433
+
434
+ if (altnum < 0)
435
+ {
436
+ return -1;
437
+ }
438
+
439
+ gpio_function_set(gpio, pinnum, altnum);
440
+ }
441
+ }
442
+
443
+ return 0;
444
+ }
445
+
446
+ /**
447
+ * Initialize the PWM DMA buffer with all zeros for non-inverted operation, or
448
+ * ones for inverted operation. The DMA buffer length is assumed to be a word
449
+ * multiple.
450
+ *
451
+ * @param ws2811 ws2811 instance pointer.
452
+ *
453
+ * @returns None
454
+ */
455
+ void pwm_raw_init(ws2811_t *ws2811)
456
+ {
457
+ volatile uint32_t *pwm_raw = (uint32_t *)ws2811->device->pwm_raw;
458
+ int maxcount = max_channel_led_count(ws2811);
459
+ int wordcount = (PWM_BYTE_COUNT(maxcount, ws2811->freq) / sizeof(uint32_t)) /
460
+ RPI_PWM_CHANNELS;
461
+ int chan;
462
+
463
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
464
+ {
465
+ ws2811_channel_t *channel = &ws2811->channel[chan];
466
+ int i, wordpos = chan;
467
+
468
+ for (i = 0; i < wordcount; i++)
469
+ {
470
+ if (channel->invert)
471
+ {
472
+ pwm_raw[wordpos] = ~0L;
473
+ }
474
+ else
475
+ {
476
+ pwm_raw[wordpos] = 0x0;
477
+ }
478
+
479
+ wordpos += 2;
480
+ }
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Cleanup previously allocated device memory and buffers.
486
+ *
487
+ * @param ws2811 ws2811 instance pointer.
488
+ *
489
+ * @returns None
490
+ */
491
+ void ws2811_cleanup(ws2811_t *ws2811)
492
+ {
493
+ int chan;
494
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
495
+ {
496
+ if (ws2811->channel[chan].leds)
497
+ {
498
+ free(ws2811->channel[chan].leds);
499
+ }
500
+ ws2811->channel[chan].leds = NULL;
501
+ }
502
+
503
+ ws2811_device_t *device = ws2811->device;
504
+ if (device) {
505
+
506
+ if (device->pwm_raw)
507
+ {
508
+ dma_page_free((uint8_t *)device->pwm_raw,
509
+ PWM_BYTE_COUNT(max_channel_led_count(ws2811),
510
+ ws2811->freq));
511
+ device->pwm_raw = NULL;
512
+ }
513
+
514
+ if (device->dma_cb)
515
+ {
516
+ dma_page_free((dma_cb_t *)device->dma_cb, sizeof(dma_cb_t));
517
+ device->dma_cb = NULL;
518
+ }
519
+
520
+ free(device);
521
+ }
522
+ ws2811->device = NULL;
523
+ }
524
+
525
+
526
+ /*
527
+ *
528
+ * Application API Functions
529
+ *
530
+ */
531
+
532
+
533
+ /**
534
+ * Allocate and initialize memory, buffers, pages, PWM, DMA, and GPIO.
535
+ *
536
+ * @param ws2811 ws2811 instance pointer.
537
+ *
538
+ * @returns 0 on success, -1 otherwise.
539
+ */
540
+ int ws2811_init(ws2811_t *ws2811)
541
+ {
542
+ ws2811_device_t *device = NULL;
543
+ int chan;
544
+
545
+ ws2811->device = malloc(sizeof(*ws2811->device));
546
+ if (!ws2811->device)
547
+ {
548
+ return -1;
549
+ }
550
+ device = ws2811->device;
551
+
552
+ // Initialize all pointers to NULL. Any non-NULL pointers will be freed on cleanup.
553
+ device->pwm_raw = NULL;
554
+ device->dma_cb = NULL;
555
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
556
+ {
557
+ ws2811->channel[chan].leds = NULL;
558
+ }
559
+
560
+ dma_page_init(&device->page_head);
561
+
562
+ // Allocate the LED buffers
563
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++)
564
+ {
565
+ ws2811_channel_t *channel = &ws2811->channel[chan];
566
+
567
+ channel->leds = malloc(sizeof(ws2811_led_t) * channel->count);
568
+ if (!channel->leds)
569
+ {
570
+ goto err;
571
+ }
572
+
573
+ memset(channel->leds, 0, sizeof(ws2811_led_t) * channel->count);
574
+ }
575
+
576
+ // Allocate the DMA buffer
577
+ device->pwm_raw = dma_alloc(&device->page_head,
578
+ PWM_BYTE_COUNT(max_channel_led_count(ws2811),
579
+ ws2811->freq));
580
+ if (!device->pwm_raw)
581
+ {
582
+ goto err;
583
+ }
584
+
585
+ pwm_raw_init(ws2811);
586
+
587
+ // Allocate the DMA control block
588
+ device->dma_cb = dma_desc_alloc(MAX_PAGES);
589
+ if (!device->dma_cb)
590
+ {
591
+ goto err;
592
+ }
593
+ memset((dma_cb_t *)device->dma_cb, 0, sizeof(dma_cb_t));
594
+
595
+ // Cache the DMA control block bus address
596
+ device->dma_cb_addr = addr_to_bus(device->dma_cb);
597
+ if (device->dma_cb_addr == ~0L)
598
+ {
599
+ goto err;
600
+ }
601
+
602
+ // Map the physical registers into userspace
603
+ if (map_registers(ws2811))
604
+ {
605
+ goto err;
606
+ }
607
+
608
+ // Initialize the GPIO pins
609
+ if (gpio_init(ws2811))
610
+ {
611
+ unmap_registers(ws2811);
612
+ goto err;
613
+ }
614
+
615
+ // Setup the PWM, clocks, and DMA
616
+ if (setup_pwm(ws2811))
617
+ {
618
+ unmap_registers(ws2811);
619
+ goto err;
620
+ }
621
+
622
+ return 0;
623
+
624
+ err:
625
+ ws2811_cleanup(ws2811);
626
+
627
+ return -1;
628
+ }
629
+
630
+ /**
631
+ * Shut down DMA, PWM, and cleanup memory.
632
+ *
633
+ * @param ws2811 ws2811 instance pointer.
634
+ *
635
+ * @returns None
636
+ */
637
+ void ws2811_fini(ws2811_t *ws2811)
638
+ {
639
+ ws2811_wait(ws2811);
640
+ stop_pwm(ws2811);
641
+
642
+ unmap_registers(ws2811);
643
+
644
+ ws2811_cleanup(ws2811);
645
+ }
646
+
647
+ /**
648
+ * Wait for any executing DMA operation to complete before returning.
649
+ *
650
+ * @param ws2811 ws2811 instance pointer.
651
+ *
652
+ * @returns 0 on success, -1 on DMA competion error
653
+ */
654
+ int ws2811_wait(ws2811_t *ws2811)
655
+ {
656
+ volatile dma_t *dma = ws2811->device->dma;
657
+
658
+ while ((dma->cs & RPI_DMA_CS_ACTIVE) &&
659
+ !(dma->cs & RPI_DMA_CS_ERROR))
660
+ {
661
+ usleep(10);
662
+ }
663
+
664
+ if (dma->cs & RPI_DMA_CS_ERROR)
665
+ {
666
+ fprintf(stderr, "DMA Error: %08x\n", dma->debug);
667
+ return -1;
668
+ }
669
+
670
+ return 0;
671
+ }
672
+
673
+ /**
674
+ * Render the PWM DMA buffer from the user supplied LED arrays and start the DMA
675
+ * controller. This will update all LEDs on both PWM channels.
676
+ *
677
+ * @param ws2811 ws2811 instance pointer.
678
+ *
679
+ * @returns None
680
+ */
681
+ int ws2811_render(ws2811_t *ws2811)
682
+ {
683
+ volatile uint8_t *pwm_raw = ws2811->device->pwm_raw;
684
+ int maxcount = max_channel_led_count(ws2811);
685
+ int bitpos = 31;
686
+ int i, j, k, l, chan;
687
+
688
+ for (chan = 0; chan < RPI_PWM_CHANNELS; chan++) // Channel
689
+ {
690
+ ws2811_channel_t *channel = &ws2811->channel[chan];
691
+ int wordpos = chan;
692
+ int scale = (channel->brightness & 0xff) + 1;
693
+
694
+ for (i = 0; i < channel->count; i++) // Led
695
+ {
696
+ uint8_t color[] =
697
+ {
698
+ (((channel->leds[i] >> 8) & 0xff) * scale) >> 8, // green
699
+ (((channel->leds[i] >> 16) & 0xff) * scale) >> 8, // red
700
+ (((channel->leds[i] >> 0) & 0xff) * scale) >> 8, // blue
701
+ };
702
+
703
+ for (j = 0; j < ARRAY_SIZE(color); j++) // Color
704
+ {
705
+ for (k = 7; k >= 0; k--) // Bit
706
+ {
707
+ uint8_t symbol = SYMBOL_LOW;
708
+
709
+ if (color[j] & (1 << k))
710
+ {
711
+ symbol = SYMBOL_HIGH;
712
+ }
713
+
714
+ if (channel->invert)
715
+ {
716
+ symbol = ~symbol & 0x7;
717
+ }
718
+
719
+ for (l = 2; l >= 0; l--) // Symbol
720
+ {
721
+ uint32_t *wordptr = &((uint32_t *)pwm_raw)[wordpos];
722
+
723
+ *wordptr &= ~(1 << bitpos);
724
+ if (symbol & (1 << l))
725
+ {
726
+ *wordptr |= (1 << bitpos);
727
+ }
728
+
729
+ bitpos--;
730
+ if (bitpos < 0)
731
+ {
732
+ // Every other word is on the same channel
733
+ wordpos += 2;
734
+
735
+ bitpos = 31;
736
+ }
737
+ }
738
+ }
739
+ }
740
+ }
741
+ }
742
+
743
+ // Ensure the CPU data cache is flushed before the DMA is started.
744
+ __clear_cache((char *)pwm_raw,
745
+ (char *)&pwm_raw[PWM_BYTE_COUNT(maxcount, ws2811->freq)]);
746
+
747
+ // Wait for any previous DMA operation to complete.
748
+ if (ws2811_wait(ws2811))
749
+ {
750
+ return -1;
751
+ }
752
+
753
+ dma_start(ws2811);
754
+
755
+ return 0;
756
+ }
757
+