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.

Dependents:   Multi_WS2811_test

After being frustrated by the SPI system's performance, I ended up using an approach inspired by Paul Stoffregen's OctoWS2811. This uses 3 of the 4 DMA channels triggered by the TPM0 timer PWM and overflow events.

This design will allow for up to 16 strings of up to 60 (limited by RAM space) WS2811/WS2812 LEDs to be driven on a single port. Adding more strings takes the same time to DMA, because the bits are output in parallel.

Here is my test program:

Import programMulti_WS2811_test

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.

Here's 60 LEDs on a single string, at 10% brightness: https://www.icloud.com/sharedalbum/#B015oqs3qeGdFY

Note though that the 3.3V output from the FRDM-KL25Z's GPIO pins is OUT OF SPEC for driving the 5V WS2812 inputs, which require 3.5V for a logic HIGH signal. It only works on my board if I don't connect my scope or logic analyzer to the output pin. I recommend that you add a 5V buffer to the outputs to properly drive the LED strings. I added a CD4504 to do the 3.3 to 5V translation (mostly because I had one). You could use (say) a 74HCT244 to do 8 strings.

Each LED in a string takes 24/800e3 seconds to DMA, so if MAX_LEDS_PER_STRING is set to 60, then it takes 1.8 msec to actually do the DMA, plus 64 usec of guard time, or 1.87 msec per frame (538 frames/second). Of course, actually composing the frame will take most of the time in a real program.

The way I have my code set up, I can use up to 8 pins on PORTD. However, changing the defines at the top of WS2811.cpp will change the selected port.

Alternatively, you could use another port to get more strings. Watch out for pin mux conflicts, though.

Here are your choices:

  • PORTE: 15 total: PTE0-PTE5, PTE20-PTE25, PTE29-PTE31
  • PORTD: 8 total: PTD0-PTD7
  • PORTC: 16 total: PTC0-PTC13, PTC16-17
  • PORTB: 16 total: PTB0-PTB11, PTB16-19
  • PORTA: 15 total: PTA0-PTA5, PTA12-PTA20

Here is how the DMA channels are interleaved:

/media/uploads/bikeNomad/ws2812.png

The way I have it set up to generate the three phases of the required waveform is this:

I have timer TPM0 set up to generate events at overflow (OVF), at 250 nsec (CH0), and at 650 nsec (CH1). At 1250 nsec it resets to 0.

At timer count = 0, DMA0 fires, because it's triggered by TPM0's overflow (OVF) event. This results in the data lines being driven to a constant "1" level, as the data that DMA0 is programmed to transfer is a single, all-1's word. (This is the easiest way to explain what is happening; this is the way I'd wanted it to work, but I had to use as much precious RAM as for the RGB data to hold 1's to get it to work).

At 250 nsec, DMA1 fires, because it's triggered by TPM0's CH0 compare event. This drives either a 0 or 1 level to the pins, because DMA1 is programmed to transfer our data bytes to the pins.

At 650 nsec, DMA2 fires, because it's triggered by TPM0's CH1 compare event. This results in the data lines being driven to a constant "0" level, as the data that DMA2 is programmed to transfer is a single, all-0's word.

At 1250 nsec, the timer resets to 0, and the whole cycle repeats.

Because this library uses three of timer TPM0's six channels (and sets TPM0 to 800kHz), you will need to select TPM1 or TPM2 output pins if you want to use PwmOut pins in your program (for instance, for RC servos, which want a 50Hz frequency). If you just want to change discrete LED brightnesses, you can use TPM0's CH3, CH4, or CH5 pins. Just make sure that you set up your PwmOut instance at the same frequency.

Here is a table showing the assignment of timer resources to PwmOut capable pins in the FRDM-KL25Z:

