#if 0

#include <SPI.h>

enum LIGNES : const uint8_t
{
  LIGNE_TEMPS_ACTUEL,
  LIGNE_TEMPS_RECORD,
};


void configurerRegistre( const uint8_t registre, const uint8_t valeur )
{
  digitalWrite( SS, LOW );
  
  // envoyer au deuxième MAX7219
  SPI.transfer( registre );
  SPI.transfer( valeur );

  // envoyer au premier MAX7219
  SPI.transfer( registre );
  SPI.transfer( valeur );

  digitalWrite( SS, HIGH );
}

void configurerRegistre1( const uint8_t registre, const uint8_t valeur )
{
  digitalWrite( SS, LOW );
  
  // envoyer au deuxième MAX7219
  SPI.transfer( 0 );
  SPI.transfer( 0 );

  // envoyer au premier MAX7219
  SPI.transfer( registre );
  SPI.transfer( valeur );

  digitalWrite( SS, HIGH );
}

void configurerRegistre2( const uint8_t registre, const uint8_t valeur )
{
  digitalWrite( SS, LOW );
  
  // envoyer au deuxième MAX7219
  SPI.transfer( registre );
  SPI.transfer( valeur );

  // envoyer au premier MAX7219
  SPI.transfer( 0 );
  SPI.transfer( 0 );

  digitalWrite( SS, HIGH );
}


void afficherCaractere( const uint8_t ligne, const uint8_t afficheur, const uint8_t caractere )
{
  digitalWrite( SS, LOW );
  
  // si c'est la ligne du premier MAX7219
  if ( ligne == 0 )
  {
    SPI.transfer16( 0 );           // envoyer 2 NOP au deuxième MAX7219
    SPI.transfer( afficheur + 1 ); // envoyer l'adresse du registre de l'afficheur
    SPI.transfer( caractere );     // envoyer le caractère à afficher
  }
  // sinon
  else
  {
    SPI.transfer( afficheur + 1 ); // envoyer l'adresse du registre de l'afficheur
    SPI.transfer( caractere );     // envoyer le caractère à afficher
    SPI.transfer16( 0 );           // envoyer 2 NOP au premier MAX7219
  }
  
  digitalWrite( SS, HIGH );
}


void afficherTemps( const uint8_t ligne, const uint32_t millisecondes )
{
  // convertir les millisecondes en minutes, secondes et centièmes
  const uint32_t t = millisecondes / 1000;
  const uint16_t ms = millisecondes % 1000;
  const uint8_t m = t / 60;
  const uint8_t s = t % 60;
  const uint8_t c = ( ligne == LIGNE_TEMPS_ACTUEL ) ? uint8_t( ( ms / 250 ) + 0.5 ) * 25 : ms / 10;
  
  // afficher les 6 chiffres
  afficherCaractere( ligne, 0, m / 10 ); // dizaines des minutes
  afficherCaractere( ligne, 1, m % 10 ); // unités des minutes
  afficherCaractere( ligne, 2, s / 10 ); // dizaines des secondes
  afficherCaractere( ligne, 3, s % 10 ); // unités des secondes
  afficherCaractere( ligne, 4, c / 10 ); // dizaines des centièmes
  afficherCaractere( ligne, 5, c % 10 ); // unités des centièmes
}


void remplirLigne( const uint8_t ligne, const uint8_t caractere )
{
  uint8_t c = caractere;
  
  // si nécessaire, convertir le caractère en "code B"
  if ( c == ' ' )
  {
    c = 0b1111;
  }
  else if ( c == '-' )
  {
    c = 0b1010;
  }
  
  for ( uint8_t i = 0; i < 6; ++i )
  {
    afficherCaractere( ligne, i, c );
  }
}

