Skip to content

Commit

Permalink
Merge pull request #31 from hackerceo/master
Browse files Browse the repository at this point in the history
Add non-blocking animation support to 6 digit displays
  • Loading branch information
jasonacox authored Apr 1, 2023
2 parents f32a6b5 + cc41d2e commit da09826
Show file tree
Hide file tree
Showing 5 changed files with 326 additions and 7 deletions.
4 changes: 2 additions & 2 deletions TM1637TinyDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -590,13 +590,13 @@ bool TM1637TinyDisplay::Animate()
memset(digits, 0, sizeof(digits));
switch(m_animation_type) {
case 1: // regular animation running
for (unsigned int a = 0; a < 4; a++) {
for (unsigned int a = 0; a < MAXDIGITS; a++) {
digits[a] = m_animation_sequence[frame_num][a];
}
setSegments(digits);
break;
case 2: // PROGMEM animation running
for(unsigned int a = 0; a < 4; a++) {
for(unsigned int a = 0; a < MAXDIGITS; a++) {
digits[a] = pgm_read_byte(&(m_animation_sequence[frame_num][a]));
}
setSegments(digits);
Expand Down
120 changes: 117 additions & 3 deletions TM1637TinyDisplay6.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ void TM1637TinyDisplay6::showNumber(double num, uint8_t decimal_length, uint8_t
if(num<0) value = value - 0.5; // round down
inum = abs((long)value);

// render display array
// render display array
if (inum == 0 && !leading_zero) {
digits[length-1] = encodeDigit(0);
}
Expand Down Expand Up @@ -392,8 +392,6 @@ void TM1637TinyDisplay6::showNumberBaseEx(int8_t base, uint32_t num, uint8_t dot
negative = true;
}

// uint8_t digits[MAXDIGITS];

if (num == 0 && !leading_zero) {
// Singular case - take care separately
for(uint8_t i = 0; i < (length-1); i++) {
Expand Down Expand Up @@ -571,6 +569,122 @@ void TM1637TinyDisplay6::showLevel(unsigned int level, bool horizontal)
setSegments(digits);
}

bool TM1637TinyDisplay6::Animate()
{
// return if no animation/scroll is running
if (m_animation_type == 0) return false;

unsigned int frame_num = (millis() - m_animation_start) / m_animation_frame_ms;

// we have run past our max frame (this can happen because of frame dropping)
if (frame_num >= m_animation_frames) {
m_animation_type = 0;
return false;
}

// bail out if the animation frame has not changed
if (frame_num == m_animation_last_frame) {
return true;
} else {
m_animation_last_frame = frame_num;
}

memset(digits, 0, sizeof(digits));
switch(m_animation_type) {
case 1: // regular animation running
for (unsigned int a = 0; a < MAXDIGITS; a++) {
digits[a] = m_animation_sequence[frame_num][a];
}
setSegments(digits, MAXDIGITS, 0);
break;
case 2: // PROGMEM animation running
for(unsigned int a = 0; a < MAXDIGITS; a++) {
digits[a] = pgm_read_byte(&(m_animation_sequence[frame_num][a]));
}
setSegments(digits, MAXDIGITS, 0);
break;
case 3: // PROGMEM text scroll running
for (int x = 0; x < MAXDIGITS; x++) {
int offset = frame_num - MAXDIGITS + x;
if (offset >= 0 && offset < m_animation_frames - (2 * MAXDIGITS)) {
digits[x] = encodeASCII(pgm_read_byte(&m_animation_string[offset]));
} else {
digits[x] = 0;
}
}
setSegments(digits, MAXDIGITS, 0);
break;
case 4: // SRAM text scroll running
for (int x = 0; x < MAXDIGITS; x++) {
int offset = frame_num - MAXDIGITS + x;
if (offset >= 0 && offset < m_animation_frames - (2 * MAXDIGITS)) {
digits[x] = encodeASCII(m_animation_string[offset]);
} else {
digits[x] = 0;
}
}
setSegments(digits, MAXDIGITS, 0);
break;
}
return true;
}

void TM1637TinyDisplay6::startAnimation_P(const uint8_t(*data)[MAXDIGITS], unsigned int frames, unsigned int ms)
{
startAnimation(data, frames, ms, true);
}

void TM1637TinyDisplay6::startAnimation(const uint8_t (*data)[MAXDIGITS], unsigned int frames, unsigned int ms, bool usePROGMEM)
{
if (usePROGMEM) {
m_animation_type = 2;
} else {
m_animation_type = 1;
}
m_animation_start = millis() ;
m_animation_frames = frames;
m_animation_frame_ms = ms;
m_animation_sequence = (uint8_t (*)[MAXDIGITS]) data;
m_animation_string = nullptr;
}


void TM1637TinyDisplay6::startStringScroll_P(const char s[], unsigned int ms)
{
startStringScroll(s, ms, true);
}

void TM1637TinyDisplay6::startStringScroll(const char s[], unsigned int ms, bool usePROGMEM) {
if (usePROGMEM) {
m_animation_frames = strlen_P(s);
if (m_animation_frames <= MAXDIGITS) {
// no need to scroll, just display it
showString_P(s, m_animation_frames, 0, 0);
return;
} else {
m_animation_type = 3;
}
} else {
m_animation_frames = strlen(s);
if (m_animation_frames <= MAXDIGITS) {
// no need to scroll, just display it
showString_P(s, m_animation_frames, 0, 0);
return;
} else {
m_animation_type = 4;
}
}
// add scroll on/off frames to the total animation frame count
m_animation_frames = m_animation_frames + (MAXDIGITS * 2);

m_animation_start = millis();
m_animation_frame_ms = ms;
m_animation_sequence = nullptr;
m_animation_string = (uint8_t *) s;
}



void TM1637TinyDisplay6::showAnimation(const uint8_t data[][6], unsigned int frames, unsigned int ms)
{
// Animation sequence
Expand Down
36 changes: 36 additions & 0 deletions TM1637TinyDisplay6.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,33 @@ class TM1637TinyDisplay6 {
//! @param ms Time to delay between each frame
void showAnimation_P(const uint8_t data[][6], unsigned int frames = 0, unsigned int ms = 10);


//! The event loop function to enable non-blocking animations
//!
//! The method does not take any input parameters and returns a boolean
//! The return value is TRUE when an animation is still occuring, it is
//! FALSE when there is no animiation occuring
//!
//! @return A boolean value indicating if an animation is occuring
bool Animate();

//! The function used to begin a non-blocking animation
//!
//! @param usePROGMEN Indicates if the passed animation data is coming from a PROGMEM defined variable
//! @param frames Number of frames in the sequence to animate
//! @param ms Time to delay between each frame
void startAnimation(const uint8_t (*data)[MAXDIGITS], unsigned int frames = 0, unsigned int ms = 10, bool usePROGMEM = false);
void startAnimation_P(const uint8_t(*data)[MAXDIGITS], unsigned int frames = 0, unsigned int ms = 10);

//! The function used to begin a non-blocking scroll of a string
//!
//! @param usePROGMEN Indicates if the passed string data is coming from a PROGMEM defined variable
//! @param ms Time to delay between each frame
void startStringScroll(const char s[], unsigned int ms = DEFAULT_SCROLL_DELAY, bool usePROGMEM = false);
void startStringScroll_P(const char s[], unsigned int ms = DEFAULT_SCROLL_DELAY);



//! Translate a single ASCII character into 7 segment code
//!
//! The method accepts a number between 0 - 255 and converts it to the
Expand All @@ -420,6 +447,7 @@ class TM1637TinyDisplay6 {
//! bit 6 - segment G; bit 7 - always zero)
uint8_t encodeASCII(uint8_t chr);


protected:
void bitDelay();

Expand All @@ -442,6 +470,14 @@ class TM1637TinyDisplay6 {
bool m_flipDisplay;
uint8_t digits[MAXDIGITS];
uint8_t digitsbuf[MAXDIGITS];

unsigned long m_animation_start;
unsigned int m_animation_frames;
unsigned int m_animation_last_frame;
unsigned int m_animation_frame_ms;
uint8_t (*m_animation_sequence)[MAXDIGITS];
uint8_t (*m_animation_string);
uint8_t m_animation_type;
};

#endif // __TM1637TINYDISPLAY6__
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// TM1637TinyDisplay Non-Blocking Animation/Scrolling Sketch
// This is a test sketch for the Arduino TM1637TinyDisplay LED Display library
// demonstrating how to do non-blocking animations and scrolling.
//
// Author: Nick Benik - @hackerceo - https://github.com/hackerceo
// Date: 26 March 2023
//

// Includes
#include <Arduino.h>
#include <TM1637TinyDisplay6.h>

// Module connection pins (Digital Pins)
#define CLK 8
#define DIO 9

// The amount of time (in milliseconds) between tests
#define TEST_DELAY 1000

// Example animation sequence for showAnimation() Test
// Built with 7-Segment Animator Tool
// https://jasonacox.github.io/TM1637TinyDisplay/examples/7-segment-animator.html

const uint8_t ANIMATION[32][6] = {
{ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 0
{ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 }, // Frame 1
{ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 }, // Frame 2
{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00 }, // Frame 3
{ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 }, // Frame 4
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }, // Frame 5
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 }, // Frame 6
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, // Frame 7
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, // Frame 8
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, // Frame 9
{ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 }, // Frame 10
{ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }, // Frame 11
{ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }, // Frame 12
{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 13
{ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 14
{ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 15
{ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 16
{ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 }, // Frame 17
{ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 }, // Frame 18
{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00 }, // Frame 19
{ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 }, // Frame 20
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }, // Frame 21
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 }, // Frame 22
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, // Frame 23
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, // Frame 24
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, // Frame 25
{ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 }, // Frame 26
{ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }, // Frame 27
{ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }, // Frame 28
{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 29
{ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 30
{ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 } // Frame 31
};

// To save RAM space, we can store the animation sequences in PROGMEM read-only flash memory.
// This requires using the showAnimation_P() function to read from PROGMEM memory space.

/* Animation Data - HGFEDCBA Map */
const uint8_t ANIMATION2[31][6] PROGMEM = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 0
{ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 1
{ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 2
{ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 3
{ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 4
{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Frame 5
{ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 }, // Frame 6
{ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00 }, // Frame 7
{ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00 }, // Frame 8
{ 0x00, 0x20, 0x00, 0x00, 0x00, 0x00 }, // Frame 9
{ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }, // Frame 10
{ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 }, // Frame 11
{ 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }, // Frame 12
{ 0x00, 0x00, 0x40, 0x00, 0x00, 0x00 }, // Frame 13
{ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00 }, // Frame 14
{ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }, // Frame 15
{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00 }, // Frame 16
{ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 }, // Frame 17
{ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00 }, // Frame 18
{ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00 }, // Frame 19
{ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 }, // Frame 20
{ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 }, // Frame 21
{ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00 }, // Frame 22
{ 0x00, 0x00, 0x00, 0x00, 0x40, 0x00 }, // Frame 23
{ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00 }, // Frame 24
{ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, // Frame 25
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }, // Frame 26
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 }, // Frame 27
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 }, // Frame 28
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20 }, // Frame 29
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } // Frame 30
};

// Strings to be scrolled from PROGMEM as well as SRAM
const PROGMEM char FlashString[] = "Flash Test - 1234567890";
const char SRamString[] = "SRAM Test - 0987654321";


byte AnimationNum;

// Initialize TM1637TinyDisplay - 6 Digit Display
TM1637TinyDisplay6 display(CLK, DIO);

void setup()
{
Serial.begin(9600);
display.begin();
AnimationNum = 0;
}

void loop()
{


// ### this line is used to run a single tick of the animation/scroll
bool isAnimationRunning = display.Animate();
// ### returns true if an animation is still running


// if there is no animation/scrolling currently running then lets start one
if (!isAnimationRunning) {

// clear display and pause before starting the non-blocking animation/scrolling
display.clear();
delay(TEST_DELAY);

// switch to the next animation/scrolling to run
AnimationNum++;

// start it
switch(AnimationNum) {
case 1:
Serial.println("");
Serial.println("Animate from SRAM");
// non-blocking animation from SRAM
display.startAnimation(ANIMATION, FRAMES(ANIMATION), TIME_MS(75));
break;
case 2:
Serial.println("");
Serial.println("Animate from PROGMEM");
// non-blocking animation from PROGMEM
display.startAnimation_P(ANIMATION2, FRAMES(ANIMATION2), TIME_MS(75));
break;
case 3:
Serial.println("");
Serial.println("Animated String Scroll from PROGMEM");
// non-blocking scrolling of a long string
display.startStringScroll_P(FlashString, 200);
break;
case 4:
Serial.println("");
Serial.println("Animated String Scroll from SRAM");
// non-blocking scrolling of a long string
display.startStringScroll(SRamString, 200);
break;
default:
display.showString("The");
delay(TEST_DELAY);
display.showString(" End");
delay(TEST_DELAY * 5);
// start the sequence over again
AnimationNum = 0;
}
}

// do something while the animation is running
Serial.print(".");
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@ byte AnimationNum;

// Initialize TM1637TinyDisplay - 4 Digit Display
TM1637TinyDisplay display(CLK, DIO);
// Initialize TM1637TinyDisplay - 6 Digit Display
// TM1637TinyDisplay6 display(CLK, DIO);

void setup()
{
Expand Down

0 comments on commit da09826

Please sign in to comment.