/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVFlashy.cpp
 * Description:
 *     Movie -- mv_Flashy
 *         Fades all LEDs in and out at random speeds.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistribtued, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Flashy()
{
  unsigned long ts;
  byte y;
  Light lights[27];
  
  for (y = 0 ; y < 27 ; y++)
  {
    lights[y].speed = random(50, 200);
    lights[y].up = true;
    lights[y].t = millis() + lights[y].speed;
  }
  
  ts = millis() + DEF_MOVIE_TIME;

  y = 0;
  while (millis() < ts)
  {
    if (millis() > lights[y].t)
    {
      displayFrame[y] += (lights[y].up ? 1 : -1);
      if (displayFrame[y] == 0 || displayFrame[y] == 7)
        lights[y].up = !lights[y].up;
        
      lights[y].t = millis() + lights[y].speed;
    }
    
    if (++y > 26)
      y = 0;
  }
  
  df_fadeOut();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVEdgeTumble.cpp
 * Description:
 *     Movie -- mv_EdgeTumble
 *         LEDs are lit along one edge, and then it "tumbles" from
 *         edge to edge randomly. The LED in the center is faded
 *         in and out.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

const byte TUMBLESPEED = 25;
const byte TUMBLEDELAY = 150;
const byte FLASHERSPEED = 50;

int getN(byte, byte);

int m[7][2][2] = { 
    {{1, 0}, {2, 0}},
    {{1, 0}, {2, 1}},
    {{1, 1}, {2, 1}},
    {{1, 1}, {2, 2}},
    {{1, 1}, {1, 2}},
    {{0, 1}, {1, 2}},
    {{0, 1}, {0, 2}}
};

unsigned long ft = 0;
boolean flashUp = true;
boolean flashOn = true;

void delayAndFlash(int d)
{
  unsigned long t, ct;
  byte a;
  
  t = millis() + d;
  
  do
  {
    ct = millis();
    
    if (ct > ft)
    {
      a = displayFrame[13];
      if (flashUp)
      {
        if (flashOn)
          if (++a >= MAXBRIGHTNESSLEVEL)
            flashUp = false;
      }
      else
      {
        if (--a < 1)
          flashUp = true;
      }
      displayFrame[13] = a;
      
      ft = ct + FLASHERSPEED;
    }
  } while (ct < t);
}

void mv_EdgeTumble()
{
  unsigned long ts;
  byte a, b, f, l;
  int nx, ny, nz;
  int px, py, pz;
  byte fn, fd;
  Point p[3], v;
  
  ft = millis() + 1000;
  flashUp = (displayFrame[13] < MAXBRIGHTNESSLEVEL);
  flashOn = true;
    
  ts = millis() + (DEF_MOVIE_TIME * 2);
  l = 7;
  
  // Random start point.
  
  a = random(0, 8);
  p[0].Set((a & 1) * 2, (a & 2), (a & 4) / 2);
  
  // Random starting direction.
  
  nx = 0;
  ny = 0;
  nz = 0;
  switch(random(0, 3))
  {
    case 0:
      nx = (p[0].x == 0) ? 1 : -1;
      break;
      
    case 1:
      ny = (p[0].y == 0) ? 1 : -1;
      break;
      
    case 2:
      nz = (p[0].z == 0) ? 1 : -1;
      break;
  }
  
  // Draw initial line.
  
  for (a = 1 ; a < 3 ; a++)
    p[a].Set(p[0].x+(nx*a), p[0].y+(ny*a), p[0].z+(nz*a));
    
  displayFrame[p[0].Pos()] = 7;
  displayFrame[p[1].Pos()] = 7;
  displayFrame[p[2].Pos()] = 7;
  
  while (l > 0)
  {
    if (millis() > ts)
    {
      --l;
      flashOn = false;
    }

    px = 0;
    py = 0;
    pz = 0;

    fn = 1;
    fd = 1;
        
    switch(random(0, 2))
    {
      case 0:
        px = (p[0].x == 0) ? 1 : -1;

        if (nx != 0 || nz != 0)
          pz = (p[0].z == 0) ? 1 : -1;

        if (ny != 0)
          py = (p[0].y == 0) ? 1 : -1;
          
        if (ny != 0 || nz != 0)
        {
          fn = 5;
          fd=255;
        }
        break;
        
      case 1:
        py = (p[0].y == 0) ? 1 : -1;

        if (nx != 0)
          px = (p[0].x == 0) ? 1 : -1;
        
        if (ny != 0 || nz != 0)
          pz = (p[0].z == 0) ? 1 : -1;
          
        if (nz != 0)
        {
          fn = 5;
          fd=255;
        }
        break;
    }
       
    displayFrame[p[0].Pos()] = l;
    
    for (f = 0 ; f < 6 ; f++, fn += fd)
    {
      for (a = 1, b = 0 ; a < 3 ; a++, b++)
      {
        displayFrame[p[a].Pos()] = 0;
        
        if (px == 0)
        {
          p[a].y = p[0].y + (m[fn][b][0] * py);
          p[a].z = p[0].z + (m[fn][b][1] * pz);
        }
        else if (py == 0)
        {
          p[a].x = p[0].x + (m[fn][b][0] * px);
          p[a].z = p[0].z + (m[fn][b][1] * pz);
        }
        else
        {
          p[a].x = p[0].x + (m[fn][b][0] * px);
          p[a].y = p[0].y + (m[fn][b][1] * py);
        }
        
        displayFrame[p[a].Pos()] = l;
      }      
            
      delayAndFlash(TUMBLESPEED);
    }
   
    v = p[0];
    p[0] = p[2];
    p[2] = v; 

    nx = getN(p[0].x, p[2].x);
    ny = getN(p[0].y, p[2].y);
    nz = getN(p[0].z, p[2].z);
    
    delayAndFlash(TUMBLEDELAY);
  }
  
  df_fadeOut();
}

int getN(byte n1, byte n2)
{
  if (n1 == n2)
    return 0;
  else if (n1 < n2)
    return 1;
  else
    return -1;
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVEdgeChase.cpp
 * Description:
 *     Movie -- mv_EdgeChase
 *         LEDs are lit along an edge from one corner to another
 *         at a random speed. After a random delay another edge
 *         is lit. If the lighting of an edge runs in to an already
 *         lit LED, then the display is cleared and it starts again.
 *
 *     Movie -- mv_EdgeChase2
 *         LEDs are lit along an edge from one corner to another,
 *         but as it moves along, each previous LED is dimmed
 *         until it goes out.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_EdgeChase()
{
  unsigned long ts, t;
  byte a, b, x, y, z;
  int nx, ny, nz;
  boolean r = false;
  int d1, d2;
  
  ts = millis() + DEF_MOVIE_TIME;

  d1 = random(40, 120);
  d2 = random(20, 60);
  
  a = random(0, 8);
  
  x = (a & 1) * 2;
  y = (a & 2);
  z = (a & 4) / 2;
  
  a = 3;
  
  displayFrame[POS(x, y, z)] = MAXBRIGHTNESSLEVEL;
  
  while (millis() < ts)
  {
    t = millis() + d1;
        
    nx = 0;
    ny = 0;
    nz = 0;
    
    do
    {
      b = random(0, 3);
    } while (a == b);

    a = b;
    
    switch(a)
    {
      case 0:
        nx = (x == 0) ? 1 : -1;
        break;
        
      case 1:
        ny = (y == 0) ? 1 : -1;
        break;
        
      case 2:
        nz = (z == 0) ? 1 : -1;
        break;
    }
    
    while (millis() < t)
      ;
    
    if (r)
      df_clear();
    
    displayFrame[POS(x, y, z)] = 4;
    displayFrame[POS(x+nx,y+ny,z+nz)] = MAXBRIGHTNESSLEVEL;
    
    delay(d2);
    
    displayFrame[POS(x+nx,y+ny,z+nz)] = 4;
    r = (displayFrame[POS(x+nx+nx,y+ny+ny,z+nz+nz)] != 0);
    displayFrame[POS(x+nx+nx,y+ny+ny,z+nz+nz)] = MAXBRIGHTNESSLEVEL;
    
    delay(d2);
    
    displayFrame[POS(x+nx,y+ny,z+nz)] = 1;
    
    x = x + nx + nx;
    y = y + ny + ny;
    z = z + nz + nz;
  }

  df_clear();
}

void mv_EdgeChase2()
{
  unsigned long ts, t;
  byte a, b, d, i, j, k, x, y, z;
  int nx, ny, nz;
  byte ss, sl;
  
  ts = millis() + DEF_MOVIE_TIME;

  d = random(50, 200);
  
  a = random(0, 8);
  
  x = (a & 1) * 2;
  y = (a & 2);
  z = (a & 4) / 2;
  points[0].Set(x, y, z);
  ss = 0;
  sl = 1;
  
  a = 3;
  
  displayFrame[points[0].Pos()] = MAXBRIGHTNESSLEVEL;

  t = millis() + d;
          
  while (millis() < ts)
  {
    nx = 0;
    ny = 0;
    nz = 0;
    
    do
    {
      b = random(0, 3);
    } while (a == b);

    a = b;
    
    switch(a)
    {
      case 0:
        nx = (x == 0) ? 1 : -1;
        break;
        
      case 1:
        ny = (y == 0) ? 1 : -1;
        break;
        
      case 2:
        nz = (z == 0) ? 1 : -1;
        break;
    }
    
    for (i = 0 ; i < 2 ; i++)
    {
      while (millis() < t)
        ;
        
      j = ss;
      for (k = 0 ; k < sl ; k++)
      {
        if (displayFrame[points[j].Pos()] > 0)
          --displayFrame[points[j].Pos()];
          
        if (j == 0)
          j = 7;
        else
          --j;
      }
      
      if (++ss > 7)
        ss = 0;
      
      if (sl < 8)
        ++sl;
  
      x = x + nx;
      y = y + ny;
      z = z + nz;
  
      points[ss].Set(x, y, z);
      displayFrame[points[ss].Pos()] = MAXBRIGHTNESSLEVEL;
      
      t = millis() + d;
    }
  }

  df_clear();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVCounter.cpp
 * Description: 
 *     Movie -- mv_Counter
 *         LED on bottom plane moves around the edge of the
 *         plane. On each complete circuit, the LED on the
 *         plane above moves along one, and when that has done
 *         a complete circuit, the led on the top plane moves
 *         along one.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

struct Item
{
  public:
    byte x;
    byte d;
    
    Item() : x(0), d(0) {};
    
    boolean inc()
    {
      boolean o = false;
      
      switch(d)
      {
        case 0:
          if (++x == 2)
            d = 1;
          break;
          
        case 1:
          x += 9;
          if (x > 18)
            d = 2;
          break;
          
        case 2:
          if (--x == 18)
            d = 3;
          break;
          
        case 3:
          x -= 9;
          if (x < 9)
          {
            d = 0;
            o = true;
          }
          break;
      }
      
      return o;
    }
};

void mv_Counter()
{
  Item i[3];
  unsigned long ts, t;
  boolean o;
  int del;
  
  i[0].x = 0;
  i[1].x = 0;
  i[2].x = 0;
  
  displayFrame[i[0].x] = 7;
  displayFrame[i[1].x + 3] = 7;
  displayFrame[i[2].x + 6] = 7;
  
  delay(1000);
  
  del = random(20, 70);
  
  ts = millis() + (DEF_MOVIE_TIME * 2);

  while (millis() < ts)
  {
    t = millis() + del;
    
    displayFrame[i[0].x] = 0;
    o = i[0].inc();
    displayFrame[i[0].x] = 7;
    if (o)
    {
      displayFrame[i[1].x + 3] = 0;
      o = i[1].inc();
      displayFrame[i[1].x + 3] = 7;
      if (o)
      {
        displayFrame[i[2].x + 6] = 0;
        i[2].inc();
        displayFrame[i[2].x + 6] = 7;
      }
    }
    
    while (millis() < t)
      ;
  }  
    
  df_fadeOut();
    
  delay(500);
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVAllOn.cpp
 * Description:
 *     Movie -- mv_AllOn
 *         Turn all LEDs on for a period, then fade out.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_AllOn()
{
  unsigned long ts;
  
  ts = millis() + (DEF_MOVIE_TIME / 10);

  df_allOn();
  
  while (millis() < ts)
    ;
    
  df_fadeOut();
    
  delay(500);
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: DisplayFrame.h
 * Description: Header file for DisplayFrame.cpp.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#ifndef DisplayFrame_h
#define DisplayFrame_h

#include "Arduino.h"

typedef byte (*POSTransFPtr)(byte, byte, byte);

byte POSTrans0(byte, byte, byte);
byte POSTrans1(byte, byte, byte);
byte POSTrans2(byte, byte, byte);
byte POSTrans3(byte, byte, byte);
byte POSTrans4(byte, byte, byte);
byte POSTrans5(byte, byte, byte);
byte POSTrans6(byte, byte, byte);
byte POSTrans7(byte, byte, byte);

POSTransFPtr GetPOSTrans(byte);

#define POS(x, y , z)  (((y)*9)+((x)*3)+(z))

struct DisplayParameters
{
  public:
    byte subFrame;
    byte subFrameBit;
    byte x;

    DisplayParameters() : subFrame(0), subFrameBit(1), x(0) {};
};

struct Point
{
  public:
    byte x;
    byte y;
    byte z;

    Point() : x(0), y(0), z(0) {};
    Point(byte a, byte b, byte c) : x(a), y(b), z(c) {};

    void Set(byte a, byte b, byte c)
    {
      x = a;
      y = b;
      z = c;
    }

    byte Pos()
    {
      return POS(x, y, z);
    }
};

class Light
{
  public:
    byte speed;
    boolean up;
    unsigned long t;
};

const byte MAXBRIGHTNESSLEVEL = 7;

extern byte displayFrame[27];
extern Point points[8];
//extern const byte brightnessSubFrames[8];

typedef boolean (*df_processAllFunc)(int *, byte);

void df_init();
void updateDisplay();
void df_allOn();
void df_clear();
boolean df_fadeOut(int = 50);
boolean df_processAll(df_processAllFunc, int *);

void df_DisplayMonoFrame(POSTransFPtr, unsigned long);

#endif
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: DisplayFrame.cpp
 * Description: Functions for updating the LED cube.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "DisplayFrame.h"
#include "DirectIO.h"
#include "ShiftReg_7495.h"
#include "Counter_4017.h"

const byte brightnessSubFrames[] = {B00000000, B00000001, B01001001, B01010101, B01011101, B01110111, B11110111, B11111111};

boolean dfpa_fadeOut(int *, byte);

byte displayFrame[27];

Point points[8]; // An array of points that animations can use.

volatile DisplayParameters dispParams;

ShiftReg_7495 DisplayZ(4, 10, 11, 12); // Parallel mode: Clock on pin 4, A on pin 10, B on pin 11 and C on pin 12. NOT ACTUALLY USING PIN 4 - PINS 10, 11 and 12 WILL DRIVE THE DISPLAY DIECTLY.
Counter_4017 DisplayXY(7, 6); // Clock pin 7, last element detect pin 6.

// Used for timing metrics in the updateDisplay function.

volatile boolean gotupt = false;
volatile unsigned int upt = 0;
volatile unsigned int upc = 0;

void df_init()
{
  df_clear();
  attachInterrupt(0, updateDisplay, RISING); // Interrupt 0 is on pin 2 (DISPLAYCLOCKPIN).
}

// Called by interrupt 0.
// Displays a 3 LED segment on each interrupt.

void updateDisplay()
{
  byte b, c, d;
  unsigned long st = 0, et = 0;

  // Timing metrics.
  
  if (gotupt == false)
  {
    st = micros(); // micros() still works in an interrupt - apparently.
  }
  
  DisplayZ.clear();    // Clear data lines.
  DisplayXY.clock();   // Trigger 4017 - move to the next row to display on.

  // Move to next row to display.
  
  dispParams.x += 3;
  if (dispParams.x > 26)
  {
    dispParams.x = 0;
  
    // Subframe for dimming LED.
    
    if (++dispParams.subFrame > MAXBRIGHTNESSLEVEL)
    {
      dispParams.subFrame = 0;
      dispParams.subFrameBit = 1;
    }
    else
      dispParams.subFrameBit <<= 2;
  }
  
  // Set bits to be displayed.
  
  c = dispParams.x;
  for (b = 0 ; b < 3 ; b++)
  {
    DisplayZ.setBit(b, (brightnessSubFrames[displayFrame[c]] & dispParams.subFrameBit) != 0);
    ++c;
  }
  
  /*
    This loop is a delay to allow the triggering of the 4017 (DisplayXY) to propagate.
    The 4017 can take up to 530ns from tigger to output at 5V, according to data sheet.

    Without this delay, we will see the new data being applied to the current data lines
    before the 4017 has acted. So you get a ghost of the next set of data on the current
    leds.
  */
  
  for (d = 0 ; d < 9 ; d++)
    digitalRead(0);

  // Display the bits (LEDs).
  
  DisplayZ.send();

  // Timing metrics.
  
  if (gotupt == false && st > 0)
  {
    et = micros();
    upt = et - st;
    st = 0;
    gotupt = true;
  }
  
  ++upc;
}

void df_allOn()
{
  for (byte i = 0 ; i < 27 ; i++)
    displayFrame[i] = MAXBRIGHTNESSLEVEL;
}

void df_clear()
{
  for (byte i = 0 ; i < 27 ; i++)
    displayFrame[i] = 0;
}

boolean df_fadeOut(int delayTime)
{
  boolean faded = false;
  int changed = 0;

  while (!faded)
  {  
    changed = 0;
    
    df_processAll(dfpa_fadeOut, &changed);
    faded = (changed == 0);
    delay(delayTime);    
  }
  
  return faded;
}

boolean dfpa_fadeOut(int *data, byte cell)
{
  if (displayFrame[cell] > 0)
  {
    displayFrame[cell] -= 1;
    *data += 1;
  }
  
  return true;
}

boolean df_processAll(df_processAllFunc func, int *data)
{
  byte x;
  boolean success = true;
  
  for (x = 0 ; x < 27 ; x++)
  {
    if (!func(data, x))
    {
      success = false;
      continue;
    }
  }
  
  return success;
}

POSTransFPtr GetPOSTrans(byte t)
{
  switch(t)
  {
    case 1:
      return POSTrans1;
      break;
    
    case 2:
      return POSTrans2;
      break;
      
    case 3:
      return POSTrans3;
      break;
    
    case 4:
      return POSTrans4;
      break;
      
    case 5:
      return POSTrans5;
      break;
      
    case 6:
      return POSTrans6;
      break;
    
    case 7:
      return POSTrans7;
      break;
      
    default:
      return POSTrans0;
      break;
  }
}

byte POSTrans0(byte x, byte y, byte z)
{
  return(POS(x, y, z));
}

byte POSTrans1(byte x, byte y, byte z)
{
  return(POS(2-x, y, z));
}

byte POSTrans2(byte x, byte y, byte z)
{
  return(POS(x, 2-y, z));
}

byte POSTrans3(byte x, byte y, byte z)
{
  return(POS(2-x, 2-y, z));
}

byte POSTrans4(byte x, byte y, byte z)
{
  return(POS(x, y, 2-z));
}

byte POSTrans5(byte x, byte y, byte z)
{
  return(POS(2-x, y, 2-z));
}

byte POSTrans6(byte x, byte y, byte z)
{
  return(POS(x, 2-y, 2-z));
}

byte POSTrans7(byte x, byte y, byte z)
{
  return(POS(2-x, 2-y, 2-z));
}

// Each bit in "box" represents an LED in the display; 0=Off, 1 = On.

void df_DisplayMonoFrame(POSTransFPtr trans, unsigned long box)
{
  register byte x = 0, y = 0, z = 0;
  
  for (int i = 0 ; i < 27 ; i++)
  {
    displayFrame[trans(x, y, z)] = (box & 1) == 1 ? MAXBRIGHTNESSLEVEL : 0;
    box >>= 1;
    if (++x > 2)
    {
      x = 0;
      if (++y > 2)
      {
        y = 0;
        ++z;
      }
    }
  }
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: _3x3x3_Mono.ino
 * Description: 3x3x3 mono LED cube using Arduino UNO.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "DirectIO.h"
#include "Counter_4017.h"
#include "ShiftReg_7495.h"

#include "DisplayFrame.h"
#include "Movies.h"

const int DISPLAYCLOCKPIN = 2; // Must be pin 2, for interrupt 0.
const int CLOCKGENPIN = 3; // Pin that will generate the interrupt pulse. Could use external pulse generator instead.

void setup()
{  
  pinMode(DISPLAYCLOCKPIN, INPUT);
  pinMode(CLOCKGENPIN, OUTPUT);
  
  // Set PWM on pin 3 to about 3.9KHz and start it.
  TCCR2B = (TCCR2B & B11111000) | B010; 
  analogWrite(CLOCKGENPIN, 127);

  // Seed the random number generator.  
  randomSeed(analogRead(0));

  // Initialise the displayFrame functions.
  df_init();
}

void loop() 
{
  static byte a = 255;
  byte b;
  
  do
  {
    b = random(0, 14);
  } while (b == a);
  a = b;

  switch(b)
  {
    case 0:
      mv_Boxy();
      break;
      
    case 1:    
      mv_EdgeChase2();
      break;
      
    case 2:    
      mv_Snake();
      break;
      
    case 3:    
      mv_Counter();
      break;
      
    case 4:    
      mv_EdgeTumble();
      break;
      
    case 5:    
      mv_Flashy();
      break;
      
    case 6:    
      mv_AllOn();
      break;
      
    case 7:    
      mv_Rain();
      break;
      
    case 8:    
      mv_EdgeChase();
      break;
      
    case 9:    
      mv_Jitter();
      break;
      
    case 10:    
      mv_Pulse();
      break;
      
    case 11:    
      mv_TwinkleAll();
      break;
      
    case 12:
      mv_Lightning();
      break;
      
    default:    
      mv_Glitter();
      break;
  }
}

4017 Decade Counter

Counter_4017.h

/* Project: 4017 Decade Counter Interfacing.
 * File: Counter_4017.h
 * Description: Interface with a 4017 decade counter.
 *     Uses the DirectIO library.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#ifndef Counter_4017_h
#define Counter_4017_h

#include "Arduino.h"
#include "DirectIO.h"

class Counter_4017
{
  public:
    Counter_4017(byte clockPin, byte lastElementDetectPin);

    void clock();
    void clockPrime();
    void clockTrigger();
    void clear();
    boolean onLastElement();

  private:
    DIO_SetPinFPtr _clockPin;
    byte _lastElementDetectPin;
};

#endif

Counter_4017.cpp

/* Project: 4017 Decade Counter Interfacing.
 * File: Counter_4017.cpp
 * Description: Interface with a 4017 decade counter.
 *     Uses the DirectIO library.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantability or fitness of purpose.
 */

#include "Arduino.h"
#include "Counter_4017.h"

Counter_4017::Counter_4017(byte clockPin, byte lastElementDetectPin)
{
  pinMode(clockPin, OUTPUT);
  pinMode(lastElementDetectPin, INPUT);

  _clockPin = DIO_GetSetPinFunction(clockPin);
  _lastElementDetectPin = lastElementDetectPin;

  clear();
}

void Counter_4017::clock()
{
  _clockPin(false);
  _clockPin(true);
}

void Counter_4017::clockTrigger()
{
  _clockPin(true);
}

void Counter_4017::clockPrime()
{
  _clockPin(false);
}

void Counter_4017::clear()
{
  while (!onLastElement())
    clock();

  while (onLastElement())
    clock();
}

boolean Counter_4017::onLastElement()
{
  return (digitalRead(_lastElementDetectPin) == HIGH ? true : false);
}

keywords.txt

Counter_4017	KEYWORD1

clock	KEYWORD2
clear	KEYWORD2
onLastElement	KEYWORD2

Directly setting IO pins high or low

See 7495 Shift Register for an example of how these can be used.

DirectIO.h

/* Project: Direct digital pin access for Arduino UNO.
 * File: DirectIO.h
 * Description: Manipulate digital IO pins directly. Depending
 *     method used can be much faster than digitalWrite().
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantability or fitness of purpose.
 */

#ifndef DirectIO_h
#define DirectIO_h

#include "Arduino.h"

/* These are about 4 times faster than digitalWrite(); */

void DIO_SetPin4(boolean);
void DIO_SetPin7(boolean);
void DIO_SetPin8(boolean);
void DIO_SetPin9(boolean);
void DIO_SetPin10(boolean);
void DIO_SetPin11(boolean);
void DIO_SetPin12(boolean);
void DIO_SetPin13(boolean);

void DIO_SetPinNull(boolean);

void DIO_SetPin(int pin, boolean state);

typedef void (*DIO_SetPinFPtr)(boolean);

DIO_SetPinFPtr DIO_GetSetPinFunction(byte);


/* These are about 30 times faster than using digitalWrite(); */

static inline void PIN4H() { PORTD = PORTD | B00010000; }
static inline void PIN4L() { PORTD = PORTD & B11101111; }

static inline void PIN7H() { PORTD = PORTD | B10000000; }
static inline void PIN7L() { PORTD = PORTD & B01111111; }

static inline void PIN8H() { PORTB = PORTB | B00000001; }
static inline void PIN8L() { PORTB = PORTB & B11111110; }

static inline void PIN9H() { PORTB = PORTB | B00000010; }
static inline void PIN9L() { PORTB = PORTB & B11111101; }

static inline void PIN10H() { PORTB = PORTB | B00000100; }
static inline void PIN10L() { PORTB = PORTB & B11111011; }

static inline void PIN11H() { PORTB = PORTB | B00001000; }
static inline void PIN11L() { PORTB = PORTB & B11110111; }

static inline void PIN12H() { PORTB = PORTB | B00010000; }
static inline void PIN12L() { PORTB = PORTB & B11101111; }

static inline void PIN13H() { PORTB = PORTB | B00100000; }
static inline void PIN13L() { PORTB = PORTB & B11011111; }


/* The inline functions above are as fast as the code below. */

#define PIN13Hi PORTB = PORTB | B00100000
#define PIN13Li PORTB = PORTB & B11011111

#define PIN13Ha asm("SBI 5, 5\n")
#define PIN13La asm("CBI 5, 5\n")

#endif

DirectIO.cpp

/* Project: Direct digital pin access for Arduino UNO.
 * File: DirectIO.cpp
 * Description: Manipulate digital IO pins directly. Depending
 *     method used can be much faster than digitalWrite().
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantability or fitness of purpose.
 */

#include "Arduino.h"
#include "DirectIO.h"

/*
  Direct digital port manipulation using the PORT variables.
  About 4 times quicker than using digitalWrite().
*/

/* 
  Returns a pointer to the function that can turn set the specified port high or low.

    DIO_SetPinFPtr func;

    func = DIO_getSetPinFunction(4);
    func(true); // Set high.
    func(low); // Set low.

  You could, of course, just as easily call the DIO_set4 fuction directly.
*/

DIO_SetPinFPtr DIO_GetSetPinFunction(byte pin)
{
  switch(pin)
  {
    case 4:
      return DIO_SetPin4;
      break;
      
    case 7:
      return DIO_SetPin7;
      break;
      
    case 8:
      return DIO_SetPin8;
      break;
      
    case 9:
      return DIO_SetPin9;
      break;
      
    case 10:
      return DIO_SetPin10;
      break;
      
    case 11:
      return DIO_SetPin11;
      break;
      
    case 12:
      return DIO_SetPin12;
      break;

    case 13:
      return DIO_SetPin13;
  }

  return DIO_SetPinNull;
}

void DIO_SetPinNull(boolean state)
{
  // Do nothing
}

void DIO_SetPin4(boolean state)
{
  if (state)
    PIN4H();
  else
    PIN4L();
}

void DIO_SetPin7(boolean state)
{
  if (state)
    PIN7H();
  else
    PIN7L();
}

void DIO_SetPin8(boolean state)
{
  if (state)
    PIN8H();
  else
    PIN8L();
}

void DIO_SetPin9(boolean state)
{
  if (state)
    PIN9H();
  else
    PIN9L();
}

void DIO_SetPin10(boolean state)
{
  if (state)
    PIN10H();
  else
    PIN10L();
}

void DIO_SetPin11(boolean state)
{
  if (state)
    PIN11H();
  else
    PIN11L();
}

void DIO_SetPin12(boolean state)
{
  if (state)
    PIN12H();
  else
    PIN12L();
}

void DIO_SetPin13(boolean state)
{
  if (state)
    PIN13H();
  else
    PIN13L();
}

void DIO_SetPin(int pin, boolean state)
{
  if (state)
  {
    switch(pin)
    {
      case 4:
        PIN4H();
        break;
			 
      case 7:
        PIN7H();
        break;
		     
      case 8:
        PIN8H();
        break;
		     
      case 9:
        PIN9H();
        break;
		     
      case 10:
        PIN10H();
        break;
		     
      case 11:
        PIN11H();
        break;
		     
      case 12:
        PIN12H();
        break;
		     
      case 13:
        PIN13H();
        break;
    }
  }
  else
  {
    switch(pin)
    {
      case 4:
        PIN4L();
        break;
			 
      case 7:
        PIN7L();
        break;
		     
      case 8:
        PIN8L();
        break;
		     
      case 9:
        PIN9L();
        break;
		     
      case 10:
        PIN10L();
        break;
		     
      case 11:
        PIN11L();
        break;
		     
      case 12:
        PIN12L();
        break;
		     
      case 13:
        PIN13L();
        break;
    }
 }	    
}

keywords.txt

ShiftReg_7495	KEYWORD1

DIO_SetPin4	KEYWORD2
DIO_SetPin7	KEYWORD2
DIO_SetPin8	KEYWORD2
DIO_SetPin9	KEYWORD2
DIO_SetPin10	KEYWORD2
DIO_SetPin11	KEYWORD2
DIO_SetPin12	KEYWORD2
DIO_SetPin13	KEYWORD2

DIO_SetPin	KEYWORD2

PIN4L	KEYWORD2
PIN4H	KEYWORD2

PIN7L	KEYWORD2
PIN7H	KEYWORD2

PIN8L	KEYWORD2
PIN8H	KEYWORD2

PIN9L	KEYWORD2
PIN9H	KEYWORD2

PIN10L	KEYWORD2
PIN10H	KEYWORD2

PIN11L	KEYWORD2
PIN11H	KEYWORD2

PIN12L	KEYWORD2
PIN12H	KEYWORD2

PIN13L	KEYWORD2
PIN13H	KEYWORD2