Test program for my Multi_WS2811 library that started out as a fork of heroic/WS2811. My library uses hardware DMA on the FRDM-KL25Z to drive up to 16 strings of WS2811 or WS2812 LEDs in parallel.
Dependencies: Multi_WS2811 mbed MMA8451Q
Fork of WS2811 by
NOTE: I have accidentally pushed changes for another fork of this program that I used in the recent Georgetown Carnival Power Tool Races. When I get some time, I will restore the test program to its original glory.
You can see my power tool racer (Nevermore's Revenge) here

This tests my FRDM-KL25Z multi-string WS2811/WS2812 library. It uses the accelerometer to change the rainbow phase on two strings of LEDs as well as the touch sense to change brightness.
A video of this program in operation is here.
Here is the library that I developed to run the LEDs:
Import libraryMulti_WS2811
Library allowing up to 16 strings of 60 WS2811 or WS2812 LEDs to be driven from a single FRDM-KL25Z board. Uses hardware DMA to do a full 800 KHz rate without much CPU burden.
WS2811.cpp@20:b9d76e567637, 2013-12-21 (annotated)
- Committer:
- bikeNomad
- Date:
- Sat Dec 21 04:32:21 2013 +0000
- Revision:
- 20:b9d76e567637
- Parent:
- 17:b4e9d8f4baa9
- Child:
- 21:4541da183397
still trying to get DMA to work
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| heroic | 10:62368b801d16 | 1 | // 800 KHz WS2811 driver, kinda. |
| heroic | 8:e3249c2b7607 | 2 | // |
| heroic | 8:e3249c2b7607 | 3 | // Parameterized and modified to use soft SPI. |
| heroic | 8:e3249c2b7607 | 4 | // Jas Strong <jasmine@electronpusher.org> |
| bikeNomad | 17:b4e9d8f4baa9 | 5 | // Modified to use hard SPI by Ned Konz <ned@bike-nomad.com> |
| heroic | 8:e3249c2b7607 | 6 | /*****************************************************************************/ |
| heroic | 8:e3249c2b7607 | 7 | |
| heroic | 8:e3249c2b7607 | 8 | #include "LedStrip.h" |
| heroic | 8:e3249c2b7607 | 9 | #include "WS2811.h" |
| bikeNomad | 20:b9d76e567637 | 10 | extern void dump_spi_settings(SPI_Type const *spi); |
| bikeNomad | 20:b9d76e567637 | 11 | extern Serial pc; |
| bikeNomad | 20:b9d76e567637 | 12 | extern DigitalOut debugOut; |
| heroic | 8:e3249c2b7607 | 13 | |
| bikeNomad | 20:b9d76e567637 | 14 | static const unsigned DMA_MUX_SRC_SPI0_Transmit = 17; |
| bikeNomad | 20:b9d76e567637 | 15 | // const unsigned DMA_MUX_SRC_SPI1_Transmit = 19; |
| bikeNomad | 20:b9d76e567637 | 16 | |
| bikeNomad | 20:b9d76e567637 | 17 | static const unsigned dmaWriteChannel = 0; |
| bikeNomad | 20:b9d76e567637 | 18 | static const unsigned dmaXmitMuxSrc = DMA_MUX_SRC_SPI0_Transmit; |
| bikeNomad | 20:b9d76e567637 | 19 | |
| bikeNomad | 20:b9d76e567637 | 20 | static volatile bool dma_done = false; |
| bikeNomad | 20:b9d76e567637 | 21 | |
| bikeNomad | 20:b9d76e567637 | 22 | // 12.8 MHz => 800KHz bit rate (1.25 usec/byte) |
| bikeNomad | 20:b9d76e567637 | 23 | |
| bikeNomad | 20:b9d76e567637 | 24 | WS2811::WS2811(int n, SPI_Type *_spi, PinName _mosi, PinName sclk) : |
| bikeNomad | 17:b4e9d8f4baa9 | 25 | LedStrip(n), |
| bikeNomad | 20:b9d76e567637 | 26 | spi(_spi), |
| bikeNomad | 20:b9d76e567637 | 27 | mosi(_mosi) |
| bikeNomad | 17:b4e9d8f4baa9 | 28 | { |
| bikeNomad | 20:b9d76e567637 | 29 | SPI spitemp(_mosi, NC, sclk); |
| bikeNomad | 20:b9d76e567637 | 30 | spitemp.format(8,3); |
| bikeNomad | 20:b9d76e567637 | 31 | spitemp.frequency(800e3 * 16 * 2); // 12 MHz (48MHz/60) => 750KHz rate (1.33 usec/byte) |
| bikeNomad | 20:b9d76e567637 | 32 | |
| bikeNomad | 20:b9d76e567637 | 33 | //Enable DMA clocking |
| bikeNomad | 20:b9d76e567637 | 34 | SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable clock to DMA mux |
| bikeNomad | 20:b9d76e567637 | 35 | SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable clock to DMA |
| bikeNomad | 20:b9d76e567637 | 36 | |
| bikeNomad | 20:b9d76e567637 | 37 | // reset DMAMUX |
| bikeNomad | 20:b9d76e567637 | 38 | DMAMUX0->CHCFG[dmaWriteChannel] = 0; |
| bikeNomad | 20:b9d76e567637 | 39 | DMAMUX0->CHCFG[dmaWriteChannel] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(dmaXmitMuxSrc); |
| bikeNomad | 20:b9d76e567637 | 40 | |
| bikeNomad | 20:b9d76e567637 | 41 | // Enable DMA features within the SPI registers |
| bikeNomad | 20:b9d76e567637 | 42 | spi->C1 |= SPI_C1_SPTIE_MASK | // enable transmit-interrupt |
| bikeNomad | 20:b9d76e567637 | 43 | SPI_C1_MSTR_MASK; |
| heroic | 8:e3249c2b7607 | 44 | } |
| heroic | 8:e3249c2b7607 | 45 | |
| heroic | 8:e3249c2b7607 | 46 | /* |
| bikeNomad | 20:b9d76e567637 | 47 | * These chips use a one-wire protocol based on a sort of NRZ signalling- jas. |
| bikeNomad | 20:b9d76e567637 | 48 | * Spec is 1.25usec +/- 600nsec => 650nsec to 1850nsec |
| heroic | 8:e3249c2b7607 | 49 | */ |
| bikeNomad | 17:b4e9d8f4baa9 | 50 | |
| bikeNomad | 20:b9d76e567637 | 51 | |
| bikeNomad | 20:b9d76e567637 | 52 | void WS2811::startDMA() |
| bikeNomad | 17:b4e9d8f4baa9 | 53 | { |
| bikeNomad | 20:b9d76e567637 | 54 | DMA0->DMA[dmaWriteChannel].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status |
| bikeNomad | 20:b9d76e567637 | 55 | DMA0->DMA[dmaWriteChannel].SAR = (uint32_t)(void*)dmaBytes; // set source address |
| bikeNomad | 20:b9d76e567637 | 56 | DMA0->DMA[dmaWriteChannel].DAR = (uint32_t)(void*)&(spi->D); // set dest address: SPI0_Data register |
| bikeNomad | 20:b9d76e567637 | 57 | DMA0->DMA[dmaWriteChannel].DSR_BCR |= DMA_DSR_BCR_BCR_MASK & sizeof(dmaBytes); // length of transfer |
| bikeNomad | 20:b9d76e567637 | 58 | DMA0->DMA[dmaWriteChannel].DCR = DMA_DCR_EINT_MASK | // enable interrupt on end of transfer |
| bikeNomad | 20:b9d76e567637 | 59 | DMA_DCR_ERQ_MASK | |
| bikeNomad | 20:b9d76e567637 | 60 | DMA_DCR_SINC_MASK | |
| bikeNomad | 20:b9d76e567637 | 61 | DMA_DCR_SSIZE(0x01) | |
| bikeNomad | 20:b9d76e567637 | 62 | DMA_DCR_DSIZE(0x01) | |
| bikeNomad | 20:b9d76e567637 | 63 | // DMA_DCR_START_MASK | |
| bikeNomad | 20:b9d76e567637 | 64 | DMA_DCR_D_REQ_MASK; // clear ERQ on end of transfer |
| bikeNomad | 20:b9d76e567637 | 65 | |
| bikeNomad | 20:b9d76e567637 | 66 | dump_spi_settings(spi); |
| bikeNomad | 20:b9d76e567637 | 67 | |
| bikeNomad | 20:b9d76e567637 | 68 | debugOut = 1; |
| bikeNomad | 20:b9d76e567637 | 69 | |
| bikeNomad | 20:b9d76e567637 | 70 | while (!(spi->S & SPI_S_SPTEF_MASK)) |
| bikeNomad | 20:b9d76e567637 | 71 | __NOP(); |
| bikeNomad | 20:b9d76e567637 | 72 | spi->D = dmaBytes[0]; |
| bikeNomad | 20:b9d76e567637 | 73 | |
| bikeNomad | 20:b9d76e567637 | 74 | dma_done = false; |
| bikeNomad | 20:b9d76e567637 | 75 | |
| bikeNomad | 20:b9d76e567637 | 76 | spi->C2 |= SPI_C2_TXDMAE_MASK; |
| bikeNomad | 20:b9d76e567637 | 77 | |
| bikeNomad | 20:b9d76e567637 | 78 | // wait until done |
| bikeNomad | 20:b9d76e567637 | 79 | // while (!(DMA0->DMA[dmaWriteChannel].DSR_BCR & DMA_DSR_BCR_DONE_MASK)) |
| bikeNomad | 20:b9d76e567637 | 80 | while (!dma_done) |
| bikeNomad | 20:b9d76e567637 | 81 | __NOP(); |
| bikeNomad | 20:b9d76e567637 | 82 | |
| bikeNomad | 20:b9d76e567637 | 83 | spi->C2 &= ~SPI_C2_TXDMAE_MASK; |
| bikeNomad | 20:b9d76e567637 | 84 | debugOut = 0; |
| bikeNomad | 20:b9d76e567637 | 85 | |
| bikeNomad | 20:b9d76e567637 | 86 | dump_spi_settings(spi); |
| bikeNomad | 20:b9d76e567637 | 87 | |
| heroic | 8:e3249c2b7607 | 88 | } |
| heroic | 8:e3249c2b7607 | 89 | |
| bikeNomad | 20:b9d76e567637 | 90 | void WS2811::writePixel(uint8_t *p) |
| bikeNomad | 17:b4e9d8f4baa9 | 91 | { |
| bikeNomad | 20:b9d76e567637 | 92 | writeByte(*p++, dmaBytes + 0); |
| bikeNomad | 20:b9d76e567637 | 93 | writeByte(*p++, dmaBytes + 16); |
| bikeNomad | 20:b9d76e567637 | 94 | writeByte(*p, dmaBytes + 32); |
| bikeNomad | 20:b9d76e567637 | 95 | // printf("DMA Bytes:\r\n"); |
| bikeNomad | 20:b9d76e567637 | 96 | // for (int i = 0; i < sizeof(dmaBytes); i++) |
| bikeNomad | 20:b9d76e567637 | 97 | // printf(" %02x", dmaBytes[i]); |
| bikeNomad | 20:b9d76e567637 | 98 | // printf("\r\n"); |
| bikeNomad | 20:b9d76e567637 | 99 | startDMA(); |
| bikeNomad | 20:b9d76e567637 | 100 | } |
| bikeNomad | 20:b9d76e567637 | 101 | |
| bikeNomad | 20:b9d76e567637 | 102 | void WS2811::writeByte(uint8_t byte, uint8_t *dest) |
| bikeNomad | 20:b9d76e567637 | 103 | { |
| bikeNomad | 20:b9d76e567637 | 104 | for (uint8_t mask = 0x80; mask; mask >>= 1) { |
| bikeNomad | 20:b9d76e567637 | 105 | if (mask & byte) |
| bikeNomad | 20:b9d76e567637 | 106 | *dest++ = 0xff; // 8 high |
| bikeNomad | 20:b9d76e567637 | 107 | else |
| bikeNomad | 20:b9d76e567637 | 108 | *dest++ = 0xe0; // 3 high, 5 low |
| bikeNomad | 20:b9d76e567637 | 109 | *dest++ = 0x00; // 8 more low |
| bikeNomad | 20:b9d76e567637 | 110 | } |
| heroic | 8:e3249c2b7607 | 111 | } |
| heroic | 8:e3249c2b7607 | 112 | |
| bikeNomad | 17:b4e9d8f4baa9 | 113 | void WS2811::begin(void) |
| bikeNomad | 17:b4e9d8f4baa9 | 114 | { |
| heroic | 8:e3249c2b7607 | 115 | blank(); |
| heroic | 8:e3249c2b7607 | 116 | show(); |
| heroic | 8:e3249c2b7607 | 117 | } |
| heroic | 8:e3249c2b7607 | 118 | |
| bikeNomad | 17:b4e9d8f4baa9 | 119 | void WS2811::blank(void) |
| bikeNomad | 17:b4e9d8f4baa9 | 120 | { |
| bikeNomad | 17:b4e9d8f4baa9 | 121 | memset(pixels, 0x00, numPixelBytes()); |
| heroic | 8:e3249c2b7607 | 122 | } |
| heroic | 8:e3249c2b7607 | 123 | |
| bikeNomad | 20:b9d76e567637 | 124 | |
| bikeNomad | 17:b4e9d8f4baa9 | 125 | void WS2811::show(void) |
| bikeNomad | 17:b4e9d8f4baa9 | 126 | { |
| bikeNomad | 20:b9d76e567637 | 127 | uint16_t i, n = numPixels(); // 3 bytes per LED |
| bikeNomad | 20:b9d76e567637 | 128 | uint8_t *p = pixels; |
| heroic | 8:e3249c2b7607 | 129 | while (guardtime.read_us() < 50) |
| bikeNomad | 20:b9d76e567637 | 130 | __NOP(); |
| bikeNomad | 20:b9d76e567637 | 131 | for (i=0; i<n; i++ ) { |
| bikeNomad | 20:b9d76e567637 | 132 | writePixel(p); |
| bikeNomad | 20:b9d76e567637 | 133 | pc.printf("%d> ", i); |
| bikeNomad | 20:b9d76e567637 | 134 | pc.getc(); |
| bikeNomad | 20:b9d76e567637 | 135 | p += 3; |
| heroic | 8:e3249c2b7607 | 136 | } |
| heroic | 8:e3249c2b7607 | 137 | guardtime.reset(); |
| heroic | 8:e3249c2b7607 | 138 | } |
| heroic | 8:e3249c2b7607 | 139 | |
| bikeNomad | 20:b9d76e567637 | 140 | extern "C" void DMA0IntHandler() |
| bikeNomad | 20:b9d76e567637 | 141 | { |
| bikeNomad | 20:b9d76e567637 | 142 | DMA0->DMA[dmaWriteChannel].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status |
| bikeNomad | 20:b9d76e567637 | 143 | dma_done = true; |
| bikeNomad | 20:b9d76e567637 | 144 | } |
| heroic | 8:e3249c2b7607 | 145 |
Ned Konz


Generic WS2811/WS2812