void setup()
{
  Serial.begin( 115200 );
  SPI.begin();

  //configurerRegistre( 0x9, 0xFF ); // Decode Mode  : code B
  //configurerRegistre( 0x9, 0x00 ); // Decode Mode  : no decode
  configurerRegistre( 0xA,   15 ); // Intensity    : maximale
  configurerRegistre( 0xB,  0x7 ); // Scan Limit   : 6 afficheurs
  configurerRegistre( 0xC,    1 ); // Shutdown     : non
  configurerRegistre( 0xF,    0 ); // Display Test : désactivé

  //configurerRegistre( 0x7, 0b10000111 );
  //configurerRegistre( 0x8, 0b10001110 );

  //remplirLigne( LIGNE_TEMPS_ACTUEL, 0 );   // 00 00 00
  //remplirLigne( LIGNE_TEMPS_RECORD, '-' ); // -- -- --
#if 0
  configurerRegistre2( 0xE, 1 ); // Shutdown     : non

  configurerRegistre1( 0xC, 1 ); // Shutdown     : non
  delay(2000);
  configurerRegistre1( 0xC, 0 ); // Shutdown     : non
  configurerRegistre1( 0xB,  0x2 ); // Scan Limit
  delay(2000);
  configurerRegistre1( 0xC, 1 ); // Shutdown     : non
  delay(2000);
  configurerRegistre1( 0xC, 0 ); // Shutdown     : non
  delay(2000);
  configurerRegistre1( 0xC, 1 ); // Shutdown     : non
  delay(2000);
  configurerRegistre1( 0xC, 0 ); // Shutdown     : non
  delay(2000);
  configurerRegistre1( 0xC, 1 ); // Shutdown     : non
  delay(2000);
  configurerRegistre1( 0xC, 0 ); // Shutdown     : non
  delay(2000);
  configurerRegistre1( 0xC, 1 ); // Shutdown     : non
  delay(2000);


  configurerRegistre2( 0xC, 0 ); // Shutdown     : non
  configurerRegistre2( 0xB,  0x0 ); // Scan Limit
  delay(2000);
  configurerRegistre2( 0xC, 1 ); // Shutdown     : non
  delay(2000);
  configurerRegistre2( 0xC, 0 ); // Shutdown     : non
  configurerRegistre2( 0xB,  0x1 ); // Scan Limit
  delay(2000);
  configurerRegistre2( 0xC, 1 ); // Shutdown     : non
  delay(2000);
  configurerRegistre2( 0xC, 0 ); // Shutdown     : non
  configurerRegistre2( 0xB,  0x5 ); // Scan Limit
  configurerRegistre2( 0x9, 0b10101010 );
  delay(2000);
  configurerRegistre2( 0xC, 1 ); // Shutdown     : non
  delay(2000);
  configurerRegistre2( 0xC, 0 ); // Shutdown     : non
  delay(2000);
  configurerRegistre2( 0xC, 1 ); // Shutdown     : non
  configurerRegistre2( 0xB,  0x5 ); // Scan Limit
  delay(2000);
  //configurerRegistre( 0b10101111,  0b01010000 );
#endif
}

void loop()
{
#if 0
  uint32_t t1 = millis();
  static uint32_t t2 = t1;
  static uint32_t t3 = t1;

  //afficherTemps( LIGNE_TEMPS_ACTUEL, t1 );
  if ( t1 - t2 > 2000 )
  {
    static bool n = true;
    n = !n;
    if ( n )
    {
      //configurerRegistre( 0xF,  0 );
    }
    else
    {
      //configurerRegistre( 0xF,  1 );
    }

    static uint8_t counter2 = 0;

    //configurerRegistre( 0xB,  counter2 );
    if ( ++counter2 == 0x8 )
    {
      counter2 = 0;
    }

    t2 = t1;
    static uint32_t counter = 10000;
    //afficherTemps( LIGNE_TEMPS_ACTUEL, ++counter );
  }
  if ( t1 - t3 > 50 )
  {
    t3 = t1;
    //afficherTemps( LIGNE_TEMPS_RECORD, t3 );
  }
  //afficherTemps( LIGNE_TEMPS_ACTUEL, t1 );
#endif
}

#endif

#if 0

int latchPin = 10;
//Pin connected to SH_CP of 74HC595
int clockPin = 13;
////Pin connected to DS of 74HC595
int dataPin = 11;

void setup2() {
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop3() {
  //count up routine
  for (int j = 0; j < 256; j++) {
    //ground latchPin and hold low for as long as you are transmitting
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, LSBFIRST, 0b11101110);
    shiftOut(dataPin, clockPin, LSBFIRST, 0b00010001);
    shiftOut(dataPin, clockPin, LSBFIRST, 0b11101110);
    shiftOut(dataPin, clockPin, LSBFIRST, 0b00010001);
    //return the latch pin high to signal chip that it
    //no longer needs to listen for information
    //delay(1000);
    digitalWrite(latchPin, HIGH);
    delay(2000);
  }
}

