From 37c23e789dbf785c29ba9dbe4067957a1f2af39c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=BD=C5=AFrek?= Date: Sat, 11 May 2024 12:52:48 +0200 Subject: [PATCH] Add support for scrolling text asynchronously --- .../TextWithArduinoGraphicsAsynchronous.ino | 64 +++++++++++++++++++ .../src/Arduino_LED_Matrix.h | 56 +++++++++++++--- .../Arduino_LED_Matrix/src/TextAnimation.h | 15 +++++ 3 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 libraries/Arduino_LED_Matrix/examples/TextWithArduinoGraphicsAsynchronous/TextWithArduinoGraphicsAsynchronous.ino create mode 100644 libraries/Arduino_LED_Matrix/src/TextAnimation.h diff --git a/libraries/Arduino_LED_Matrix/examples/TextWithArduinoGraphicsAsynchronous/TextWithArduinoGraphicsAsynchronous.ino b/libraries/Arduino_LED_Matrix/examples/TextWithArduinoGraphicsAsynchronous/TextWithArduinoGraphicsAsynchronous.ino new file mode 100644 index 00000000..d2d48101 --- /dev/null +++ b/libraries/Arduino_LED_Matrix/examples/TextWithArduinoGraphicsAsynchronous/TextWithArduinoGraphicsAsynchronous.ino @@ -0,0 +1,64 @@ +// To use ArduinoGraphics APIs, please include BEFORE Arduino_LED_Matrix and TextAnimation +#include "ArduinoGraphics.h" +#include "Arduino_LED_Matrix.h" +#include "TextAnimation.h" + +ArduinoLEDMatrix matrix; + +// 100 frames maximum. Compute as maximum length of text you want to print (eg. 20 chars) +// multiplied by font width (eg. 5 for Font_5x7), so 20 chars * 5 px = 100. If you enter lower +// value (or your text get unexpectedly long), animation will be cut and end soon. +TEXT_ANIMATION_DEFINE(anim, 100) + +int counter; +bool requestNext = false; + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + matrix.begin(); + matrix.beginDraw(); + + matrix.stroke(0xFFFFFFFF); + matrix.textFont(Font_5x7); + matrix.textScrollSpeed(60); + matrix.setCallback(matrixCallback); + + const char text[] = " UNO r4 "; + matrix.beginText(0, 1, 0xFFFFFF); + matrix.println(text); + matrix.endTextAnimation(SCROLL_LEFT, anim); + + matrix.loadTextAnimationSequence(anim); + matrix.play(); + + // now animation play asynchronously. Will call matrixCallback once completed. +} + +void matrixCallback() { + // callback is executed in IRQ and should run as fast as possible + requestNext = true; +} + +void loop() { + if (requestNext) { + requestNext = false; + + matrix.beginText(0, 1, 0xFFFFFF); + matrix.print(" "); + matrix.println(counter); + matrix.endTextAnimation(SCROLL_RIGHT, anim); + + if (counter++ >= 20) { + counter = 0; + } + + matrix.loadTextAnimationSequence(anim); + matrix.play(); + } + + delay(500); + digitalWrite(LED_BUILTIN, 0); + delay(500); + digitalWrite(LED_BUILTIN, 1); +} diff --git a/libraries/Arduino_LED_Matrix/src/Arduino_LED_Matrix.h b/libraries/Arduino_LED_Matrix/src/Arduino_LED_Matrix.h index f7e685dc..5d769161 100644 --- a/libraries/Arduino_LED_Matrix/src/Arduino_LED_Matrix.h +++ b/libraries/Arduino_LED_Matrix/src/Arduino_LED_Matrix.h @@ -141,8 +141,10 @@ static uint32_t reverse(uint32_t x) } // TODO: this is dangerous, use with care -#define loadSequence(frames) loadWrapper(frames, sizeof(frames)) -#define renderBitmap(bitmap, rows, columns) loadPixels(&bitmap[0][0], rows*columns) +#define loadSequence(frames) loadWrapper(frames, sizeof(frames)) +#define renderBitmap(bitmap, rows, columns) loadPixels(&bitmap[0][0], rows*columns) +#define endTextAnimation(scrollDirection, anim) endTextToAnimationBuffer(scrollDirection, anim ## _buf, sizeof(anim ## _buf), anim ## _buf_used) +#define loadTextAnimationSequence(anim) loadWrapper(anim ## _buf, anim ## _buf_used) static uint8_t __attribute__((aligned)) framebuffer[NUM_LEDS / 8]; @@ -227,11 +229,11 @@ class ArduinoLEDMatrix return false; } - void loadPixels(uint8_t *arr, size_t size){ + static void loadPixelsToBuffer(uint8_t* arr, size_t size, uint32_t* dst) { uint32_t partialBuffer = 0; uint8_t pixelIndex = 0; uint8_t *frameP = arr; - uint32_t *frameHolderP = _frameHolder; + uint32_t *frameHolderP = dst; while (pixelIndex < size) { partialBuffer |= *frameP++; if ((pixelIndex + 1) % 32 == 0) { @@ -240,6 +242,10 @@ class ArduinoLEDMatrix partialBuffer = partialBuffer << 1; pixelIndex++; } + } + + void loadPixels(uint8_t *arr, size_t size){ + loadPixelsToBuffer(arr, size, _frameHolder); loadFrame(_frameHolder); }; @@ -255,9 +261,9 @@ class ArduinoLEDMatrix void clear() { const uint32_t fullOff[] = { - 0x00000000, - 0x00000000, - 0x00000000 + 0x00000000, + 0x00000000, + 0x00000000 }; loadFrame(fullOff); } @@ -277,16 +283,48 @@ class ArduinoLEDMatrix renderBitmap(_canvasBuffer, canvasHeight, canvasWidth); } - // display the drawing + // display the drawing or capture it to buffer when rendering dynamic anymation void endDraw() { ArduinoGraphics::endDraw(); - renderBitmap(_canvasBuffer, canvasHeight, canvasWidth); + + if (!captureAnimation) { + renderBitmap(_canvasBuffer, canvasHeight, canvasWidth); + } else { + if (captureAnimationHowManyRemains >= 4) { + loadPixelsToBuffer(&_canvasBuffer[0][0], sizeof(_canvasBuffer), captureAnimationFrame); + captureAnimationFrame[3] = _textScrollSpeed; + captureAnimationFrame += 4; + captureAnimationHowManyRemains -= 16; + } + } + } + + void endTextToAnimationBuffer(int scrollDirection, uint32_t frames[][4], uint32_t howManyMax, uint32_t& howManyUsed) { + captureAnimationFrame = &frames[0][0]; + captureAnimationHowManyRemains = howManyMax; + + captureAnimation = true; + ArduinoGraphics::textScrollSpeed(0); + ArduinoGraphics::endText(scrollDirection); + ArduinoGraphics::textScrollSpeed(_textScrollSpeed); + captureAnimation = false; + + howManyUsed = howManyMax - captureAnimationHowManyRemains; + } + + void textScrollSpeed(unsigned long speed) { + ArduinoGraphics::textScrollSpeed(speed); + _textScrollSpeed = speed; } private: + uint32_t* captureAnimationFrame = nullptr; + uint32_t captureAnimationHowManyRemains = 0; + bool captureAnimation = false; static const byte canvasWidth = 12; static const byte canvasHeight = 8; uint8_t _canvasBuffer[canvasHeight][canvasWidth] = {{0}}; + unsigned long _textScrollSpeed = 100; #endif private: diff --git a/libraries/Arduino_LED_Matrix/src/TextAnimation.h b/libraries/Arduino_LED_Matrix/src/TextAnimation.h new file mode 100644 index 00000000..9260b06e --- /dev/null +++ b/libraries/Arduino_LED_Matrix/src/TextAnimation.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Arduino.h" + +#if not __has_include("ArduinoGraphics.h") +#error "TextAnimation work only when ArduinoGraphics is installed and used. Include ArduinoGraphics first." +#endif + +#define TEXT_ANIMATION_DECLARE(NAME, MAX_CHARS) \ + extern uint32_t NAME ## _buf[MAX_CHARS][4]; \ + extern uint32_t NAME ## _buf_used; + +#define TEXT_ANIMATION_DEFINE(NAME, MAX_CHARS) \ + uint32_t NAME ## _buf[MAX_CHARS][4]; \ + uint32_t NAME ## _buf_used = 0; \ No newline at end of file