Split screen marquee/scroll

The Dot Matrix Display (DMD) is a 32x16 array of high-brightness LEDs for visually striking effects. [Product Page]
Post Reply
Brissieboy
Posts: 141
Joined: Fri Sep 20, 2013 7:25 am

Split screen marquee/scroll

Post by Brissieboy » Wed Apr 30, 2014 4:58 am

Here is a little addition to the current DMD library (NOT THE DMD2 LIBRARY).

It enables a mixture of scrolling text and stationary text on the same display. It is a cut-down modified version of dmd.stepMarquee(). It only handles 'normal' horizontal, right to left, single step scrolling. It allow you to specify exactly what group of rows you want to scroll.
This is an additional function to add to the library so that no other library calls are impacted - fully backward compatible.

To use this, add the following function into the DMD.cpp file:

Code: Select all

//**************************************************************************************
// stepSplitMarquee(int topRow, int bottomRow)
// This is a cut-down modification of stepMarquee(). It steps only the rows specified.
// Only horizontal, single step, left scroll is catered for.
// This function allows you to have only a part of the DMD array scrolling, while the
// remainder stays still and is not cleared during scrolling.
//**************************************************************************************

boolean DMD::stepSplitMarquee(int topRow, int bottomRow)
{
  boolean ret=false;
  marqueeOffsetX += -1;  // only scroll horizontally, 1 place left
  if (marqueeOffsetX < -marqueeWidth) { // if text has scrolled off the screen
    marqueeOffsetX = DMD_PIXELS_ACROSS * DisplaysWide; // set up to scroll again from the far right
    drawFilledBox(0, topRow, DisplaysWide*DMD_PIXELS_ACROSS-1, bottomRow, GRAPHICS_INVERSE); // clear the scroll rows
    ret=true; // indicates that this scroll has completed
  }

// This is the main change from the original function.
// It splits the left shift task into rows and bytes within a row to allow rows to be identified
// and treated separately.
  for (int row=topRow ;row<=bottomRow; row++) { // loop for each row in the scroll area
    for (int byteInRow=0; byteInRow<DisplaysWide*4; byteInRow++) { // loop for each byte within a row (4 per DMD across)
      int thisIndex = (DisplaysTotal*4)*(row%16) + (4*DisplaysWide*(row/16)) + byteInRow; //calculate index into screen buffer
      if ((byteInRow%(DisplaysWide*4)) == (DisplaysWide*4) -1) {  // if it's the last byte in a row
        bDMDScreenRAM[thisIndex]=(bDMDScreenRAM[thisIndex]<<1)+1;
        // shift bits left, and puts a '1' in last position (a '1' = LED OFF)
    
      } else {                                            // if it's NOT the last byte in a row
        bDMDScreenRAM[thisIndex]=(bDMDScreenRAM[thisIndex]<<1) +
                    ((bDMDScreenRAM[thisIndex+1] & 0x80) >>7);
        // shift bits left as well as the shifting the MSB of the next byte into the LSB
      }
    }
  }
  // Redraw last char on screen
  // required because 
  int strWidth=marqueeOffsetX;
  for (byte i=0; i < marqueeLength; i++) {
    int wide = charWidth(marqueeText[i]);
    if (strWidth+wide >= DisplaysWide*DMD_PIXELS_ACROSS) {
      drawChar(strWidth, marqueeOffsetY,marqueeText[i],GRAPHICS_NORMAL);
      return ret;
    }
    strWidth += wide+1;
  }
  return ret;
}
and the following lines to the DMD.h file:

Code: Select all

  //Move the marquee left 1 place - special case of stepMarquee()
  // which only scrolls a limited portion of the display 
  boolean stepSplitMarquee(int topRow, int bottomRow);
just after the line "boolean stepMarquee( int amountX, int amountY);"

Here is a little demo sketch to demonstrate what it does and how to use it.

Code: Select all

