diff --git a/src/internal/methods/NeoArmMethod.h b/src/internal/methods/NeoArmMethod.h index a31365f..2ff30d3 100644 --- a/src/internal/methods/NeoArmMethod.h +++ b/src/internal/methods/NeoArmMethod.h @@ -37,21 +37,19 @@ template class NeoArmMethodBase public: typedef NeoNoSettings SettingsObject; - NeoArmMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : - _sizeData(pixelCount * elementSize + settingsSize), - _pin(pin) + NeoArmMethodBase(uint8_t pin, + uint16_t pixelCount, + [[maybe_unused]] size_t elementSize, + [[maybe_unused]] size_t settingsSize) : + _pin(pin), + _pixelCount(pixelCount) { pinMode(pin, OUTPUT); - - _data = static_cast(malloc(_sizeData)); - // data cleared later in Begin() } ~NeoArmMethodBase() { pinMode(_pin, INPUT); - - free(_data); } bool IsReadyToUpdate() const @@ -68,55 +66,97 @@ public: _endTime = micros(); } - void Update(bool) + template + void Update( + T_COLOR_OBJECT* pixels, + size_t countPixels, + const typename T_COLOR_FEATURE::SettingsObject& featureSettings, + const T_SHADER& shader) { - // Data latch = 50+ microsecond pause in the output stream. Rather than - // put a delay at the end of the function, the ending time is noted and - // the function will simply hold off (if needed) on issuing the - // subsequent round of data until the latch time has elapsed. This - // allows the mainline code to start generating the next frame of data - // rather than stalling for the latch. + // wait for not actively sending data while (!IsReadyToUpdate()) { - yield(); // allows for system yield if needed + yield(); } + const size_t sendDataSize = T_COLOR_FEATURE::SettingsSize >= T_COLOR_FEATURE::PixelSize ? T_COLOR_FEATURE::SettingsSize : T_COLOR_FEATURE::PixelSize; + uint8_t sendData[sendDataSize]; + uint8_t* transaction = this->BeginTransaction(); + + // if there are settings at the front + // + if (T_COLOR_FEATURE::applyFrontSettings(sendData, sendDataSize, featureSettings)) + { + this->AppendData(&transaction, sendData, T_COLOR_FEATURE::SettingsSize); + } + + // fill primary color data + // + T_COLOR_OBJECT* pixel = pixels; + const T_COLOR_OBJECT* pixelEnd = pixel + countPixels; + uint16_t stripCount = this->_pixelCount; + + while (stripCount--) + { + typename T_COLOR_FEATURE::ColorObject color = shader.Apply(*pixel); + T_COLOR_FEATURE::applyPixelColor(sendData, sendDataSize, color); + + this->AppendData(&transaction, sendData, T_COLOR_FEATURE::PixelSize); + + pixel++; + if (pixel >= pixelEnd) + { + // restart at first + pixel = pixels; + } + } + + // if there are settings at the back + // + if (T_COLOR_FEATURE::applyBackSettings(sendData, sendDataSize, featureSettings)) + { + this->AppendData(&transaction, sendData, T_COLOR_FEATURE::SettingsSize); + } + + this->EndTransaction(); + } + + uint8_t* BeginTransaction() + { noInterrupts(); // Need 100% focus on instruction timing + // weird, but we need a valid pointer + // the pointer is dereferenced, but what it points out is not + return reinterpret_cast(this); + } - T_SPEED::send_pixels(_data, _sizeData, _pin); + bool AppendData([[maybe_unused]] uint8_t** buffer, const uint8_t* sendData, size_t sendDataSize) + { + T_SPEED::send_data(sendData, sendDataSize, _pin); + return true; + } + bool EndTransaction() + { interrupts(); // save EOD time for latch on next call _endTime = micros(); + + return true; } - bool AlwaysUpdate() - { - // this method requires update to be called only if changes to buffer - return false; - } - - uint8_t* getData() const - { - return _data; - }; - - size_t getDataSize() const - { - return _sizeData; - }; - void applySettings([[maybe_unused]] const SettingsObject& settings) { } private: - const size_t _sizeData; // Size of '_data' buffer below + const uint8_t _pin; // output pin number + const uint16_t _pixelCount; // count of pixels in the strip uint32_t _endTime; // Latch timing reference uint8_t* _data; // Holds LED color values - uint8_t _pin; // output pin number }; // Teensy 3.0 or 3.1 (3.2) or 3.5 or 3.6 @@ -192,10 +232,10 @@ template class NeoArmMk20dxSpeedBase public: static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs; - static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + static void send_data(const uint8_t* data, size_t sizeData, uint8_t pin) { - uint8_t* p = pixels; - uint8_t* end = p + sizePixels; + const uint8_t* p = data; + const uint8_t* end = p + sizeData; uint8_t pix; uint8_t mask; @@ -249,15 +289,15 @@ typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod; class NeoArmMk26z64Speed800KbpsBase { public: - static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + static void send_data(const uint8_t* data, size_t sizeData, uint8_t pin) { - uint8_t* p = pixels; + const uint8_t* p = data; uint8_t pix; uint8_t count; uint8_t dly; uint8_t bitmask = digitalPinToBitMask(pin); volatile uint8_t* reg = portSetRegister(pin); - uint32_t num = sizePixels; + uint32_t num = sizeData; asm volatile( "L%=_begin:" "\n\t" @@ -491,12 +531,12 @@ template class NeoArmSamd21g18aSpeedBase public: static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs; - static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + static void send_data(const uint8_t* data, size_t sizeData, uint8_t pin) { // Tried this with a timer/counter, couldn't quite get adequate // resolution. So yay, you get a load of goofball NOPs... - uint8_t* ptr = pixels; - uint8_t* end = ptr + sizePixels;; + const uint8_t* ptr = data; + const uint8_t* end = ptr + sizeData;; uint8_t p = *ptr++; uint8_t bitMask = 0x80; uint8_t portNum = g_APinDescription[pin].ulPort; @@ -657,13 +697,13 @@ template class NeoArmStm32SpeedBase public: static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs; - static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + static void send_data(const uint8_t* data, size_t sizeData, uint8_t pin) { // Tried this with a timer/counter, couldn't quite get adequate // resolution. So yay, you get a load of goofball NOPs... - uint8_t* ptr = pixels; - uint8_t* end = ptr + sizePixels; + const uint8_t* ptr = data; + const uint8_t* end = ptr + sizeData; uint8_t p = *ptr++; uint8_t bitMask = 0x80; @@ -814,7 +854,7 @@ template class NeoArmOtherSpeedBase public: static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs; - static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + static void send_data(const uint8_t* data, size_t sizeData, uint8_t pin) { uint32_t pinMask; uint32_t t; @@ -823,8 +863,8 @@ public: volatile WoReg* portClear; volatile WoReg* timeValue; volatile WoReg* timeReset; - uint8_t* p; - uint8_t* end; + const uint8_t* p; + const uint8_t* end; uint8_t pix; uint8_t mask; @@ -841,8 +881,8 @@ public: portClear = &(port->PIO_CODR); // starting timer to minimize timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'. timeReset = &(TC1->TC_CHANNEL[0].TC_CCR); - p = pixels; - end = p + sizePixels; + p = data; + end = p + sizeData; pix = *p++; mask = 0x80; diff --git a/src/internal/methods/NeoNrf52xMethod.h b/src/internal/methods/NeoNrf52xMethod.h index b788e55..f92caba 100644 --- a/src/internal/methods/NeoNrf52xMethod.h +++ b/src/internal/methods/NeoNrf52xMethod.h @@ -360,18 +360,18 @@ public: typedef NeoNoSettings SettingsObject; NeoNrf52xMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : - _sizeData(pixelCount * elementSize + settingsSize), - _pin(pin) + _pin(pin), + _pixelCount(pixelCount) { - construct(); + construct(pixelCount * elementSize + settingsSize); } NeoNrf52xMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) : - _sizeData(pixelCount* elementSize + settingsSize), _pin(pin), + _pixelCount(pixelCount), _bus(channel) { - construct(); + construct(pixelCount * elementSize + settingsSize); } ~NeoNrf52xMethodBase() @@ -385,7 +385,6 @@ public: pinMode(_pin, INPUT); - free(_data); free(_dmaBuffer); } @@ -406,60 +405,112 @@ public: dmaStart(); } - void Update(bool) + template + void Update( + T_COLOR_OBJECT* pixels, + size_t countPixels, + const typename T_COLOR_FEATURE::SettingsObject& featureSettings, + const T_SHADER& shader) { - // Data latch = 50+ microsecond pause in the output stream. Rather than - // put a delay at the end of the function, the ending time is noted and - // the function will simply hold off (if needed) on issuing the - // subsequent round of data until the latch time has elapsed. This - // allows the mainline code to start generating the next frame of data - // rather than stalling for the latch. + // wait for not actively sending data while (!IsReadyToUpdate()) { - yield(); // allows for system yield if needed + yield(); } - FillBuffer(); + const size_t sendDataSize = T_COLOR_FEATURE::SettingsSize >= T_COLOR_FEATURE::PixelSize ? T_COLOR_FEATURE::SettingsSize : T_COLOR_FEATURE::PixelSize; + uint8_t sendData[sendDataSize]; + uint8_t* transaction = this->BeginTransaction(); + + // if there are settings at the front + // + if (T_COLOR_FEATURE::applyFrontSettings(sendData, sendDataSize, featureSettings)) + { + this->AppendData(&transaction, sendData, T_COLOR_FEATURE::SettingsSize); + } + + // fill primary color data + // + T_COLOR_OBJECT* pixel = pixels; + const T_COLOR_OBJECT* pixelEnd = pixel + countPixels; + uint16_t stripCount = this->_pixelCount; + + while (stripCount--) + { + typename T_COLOR_FEATURE::ColorObject color = shader.Apply(*pixel); + T_COLOR_FEATURE::applyPixelColor(sendData, sendDataSize, color); + + this->AppendData(&transaction, sendData, T_COLOR_FEATURE::PixelSize); + + pixel++; + if (pixel >= pixelEnd) + { + // restart at first + pixel = pixels; + } + } + + // if there are settings at the back + // + if (T_COLOR_FEATURE::applyBackSettings(sendData, sendDataSize, featureSettings)) + { + this->AppendData(&transaction, sendData, T_COLOR_FEATURE::SettingsSize); + } + + this->EndTransaction(&transaction); + } + + uint8_t* BeginTransaction() + { + return reinterpret_cast(_dmaBuffer); + } + + bool AppendData(uint8_t** buffer, const uint8_t* sendData, size_t sendDataSize) + { + nrf_pwm_values_common_t* pDma = reinterpret_cast(*buffer); + const uint8_t* pEnd = sendData + sendDataSize; + + for (const uint8_t* pData = sendData; pData < pEnd; pData++) + { + uint8_t data = *pData; + + for (uint8_t bit = 0; bit < 8; bit++) + { + *(pDma++) = (data & 0x80) ? T_SPEED::Bit1 : T_SPEED::Bit0; + data <<= 1; + } + } + + *buffer = reinterpret_cast(pDma); + return true; + } + + bool EndTransaction([[maybe_unused]] uint8_t** buffer) + { dmaStart(); + + return true; } - bool AlwaysUpdate() - { - // this method requires update to be called only if changes to buffer - return false; - } - - uint8_t* getData() const - { - return _data; - }; - - size_t getDataSize() const - { - return _sizeData; - }; - void applySettings([[maybe_unused]] const SettingsObject& settings) { } private: - const size_t _sizeData; // Size of '_data' buffer below const uint8_t _pin; // output pin number + const uint16_t _pixelCount; // count of pixels in the strip const T_BUS _bus; // holds instance for multi channel support - uint8_t* _data; // Holds LED color values size_t _dmaBufferSize; // total size of _dmaBuffer nrf_pwm_values_common_t* _dmaBuffer; // Holds pixel data in native format for PWM hardware - void construct() + void construct(size_t sizeData) { pinMode(_pin, OUTPUT); - _data = static_cast(malloc(_sizeData)); - // data cleared later in Begin() - - _dmaBufferSize = c_dmaBytesPerDataByte * _sizeData + sizeof(nrf_pwm_values_common_t); + _dmaBufferSize = c_dmaBytesPerDataByte * sizeData + sizeof(nrf_pwm_values_common_t); _dmaBuffer = static_cast(malloc(_dmaBufferSize)); } @@ -507,22 +558,10 @@ private: void FillBuffer() { nrf_pwm_values_common_t* pDma = _dmaBuffer; - nrf_pwm_values_common_t* pDmaEnd = _dmaBuffer + (_dmaBufferSize / sizeof(nrf_pwm_values_common_t)); - uint8_t* pEnd = _data + _sizeData; + const nrf_pwm_values_common_t* pDmaEnd = _dmaBuffer + (_dmaBufferSize / sizeof(nrf_pwm_values_common_t)); - for (uint8_t* pData = _data; pData < pEnd; pData++) - { - uint8_t data = *pData; - - for (uint8_t bit = 0; bit < 8; bit++) - { - *(pDma++) = (data & 0x80) ? T_SPEED::Bit1 : T_SPEED::Bit0; - data <<= 1; - } - } - - // fill the rest with BitReset as it will get repeated when delaying or - // at the end before being stopped + // fill the dma buffer with BitReset + // while (pDma < pDmaEnd) { *(pDma++) = T_SPEED::BitReset; diff --git a/src/internal/methods/Rp2040/NeoRp2040x4Method.h b/src/internal/methods/Rp2040/NeoRp2040x4Method.h index 26763fd..ba4a642 100644 --- a/src/internal/methods/Rp2040/NeoRp2040x4Method.h +++ b/src/internal/methods/Rp2040/NeoRp2040x4Method.h @@ -26,7 +26,7 @@ License along with NeoPixel. If not, see #pragma once -#ifdef ARDUINO_ARCH_RP2040 +#if defined(ARDUINO_ARCH_RP2040) //#define NEORP2040_DEBUG