#endif



#if 0

#include <MD_MAX72xx.h>
#include <SPI.h>

#define PRINT(s, v) { Serial.print(F(s)); Serial.print(v); }

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES 1

#define CLK_PIN   13  // or SCK
#define DATA_PIN  11  // or MOSI
#define CS_PIN    10  // or SS

// SPI hardware interface
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary pins
//MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

// Text parameters
#define CHAR_SPACING  1 // pixels between characters

// Global message buffers shared by Serial and Scrolling functions
#define BUF_SIZE  75
char message[BUF_SIZE] = "hi";
bool newMessageAvailable = true;

void readSerial(void)
{
  static uint8_t	putIndex = 0;

  while (Serial.available())
  {
    message[putIndex] = (char)Serial.read();
    if ((message[putIndex] == '\n') || (putIndex >= BUF_SIZE-3))  // end of message character or full buffer
    {
      // put in a message separator and end the string
      message[putIndex] = '\0';
      // restart the index for next filling spree and flag we have a message waiting
      putIndex = 0;
      newMessageAvailable = true;
    }
    else
      // Just save the next char in next location
      message[putIndex++];
  }
}

void printText(uint8_t modStart, uint8_t modEnd, char *pMsg)
// Print the text string to the LED matrix modules specified.
// Message area is padded with blank columns after printing.
{
  uint8_t   state = 0;
  uint8_t   curLen;
  uint16_t  showLen;
  uint8_t   cBuf[8];
  int16_t   col = ((modEnd + 1) * COL_SIZE) - 1;

  mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);

  do     // finite state machine to print the characters in the space available
  {
    switch(state)
    {
      case 0: // Load the next character from the font table
        // if we reached end of message, reset the message pointer
        if (*pMsg == '\0')
        {
          showLen = col - (modEnd * COL_SIZE);  // padding characters
          state = 2;
          break;
        }

        // retrieve the next character form the font file
        showLen = mx.getChar(*pMsg++, sizeof(cBuf)/sizeof(cBuf[0]), cBuf);
        curLen = 0;
        state++;
        // !! deliberately fall through to next state to start displaying

      case 1: // display the next part of the character
        mx.setColumn(col--, cBuf[curLen++]);

        // done with font character, now display the space between chars
        if (curLen == showLen)
        {
          showLen = CHAR_SPACING;
          state = 2;
        }
        break;

      case 2: // initialize state for displaying empty columns
        curLen = 0;
        state++;
        // fall through

      case 3:	// display inter-character spacing or end of message padding (blank columns)
        mx.setColumn(col--, 0);
        curLen++;
        if (curLen == showLen)
          state = 0;
        break;

      default:
        col = -1;   // this definitely ends the do loop
    }
  } while (col >= (modStart * COL_SIZE));

  mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}

void setup()
{
  mx.begin();

  Serial.begin(57600);
  Serial.print("\n[MD_MAX72XX Message Display]\nType a message for the display\nEnd message line with a newline");
}

void loop()
{
  readSerial();
  if (newMessageAvailable)
  {
    PRINT("\nProcessing new message: ", message);
    printText(0, MAX_DEVICES-1, message);
    newMessageAvailable = false;
  }
}

#endif






#if 1 
// Program to exercise the MD_MAX72XX library
//
// Uses most of the functions in the library
#include <MD_MAX72xx.h>

// Turn on debug statements to the serial output
#define  DEBUG  0

#if  DEBUG
#define PRINT(s, x) { Serial.print(F(s)); Serial.print(x); }
#define PRINTS(x) Serial.print(F(x))
#define PRINTD(x) Serial.println(x, DEC)

#else
#define PRINT(s, x)
#define PRINTS(x)
#define PRINTD(x)

#endif

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES	1

#define CLK_PIN   13  // or SCK
#define DATA_PIN  11  // or MOSI
#define CS_PIN    10  // or SS

// SPI hardware interface
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Specific SPI hardware interface
//MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, SPI1, CS_PIN, MAX_DEVICES);
// Arbitrary pins
//MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

// We always wait a bit between updates of the display
#define  DELAYTIME  100  // in milliseconds

