Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of Multi_WS2811 by
WS2811.cpp@1:39db2057b5da, 2014-12-09 (annotated)
- Committer:
- antoniorohit
- Date:
- Tue Dec 09 23:42:23 2014 +0000
- Revision:
- 1:39db2057b5da
- Parent:
- 0:a8535703f23b
Forked;
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
bikeNomad | 0:a8535703f23b | 1 | // 800 KHz WS2811 driver driving potentially many LED strings. |
bikeNomad | 0:a8535703f23b | 2 | // Uses 3-phase DMA |
bikeNomad | 0:a8535703f23b | 3 | // 16K SRAM less stack, etc. |
bikeNomad | 0:a8535703f23b | 4 | // |
bikeNomad | 0:a8535703f23b | 5 | // Per LED: 3 bytes (malloc'd) for RGB data |
bikeNomad | 0:a8535703f23b | 6 | // |
bikeNomad | 0:a8535703f23b | 7 | // Per LED strip / per LED |
bikeNomad | 0:a8535703f23b | 8 | // 96 bytes (static) for bit data |
bikeNomad | 0:a8535703f23b | 9 | // + 96 bytes (static) for ones data |
bikeNomad | 0:a8535703f23b | 10 | // = 192 bytes |
bikeNomad | 0:a8535703f23b | 11 | // |
bikeNomad | 0:a8535703f23b | 12 | // 40 LEDs max per string = 7680 bytes static |
bikeNomad | 0:a8535703f23b | 13 | // |
bikeNomad | 0:a8535703f23b | 14 | // 40 LEDs: 7680 + 40*3 = 7800 bytes |
bikeNomad | 0:a8535703f23b | 15 | // 80 LEDs: 7680 + 80*3 = 7920 bytes |
bikeNomad | 0:a8535703f23b | 16 | |
bikeNomad | 0:a8535703f23b | 17 | #include "MKL25Z4.h" |
bikeNomad | 0:a8535703f23b | 18 | #include "LedStrip.h" |
bikeNomad | 0:a8535703f23b | 19 | #include "WS2811.h" |
bikeNomad | 0:a8535703f23b | 20 | |
bikeNomad | 0:a8535703f23b | 21 | // |
bikeNomad | 0:a8535703f23b | 22 | // Configuration |
bikeNomad | 0:a8535703f23b | 23 | // |
bikeNomad | 0:a8535703f23b | 24 | |
bikeNomad | 0:a8535703f23b | 25 | // Define MONITOR_TPM0_PWM as non-zero to monitor PWM timing on PTD0 and PTD1 |
bikeNomad | 0:a8535703f23b | 26 | // PTD0 TPM0/CH0 PWM_1 J2/06 |
bikeNomad | 0:a8535703f23b | 27 | // PTD1 TPM0/CH1 PWM_2 J2/12 (also LED_BLUE) |
bikeNomad | 0:a8535703f23b | 28 | #define MONITOR_TPM0_PWM 0 |
bikeNomad | 0:a8535703f23b | 29 | |
bikeNomad | 0:a8535703f23b | 30 | // define DEBUG_PIN to identify a pin in PORTD used for debug output |
bikeNomad | 0:a8535703f23b | 31 | // #define DEBUG_PIN 4 /* PTD4 debugOut */ |
bikeNomad | 0:a8535703f23b | 32 | |
bikeNomad | 0:a8535703f23b | 33 | #ifdef DEBUG_PIN |
antoniorohit | 1:39db2057b5da | 34 | #define DEBUG 0 |
bikeNomad | 0:a8535703f23b | 35 | #endif |
bikeNomad | 0:a8535703f23b | 36 | |
bikeNomad | 0:a8535703f23b | 37 | #if DEBUG |
bikeNomad | 0:a8535703f23b | 38 | #define DEBUG_MASK (1<<DEBUG_PIN) |
bikeNomad | 0:a8535703f23b | 39 | #define RESET_DEBUG (IO_GPIO->PDOR &= ~DEBUG_MASK) |
bikeNomad | 0:a8535703f23b | 40 | #define SET_DEBUG (IO_GPIO->PDOR |= DEBUG_MASK) |
bikeNomad | 0:a8535703f23b | 41 | #else |
bikeNomad | 0:a8535703f23b | 42 | #define DEBUG_MASK 0 |
bikeNomad | 0:a8535703f23b | 43 | #define RESET_DEBUG (void)0 |
bikeNomad | 0:a8535703f23b | 44 | #define SET_DEBUG (void)0 |
bikeNomad | 0:a8535703f23b | 45 | #endif |
bikeNomad | 0:a8535703f23b | 46 | |
antoniorohit | 1:39db2057b5da | 47 | static PORT_Type volatile * const IO_PORT = PORTC; |
antoniorohit | 1:39db2057b5da | 48 | static GPIO_Type volatile * const IO_GPIO = PTC; |
bikeNomad | 0:a8535703f23b | 49 | |
bikeNomad | 0:a8535703f23b | 50 | // 48 MHz clock, no prescaling. |
bikeNomad | 0:a8535703f23b | 51 | #define NSEC_TO_TICKS(nsec) ((nsec)*48/1000) |
bikeNomad | 0:a8535703f23b | 52 | #define USEC_TO_TICKS(usec) ((usec)*48) |
bikeNomad | 0:a8535703f23b | 53 | static const uint32_t CLK_NSEC = 1250; |
bikeNomad | 0:a8535703f23b | 54 | static const uint32_t tpm_period = NSEC_TO_TICKS(CLK_NSEC); |
bikeNomad | 0:a8535703f23b | 55 | static const uint32_t tpm_p0_period = NSEC_TO_TICKS(250); |
bikeNomad | 0:a8535703f23b | 56 | static const uint32_t tpm_p1_period = NSEC_TO_TICKS(650); |
bikeNomad | 0:a8535703f23b | 57 | static const uint32_t guardtime_period = USEC_TO_TICKS(55); // guardtime minimum 50 usec. |
bikeNomad | 0:a8535703f23b | 58 | |
bikeNomad | 0:a8535703f23b | 59 | enum DMA_MUX_SRC { |
bikeNomad | 0:a8535703f23b | 60 | DMA_MUX_SRC_TPM0_CH_0 = 24, |
bikeNomad | 0:a8535703f23b | 61 | DMA_MUX_SRC_TPM0_CH_1, |
bikeNomad | 0:a8535703f23b | 62 | DMA_MUX_SRC_TPM0_Overflow = 54, |
bikeNomad | 0:a8535703f23b | 63 | }; |
bikeNomad | 0:a8535703f23b | 64 | |
bikeNomad | 0:a8535703f23b | 65 | enum DMA_CHAN { |
bikeNomad | 0:a8535703f23b | 66 | DMA_CHAN_START = 0, |
bikeNomad | 0:a8535703f23b | 67 | DMA_CHAN_0_LOW = 1, |
bikeNomad | 0:a8535703f23b | 68 | DMA_CHAN_1_LOW = 2, |
bikeNomad | 0:a8535703f23b | 69 | N_DMA_CHANNELS |
bikeNomad | 0:a8535703f23b | 70 | }; |
bikeNomad | 0:a8535703f23b | 71 | |
bikeNomad | 0:a8535703f23b | 72 | volatile bool WS2811::dma_done = true; |
bikeNomad | 0:a8535703f23b | 73 | |
bikeNomad | 0:a8535703f23b | 74 | // class static |
bikeNomad | 0:a8535703f23b | 75 | bool WS2811::initialized = false; |
bikeNomad | 0:a8535703f23b | 76 | |
bikeNomad | 0:a8535703f23b | 77 | // class static |
bikeNomad | 0:a8535703f23b | 78 | uint32_t WS2811::enabledPins = 0; |
bikeNomad | 0:a8535703f23b | 79 | |
bikeNomad | 0:a8535703f23b | 80 | #define WORD_ALIGNED __attribute__ ((aligned(4))) |
bikeNomad | 0:a8535703f23b | 81 | |
bikeNomad | 0:a8535703f23b | 82 | #define DMA_LEADING_ZEROS 2 |
bikeNomad | 0:a8535703f23b | 83 | #define BITS_PER_RGB 24 |
bikeNomad | 0:a8535703f23b | 84 | #define DMA_TRAILING_ZEROS 1 |
bikeNomad | 0:a8535703f23b | 85 | |
bikeNomad | 0:a8535703f23b | 86 | static struct { |
bikeNomad | 0:a8535703f23b | 87 | uint32_t start_t1_low[ DMA_LEADING_ZEROS ]; |
bikeNomad | 0:a8535703f23b | 88 | uint32_t dmaWords[ BITS_PER_RGB * MAX_LEDS_PER_STRIP ]; |
bikeNomad | 0:a8535703f23b | 89 | uint32_t trailing_zeros_1[ DMA_TRAILING_ZEROS ]; |
bikeNomad | 0:a8535703f23b | 90 | |
bikeNomad | 0:a8535703f23b | 91 | uint32_t start_t0_high[ DMA_LEADING_ZEROS - 1 ]; |
bikeNomad | 0:a8535703f23b | 92 | uint32_t allOnes[ BITS_PER_RGB * MAX_LEDS_PER_STRIP ]; |
bikeNomad | 0:a8535703f23b | 93 | uint32_t trailing_zeros_2[ DMA_TRAILING_ZEROS + 1 ]; |
bikeNomad | 0:a8535703f23b | 94 | } dmaData WORD_ALIGNED; |
bikeNomad | 0:a8535703f23b | 95 | |
bikeNomad | 0:a8535703f23b | 96 | // class static |
bikeNomad | 0:a8535703f23b | 97 | void WS2811::hw_init() |
bikeNomad | 0:a8535703f23b | 98 | { |
bikeNomad | 0:a8535703f23b | 99 | if (initialized) return; |
bikeNomad | 0:a8535703f23b | 100 | |
bikeNomad | 0:a8535703f23b | 101 | dma_data_init(); |
bikeNomad | 0:a8535703f23b | 102 | clock_init(); |
bikeNomad | 0:a8535703f23b | 103 | dma_init(); |
bikeNomad | 0:a8535703f23b | 104 | io_init(); |
bikeNomad | 0:a8535703f23b | 105 | tpm_init(); |
bikeNomad | 0:a8535703f23b | 106 | |
bikeNomad | 0:a8535703f23b | 107 | initialized = true; |
bikeNomad | 0:a8535703f23b | 108 | |
bikeNomad | 0:a8535703f23b | 109 | SET_DEBUG; |
bikeNomad | 0:a8535703f23b | 110 | RESET_DEBUG; |
bikeNomad | 0:a8535703f23b | 111 | } |
bikeNomad | 0:a8535703f23b | 112 | |
bikeNomad | 0:a8535703f23b | 113 | // class static |
bikeNomad | 0:a8535703f23b | 114 | void WS2811::dma_data_init() |
bikeNomad | 0:a8535703f23b | 115 | { |
bikeNomad | 0:a8535703f23b | 116 | memset(dmaData.allOnes, 0xFF, sizeof(dmaData.allOnes)); |
bikeNomad | 0:a8535703f23b | 117 | |
bikeNomad | 0:a8535703f23b | 118 | #if DEBUG |
bikeNomad | 0:a8535703f23b | 119 | for (unsigned i = 0; i < BITS_PER_RGB * MAX_LEDS_PER_STRIP; i++) |
bikeNomad | 0:a8535703f23b | 120 | dmaData.dmaWords[i] = DEBUG_MASK; |
bikeNomad | 0:a8535703f23b | 121 | #endif |
bikeNomad | 0:a8535703f23b | 122 | } |
bikeNomad | 0:a8535703f23b | 123 | |
bikeNomad | 0:a8535703f23b | 124 | // class static |
bikeNomad | 0:a8535703f23b | 125 | |
bikeNomad | 0:a8535703f23b | 126 | /// Enable PORTD, DMA and TPM0 clocking |
bikeNomad | 0:a8535703f23b | 127 | void WS2811::clock_init() |
bikeNomad | 0:a8535703f23b | 128 | { |
antoniorohit | 1:39db2057b5da | 129 | SIM->SCGC5 |= SIM_SCGC5_PORTC_MASK; |
bikeNomad | 0:a8535703f23b | 130 | SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK | SIM_SCGC6_TPM0_MASK; // Enable clock to DMA mux and TPM0 |
bikeNomad | 0:a8535703f23b | 131 | SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Enable clock to DMA |
bikeNomad | 0:a8535703f23b | 132 | |
bikeNomad | 0:a8535703f23b | 133 | SIM->SOPT2 |= SIM_SOPT2_TPMSRC(1); // Clock source: MCGFLLCLK or MCGPLLCLK |
bikeNomad | 0:a8535703f23b | 134 | } |
bikeNomad | 0:a8535703f23b | 135 | |
bikeNomad | 0:a8535703f23b | 136 | // class static |
bikeNomad | 0:a8535703f23b | 137 | |
bikeNomad | 0:a8535703f23b | 138 | /// Configure GPIO output pins |
bikeNomad | 0:a8535703f23b | 139 | void WS2811::io_init() |
bikeNomad | 0:a8535703f23b | 140 | { |
bikeNomad | 0:a8535703f23b | 141 | uint32_t m = 1; |
bikeNomad | 0:a8535703f23b | 142 | for (uint32_t i = 0; i < 32; i++) { |
bikeNomad | 0:a8535703f23b | 143 | // set up each pin |
bikeNomad | 0:a8535703f23b | 144 | if (m & enabledPins) { |
bikeNomad | 0:a8535703f23b | 145 | IO_PORT->PCR[i] = PORT_PCR_MUX(1) // GPIO |
bikeNomad | 0:a8535703f23b | 146 | | PORT_PCR_DSE_MASK; // high drive strength |
bikeNomad | 0:a8535703f23b | 147 | } |
bikeNomad | 0:a8535703f23b | 148 | m <<= 1; |
bikeNomad | 0:a8535703f23b | 149 | } |
bikeNomad | 0:a8535703f23b | 150 | |
bikeNomad | 0:a8535703f23b | 151 | IO_GPIO->PDDR |= enabledPins; // set as outputs |
bikeNomad | 0:a8535703f23b | 152 | |
bikeNomad | 0:a8535703f23b | 153 | #if MONITOR_TPM0_PWM |
bikeNomad | 0:a8535703f23b | 154 | // PTD0 CH0 monitor: TPM0, high drive strength |
bikeNomad | 0:a8535703f23b | 155 | IO_PORT->PCR[0] = PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; |
bikeNomad | 0:a8535703f23b | 156 | // PTD1 CH1 monitor: TPM0, high drive strength |
bikeNomad | 0:a8535703f23b | 157 | IO_PORT->PCR[1] = PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; |
bikeNomad | 0:a8535703f23b | 158 | IO_GPIO->PDDR |= 3; // set as outputs |
bikeNomad | 0:a8535703f23b | 159 | IO_GPIO->PDOR &= ~(enabledPins | 3); // initially low |
bikeNomad | 0:a8535703f23b | 160 | #else |
bikeNomad | 0:a8535703f23b | 161 | IO_GPIO->PDOR &= ~enabledPins; // initially low |
bikeNomad | 0:a8535703f23b | 162 | #endif |
bikeNomad | 0:a8535703f23b | 163 | |
bikeNomad | 0:a8535703f23b | 164 | #if DEBUG |
bikeNomad | 0:a8535703f23b | 165 | IO_PORT->PCR[DEBUG_PIN] = PORT_PCR_MUX(1) | PORT_PCR_DSE_MASK; |
bikeNomad | 0:a8535703f23b | 166 | IO_GPIO->PDDR |= DEBUG_MASK; |
bikeNomad | 0:a8535703f23b | 167 | IO_GPIO->PDOR &= ~DEBUG_MASK; |
bikeNomad | 0:a8535703f23b | 168 | #endif |
bikeNomad | 0:a8535703f23b | 169 | } |
bikeNomad | 0:a8535703f23b | 170 | |
bikeNomad | 0:a8535703f23b | 171 | // class static |
bikeNomad | 0:a8535703f23b | 172 | |
bikeNomad | 0:a8535703f23b | 173 | /// Configure DMA and DMAMUX |
bikeNomad | 0:a8535703f23b | 174 | void WS2811::dma_init() |
bikeNomad | 0:a8535703f23b | 175 | { |
bikeNomad | 0:a8535703f23b | 176 | // reset DMAMUX |
bikeNomad | 0:a8535703f23b | 177 | DMAMUX0->CHCFG[DMA_CHAN_START] = 0; |
bikeNomad | 0:a8535703f23b | 178 | DMAMUX0->CHCFG[DMA_CHAN_0_LOW] = 0; |
bikeNomad | 0:a8535703f23b | 179 | DMAMUX0->CHCFG[DMA_CHAN_1_LOW] = 0; |
bikeNomad | 0:a8535703f23b | 180 | |
bikeNomad | 0:a8535703f23b | 181 | // wire our DMA event sources into the first three DMA channels |
bikeNomad | 0:a8535703f23b | 182 | // t=0: all enabled outputs go high on TPM0 overflow |
bikeNomad | 0:a8535703f23b | 183 | DMAMUX0->CHCFG[DMA_CHAN_START] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMA_MUX_SRC_TPM0_Overflow); |
bikeNomad | 0:a8535703f23b | 184 | // t=tpm_p0_period: all of the 0 bits go low. |
bikeNomad | 0:a8535703f23b | 185 | DMAMUX0->CHCFG[DMA_CHAN_0_LOW] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMA_MUX_SRC_TPM0_CH_0); |
bikeNomad | 0:a8535703f23b | 186 | // t=tpm_p1_period: all outputs go low. |
bikeNomad | 0:a8535703f23b | 187 | DMAMUX0->CHCFG[DMA_CHAN_1_LOW] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMA_MUX_SRC_TPM0_CH_1); |
bikeNomad | 0:a8535703f23b | 188 | |
bikeNomad | 0:a8535703f23b | 189 | NVIC_SetVector(DMA0_IRQn, (uint32_t)&DMA0_IRQHandler); |
bikeNomad | 0:a8535703f23b | 190 | NVIC_EnableIRQ(DMA0_IRQn); |
bikeNomad | 0:a8535703f23b | 191 | } |
bikeNomad | 0:a8535703f23b | 192 | |
bikeNomad | 0:a8535703f23b | 193 | // class static |
bikeNomad | 0:a8535703f23b | 194 | |
bikeNomad | 0:a8535703f23b | 195 | /// Configure TPM0 to do two different PWM periods at 800kHz rate |
bikeNomad | 0:a8535703f23b | 196 | void WS2811::tpm_init() |
bikeNomad | 0:a8535703f23b | 197 | { |
bikeNomad | 0:a8535703f23b | 198 | // set up TPM0 for proper period (800 kHz = 1.25 usec ±600nsec) |
bikeNomad | 0:a8535703f23b | 199 | TPM_Type volatile *tpm = TPM0; |
bikeNomad | 0:a8535703f23b | 200 | tpm->SC = TPM_SC_DMA_MASK // enable DMA |
bikeNomad | 0:a8535703f23b | 201 | | TPM_SC_TOF_MASK // reset TOF flag if set |
bikeNomad | 0:a8535703f23b | 202 | | TPM_SC_CMOD(0) // disable clocks |
bikeNomad | 0:a8535703f23b | 203 | | TPM_SC_PS(0); // 48MHz / 1 = 48MHz clock |
bikeNomad | 0:a8535703f23b | 204 | tpm->MOD = tpm_period - 1; // 48MHz / 800kHz |
bikeNomad | 0:a8535703f23b | 205 | |
bikeNomad | 0:a8535703f23b | 206 | // No Interrupts; High True pulses on Edge Aligned PWM |
bikeNomad | 0:a8535703f23b | 207 | tpm->CONTROLS[0].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK | TPM_CnSC_DMA_MASK; |
bikeNomad | 0:a8535703f23b | 208 | tpm->CONTROLS[1].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK | TPM_CnSC_DMA_MASK; |
bikeNomad | 0:a8535703f23b | 209 | |
bikeNomad | 0:a8535703f23b | 210 | // set TPM0 channel 0 for 0.35 usec (±150nsec) (0 code) |
bikeNomad | 0:a8535703f23b | 211 | // 1.25 usec * 1/3 = 417 nsec |
bikeNomad | 0:a8535703f23b | 212 | tpm->CONTROLS[0].CnV = tpm_p0_period; |
bikeNomad | 0:a8535703f23b | 213 | |
bikeNomad | 0:a8535703f23b | 214 | // set TPM0 channel 1 for 0.7 usec (±150nsec) (1 code) |
bikeNomad | 0:a8535703f23b | 215 | // 1.25 usec * 2/3 = 833 nsec |
bikeNomad | 0:a8535703f23b | 216 | tpm->CONTROLS[1].CnV = tpm_p1_period; |
bikeNomad | 0:a8535703f23b | 217 | |
bikeNomad | 0:a8535703f23b | 218 | NVIC_SetVector(TPM0_IRQn, (uint32_t)&TPM0_IRQHandler); |
bikeNomad | 0:a8535703f23b | 219 | NVIC_EnableIRQ(TPM0_IRQn); |
bikeNomad | 0:a8535703f23b | 220 | } |
bikeNomad | 0:a8535703f23b | 221 | |
bikeNomad | 0:a8535703f23b | 222 | WS2811::WS2811(unsigned n, unsigned pinNumber) |
bikeNomad | 0:a8535703f23b | 223 | : LedStrip(n) |
bikeNomad | 0:a8535703f23b | 224 | , pinMask(1U << pinNumber) |
bikeNomad | 0:a8535703f23b | 225 | { |
bikeNomad | 0:a8535703f23b | 226 | enabledPins |= pinMask; |
bikeNomad | 0:a8535703f23b | 227 | initialized = false; |
bikeNomad | 0:a8535703f23b | 228 | } |
bikeNomad | 0:a8535703f23b | 229 | |
bikeNomad | 0:a8535703f23b | 230 | // class static |
bikeNomad | 0:a8535703f23b | 231 | void WS2811::startDMA() |
bikeNomad | 0:a8535703f23b | 232 | { |
bikeNomad | 0:a8535703f23b | 233 | hw_init(); |
bikeNomad | 0:a8535703f23b | 234 | |
bikeNomad | 0:a8535703f23b | 235 | wait_for_dma_done(); |
bikeNomad | 0:a8535703f23b | 236 | dma_done = false; |
bikeNomad | 0:a8535703f23b | 237 | |
bikeNomad | 0:a8535703f23b | 238 | DMA_Type volatile * dma = DMA0; |
bikeNomad | 0:a8535703f23b | 239 | TPM_Type volatile *tpm = TPM0; |
bikeNomad | 0:a8535703f23b | 240 | uint32_t nBytes = sizeof(dmaData.start_t1_low) |
bikeNomad | 0:a8535703f23b | 241 | + sizeof(dmaData.dmaWords) |
bikeNomad | 0:a8535703f23b | 242 | + sizeof(dmaData.trailing_zeros_1); |
bikeNomad | 0:a8535703f23b | 243 | |
bikeNomad | 0:a8535703f23b | 244 | tpm->SC = TPM_SC_DMA_MASK // enable DMA |
bikeNomad | 0:a8535703f23b | 245 | | TPM_SC_TOF_MASK // reset TOF flag if set |
bikeNomad | 0:a8535703f23b | 246 | | TPM_SC_CMOD(0) // disable clocks |
bikeNomad | 0:a8535703f23b | 247 | | TPM_SC_PS(0); // 48MHz / 1 = 48MHz clock |
bikeNomad | 0:a8535703f23b | 248 | tpm->MOD = tpm_period - 1; // 48MHz / 800kHz |
bikeNomad | 0:a8535703f23b | 249 | |
bikeNomad | 0:a8535703f23b | 250 | tpm->CNT = tpm_p0_period - 2 ; |
bikeNomad | 0:a8535703f23b | 251 | tpm->STATUS = 0xFFFFFFFF; |
bikeNomad | 0:a8535703f23b | 252 | |
bikeNomad | 0:a8535703f23b | 253 | dma->DMA[DMA_CHAN_START].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status |
bikeNomad | 0:a8535703f23b | 254 | dma->DMA[DMA_CHAN_0_LOW].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status |
bikeNomad | 0:a8535703f23b | 255 | dma->DMA[DMA_CHAN_1_LOW].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status |
bikeNomad | 0:a8535703f23b | 256 | |
bikeNomad | 0:a8535703f23b | 257 | // t=0: all outputs go high |
bikeNomad | 0:a8535703f23b | 258 | // triggered by TPM0_Overflow |
bikeNomad | 0:a8535703f23b | 259 | // source is one word of 0 then 24 x 0xffffffff, then another 0 word |
bikeNomad | 0:a8535703f23b | 260 | dma->DMA[DMA_CHAN_START].SAR = (uint32_t)(void*)dmaData.start_t0_high; |
bikeNomad | 0:a8535703f23b | 261 | dma->DMA[DMA_CHAN_START].DSR_BCR = DMA_DSR_BCR_BCR_MASK & nBytes; // length of transfer in bytes |
bikeNomad | 0:a8535703f23b | 262 | |
bikeNomad | 0:a8535703f23b | 263 | // t=tpm_p0_period: some outputs (the 0 bits) go low. |
bikeNomad | 0:a8535703f23b | 264 | // Triggered by TPM0_CH0 |
bikeNomad | 0:a8535703f23b | 265 | // Start 2 words before the actual data to avoid garbage pulses. |
bikeNomad | 0:a8535703f23b | 266 | dma->DMA[DMA_CHAN_0_LOW].SAR = (uint32_t)(void*)dmaData.start_t1_low; // set source address |
bikeNomad | 0:a8535703f23b | 267 | dma->DMA[DMA_CHAN_0_LOW].DSR_BCR = DMA_DSR_BCR_BCR_MASK & nBytes; // length of transfer in bytes |
bikeNomad | 0:a8535703f23b | 268 | |
bikeNomad | 0:a8535703f23b | 269 | // t=tpm_p1_period: all outputs go low. |
bikeNomad | 0:a8535703f23b | 270 | // Triggered by TPM0_CH1 |
bikeNomad | 0:a8535703f23b | 271 | // source is constant 0x00000000 (first word of dmaWords) |
bikeNomad | 0:a8535703f23b | 272 | dma->DMA[DMA_CHAN_1_LOW].SAR = (uint32_t)(void*)dmaData.start_t1_low; // set source address |
bikeNomad | 0:a8535703f23b | 273 | dma->DMA[DMA_CHAN_1_LOW].DSR_BCR = DMA_DSR_BCR_BCR_MASK & nBytes; // length of transfer in bytes |
bikeNomad | 0:a8535703f23b | 274 | |
bikeNomad | 0:a8535703f23b | 275 | dma->DMA[DMA_CHAN_0_LOW].DAR |
bikeNomad | 0:a8535703f23b | 276 | = dma->DMA[DMA_CHAN_1_LOW].DAR |
bikeNomad | 0:a8535703f23b | 277 | = dma->DMA[DMA_CHAN_START].DAR |
bikeNomad | 0:a8535703f23b | 278 | = (uint32_t)(void*)&IO_GPIO->PDOR; |
bikeNomad | 0:a8535703f23b | 279 | |
bikeNomad | 0:a8535703f23b | 280 | SET_DEBUG; |
bikeNomad | 0:a8535703f23b | 281 | |
bikeNomad | 0:a8535703f23b | 282 | dma->DMA[DMA_CHAN_0_LOW].DCR = DMA_DCR_EINT_MASK // enable interrupt on end of transfer |
bikeNomad | 0:a8535703f23b | 283 | | DMA_DCR_ERQ_MASK |
bikeNomad | 0:a8535703f23b | 284 | | DMA_DCR_D_REQ_MASK // clear ERQ on end of transfer |
bikeNomad | 0:a8535703f23b | 285 | | DMA_DCR_SINC_MASK // increment source each transfer |
bikeNomad | 0:a8535703f23b | 286 | | DMA_DCR_CS_MASK |
bikeNomad | 0:a8535703f23b | 287 | | DMA_DCR_SSIZE(0) // 32-bit source transfers |
bikeNomad | 0:a8535703f23b | 288 | | DMA_DCR_DSIZE(0); // 32-bit destination transfers |
bikeNomad | 0:a8535703f23b | 289 | |
bikeNomad | 0:a8535703f23b | 290 | dma->DMA[DMA_CHAN_1_LOW].DCR = DMA_DCR_EINT_MASK // enable interrupt on end of transfer |
bikeNomad | 0:a8535703f23b | 291 | | DMA_DCR_ERQ_MASK |
bikeNomad | 0:a8535703f23b | 292 | | DMA_DCR_D_REQ_MASK // clear ERQ on end of transfer |
bikeNomad | 0:a8535703f23b | 293 | | DMA_DCR_CS_MASK |
bikeNomad | 0:a8535703f23b | 294 | | DMA_DCR_SSIZE(0) // 32-bit source transfers |
bikeNomad | 0:a8535703f23b | 295 | | DMA_DCR_DSIZE(0); // 32-bit destination transfers |
bikeNomad | 0:a8535703f23b | 296 | |
bikeNomad | 0:a8535703f23b | 297 | dma->DMA[DMA_CHAN_START].DCR = DMA_DCR_EINT_MASK // enable interrupt on end of transfer |
bikeNomad | 0:a8535703f23b | 298 | | DMA_DCR_ERQ_MASK |
bikeNomad | 0:a8535703f23b | 299 | | DMA_DCR_D_REQ_MASK // clear ERQ on end of transfer |
bikeNomad | 0:a8535703f23b | 300 | | DMA_DCR_SINC_MASK // increment source each transfer |
bikeNomad | 0:a8535703f23b | 301 | | DMA_DCR_CS_MASK |
bikeNomad | 0:a8535703f23b | 302 | | DMA_DCR_SSIZE(0) // 32-bit source transfers |
bikeNomad | 0:a8535703f23b | 303 | | DMA_DCR_DSIZE(0); |
bikeNomad | 0:a8535703f23b | 304 | |
bikeNomad | 0:a8535703f23b | 305 | tpm->SC |= TPM_SC_CMOD(1); // enable internal clocking |
bikeNomad | 0:a8535703f23b | 306 | } |
bikeNomad | 0:a8535703f23b | 307 | |
bikeNomad | 0:a8535703f23b | 308 | void WS2811::writePixel(unsigned n, uint8_t *p) |
bikeNomad | 0:a8535703f23b | 309 | { |
bikeNomad | 0:a8535703f23b | 310 | uint32_t *dest = dmaData.dmaWords + n * BITS_PER_RGB; |
bikeNomad | 0:a8535703f23b | 311 | writeByte(*p++, pinMask, dest + 0); // G |
bikeNomad | 0:a8535703f23b | 312 | writeByte(*p++, pinMask, dest + 8); // R |
bikeNomad | 0:a8535703f23b | 313 | writeByte(*p, pinMask, dest + 16); // B |
bikeNomad | 0:a8535703f23b | 314 | } |
bikeNomad | 0:a8535703f23b | 315 | |
bikeNomad | 0:a8535703f23b | 316 | // class static |
bikeNomad | 0:a8535703f23b | 317 | void WS2811::writeByte(uint8_t byte, uint32_t mask, uint32_t *dest) |
bikeNomad | 0:a8535703f23b | 318 | { |
bikeNomad | 0:a8535703f23b | 319 | for (uint8_t bm = 0x80; bm; bm >>= 1) { |
bikeNomad | 0:a8535703f23b | 320 | // MSBit first |
bikeNomad | 0:a8535703f23b | 321 | if (byte & bm) |
bikeNomad | 0:a8535703f23b | 322 | *dest |= mask; |
bikeNomad | 0:a8535703f23b | 323 | else |
bikeNomad | 0:a8535703f23b | 324 | *dest &= ~mask; |
bikeNomad | 0:a8535703f23b | 325 | dest++; |
bikeNomad | 0:a8535703f23b | 326 | } |
bikeNomad | 0:a8535703f23b | 327 | } |
bikeNomad | 0:a8535703f23b | 328 | |
bikeNomad | 0:a8535703f23b | 329 | void WS2811::begin() |
bikeNomad | 0:a8535703f23b | 330 | { |
bikeNomad | 0:a8535703f23b | 331 | blank(); |
bikeNomad | 0:a8535703f23b | 332 | show(); |
bikeNomad | 0:a8535703f23b | 333 | } |
bikeNomad | 0:a8535703f23b | 334 | |
bikeNomad | 0:a8535703f23b | 335 | void WS2811::blank() |
bikeNomad | 0:a8535703f23b | 336 | { |
bikeNomad | 0:a8535703f23b | 337 | memset(pixels, 0x00, numPixelBytes()); |
bikeNomad | 0:a8535703f23b | 338 | |
bikeNomad | 0:a8535703f23b | 339 | #if DEBUG |
bikeNomad | 0:a8535703f23b | 340 | for (unsigned i = DMA_LEADING_ZEROS; i < DMA_LEADING_ZEROS + BITS_PER_RGB; i++) |
bikeNomad | 0:a8535703f23b | 341 | dmaData.dmaWords[i] = DEBUG_MASK; |
bikeNomad | 0:a8535703f23b | 342 | #else |
bikeNomad | 0:a8535703f23b | 343 | memset(dmaData.dmaWords, 0x00, sizeof(dmaData.dmaWords)); |
bikeNomad | 0:a8535703f23b | 344 | #endif |
bikeNomad | 0:a8535703f23b | 345 | } |
bikeNomad | 0:a8535703f23b | 346 | |
bikeNomad | 0:a8535703f23b | 347 | void WS2811::show() |
bikeNomad | 0:a8535703f23b | 348 | { |
bikeNomad | 0:a8535703f23b | 349 | |
bikeNomad | 0:a8535703f23b | 350 | uint16_t i, n = numPixels(); // 3 bytes per LED |
bikeNomad | 0:a8535703f23b | 351 | uint8_t *p = pixels; |
bikeNomad | 0:a8535703f23b | 352 | |
bikeNomad | 0:a8535703f23b | 353 | for (i=0; i<n; i++ ) { |
bikeNomad | 0:a8535703f23b | 354 | writePixel(i, p); |
bikeNomad | 0:a8535703f23b | 355 | p += 3; |
bikeNomad | 0:a8535703f23b | 356 | } |
bikeNomad | 0:a8535703f23b | 357 | } |
bikeNomad | 0:a8535703f23b | 358 | |
bikeNomad | 0:a8535703f23b | 359 | extern "C" void DMA0_IRQHandler() |
bikeNomad | 0:a8535703f23b | 360 | { |
bikeNomad | 0:a8535703f23b | 361 | DMA_Type volatile *dma = DMA0; |
bikeNomad | 0:a8535703f23b | 362 | TPM_Type volatile *tpm = TPM0; |
bikeNomad | 0:a8535703f23b | 363 | |
bikeNomad | 0:a8535703f23b | 364 | uint32_t db; |
bikeNomad | 0:a8535703f23b | 365 | |
bikeNomad | 0:a8535703f23b | 366 | db = dma->DMA[DMA_CHAN_0_LOW].DSR_BCR; |
bikeNomad | 0:a8535703f23b | 367 | if (db & DMA_DSR_BCR_DONE_MASK) { |
bikeNomad | 0:a8535703f23b | 368 | dma->DMA[DMA_CHAN_0_LOW].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status |
bikeNomad | 0:a8535703f23b | 369 | } |
bikeNomad | 0:a8535703f23b | 370 | |
bikeNomad | 0:a8535703f23b | 371 | db = dma->DMA[DMA_CHAN_1_LOW].DSR_BCR; |
bikeNomad | 0:a8535703f23b | 372 | if (db & DMA_DSR_BCR_DONE_MASK) { |
bikeNomad | 0:a8535703f23b | 373 | dma->DMA[DMA_CHAN_1_LOW].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status |
bikeNomad | 0:a8535703f23b | 374 | } |
bikeNomad | 0:a8535703f23b | 375 | |
bikeNomad | 0:a8535703f23b | 376 | db = dma->DMA[DMA_CHAN_START].DSR_BCR; |
bikeNomad | 0:a8535703f23b | 377 | if (db & DMA_DSR_BCR_DONE_MASK) { |
bikeNomad | 0:a8535703f23b | 378 | dma->DMA[DMA_CHAN_START].DSR_BCR = DMA_DSR_BCR_DONE_MASK; // clear/reset DMA status |
bikeNomad | 0:a8535703f23b | 379 | } |
bikeNomad | 0:a8535703f23b | 380 | |
bikeNomad | 0:a8535703f23b | 381 | tpm->SC = TPM_SC_TOF_MASK; // reset TOF flag; disable internal clocking |
bikeNomad | 0:a8535703f23b | 382 | |
bikeNomad | 0:a8535703f23b | 383 | SET_DEBUG; |
bikeNomad | 0:a8535703f23b | 384 | |
bikeNomad | 0:a8535703f23b | 385 | // set TPM0 to interrrupt after guardtime |
bikeNomad | 0:a8535703f23b | 386 | tpm->MOD = guardtime_period - 1; // 48MHz * 55 usec |
bikeNomad | 0:a8535703f23b | 387 | tpm->CNT = 0; |
bikeNomad | 0:a8535703f23b | 388 | tpm->SC = TPM_SC_PS(0) // 48MHz / 1 = 48MHz clock |
bikeNomad | 0:a8535703f23b | 389 | | TPM_SC_TOIE_MASK // enable interrupts |
bikeNomad | 0:a8535703f23b | 390 | | TPM_SC_CMOD(1); // and internal clocking |
bikeNomad | 0:a8535703f23b | 391 | } |
bikeNomad | 0:a8535703f23b | 392 | |
bikeNomad | 0:a8535703f23b | 393 | extern "C" void TPM0_IRQHandler() |
bikeNomad | 0:a8535703f23b | 394 | { |
bikeNomad | 0:a8535703f23b | 395 | TPM0->SC = 0; // disable internal clocking |
bikeNomad | 0:a8535703f23b | 396 | TPM0->SC = TPM_SC_TOF_MASK; |
bikeNomad | 0:a8535703f23b | 397 | RESET_DEBUG; |
bikeNomad | 0:a8535703f23b | 398 | WS2811::dma_done = true; |
bikeNomad | 0:a8535703f23b | 399 | } |