/*
Test sketch for split screen scrolling
NOTE that this sketch uses the Freetronics version of the DMD library available here:
https://github.com/freetronics/DMD/
but with the additional dmd.stepSplitMarquee() function.

I also suggest that you make sure this has been changed DMD.cpp
change the line:
  SPI.setClockDivider(SPI_CLOCK_DIV128);
to:
  SPI.setClockDivider(SPI_CLOCK_DIV2);  // system clock / 2 = 8MHz SPI CLK to shift registers
This provides a marked improvement in the DMD update time within the interrupt routine.
*/

#include "SPI.h"        
#include "DMD.h"        
#include "TimerOne.h"
#include "SystemFont5x7.h"

#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1

DMD dmd( DISPLAYS_ACROSS , DISPLAYS_DOWN);

void ScanDMD()
{
  dmd.scanDisplayBySPI();
}

void setup()
{
  Timer1.initialize( 4000 );   // I find the default 5000 gives a visible flicker         
  Timer1.attachInterrupt( ScanDMD );  
  dmd.selectFont(SystemFont5x7);
  dmd.clearScreen( true );     // start with a blank screen
}

void loop()
{
  unsigned long time;
  int n;
  boolean ret = false;

  // Show some stationary text on the bottom half, and scrolling text on the top half
  // scrolls 3 times
  dmd.drawString(0,8,"Stays still",11,GRAPHICS_NORMAL); // the stationary string
  dmd.drawMarquee("Scrolling text",14,0,0); // set up the marquee
  time = millis();
  n=0;
  while(n<3) {
    while (!ret) {
      if ((time+30) < millis()) {
        ret = dmd.stepSplitMarquee(0,7); // parameters are the top & bottom rows to be scrolled
        time = millis();
      }
    }
    ret = false;
    n++;
  }
  dmd.clearScreen( true );

  // Now some stationary text on the top half, and scrolling text on the bottom half
  // scrolls for 10 seconds
  dmd.drawString(0,0,"Stays still",11,GRAPHICS_NORMAL); // stationary text
  dmd.drawMarquee("Scrolling text",14,0,8); // set up the marquee
  time = millis();
  while ((millis() - time)<10000){ // loop for 10 seconds
    dmd.stepSplitMarquee(8,15); // only scroll rows 8 to 15
    delay(30);
  } // a slightly different way to loop for stepping the marquee
    // note that this does not test for completion of the scroll, but continues until
    // the 10 second time has expired
  dmd.clearScreen( true );

  // Now a bit of fun
  dmd.drawString(0,-4,"vvvvvvvvvvv",11,GRAPHICS_NORMAL); // note the position is above a single DMD so
                                                         // only part of the text will be visible
  dmd.drawString(0,13,"^^^^^^^^^^^",11,GRAPHICS_NORMAL); // and this is too far down a single DMD so
                                                         // only part will be visible
  // these 2 lines above use partial characters displayed on the screen by placing the text at non-standard positions
  // to give a graphical highlight effect.
  dmd.drawMarquee("Scrolling text",14,0,5);
  time = millis();
  while ((millis() - time)<10000){ // again we will scroll for 10 seconds
    dmd.stepSplitMarquee(5,11); 
    delay(30);
  }
  dmd.clearScreen( true );
}
Of course you will need to exercise a bit of care with placement of text, the size of the text, and the rows you specify to step in the new function. I have added comments throughout to help explain what is going on.

I hope someone else finds a use for it.

UPDATE:
I updated the stepSplitMarquee() code above to allow for multi-dimensional DMD arrays. Original version only worked for single DMD high but multiple DMDs wide.

Brissieboy
Last edited by Brissieboy on Mon Jan 26, 2015 3:14 am, edited 1 time in total.

ariess128
Posts: 1
Joined: Fri Jan 23, 2015 9:32 am

Re: Split screen marquee/scroll

Post by ariess128 » Fri Jan 23, 2015 9:49 am

my led configure is:
#define DISPLAYS_ACROSS 3
#define DISPLAYS_DOWN 2

how to scroll Line-1 and Line 2 stay?

dmd.drawString(10,0,"1234567890",10,GRAPHICS_NORMAL);// stay TEXT line 1
dmd.drawMarquee(Text,Long_word,(32*DISPLAYS_ACROSS)-0,16); // Scroll text Line 2
timess=millis();
bool ret =false;