void scrollText(const char *p)
{
  uint8_t charWidth;
  uint8_t cBuf[8];  // this should be ok for all built-in fonts

  PRINTS("\nScrolling text");
  mx.clear();

  while (*p != '\0')
  {
    charWidth = mx.getChar(*p++, sizeof(cBuf) / sizeof(cBuf[0]), cBuf);

    for (uint8_t i=0; i<=charWidth; i++)	// allow space between characters
    {
      mx.transform(MD_MAX72XX::TSL);
      if (i < charWidth)
        mx.setColumn(0, cBuf[i]);
      delay(DELAYTIME);
    }
  }
}

void zeroPointSet()
// Demonstrates the use of setPoint and
// show where the zero point is in the display
{
  PRINTS("\nZero point highlight");
  mx.clear();

  if (MAX_DEVICES > 1)
    mx.setChar((2*COL_SIZE)-1, '0');

  for (uint8_t i=0; i<ROW_SIZE; i++)
  {
    mx.setPoint(i, i, true);
    mx.setPoint(0, i, true);
    mx.setPoint(i, 0, true);
    delay(DELAYTIME);
  }

  delay(DELAYTIME*3);
}

void rows()
// Demonstrates the use of setRow()
{
  PRINTS("\nRows 0->7");
  mx.clear();

  for (uint8_t row=0; row<ROW_SIZE; row++)
  {
    mx.setRow(row, 0xff);
    delay(2*DELAYTIME);
    mx.setRow(row, 0x00);
  }
}

void checkboard()
// nested rectangles spanning the entire display
{
  uint8_t chkCols[][2] = { { 0x55, 0xaa }, { 0x33, 0xcc }, { 0x0f, 0xf0 }, { 0xff, 0x00 } };

  PRINTS("\nCheckboard");
  mx.clear();

  for (uint8_t pattern = 0; pattern < sizeof(chkCols)/sizeof(chkCols[0]); pattern++)
  {
    uint8_t col = 0;
    uint8_t idx = 0;
    uint8_t rep = 1 << pattern;

    while (col < mx.getColumnCount())
    {
      for (uint8_t r = 0; r < rep; r++)
        mx.setColumn(col++, chkCols[pattern][idx]);   // use odd/even column masks
      idx++;
      if (idx > 1) idx = 0;
    }

    delay(10 * DELAYTIME);
  }
}

void columns()
// Demonstrates the use of setColumn()
{
  PRINTS("\nCols 0->max");
  mx.clear();

  for (uint8_t col=0; col<mx.getColumnCount(); col++)
  {
    mx.setColumn(col, 0xff);
    delay(DELAYTIME/MAX_DEVICES);
    mx.setColumn(col, 0x00);
  }
}

void cross()
// Combination of setRow() and setColumn() with user controlled
// display updates to ensure concurrent changes.
{
  PRINTS("\nMoving cross");
  mx.clear();
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);

  // diagonally down the display R to L
  for (uint8_t i=0; i<ROW_SIZE; i++)
  {
    for (uint8_t j=0; j<MAX_DEVICES; j++)
    {
      mx.setColumn(j, i, 0xff);
      mx.setRow(j, i, 0xff);
    }
    mx.update();
    delay(DELAYTIME);
    for (uint8_t j=0; j<MAX_DEVICES; j++)
    {
      mx.setColumn(j, i, 0x00);
      mx.setRow(j, i, 0x00);
    }
  }

  // moving up the display on the R
  for (int8_t i=ROW_SIZE-1; i>=0; i--)
  {
    for (uint8_t j=0; j<MAX_DEVICES; j++)
    {
      mx.setColumn(j, i, 0xff);
      mx.setRow(j, ROW_SIZE-1, 0xff);
    }
    mx.update();
    delay(DELAYTIME);
    for (uint8_t j=0; j<MAX_DEVICES; j++)
    {
      mx.setColumn(j, i, 0x00);
      mx.setRow(j, ROW_SIZE-1, 0x00);
    }
  }

  // diagonally up the display L to R
  for (uint8_t i=0; i<ROW_SIZE; i++)
  {
    for (uint8_t j=0; j<MAX_DEVICES; j++)
    {
      mx.setColumn(j, i, 0xff);
      mx.setRow(j, ROW_SIZE-1-i, 0xff);
    }
    mx.update();
    delay(DELAYTIME);
    for (uint8_t j=0; j<MAX_DEVICES; j++)
    {
      mx.setColumn(j, i, 0x00);
      mx.setRow(j, ROW_SIZE-1-i, 0x00);
    }
  }
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}

