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:
Fri Jun 12 18:23:03 2015 -0700
Revision:
3:df4319053bfa
Parent:
2:9447404f2d16
Child:
4:990838718b51
Trying to get dma_done to work

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