while (!ret)
{
wdt_reset();
if ((timess+100) < millis())
{
ret = dmd.stepSplitMarquee(16,32);
timess = millis();
wdt_reset();
}
}


i try this but not success, can hel me sir? thans..!

Brissieboy
Posts: 141
Joined: Fri Sep 20, 2013 7:25 am

Re: Split screen marquee/scroll

Post by Brissieboy » Sat Jan 24, 2015 2:07 am

ariess128
This split scroll was made for my own specific requirement at the time. It will only work properly with a single DMD in height, but multiple DMDs across. It ignores DISPLAYS_DOWN and just assumes it is 1. This was an oversight on my part, but it suited my needs. Sorry.

There is only a couple of lines where the work is done, and there are some comments that may help, so have a play around with the code.

If I get a chance I will try to have another look at it in the next week or so.

Brissieboy
Posts: 141
Joined: Fri Sep 20, 2013 7:25 am

Re: Split screen marquee/scroll

Post by Brissieboy » Mon Jan 26, 2015 3:17 am

ariess128
I have updated the code in the above message to handle multi-dimensional DMD arrays. I have only tested it with 2 DMDs as that is all I have available at the moment.

Try it with your array. It should now work.

Loki
Posts: 18
Joined: Mon Jul 06, 2015 7:12 pm

Re: Split screen marquee/scroll

Post by Loki » Thu Aug 18, 2016 5:41 pm

Hi I updated your code to start scroll from specific position:

DMD.cpp:

Code: Select all

boolean DMD::stepSplitMarquee(int topRow, int bottomRow,int StartColumn)
{
  boolean ret=false;
  marqueeOffsetX += -1;  // only scroll horizontally, 1 place left
  if (marqueeOffsetX-StartColumn < -marqueeWidth) { // if text has scrolled off the screen
    marqueeOffsetX = DMD_PIXELS_ACROSS * DisplaysWide; // set up to scroll again from the far right
    drawFilledBox(StartColumn, topRow, ((7*32-StartColumn)/32)*DMD_PIXELS_ACROSS-1, bottomRow, GRAPHICS_INVERSE); // clear the scroll rows
    ret=true; // indicates that this scroll has completed
  }

// This is the main change from the original function.
// It splits the left shift task into rows and bytes within a row to allow rows to be identified
// and treated separately.
  for (int row=topRow ;row<=bottomRow; row++) { // loop for each row in the scroll area
    for (int byteInRow=(StartColumn/8); byteInRow<DisplaysWide*4; byteInRow++) { // loop for each byte within a row (4 per DMD across)
      int thisIndex = (DisplaysTotal*4)*(row%16) + (4*DisplaysWide*(row/16)) + byteInRow; //calculate index into screen buffer
      if ((byteInRow%(DisplaysWide*4)) == (DisplaysWide*4) -1) {  // if it's the last byte in a row
        bDMDScreenRAM[thisIndex]=(bDMDScreenRAM[thisIndex]<<1)+1;
        // shift bits left, and puts a '1' in last position (a '1' = LED OFF)
   
      } else {                                            // if it's NOT the last byte in a row
        bDMDScreenRAM[thisIndex]=(bDMDScreenRAM[thisIndex]<<1) + 
                    ((bDMDScreenRAM[thisIndex+1] & 0x80) >>7);
        // shift bits left as well as the shifting the MSB of the next byte into the LSB
      }
    }
  }
  // Redraw last char on screen
  // required because
  int strWidth=marqueeOffsetX;
  for (byte i=0; i < marqueeLength; i++) {
    int wide = charWidth(marqueeText[i]);
    if (strWidth+wide >= DisplaysWide*DMD_PIXELS_ACROSS) {
      drawChar(strWidth, marqueeOffsetY,marqueeText[i],GRAPHICS_NORMAL);
      return ret;
    }
    strWidth += wide+1;
  }
  return ret;
}
DMD.h

Code: Select all

    //Move the marquee left 1 place - special case of stepMarquee()
  // which only scrolls a limited portion of the display
  boolean stepSplitMarquee(int topRow, int bottomRow,int StartColumn);
video: https://youtu.be/wRZnh1gXbrU

Thank you for your Code

Post Reply