void bullseye()
// Demonstrate the use of buffer based repeated patterns
// across all devices.
{
  PRINTS("\nBullseye");
  mx.clear();
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);

  for (uint8_t n=0; n<3; n++)
  {
    byte  b = 0xff;
    int   i = 0;

    while (b != 0x00)
    {
      for (uint8_t j=0; j<MAX_DEVICES+1; j++)
      {
        mx.setRow(j, i, b);
        mx.setColumn(j, i, b);
        mx.setRow(j, ROW_SIZE-1-i, b);
        mx.setColumn(j, COL_SIZE-1-i, b);
      }
      mx.update();
      delay(3*DELAYTIME);
      for (uint8_t j=0; j<MAX_DEVICES+1; j++)
      {
        mx.setRow(j, i, 0);
        mx.setColumn(j, i, 0);
        mx.setRow(j, ROW_SIZE-1-i, 0);
        mx.setColumn(j, COL_SIZE-1-i, 0);
      }

      bitClear(b, i);
      bitClear(b, 7-i);
      i++;
    }

    while (b != 0xff)
    {
      for (uint8_t j=0; j<MAX_DEVICES+1; j++)
      {
        mx.setRow(j, i, b);
        mx.setColumn(j, i, b);
        mx.setRow(j, ROW_SIZE-1-i, b);
        mx.setColumn(j, COL_SIZE-1-i, b);
      }
      mx.update();
      delay(3*DELAYTIME);
      for (uint8_t j=0; j<MAX_DEVICES+1; j++)
      {
        mx.setRow(j, i, 0);
        mx.setColumn(j, i, 0);
        mx.setRow(j, ROW_SIZE-1-i, 0);
        mx.setColumn(j, COL_SIZE-1-i, 0);
      }

      i--;
      bitSet(b, i);
      bitSet(b, 7-i);
    }
  }

  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}

void stripe()
// Demonstrates animation of a diagonal stripe moving across the display
// with points plotted outside the display region ignored.
{
  const uint16_t maxCol = MAX_DEVICES*ROW_SIZE;
  const uint8_t	stripeWidth = 10;

  PRINTS("\nEach individually by row then col");
  mx.clear();

  for (uint16_t col=0; col<maxCol + ROW_SIZE + stripeWidth; col++)
  {
    for (uint8_t row=0; row < ROW_SIZE; row++)
    {
      mx.setPoint(row, col-row, true);
      mx.setPoint(row, col-row - stripeWidth, false);
    }
    delay(DELAYTIME);
  }
}

void spiral()
// setPoint() used to draw a spiral across the whole display
{
  PRINTS("\nSpiral in");
  int  rmin = 0, rmax = ROW_SIZE-1;
  int  cmin = 0, cmax = (COL_SIZE*MAX_DEVICES)-1;

  mx.clear();
  while ((rmax > rmin) && (cmax > cmin))
  {
    // do row
    for (int i=cmin; i<=cmax; i++)
    {
      mx.setPoint(rmin, i, true);
      delay(DELAYTIME/MAX_DEVICES);
    }
    rmin++;

    // do column
    for (uint8_t i=rmin; i<=rmax; i++)
    {
      mx.setPoint(i, cmax, true);
      delay(DELAYTIME/MAX_DEVICES);
    }
    cmax--;

    // do row
    for (int i=cmax; i>=cmin; i--)
    {
      mx.setPoint(rmax, i, true);
      delay(DELAYTIME/MAX_DEVICES);
    }
    rmax--;

    // do column
    for (uint8_t i=rmax; i>=rmin; i--)
    {
      mx.setPoint(i, cmin, true);
      delay(DELAYTIME/MAX_DEVICES);
    }
    cmin++;
  }
}

