crees
Well-known member
First the octows2811 is brilliant way to generate the ws2811 protocol! I want to really understand every bit of how this code does it job.
I have made some comments to try to break this up and looking for corrections on my assumptions. I have placed ////////////// marks for comments on the code. More questions to follow below.
Code below is portions of Octows2811 key points I am focusing on. Please see the actual library for full code.
If I understand this correctly we are basically setting the values to generate two kinds of wave forms to satisfy a 0 and 1 in a synthesized NRZ. We use DMA to process the buffer values (0 or 1) to the 8 Pins. DMA1 sets the bits high (all 1's?). DMA 2 triggers the actual pixel data buffer, 0's pull the wave form low and 1's keep it high until DMA 3 brings them all low (0 ??)
The question I have is in this code snippet and how its done.
on dma3.source(ones).... Is this all FF values (1's)??? this is my confusion. I would think we should be all 0's Or is setting the DMA the same value do the opposite???
I have made some comments to try to break this up and looking for corrections on my assumptions. I have placed ////////////// marks for comments on the code. More questions to follow below.
Code below is portions of Octows2811 key points I am focusing on. Please see the actual library for full code.
Code:
////////// Set ones as all 1's 11111111
static uint8_t ones = 0xFF;
//////////// Set PWM TIMING Values
#define WS2811_TIMING_T0H 60
#define WS2811_TIMING_T1H 176
......
//////////// SET UP TIMER based off of MCU TYPE
#if defined(__MK20DX128__)
FTM1_SC = 0;
FTM1_CNT = 0;
uint32_t mod = (F_BUS + frequency / 2) / frequency;
FTM1_MOD = mod - 1;
FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0);
FTM1_C0SC = 0x69;
FTM1_C1SC = 0x69;
FTM1_C0V = (mod * WS2811_TIMING_T0H) >> 8;
FTM1_C1V = (mod * WS2811_TIMING_T1H) >> 8;
// pin 16 triggers DMA(port B) on rising edge
CORE_PIN16_CONFIG = PORT_PCR_IRQC(1)|PORT_PCR_MUX(3);
//CORE_PIN4_CONFIG = PORT_PCR_MUX(3); // testing only
#elif defined(__MK20DX256__)
FTM2_SC = 0;
FTM2_CNT = 0;
uint32_t mod = (F_BUS + frequency / 2) / frequency;
FTM2_MOD = mod - 1;
FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0);
FTM2_C0SC = 0x69;
FTM2_C1SC = 0x69;
FTM2_C0V = (mod * WS2811_TIMING_T0H) >> 8;
FTM2_C1V = (mod * WS2811_TIMING_T1H) >> 8;
// pin 32 is FTM2_CH0, PTB18, triggers DMA(port B) on rising edge
// pin 25 is FTM2_CH1, PTB19
CORE_PIN32_CONFIG = PORT_PCR_IRQC(1)|PORT_PCR_MUX(3);
//CORE_PIN25_CONFIG = PORT_PCR_MUX(3); // testing only
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
FTM2_SC = 0;
FTM2_CNT = 0;
uint32_t mod = (F_BUS + frequency / 2) / frequency;
FTM2_MOD = mod - 1;
FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0);
FTM2_C0SC = 0x69;
FTM2_C1SC = 0x69;
FTM2_C0V = (mod * WS2811_TIMING_T0H) >> 8;
FTM2_C1V = (mod * WS2811_TIMING_T1H) >> 8;
// FTM2_CH0, PTA10 (not connected), triggers DMA(port A) on rising edge
PORTA_PCR10 = PORT_PCR_IRQC(1)|PORT_PCR_MUX(3);
#elif defined(__MKL26Z64__)
FTM2_SC = 0;
FTM2_CNT = 0;
uint32_t mod = F_CPU / frequency;
FTM2_MOD = mod - 1;
FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0);
FTM2_C0SC = FTM_CSC_CHF | FTM_CSC_MSB | FTM_CSC_ELSB;
FTM2_C1SC = FTM_CSC_CHF | FTM_CSC_MSB | FTM_CSC_ELSB;
TPM2_C0V = mod - ((mod * WS2811_TIMING_T1H) >> 8);
TPM2_C1V = mod - ((mod * WS2811_TIMING_T1H) >> 8) + ((mod * WS2811_TIMING_T0H) >> 8);
#endif
////////////// PREPARE DMA 1, 2, 3 VALUES
// DMA channel #1 sets WS2811 high at the beginning of each cycle
dma1.source(ones);
dma1.destination(GPIOD_PSOR);
dma1.transferSize(1);
dma1.transferCount(bufsize);
dma1.disableOnCompletion();
// DMA channel #2 writes the pixel data at 23% of the cycle
dma2.sourceBuffer((uint8_t *)frameBuffer, bufsize);
dma2.destination(GPIOD_PDOR);
dma2.transferSize(1);
dma2.transferCount(bufsize);
dma2.disableOnCompletion();
// DMA channel #3 clear all the pins low at 69% of the cycle
dma3.source(ones);
dma3.destination(GPIOD_PCOR);
dma3.transferSize(1);
dma3.transferCount(bufsize);
dma3.disableOnCompletion();
dma3.interruptAtCompletion();
///////// DEFINE TRIGGERS FROM PORTB=DMA1 High Bit 1? AND TIMERS FOR THE DMA 2 3 CHANNELS? DEPENDING ON CHIP TYPE
#if defined(__MK20DX128__)
// route the edge detect interrupts to trigger the 3 channels
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PORTB);
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM1_CH0);
dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM1_CH1);
DMAPriorityOrder(dma3, dma2, dma1);
#elif defined(__MK20DX256__)
// route the edge detect interrupts to trigger the 3 channels
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PORTB);
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH0);
dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH1);
DMAPriorityOrder(dma3, dma2, dma1);
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
// route the edge detect interrupts to trigger the 3 channels
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PORTA);
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH0);
dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_CH1);
DMAPriorityOrder(dma3, dma2, dma1);
#elif defined(__MKL26Z64__)
// route the timer interrupts to trigger the 3 channels
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_TPM2_CH0);
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_TPM2_CH1);
dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM2_OV);
#endif
// enable a done interrupts when channel #3 completes
dma3.attachInterrupt(isr);
//pinMode(9, OUTPUT); // testing: oscilloscope trigger
}
void OctoWS2811::isr(void)
{
//digitalWriteFast(9, HIGH);
//Serial1.print(".");
//Serial1.println(dma3.CFG->DCR, HEX);
//Serial1.print(dma3.CFG->DSR_BCR > 24, HEX);
dma3.clearInterrupt();
#if defined(__MKL26Z64__)
GPIOD_PCOR = 0xFF;
#endif
//Serial1.print("*");
update_completed_at = micros();
update_in_progress = 0;
//digitalWriteFast(9, LOW);
}
int OctoWS2811::busy(void)
{
if (update_in_progress) return 1;
// busy for 50 (or 300 for ws2813) us after the done interrupt, for WS2811 reset
if (micros() - update_completed_at < 300) return 1;
return 0;
}
void OctoWS2811::show(void)
{
// wait for any prior DMA operation
//Serial1.print("1");
while (update_in_progress) ;
//Serial1.print("2");
// it's ok to copy the drawing buffer to the frame buffer
// during the 50us WS2811 reset time
if (drawBuffer != frameBuffer) {
// TODO: this could be faster with DMA, especially if the
// buffers are 32 bit aligned... but does it matter?
memcpy(frameBuffer, drawBuffer, stripLen * 24);
}
// wait for WS2811 reset
while (micros() - update_completed_at < 300) ;
// ok to start, but we must be very careful to begin
// without any prior 3 x 800kHz DMA requests pending
/////////// DEPENDING ON CHIP TYPE SET THE REGISTERS APPROPRIATELY AND SEND DMA VALUES (DMA x ENABLE?)
#if defined(__MK20DX128__)
uint32_t cv = FTM1_C0V;
noInterrupts();
// CAUTION: this code is timing critical.
while (FTM1_CNT <= cv) ;
while (FTM1_CNT > cv) ; // wait for beginning of an 800 kHz cycle
while (FTM1_CNT < cv) ;
FTM1_SC = 0; // stop FTM1 timer (hopefully before it rolls over)
FTM1_CNT = 0;
update_in_progress = 1;
//digitalWriteFast(9, HIGH); // oscilloscope trigger
PORTB_ISFR = (1<<0); // clear any prior rising edge
uint32_t tmp __attribute__((unused));
FTM1_C0SC = 0x28;
tmp = FTM1_C0SC; // clear any prior timer DMA triggers
FTM1_C0SC = 0x69;
FTM1_C1SC = 0x28;
tmp = FTM1_C1SC;
FTM1_C1SC = 0x69;
dma1.enable();
dma2.enable(); // enable all 3 DMA channels
dma3.enable();
FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // restart FTM1 timer
//digitalWriteFast(9, LOW);
#elif defined(__MK20DX256__)
FTM2_C0SC = 0x28;
FTM2_C1SC = 0x28;
uint32_t cv = FTM2_C0V;
noInterrupts();
// CAUTION: this code is timing critical.
while (FTM2_CNT <= cv) ;
while (FTM2_CNT > cv) ; // wait for beginning of an 800 kHz cycle
while (FTM2_CNT < cv) ;
FTM2_SC = 0; // stop FTM2 timer (hopefully before it rolls over)
FTM2_CNT = 0;
update_in_progress = 1;
//digitalWriteFast(9, HIGH); // oscilloscope trigger
PORTB_ISFR = (1<<18); // clear any prior rising edge
uint32_t tmp __attribute__((unused));
FTM2_C0SC = 0x28;
tmp = FTM2_C0SC; // clear any prior timer DMA triggers
FTM2_C0SC = 0x69;
FTM2_C1SC = 0x28;
tmp = FTM2_C1SC;
FTM2_C1SC = 0x69;
dma1.enable();
dma2.enable(); // enable all 3 DMA channels
dma3.enable();
FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // restart FTM2 timer
//digitalWriteFast(9, LOW);
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
FTM2_C0SC = 0x28;
FTM2_C1SC = 0x28;
uint32_t cv = FTM2_C1V;
noInterrupts();
// CAUTION: this code is timing critical.
while (FTM2_CNT <= cv) ;
while (FTM2_CNT > cv) ; // wait for beginning of an 800 kHz cycle
while (FTM2_CNT < cv) ;
FTM2_SC = 0; // stop FTM2 timer (hopefully before it rolls over)
FTM2_CNT = 0;
update_in_progress = 1;
//digitalWriteFast(9, HIGH); // oscilloscope trigger
#if defined(__MK64FX512__)
asm("nop");
#endif
PORTA_ISFR = (1<<10); // clear any prior rising edge
uint32_t tmp __attribute__((unused));
FTM2_C0SC = 0x28;
tmp = FTM2_C0SC; // clear any prior timer DMA triggers
FTM2_C0SC = 0x69;
FTM2_C1SC = 0x28;
tmp = FTM2_C1SC;
FTM2_C1SC = 0x69;
dma1.enable();
dma2.enable(); // enable all 3 DMA channels
dma3.enable();
FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); // restart FTM2 timer
//digitalWriteFast(9, LOW);
#elif defined(__MKL26Z64__)
uint32_t sc __attribute__((unused)) = FTM2_SC;
uint32_t cv = FTM2_C1V;
noInterrupts();
while (FTM2_CNT <= cv) ;
while (FTM2_CNT > cv) ; // wait for beginning of an 800 kHz cycle
while (FTM2_CNT < cv) ;
FTM2_SC = 0; // stop FTM2 timer (hopefully before it rolls over)
update_in_progress = 1;
//digitalWriteFast(9, HIGH); // oscilloscope trigger
dma1.clearComplete();
dma2.clearComplete();
dma3.clearComplete();
uint32_t bufsize = stripLen*24;
dma1.transferCount(bufsize);
dma2.transferCount(bufsize);
dma3.transferCount(bufsize);
dma2.sourceBuffer((uint8_t *)frameBuffer, bufsize);
// clear any pending event flags
FTM2_SC = FTM_SC_TOF;
FTM2_C0SC = FTM_CSC_CHF | FTM_CSC_MSB | FTM_CSC_ELSB | FTM_CSC_DMA;
FTM2_C1SC = FTM_CSC_CHF | FTM_CSC_MSB | FTM_CSC_ELSB | FTM_CSC_DMA;
// clear any prior pending DMA requests
dma1.enable();
dma2.enable(); // enable all 3 DMA channels
dma3.enable();
FTM2_CNT = 0; // writing any value resets counter
FTM2_SC = FTM_SC_DMA | FTM_SC_CLKS(1) | FTM_SC_PS(0);
//digitalWriteFast(9, LOW);
#endif
//Serial1.print("3");
interrupts();
//Serial1.print("4");
}
If I understand this correctly we are basically setting the values to generate two kinds of wave forms to satisfy a 0 and 1 in a synthesized NRZ. We use DMA to process the buffer values (0 or 1) to the 8 Pins. DMA1 sets the bits high (all 1's?). DMA 2 triggers the actual pixel data buffer, 0's pull the wave form low and 1's keep it high until DMA 3 brings them all low (0 ??)
The question I have is in this code snippet and how its done.
Code:
// DMA channel #3 clear all the pins low at 69% of the cycle
dma3.source(ones); ///Is this all FF values (1's)??? this is my confusion. I would think we should be all 0's Or is setting the DMA the same value do the opposite???
dma3.destination(GPIOD_PCOR);
dma3.transferSize(1);
dma3.transferCount(bufsize);
dma3.disableOnCompletion();
dma3.interruptAtCompletion();
on dma3.source(ones).... Is this all FF values (1's)??? this is my confusion. I would think we should be all 0's Or is setting the DMA the same value do the opposite???