pixel_pi 0.0.1

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.
@@ -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
+