void bounce()
// Animation of a bouncing ball
{
  const int minC = 0;
  const int maxC = mx.getColumnCount()-1;
  const int minR = 0;
  const int maxR = ROW_SIZE-1;

  int  nCounter = 0;

  int  r = 0, c = 2;
  int8_t dR = 1, dC = 1;	// delta row and column

  PRINTS("\nBouncing ball");
  mx.clear();

  while (nCounter++ < 200)
  {
    mx.setPoint(r, c, false);
    r += dR;
    c += dC;
    mx.setPoint(r, c, true);
    delay(DELAYTIME/2);

    if ((r == minR) || (r == maxR))
      dR = -dR;
    if ((c == minC) || (c == maxC))
      dC = -dC;
  }
}

void intensity()
// Demonstrates the control of display intensity (brightness) across
// the full range.
{
  uint8_t row;

  PRINTS("\nVary intensity ");

  mx.clear();
  mx.control(MD_MAX72XX::SCANLIMIT, MAX_SCANLIMIT);
  mx.control(MD_MAX72XX::INTENSITY, 0);
  
  for (int8_t i=0; i<8; i++)
  {
    mx.setRow(i, 0xff);
  }

  delay( 1000 );
  mx.control(MD_MAX72XX::INTENSITY, 2);
  //delay( 1000 );
  //mx.control(MD_MAX72XX::INTENSITY, 4);
  delay( 1000 );
  mx.control(MD_MAX72XX::INTENSITY, 6);
  //delay( 1000 );
  //mx.control(MD_MAX72XX::INTENSITY, 8);
  delay( 1000 );
  mx.control(MD_MAX72XX::INTENSITY, 10);
  //delay(1000);
  //mx.control(MD_MAX72XX::INTENSITY, 12);
  delay(1000);
  mx.control(MD_MAX72XX::INTENSITY, 15);
  //delay(1000);
  //mx.control(MD_MAX72XX::INTENSITY, 2);

  mx.control(MD_MAX72XX::SCANLIMIT, MAX_SCANLIMIT);
  // Grow and get brighter
  /*row = 0;
  for (int8_t i=0; i<=MAX_INTENSITY; i++)
  {
    mx.control(MD_MAX72XX::INTENSITY, i);
    //if (i%2 == 0)
    //  mx.setRow(row++, 0xff);
    //Serial.println( i );
    delay( 100 );
  }

  delay( 2000 );*/
  //mx.control(MD_MAX72XX::INTENSITY, 8);
}

void blinking()
// Uses the test function of the MAX72xx to blink the display on and off.
{
  int  nDelay = 1000;

  PRINTS("\nBlinking");
  mx.clear();

  while (nDelay > 0)
  {
    mx.control(MD_MAX72XX::TEST, MD_MAX72XX::ON);
    delay(nDelay);
    mx.control(MD_MAX72XX::TEST, MD_MAX72XX::OFF);
    delay(nDelay);

    nDelay -= DELAYTIME;
  }
}

void scanLimit(void)
// Uses scan limit function to restrict the number of rows displayed.
{
  PRINTS("\nScan Limit");
  mx.clear();

  //mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
  //delay(2000);
  for (uint8_t row=0; row<ROW_SIZE; row++)
    mx.setRow(row, 0xff);
  //mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
  //delay( 2000);
  mx.control(MD_MAX72XX::SCANLIMIT, MAX_SCANLIMIT);
  delay(1000);
  mx.control(MD_MAX72XX::SCANLIMIT, MAX_SCANLIMIT);
  delay(1000);
  mx.control(MD_MAX72XX::SCANLIMIT, MAX_SCANLIMIT);
  delay(1000);
  for (int8_t s=MAX_SCANLIMIT; s>=0; s--)
  {
    mx.control(MD_MAX72XX::SCANLIMIT, s);
    delay(DELAYTIME*5);
  }
  mx.control(MD_MAX72XX::SCANLIMIT, MAX_SCANLIMIT);
}