KL25Z pinArduino nameTimerChannel
PTA3TPM0CH0
PTC1A5TPM0CH0
PTD0D10TPM0CH0
PTE24TPM0CH0
PTA4D4TPM0CH1
PTC2A4TPM0CH1
PTD1D13/LED_BLUETPM0CH1
PTE25TPM0CH1
PTA5D5TPM0CH2
PTC3TPM0CH2
PTD2D11TPM0CH2
PTE29TPM0CH2
PTC4TPM0CH3
PTD3D12TPM0CH3
PTE30TPM0CH3
PTC8D6TPM0CH4
PTD4D2TPM0CH4
PTE31TPM0CH4
PTA0TPM0CH5
PTC9D7TPM0CH5
PTD5D9TPM0CH5
PTE26TPM0CH5
PTA12D3TPM1CH0
PTB0A0TPM1CH0
PTE20TPM1CH0
PTA13D8TPM1CH1
PTB1A1TPM1CH1
PTE21TPM1CH1
PTA1D0/USBRXTPM2CH0
PTB18LED_REDTPM2CH0
PTB2A2TPM2CH0
PTE22TPM2CH0
PTA2D1/USBTXTPM2CH1
PTB19LED_GREENTPM2CH1
PTB3A3TPM2CH1
PTE23TPM2CH1
Committer:
Ned Konz
Date:
Sat Jun 13 00:18:32 2015 -0700
Revision:
5:2c3b76ea0b40
Parent:
4:990838718b51
Worked out flapping and color animation

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Ned Konz 3:df4319053bfa 1 // 800 KHz WS2811 driver driving potentially many LED strings.
Ned Konz 3:df4319053bfa 2 // Uses 3-phase DMA
Ned Konz 3:df4319053bfa 3 // 16K SRAM less stack, etc.
Ned Konz 3:df4319053bfa 4 //
Ned Konz 3:df4319053bfa 5 // Per LED: 3 bytes (malloc'd) for RGB data
Ned Konz 3:df4319053bfa 6 //
Ned Konz 3:df4319053bfa 7 // Per LED strip / per LED
Ned Konz 3:df4319053bfa 8 // 96 bytes (static) for bit data
Ned Konz 3:df4319053bfa 9 // + 96 bytes (static) for ones data
Ned Konz 3:df4319053bfa 10 // = 192 bytes
Ned Konz 3:df4319053bfa 11 //
Ned Konz 3:df4319053bfa 12 // 40 LEDs max per string = 7680 bytes static
Ned Konz 3:df4319053bfa 13 //
Ned Konz 3:df4319053bfa 14 // 40 LEDs: 7680 + 40*3 = 7800 bytes
Ned Konz 3:df4319053bfa 15 // 80 LEDs: 7680 + 80*3 = 7920 bytes
Ned Konz 3:df4319053bfa 16 #include <mbed.h>
Ned Konz 3:df4319053bfa 17 #include "MKL25Z4.h"
Ned Konz 3:df4319053bfa 18
Ned Konz 3:df4319053bfa 19 #ifndef MBED_WS2811_H
Ned Konz 3:df4319053bfa 20 #include "WS2811.h"
Ned Konz 3:df4319053bfa 21 #endif
Ned Konz 3:df4319053bfa 22
Ned Konz 3:df4319053bfa 23 #if defined(WS2811_DEBUG_PIN)
Ned Konz 3:df4319053bfa 24 #define DEBUG_MASK (1<<WS2811_DEBUG_PIN)
Ned Konz 3:df4319053bfa 25 #define RESET_DEBUG (WS2811_IO_GPIO->PDOR &= ~DEBUG_MASK)
Ned Konz 3:df4319053bfa 26 #define SET_DEBUG (WS2811_IO_GPIO->PDOR |= DEBUG_MASK)
Ned Konz 3:df4319053bfa 27 #else
Ned Konz 3:df4319053bfa 28 #define DEBUG_MASK 0
Ned Konz 3:df4319053bfa 29 #define RESET_DEBUG (void)0
Ned Konz 3:df4319053bfa 30 #define SET_DEBUG (void)0
Ned Konz 3:df4319053bfa 31 #endif
Ned Konz 3:df4319053bfa 32
Ned Konz 4:990838718b51 33 static volatile unsigned dma_done = 3;
Ned Konz 3:df4319053bfa 34
Ned Konz 3:df4319053bfa 35 // 48 MHz clock, no prescaling.
Ned Konz 3:df4319053bfa 36 #define NSEC_TO_TICKS(nsec) ((nsec)*48/1000)
Ned Konz 3:df4319053bfa 37 #define USEC_TO_TICKS(usec) ((usec)*48)
Ned Konz 3:df4319053bfa 38 #define CLK_NSEC 1250
Ned Konz 3:df4319053bfa 39 #define tpm_period NSEC_TO_TICKS(CLK_NSEC)
Ned Konz 3:df4319053bfa 40 #define tpm_p0_period NSEC_TO_TICKS(250)
Ned Konz 3:df4319053bfa 41 #define tpm_p1_period NSEC_TO_TICKS(650)
Ned Konz 5:2c3b76ea0b40 42 #define guardtime_us 55
Ned Konz 5:2c3b76ea0b40 43 #define guardtime_period USEC_TO_TICKS(guardtime_us) /* guardtime minimum 50 usec. */
Ned Konz 3:df4319053bfa 44
Ned Konz 3:df4319053bfa 45 enum DMA_MUX_SRC {
Ned Konz 3:df4319053bfa 46 DMA_MUX_SRC_TPM0_CH_0 = 24,
Ned Konz 3:df4319053bfa 47 DMA_MUX_SRC_TPM0_CH_1,
Ned Konz 3:df4319053bfa 48 DMA_MUX_SRC_TPM0_Overflow = 54,
Ned Konz 3:df4319053bfa 49 };
Ned Konz 3:df4319053bfa 50
Ned Konz 3:df4319053bfa 51 enum DMA_CHAN {
Ned Konz 3:df4319053bfa 52 DMA_CHAN_START = 0,
Ned Konz 3:df4319053bfa 53 DMA_CHAN_0_LOW = 1,
Ned Konz 3:df4319053bfa 54 DMA_CHAN_1_LOW = 2,
Ned Konz 3:df4319053bfa 55 N_DMA_CHANNELS
Ned Konz 3:df4319053bfa 56 };
Ned Konz 3:df4319053bfa 57
Ned Konz 3:df4319053bfa 58 // class static
Ned Konz 3:df4319053bfa 59 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 4:990838718b51 60 void WS2811<MAX_LEDS_PER_STRIP>::wait_for_dma_done()
Ned Konz 4:990838718b51 61 {
Ned Konz 5:2c3b76ea0b40 62 if ((DMA0->DMA[DMA_CHAN_1_LOW].DSR_BCR & DMA_DSR_BCR_BSY_MASK) == 0)
Ned Konz 5:2c3b76ea0b40 63 return;
Ned Konz 5:2c3b76ea0b40 64 while ((DMA0->DMA[DMA_CHAN_1_LOW].DSR_BCR & DMA_DSR_BCR_BSY_MASK))
Ned Konz 5:2c3b76ea0b40 65 __WFI();
Ned Konz 5:2c3b76ea0b40 66 wait_us(guardtime_us);
Ned Konz 4:990838718b51 67 }
Ned Konz 3:df4319053bfa 68
Ned Konz 3:df4319053bfa 69 // class static
Ned Konz 3:df4319053bfa 70 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 71 bool WS2811<MAX_LEDS_PER_STRIP>::initialized = false;
Ned Konz 3:df4319053bfa 72
Ned Konz 3:df4319053bfa 73 // class static
Ned Konz 3:df4319053bfa 74 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 75 uint32_t WS2811<MAX_LEDS_PER_STRIP>::enabledPins = 0;
Ned Konz 3:df4319053bfa 76
Ned Konz 3:df4319053bfa 77 #define WORD_ALIGNED __attribute__ ((aligned(4)))
Ned Konz 3:df4319053bfa 78
Ned Konz 3:df4319053bfa 79 // class static
Ned Konz 3:df4319053bfa 80 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 81 struct WS2811<MAX_LEDS_PER_STRIP>::DMALayout WS2811<MAX_LEDS_PER_STRIP>::dmaData WORD_ALIGNED;
Ned Konz 3:df4319053bfa 82
Ned Konz 3:df4319053bfa 83 // class static
Ned Konz 3:df4319053bfa 84 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 85 void WS2811<MAX_LEDS_PER_STRIP>::hw_init()
Ned Konz 3:df4319053bfa 86 {
Ned Konz 3:df4319053bfa 87 if (initialized) return;
Ned Konz 3:df4319053bfa 88
Ned Konz 3:df4319053bfa 89 dma_data_init();
Ned Konz 3:df4319053bfa 90 clock_init();
Ned Konz 3:df4319053bfa 91 dma_init();
Ned Konz 3:df4319053bfa 92 io_init();
Ned Konz 3:df4319053bfa 93 tpm_init();
Ned Konz 3:df4319053bfa 94
Ned Konz 3:df4319053bfa 95 initialized = true;
Ned Konz 3:df4319053bfa 96
Ned Konz 3:df4319053bfa 97 SET_DEBUG;
Ned Konz 3:df4319053bfa 98 RESET_DEBUG;
Ned Konz 3:df4319053bfa 99 }
Ned Konz 3:df4319053bfa 100
Ned Konz 3:df4319053bfa 101 // class static
Ned Konz 3:df4319053bfa 102 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 103 void WS2811<MAX_LEDS_PER_STRIP>::dma_data_init()
Ned Konz 3:df4319053bfa 104 {
Ned Konz 3:df4319053bfa 105 memset(dmaData.allOnes, 0xFF, sizeof(dmaData.allOnes));
Ned Konz 3:df4319053bfa 106 }
Ned Konz 3:df4319053bfa 107
Ned Konz 3:df4319053bfa 108 // class static
Ned Konz 3:df4319053bfa 109
Ned Konz 3:df4319053bfa 110 /// Enable PORTD, DMA and TPM0 clocking
Ned Konz 3:df4319053bfa 111 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 112 void WS2811<MAX_LEDS_PER_STRIP>::clock_init()
Ned Konz 3:df4319053bfa 113 {
Ned Konz 3:df4319053bfa 114 SIM->SCGC5 |= SIM_SCGC5_PORTD_MASK;
Ned Konz 3:df4319053bfa 115 SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK | SIM_SCGC6_TPM0_MASK; // Enable clock to DMA mux and TPM0
Ned Konz 3:df4319053bfa 116 SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable clock to DMA
Ned Konz 3:df4319053bfa 117
Ned Konz 3:df4319053bfa 118 SIM->SOPT2 |= SIM_SOPT2_TPMSRC(1); // Clock source: MCGFLLCLK or MCGPLLCLK
Ned Konz 3:df4319053bfa 119 }
Ned Konz 3:df4319053bfa 120
Ned Konz 3:df4319053bfa 121 // class static
Ned Konz 3:df4319053bfa 122
Ned Konz 3:df4319053bfa 123 /// Configure GPIO output pins
Ned Konz 3:df4319053bfa 124 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 125 void WS2811<MAX_LEDS_PER_STRIP>::io_init()
Ned Konz 3:df4319053bfa 126 {
Ned Konz 3:df4319053bfa 127 uint32_t m = 1;
Ned Konz 3:df4319053bfa 128 for (uint32_t i = 0; i < 32; i++) {
Ned Konz 3:df4319053bfa 129 // set up each pin
Ned Konz 3:df4319053bfa 130 if (m & enabledPins) {
Ned Konz 3:df4319053bfa 131 WS2811_IO_PORT->PCR[i] = PORT_PCR_MUX(1) // GPIO
Ned Konz 3:df4319053bfa 132 | PORT_PCR_DSE_MASK; // high drive strength
Ned Konz 3:df4319053bfa 133 }
Ned Konz 3:df4319053bfa 134 m <<= 1;
Ned Konz 3:df4319053bfa 135 }
Ned Konz 3:df4319053bfa 136
Ned Konz 3:df4319053bfa 137 WS2811_IO_GPIO->PDDR |= enabledPins; // set as outputs
Ned Konz 3:df4319053bfa 138
Ned Konz 3:df4319053bfa 139 #if WS2811_MONITOR_TPM0_PWM
Ned Konz 3:df4319053bfa 140 // PTD0 CH0 monitor: TPM0, high drive strength
Ned Konz 3:df4319053bfa 141 WS2811_IO_PORT->PCR[0] = PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK;
Ned Konz 3:df4319053bfa 142 // PTD1 CH1 monitor: TPM0, high drive strength
Ned Konz 3:df4319053bfa 143 WS2811_IO_PORT->PCR[1] = PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK;
Ned Konz 3:df4319053bfa 144 WS2811_IO_GPIO->PDDR |= 3; // set as outputs
Ned Konz 3:df4319053bfa 145 WS2811_IO_GPIO->PDOR &= ~(enabledPins | 3); // initially low
Ned Konz 3:df4319053bfa 146 #else
Ned Konz 3:df4319053bfa 147 WS2811_IO_GPIO->PDOR &= ~enabledPins; // initially low
Ned Konz 3:df4319053bfa 148 #endif
Ned Konz 3:df4319053bfa 149
Ned Konz 4:990838718b51 150 #ifdef WS2811_DEBUG_PIN
Ned Konz 3:df4319053bfa 151 WS2811_IO_PORT->PCR[WS2811_DEBUG_PIN] = PORT_PCR_MUX(1) | PORT_PCR_DSE_MASK;
Ned Konz 3:df4319053bfa 152 WS2811_IO_GPIO->PDDR |= DEBUG_MASK;
Ned Konz 3:df4319053bfa 153 WS2811_IO_GPIO->PDOR &= ~DEBUG_MASK;
Ned Konz 3:df4319053bfa 154 #endif
Ned Konz 3:df4319053bfa 155 }
Ned Konz 3:df4319053bfa 156
Ned Konz 3:df4319053bfa 157 // class static
Ned Konz 3:df4319053bfa 158
Ned Konz 3:df4319053bfa 159 /// Configure DMA and DMAMUX
Ned Konz 3:df4319053bfa 160 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 161 void WS2811<MAX_LEDS_PER_STRIP>::dma_init()
Ned Konz 3:df4319053bfa 162 {
Ned Konz 3:df4319053bfa 163 // reset DMAMUX
Ned Konz 3:df4319053bfa 164 DMAMUX0->CHCFG[DMA_CHAN_START] = 0;
Ned Konz 3:df4319053bfa 165 DMAMUX0->CHCFG[DMA_CHAN_0_LOW] = 0;
Ned Konz 3:df4319053bfa 166 DMAMUX0->CHCFG[DMA_CHAN_1_LOW] = 0;
Ned Konz 3:df4319053bfa 167
Ned Konz 3:df4319053bfa 168 // wire our DMA event sources into the first three DMA channels
Ned Konz 3:df4319053bfa 169 // t=0: all enabled outputs go high on TPM0 overflow
Ned Konz 3:df4319053bfa 170 DMAMUX0->CHCFG[DMA_CHAN_START] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMA_MUX_SRC_TPM0_Overflow);
Ned Konz 3:df4319053bfa 171 // t=tpm_p0_period: all of the 0 bits go low.
Ned Konz 3:df4319053bfa 172 DMAMUX0->CHCFG[DMA_CHAN_0_LOW] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMA_MUX_SRC_TPM0_CH_0);
Ned Konz 3:df4319053bfa 173 // t=tpm_p1_period: all outputs go low.
Ned Konz 3:df4319053bfa 174 DMAMUX0->CHCFG[DMA_CHAN_1_LOW] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMA_MUX_SRC_TPM0_CH_1);
Ned Konz 3:df4319053bfa 175
Ned Konz 3:df4319053bfa 176 NVIC_SetVector(DMA0_IRQn, (uint32_t)&DMA0_IRQHandler);
Ned Konz 3:df4319053bfa 177 NVIC_EnableIRQ(DMA0_IRQn);
Ned Konz 3:df4319053bfa 178 }
Ned Konz 3:df4319053bfa 179
Ned Konz 3:df4319053bfa 180 // class static
Ned Konz 3:df4319053bfa 181
Ned Konz 3:df4319053bfa 182 /// Configure TPM0 to do two different PWM periods at 800kHz rate
Ned Konz 3:df4319053bfa 183 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 184 void WS2811<MAX_LEDS_PER_STRIP>::tpm_init()
Ned Konz 3:df4319053bfa 185 {
Ned Konz 3:df4319053bfa 186 // set up TPM0 for proper period (800 kHz = 1.25 usec ±600nsec)
Ned Konz 3:df4319053bfa 187 TPM_Type volatile *tpm = TPM0;
Ned Konz 3:df4319053bfa 188 tpm->SC = TPM_SC_DMA_MASK // enable DMA
Ned Konz 3:df4319053bfa 189 | TPM_SC_TOF_MASK // reset TOF flag if set
Ned Konz 3:df4319053bfa 190 | TPM_SC_CMOD(0) // disable clocks
Ned Konz 3:df4319053bfa 191 | TPM_SC_PS(0); // 48MHz / 1 = 48MHz clock
Ned Konz 3:df4319053bfa 192 tpm->MOD = tpm_period - 1; // 48MHz / 800kHz
Ned Konz 3:df4319053bfa 193
Ned Konz 3:df4319053bfa 194 // No Interrupts; High True pulses on Edge Aligned PWM
Ned Konz 3:df4319053bfa 195 tpm->CONTROLS[0].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK | TPM_CnSC_DMA_MASK;
Ned Konz 3:df4319053bfa 196 tpm->CONTROLS[1].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK | TPM_CnSC_DMA_MASK;
Ned Konz 3:df4319053bfa 197
Ned Konz 3:df4319053bfa 198 // set TPM0 channel 0 for 0.35 usec (±150nsec) (0 code)
Ned Konz 3:df4319053bfa 199 // 1.25 usec * 1/3 = 417 nsec
Ned Konz 3:df4319053bfa 200 tpm->CONTROLS[0].CnV = tpm_p0_period;
Ned Konz 3:df4319053bfa 201
Ned Konz 3:df4319053bfa 202 // set TPM0 channel 1 for 0.7 usec (±150nsec) (1 code)
Ned Konz 3:df4319053bfa 203 // 1.25 usec * 2/3 = 833 nsec
Ned Konz 3:df4319053bfa 204 tpm->CONTROLS[1].CnV = tpm_p1_period;
Ned Konz 3:df4319053bfa 205 }
Ned Konz 3:df4319053bfa 206
Ned Konz 3:df4319053bfa 207 // class static
Ned Konz 3:df4319053bfa 208 template <unsigned MAX_LEDS_PER_STRIP>
Ned Konz 3:df4319053bfa 209 void WS2811<MAX_LEDS_PER_STRIP>::startDMA()
Ned Konz 3:df4319053bfa 210 {
Ned Konz 3:df4319053bfa 211 if (!initialized) hw_init();
Ned Konz 4:990838718b51 212
Ned Konz 3:df4319053bfa 213 wait_for_dma_done();
Ned Konz 3:df4319053bfa 214
Ned Konz 3:df4319053bfa 215 DMA_Type volatile * dma = DMA0;
Ned Konz 3:df4319053bfa 216 TPM_Type volatile *tpm = TPM0;
Ned Konz 3:df4319053bfa 217 uint32_t nBytes = sizeof(dmaData.start_t1_low)
Ned Konz 3:df4319053bfa 218 + sizeof(dmaData.dmaWords)
Ned Konz 3:df4319053bfa 219 + sizeof(dmaData.trailing_zeros_1);
Ned Konz 3:df4319053bfa 220
Ned Konz 3:df4319053bfa 221 tpm->SC = TPM_SC_DMA_MASK // enable DMA
Ned Konz 3:df4319053bfa 222 | TPM_SC_TOF_MASK // reset TOF flag if set
Ned Konz 3:df4319053bfa 223 | TPM_SC_CMOD(0) // disable clocks
Ned Konz 3:df4319053bfa 224 | TPM_SC_PS(0); // 48MHz / 1 = 48MHz clock
Ned Konz 3:df4319053bfa 225 tpm->MOD = tpm_period - 1; // 48MHz / 800kHz
Ned Konz 3:df4319053bfa 226
Ned Konz 3:df4319053bfa 227 tpm->CNT = tpm_p0_period - 2 ;
Ned Konz 3:df4319053bfa 228 tpm->STATUS = 0xFFFFFFFF;
Ned Konz 3:df4319053bfa 229
Ned Konz 3:df4319053bfa 230 dma->DMA[DMA_CHAN_START].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status
Ned Konz 3:df4319053bfa 231 dma->DMA[DMA_CHAN_0_LOW].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status
Ned Konz 3:df4319053bfa 232 dma->DMA[DMA_CHAN_1_LOW].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status
Ned Konz 3:df4319053bfa 233
Ned Konz 3:df4319053bfa 234 // t=0: all outputs go high
Ned Konz 3:df4319053bfa 235 // triggered by TPM0_Overflow
Ned Konz 3:df4319053bfa 236 // source is one word of 0 then 24 x 0xffffffff, then another 0 word
Ned Konz 3:df4319053bfa 237 dma->DMA[DMA_CHAN_START].SAR = (uint32_t)(void*)dmaData.start_t0_high;
Ned Konz 3:df4319053bfa 238 dma->DMA[DMA_CHAN_START].DSR_BCR = DMA_DSR_BCR_BCR_MASK & nBytes; // length of transfer in bytes
Ned Konz 3:df4319053bfa 239
Ned Konz 3:df4319053bfa 240 // t=tpm_p0_period: some outputs (the 0 bits) go low.
Ned Konz 3:df4319053bfa 241 // Triggered by TPM0_CH0
Ned Konz 3:df4319053bfa 242 // Start 2 words before the actual data to avoid garbage pulses.
Ned Konz 3:df4319053bfa 243 dma->DMA[DMA_CHAN_0_LOW].SAR = (uint32_t)(void*)dmaData.start_t1_low; // set source address
Ned Konz 3:df4319053bfa 244 dma->DMA[DMA_CHAN_0_LOW].DSR_BCR = DMA_DSR_BCR_BCR_MASK & nBytes; // length of transfer in bytes
Ned Konz 3:df4319053bfa 245
Ned Konz 3:df4319053bfa 246 // t=tpm_p1_period: all outputs go low.
Ned Konz 3:df4319053bfa 247 // Triggered by TPM0_CH1
Ned Konz 3:df4319053bfa 248 // source is constant 0x00000000 (first word of dmaWords)
Ned Konz 3:df4319053bfa 249 dma->DMA[DMA_CHAN_1_LOW].SAR = (uint32_t)(void*)dmaData.start_t1_low; // set source address
Ned Konz 3:df4319053bfa 250 dma->DMA[DMA_CHAN_1_LOW].DSR_BCR = DMA_DSR_BCR_BCR_MASK & nBytes; // length of transfer in bytes
Ned Konz 3:df4319053bfa 251
Ned Konz 3:df4319053bfa 252 dma->DMA[DMA_CHAN_0_LOW].DAR
Ned Konz 3:df4319053bfa 253 = dma->DMA[DMA_CHAN_1_LOW].DAR
Ned Konz 3:df4319053bfa 254 = dma->DMA[DMA_CHAN_START].DAR
Ned Konz 3:df4319053bfa 255 = (uint32_t)(void*)&WS2811_IO_GPIO->PDOR;
Ned Konz 3:df4319053bfa 256
Ned Konz 3:df4319053bfa 257 SET_DEBUG;
Ned Konz 3:df4319053bfa 258
Ned Konz 3:df4319053bfa 259 dma->DMA[DMA_CHAN_0_LOW].DCR = DMA_DCR_EINT_MASK // enable interrupt on end of transfer
Ned Konz 3:df4319053bfa 260 | DMA_DCR_ERQ_MASK
Ned Konz 3:df4319053bfa 261 | DMA_DCR_D_REQ_MASK // clear ERQ on end of transfer
Ned Konz 3:df4319053bfa 262 | DMA_DCR_SINC_MASK // increment source each transfer
Ned Konz 3:df4319053bfa 263 | DMA_DCR_CS_MASK
Ned Konz 3:df4319053bfa 264 | DMA_DCR_SSIZE(0) // 32-bit source transfers
Ned Konz 3:df4319053bfa 265 | DMA_DCR_DSIZE(0); // 32-bit destination transfers
Ned Konz 3:df4319053bfa 266
Ned Konz 3:df4319053bfa 267 dma->DMA[DMA_CHAN_1_LOW].DCR = DMA_DCR_EINT_MASK // enable interrupt on end of transfer
Ned Konz 3:df4319053bfa 268 | DMA_DCR_ERQ_MASK
Ned Konz 3:df4319053bfa 269 | DMA_DCR_D_REQ_MASK // clear ERQ on end of transfer
Ned Konz 3:df4319053bfa 270 | DMA_DCR_CS_MASK
Ned Konz 3:df4319053bfa 271 | DMA_DCR_SSIZE(0) // 32-bit source transfers
Ned Konz 3:df4319053bfa 272 | DMA_DCR_DSIZE(0); // 32-bit destination transfers
Ned Konz 3:df4319053bfa 273
Ned Konz 3:df4319053bfa 274 dma->DMA[DMA_CHAN_START].DCR = DMA_DCR_EINT_MASK // enable interrupt on end of transfer
Ned Konz 3:df4319053bfa 275 | DMA_DCR_ERQ_MASK
Ned Konz 3:df4319053bfa 276 | DMA_DCR_D_REQ_MASK // clear ERQ on end of transfer
Ned Konz 3:df4319053bfa 277 | DMA_DCR_SINC_MASK // increment source each transfer
Ned Konz 3:df4319053bfa 278 | DMA_DCR_CS_MASK
Ned Konz 3:df4319053bfa 279 | DMA_DCR_SSIZE(0) // 32-bit source transfers
Ned Konz 3:df4319053bfa 280 | DMA_DCR_DSIZE(0);
Ned Konz 3:df4319053bfa 281
Ned Konz 3:df4319053bfa 282 tpm->SC |= TPM_SC_CMOD(1); // enable internal clocking
Ned Konz 3:df4319053bfa 283 }
Ned Konz 3:df4319053bfa 284
Ned Konz 3:df4319053bfa 285 #if !INSTANTIATE_TEMPLATES
Ned Konz 3:df4319053bfa 286
Ned Konz 3:df4319053bfa 287 extern "C" void DMA0_IRQHandler()
Ned Konz 3:df4319053bfa 288 {
Ned Konz 3:df4319053bfa 289 DMA_Type volatile *dma = DMA0;
Ned Konz 3:df4319053bfa 290 TPM_Type volatile *tpm = TPM0;
Ned Konz 3:df4319053bfa 291
Ned Konz 3:df4319053bfa 292 uint32_t db;
Ned Konz 3:df4319053bfa 293
Ned Konz 3:df4319053bfa 294 db = dma->DMA[DMA_CHAN_0_LOW].DSR_BCR;
Ned Konz 3:df4319053bfa 295 if (db & DMA_DSR_BCR_DONE_MASK) {
Ned Konz 3:df4319053bfa 296 dma->DMA[DMA_CHAN_0_LOW].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status
Ned Konz 3:df4319053bfa 297 }
Ned Konz 3:df4319053bfa 298
Ned Konz 3:df4319053bfa 299 db = dma->DMA[DMA_CHAN_1_LOW].DSR_BCR;
Ned Konz 3:df4319053bfa 300 if (db & DMA_DSR_BCR_DONE_MASK) {
Ned Konz 3:df4319053bfa 301 dma->DMA[DMA_CHAN_1_LOW].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status
Ned Konz 3:df4319053bfa 302 }
Ned Konz 3:df4319053bfa 303
Ned Konz 3:df4319053bfa 304 db = dma->DMA[DMA_CHAN_START].DSR_BCR;
Ned Konz 3:df4319053bfa 305 if (db & DMA_DSR_BCR_DONE_MASK) {
Ned Konz 3:df4319053bfa 306 dma->DMA[DMA_CHAN_START].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status
Ned Konz 3:df4319053bfa 307 }
Ned Konz 3:df4319053bfa 308
Ned Konz 3:df4319053bfa 309 tpm->SC = TPM_SC_TOF_MASK; // reset TOF flag; disable internal clocking
Ned Konz 3:df4319053bfa 310
Ned Konz 3:df4319053bfa 311 SET_DEBUG;
Ned Konz 3:df4319053bfa 312 }
Ned Konz 3:df4319053bfa 313
Ned Konz 3:df4319053bfa 314 #endif