From ffc2ec1d028a7244a79ae8667fcd09a02a58d9ed Mon Sep 17 00:00:00 2001 From: Phillip Burgess Date: Fri, 13 Mar 2020 17:58:57 -0700 Subject: [PATCH] Handle word & long PORT bit arrangements, parallel matrices --- arch.h | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- core.c | 14 ++-- 2 files changed, 204 insertions(+), 9 deletions(-) diff --git a/arch.h b/arch.h index 8df03ef..5823092 100644 --- a/arch.h +++ b/arch.h @@ -585,7 +585,7 @@ void _PM_convert_565_byte(Protomatter_core *core, uint16_t *source, for(uint16_t x=0; xnumRowPairs; // " bottom half + uint16_t *pinMask = (uint16_t *)core->rgbMask; // Pin bitmasks + uint16_t *dest = (uint16_t *)core->screenData; + if(core->doubleBuffer) { + dest += core->bufferSize / core->bytesPerElement * + (1 - core->activeBuffer); + } + + uint32_t bitplaneSize = _PM_chunkSize * + ((width + (_PM_chunkSize - 1)) / _PM_chunkSize); // 1 plane of row pair + uint8_t pad = bitplaneSize - width; // Start-of-plane pad + + uint32_t initialRedBit, initialGreenBit, initialBlueBit; + if(core->numPlanes == 6) { + initialRedBit = 0b1000000000000000; // MSB red + initialGreenBit = 0b0000000000100000; // LSB green + initialBlueBit = 0b0000000000010000; // MSB blue + } else { + uint8_t shiftLeft = 5 - core->numPlanes; + initialRedBit = 0b0000100000000000 << shiftLeft; + initialGreenBit = 0b0000000001000000 << shiftLeft; + initialBlueBit = 0b0000000000000001 << shiftLeft; + } + + // Unlike the 565 byte converter, the word converter DOES clear out the + // matrix buffer (because each chain is OR'd into place). If a toggle + // register exists, "clear" really means the clock mask is set in all + // but the first element on a scanline (per bitplane). If no toggle + // register, can just zero everything out. +#if defined(_PM_portToggleRegister) + // No per-chain loop is required; one clock bit handles all chains + uint32_t offset = 0; // Current position in the 'dest' buffer + for(uint8_t row=0; rownumRowPairs; row++) { + for(uint8_t plane=0; planenumPlanes; plane++) { + dest[offset++] = 0; // First element of each plane + for(uint16_t x=1; xclockMask; + } + } + } +#else + memset(dest, 0, core->bufferSize); +#endif + + dest += pad; // Pad value is in 'elements,' not bytes, so this is OK + + // After a set of rows+bitplanes are processed, upperSrc and lowerSrc + // have advanced halfway down one matrix. This offset is used after + // each chain to advance them to the start/middle of the next matrix. + uint32_t halfMatrixOffset = width * core->numPlanes * core->numRowPairs; + + for(uint8_t chain=0; chainparallel; chain++) { + for(uint8_t row=0; rownumRowPairs; row++) { + uint32_t redBit = initialRedBit; + uint32_t greenBit = initialGreenBit; + uint32_t blueBit = initialBlueBit; + for(uint8_t plane=0; planenumPlanes; plane++) { +#if defined(_PM_portToggleRegister) + // Since we're ORing in bits over an existing clock bit, + // prior is 0 rather than clockMask as in the byte case. + uint16_t prior = 0; +#endif + for(uint16_t x=0; xnumPlanes < 6)) { + redBit <<= 1; + blueBit <<= 1; + } else { + redBit = 0b0000100000000000; + blueBit = 0b0000000000000001; + } + dest += bitplaneSize; // Advance one scanline in dest buffer + } // end plane + upperSrc += width; // Advance one scanline in source buffer + lowerSrc += width; + } // end row + pinMask += 6; // Next chain's RGB pin masks + upperSrc += halfMatrixOffset; // Advance to next matrix start pos + lowerSrc += halfMatrixOffset; + } } +// Corresponding function for long output -- either several parallel chains +// (up to 5), or 1 chain with RGB bits scattered widely about the PORT. +// Same deal, comments are pared back, see above functions for explanations. void _PM_convert_565_long(Protomatter_core *core, uint16_t *source, uint16_t width) { - // TO DO + uint16_t *upperSrc = source; // Matrix top half + uint16_t *lowerSrc = source + width * core->numRowPairs; // " bottom half + uint32_t *pinMask = (uint32_t *)core->rgbMask; // Pin bitmasks + uint32_t *dest = (uint32_t *)core->screenData; + if(core->doubleBuffer) { + dest += core->bufferSize / core->bytesPerElement * + (1 - core->activeBuffer); + } + + uint32_t bitplaneSize = _PM_chunkSize * + ((width + (_PM_chunkSize - 1)) / _PM_chunkSize); // 1 plane of row pair + uint8_t pad = bitplaneSize - width; // Start-of-plane pad + + uint32_t initialRedBit, initialGreenBit, initialBlueBit; + if(core->numPlanes == 6) { + initialRedBit = 0b1000000000000000; // MSB red + initialGreenBit = 0b0000000000100000; // LSB green + initialBlueBit = 0b0000000000010000; // MSB blue + } else { + uint8_t shiftLeft = 5 - core->numPlanes; + initialRedBit = 0b0000100000000000 << shiftLeft; + initialGreenBit = 0b0000000001000000 << shiftLeft; + initialBlueBit = 0b0000000000000001 << shiftLeft; + } + +#if defined(_PM_portToggleRegister) + // No per-chain loop is required; one clock bit handles all chains + uint32_t offset = 0; // Current position in the 'dest' buffer + for(uint8_t row=0; rownumRowPairs; row++) { + for(uint8_t plane=0; planenumPlanes; plane++) { + dest[offset++] = 0; // First element of each plane + for(uint16_t x=1; xclockMask; + } + } + } +#else + memset(dest, 0, core->bufferSize); +#endif + + dest += pad; // Pad value is in 'elements,' not bytes, so this is OK + + uint32_t halfMatrixOffset = width * core->numPlanes * core->numRowPairs; + + for(uint8_t chain=0; chainparallel; chain++) { + for(uint8_t row=0; rownumRowPairs; row++) { + uint32_t redBit = initialRedBit; + uint32_t greenBit = initialGreenBit; + uint32_t blueBit = initialBlueBit; + for(uint8_t plane=0; planenumPlanes; plane++) { +#if defined(_PM_portToggleRegister) + uint32_t prior = 0; +#endif + for(uint16_t x=0; xnumPlanes < 6)) { + redBit <<= 1; + blueBit <<= 1; + } else { + redBit = 0b0000100000000000; + blueBit = 0b0000000000000001; + } + dest += bitplaneSize; // Advance one scanline in dest buffer + } // end plane + upperSrc += width; // Advance one scanline in source buffer + lowerSrc += width; + } // end row + pinMask += 6; // Next chain's RGB pin masks + upperSrc += halfMatrixOffset; // Advance to next matrix start pos + lowerSrc += halfMatrixOffset; + } } #endif // ARDUINO diff --git a/core.c b/core.c index 7797127..5356060 100644 --- a/core.c +++ b/core.c @@ -394,12 +394,11 @@ void _PM_row_handler(Protomatter_core *core) { *core->oe.setReg = core->oe.bit; // Disable LED output + *core->latch.setReg = core->latch.bit; // Latch data from PRIOR pass // Stop timer, save count value at stop uint32_t elapsed = _PM_timerStop(core->timer); - - _PM_pinHigh(core->latch.pin); // Latch data loaded on PRIOR pass - _PM_pinLow(core->latch.pin); uint8_t prevPlane = core->plane; // Save that plane # for later timing + *core->latch.clearReg = core->latch.bit; // (split to add a few cycles) // If plane 0 just finished being displayed (plane 1 was loaded on prior // pass, or there's only one plane...I know, it's confusing), take note @@ -553,7 +552,8 @@ static void blast_byte(Protomatter_core *core, uint8_t *data) { // clock are all within the same byte of a PORT register, else we'd be // in the word- or long-blasting functions now. So we just need an // 8-bit pointer to the PORT. - volatile uint8_t *toggle = (volatile uint8_t *)core->toggleReg + core->portOffset; + volatile uint8_t *toggle = (volatile uint8_t *)core->toggleReg + + core->portOffset; #else // No-toggle version is a little different. If here, RGB data is all // in one byte of PORT register, clock can be any bit in 32-bit PORT. @@ -589,7 +589,8 @@ static void blast_byte(Protomatter_core *core, uint8_t *data) { static void blast_word(Protomatter_core *core, uint16_t *data) { #if defined(_PM_portToggleRegister) // See notes above -- except now 16-bit word in PORT. - volatile uint16_t *toggle = (volatile uint16_t *)core->toggleReg + core->portOffset; + volatile uint16_t *toggle = (volatile uint16_t *)core->toggleReg + + core->portOffset; #else volatile uint16_t *set; // For RGB data set volatile uint32_t *set32; // For clock set @@ -606,7 +607,8 @@ static void blast_word(Protomatter_core *core, uint16_t *data) { } #if defined(_PM_portToggleRegister) // rgbAndClockMask is a 16-bit value when toggling, hence offset here. - *((volatile uint16_t *)core->clearReg + core->portOffset) = core->rgbAndClockMask; + *((volatile uint16_t *)core->clearReg + core->portOffset) = + core->rgbAndClockMask; #endif }