void transformation1()
// Demonstrates the use of transform() to move bitmaps on the display
// In this case a user defined bitmap is created and animated.
{
  uint8_t arrow[COL_SIZE] =
  {
    0b00001000,
    0b00011100,
    0b00111110,
    0b01111111,
    0b00011100,
    0b00011100,
    0b00111110,
    0b00000000
  };

  MD_MAX72XX::transformType_t  t[] =
  {
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TFLR,
    MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR,
    MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR,
    MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR,
    MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR,
    MD_MAX72XX::TRC,
    MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD,
    MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD, MD_MAX72XX::TSD,
    MD_MAX72XX::TFUD,
    MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU,
    MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU, MD_MAX72XX::TSU,
    MD_MAX72XX::TINV,
    MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC,
    MD_MAX72XX::TINV
  };

  PRINTS("\nTransformation1");
  mx.clear();

  // use the arrow bitmap
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
  for (uint8_t j=0; j<mx.getDeviceCount(); j++)
    mx.setBuffer(((j+1)*COL_SIZE)-1, COL_SIZE, arrow);
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
  delay(DELAYTIME);

  // run through the transformations
  mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::ON);
  for (uint8_t i=0; i<(sizeof(t)/sizeof(t[0])); i++)
  {
    mx.transform(t[i]);
    delay(DELAYTIME*4);
  }
  mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::OFF);
}

void transformation2()
// Demonstrates the use of transform() to move bitmaps on the display
// In this case font characters are loaded into the display for animation.
{
  MD_MAX72XX::transformType_t  t[] =
  {
    MD_MAX72XX::TINV,
    MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC, MD_MAX72XX::TRC,
    MD_MAX72XX::TINV,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR,
    MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSR, MD_MAX72XX::TSR, MD_MAX72XX::TSR,
    MD_MAX72XX::TSD, MD_MAX72XX::TSU, MD_MAX72XX::TSD, MD_MAX72XX::TSU,
    MD_MAX72XX::TFLR, MD_MAX72XX::TFLR, MD_MAX72XX::TFUD, MD_MAX72XX::TFUD
  };

  PRINTS("\nTransformation2");
  mx.clear();
  mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::OFF);

  // draw something that will show changes
  for (uint8_t j=0; j<mx.getDeviceCount(); j++)
  {
    mx.setChar(((j+1)*COL_SIZE)-1, '0'+j);
  }
  delay(DELAYTIME*5);

  // run thru transformations
  for (uint8_t i=0; i<(sizeof(t)/sizeof(t[0])); i++)
  {
    mx.transform(t[i]);
    delay(DELAYTIME*3);
  }
}

void wrapText()
// Display text and animate scrolling using auto wraparound of the buffer
{
  PRINTS("\nwrapText");
  mx.clear();
  mx.wraparound(MD_MAX72XX::ON);

  // draw something that will show changes
  for (uint16_t j=0; j<mx.getDeviceCount(); j++)
  {
    mx.setChar(((j+1)*COL_SIZE)-1, (j&1 ? 'M' : 'W'));
  }
  delay(DELAYTIME*5);

  // run thru transformations
  for (uint16_t i=0; i<3*COL_SIZE*MAX_DEVICES; i++)
  {
    mx.transform(MD_MAX72XX::TSL);
    delay(DELAYTIME/2);
  }
  for (uint16_t i=0; i<3*COL_SIZE*MAX_DEVICES; i++)
  {
    mx.transform(MD_MAX72XX::TSR);
    delay(DELAYTIME/2);
  }
  for (uint8_t i=0; i<ROW_SIZE; i++)
  {
    mx.transform(MD_MAX72XX::TSU);
    delay(DELAYTIME*2);
  }
  for (uint8_t i=0; i<ROW_SIZE; i++)
  {
    mx.transform(MD_MAX72XX::TSD);
    delay(DELAYTIME*2);
  }

  mx.wraparound(MD_MAX72XX::OFF);
}

void showCharset(void)
// Run through display of the the entire font characters set
{
  mx.clear();
  mx.update(MD_MAX72XX::OFF);

  for (uint16_t i=0; i<256; i++)
  {
    mx.clear(0);
    mx.setChar(COL_SIZE-1, i);

    if (MAX_DEVICES >= 3)
    {
      char hex[3];

      sprintf(hex, "%02X", i);

      mx.clear(1);
      mx.setChar((2*COL_SIZE)-1,hex[1]);
      mx.clear(2);
      mx.setChar((3*COL_SIZE)-1,hex[0]);
    }

    mx.update();
    delay(DELAYTIME*2);
  }
  mx.update(MD_MAX72XX::ON);
}

void setup()
{
  mx.begin();
  mx.control(MD_MAX72XX::INTENSITY, 15);
#if  DEBUG
  Serial.begin(57600);
#endif
  PRINTS("\n[MD_MAX72XX Test & Demo]");
//  scrollText("MD_MAX72xx Test  ");
}

void loop()
{
#if 1
  //scrollText("Graphics");
  //zeroPointSet();
  //rows();
  //columns();
  cross();
  //stripe();
  checkboard();
  //bullseye();
  //bounce();
  //spiral();
#endif

#if 1
  //scrollText("Control");
  intensity();
  scanLimit();
  //blinking();
#endif

#if 0
  scrollText("Transform");
  transformation1();
  transformation2();
#endif

#if 0
  scrollText("Charset");
  wrapText();
  showCharset();
#endif
}

#endif
a:12
a:11
a:10
a:9
a:8
a:7
a:6
a:5
a:4
a:3
a:2
a:GND.2
a:RESET.2
a:0
a:1
a:13
a:3.3V
a:AREF
a:A0
a:A1
a:A2
a:A3
a:A4
a:A5
a:A6
a:A7
a:5V
a:RESET
a:GND.1
a:VIN
a:12.2
a:5V.2
a:13.2
a:11.2
a:RESET.3
a:GND.3
MAX7219Breakout
r1:1
r1:2
m0_r0c0:A
m0_r0c0:C
m0_r0c1:A
m0_r0c1:C
m0_r0c2:A
m0_r0c2:C
m0_r0c3:A
m0_r0c3:C
m0_r0c4:A
m0_r0c4:C
m0_r0c5:A
m0_r0c5:C
m0_r0c6:A
m0_r0c6:C
m0_r0c7:A
m0_r0c7:C
m0_r1c0:A
m0_r1c0:C
m0_r1c1:A
m0_r1c1:C
m0_r1c2:A
m0_r1c2:C
m0_r1c3:A
m0_r1c3:C
m0_r1c4:A
m0_r1c4:C
m0_r1c5:A
m0_r1c5:C
m0_r1c6:A
m0_r1c6:C
m0_r1c7:A
m0_r1c7:C
m0_r2c0:A
m0_r2c0:C
m0_r2c1:A
m0_r2c1:C
m0_r2c2:A
m0_r2c2:C
m0_r2c3:A
m0_r2c3:C
m0_r2c4:A
m0_r2c4:C
m0_r2c5:A
m0_r2c5:C
m0_r2c6:A
m0_r2c6:C
m0_r2c7:A
m0_r2c7:C
m0_r3c0:A
m0_r3c0:C
m0_r3c1:A
m0_r3c1:C
m0_r3c2:A
m0_r3c2:C
m0_r3c3:A
m0_r3c3:C
m0_r3c4:A
m0_r3c4:C
m0_r3c5:A
m0_r3c5:C
m0_r3c6:A
m0_r3c6:C
m0_r3c7:A
m0_r3c7:C
m0_r4c0:A
m0_r4c0:C
m0_r4c1:A
m0_r4c1:C
m0_r4c2:A
m0_r4c2:C
m0_r4c3:A
m0_r4c3:C
m0_r4c4:A
m0_r4c4:C
m0_r4c5:A
m0_r4c5:C
m0_r4c6:A
m0_r4c6:C
m0_r4c7:A
m0_r4c7:C
m0_r5c0:A
m0_r5c0:C
m0_r5c1:A
m0_r5c1:C
m0_r5c2:A
m0_r5c2:C
m0_r5c3:A
m0_r5c3:C
m0_r5c4:A
m0_r5c4:C
m0_r5c5:A
m0_r5c5:C
m0_r5c6:A
m0_r5c6:C
m0_r5c7:A
m0_r5c7:C
m0_r6c0:A
m0_r6c0:C
m0_r6c1:A
m0_r6c1:C
m0_r6c2:A
m0_r6c2:C
m0_r6c3:A
m0_r6c3:C
m0_r6c4:A
m0_r6c4:C
m0_r6c5:A
m0_r6c5:C
m0_r6c6:A
m0_r6c6:C
m0_r6c7:A
m0_r6c7:C
m0_r7c0:A
m0_r7c0:C
m0_r7c1:A
m0_r7c1:C
m0_r7c2:A
m0_r7c2:C
m0_r7c3:A
m0_r7c3:C
m0_r7c4:A
m0_r7c4:C
m0_r7c5:A
m0_r7c5:C
m0_r7c6:A
m0_r7c6:C
m0_r7c7:A
m0_r7c7:C