Shift Register - Seven segments, daisy chained

Breadboard for "Shift Register - Seven segments, daisy chained"

docs/breadboard/shift-register-daisy-chain.png

Fritzing diagram: docs/breadboard/shift-register-daisy-chain.fzz

 

Run with:

node eg/shift-register-daisy-chain.js
/**
 * This example illustrates a 20-sided die roller using two seven-segment
 * displays and two daisy-chained 74HC595 shift registers.
 *
 * The button is a "momentary off" button.
 *
 * See docs/breadboard/seven-segment-daisy-chain.png
 * for the wiring.
 */
var five = require("johnny-five"),
  async = require("async"),
  _ = require("lodash");

var board = new five.Board(),
  Button = five.Button,
  ShiftRegister = five.ShiftRegister,
  Pin = five.Pin,

  /**
   * Change this to "true" if you are using common anode displays.
   *
   * This example uses common cathode displays.  If you only have common
   * anode seven-segment displays, you will need to change the wiring to use VCC
   * in place of GND.
   * @type {boolean}
   */
  COMMON_ANODE = false,

  /**
   * This is optional; you can reset the 74HC595's if one or both has
   * bizarre stuff stored in its EEPROM.
   *
   * Comment out if unused.
   * @type {number}
   */
  RESET_PIN = 9,

  /**
   * Segments we'll be using.  Die rolls produce no decimals, so we will
   * not be using "DP".
   * @type {string[]}
   */
  SEGMENTS = ["a", "b", "c", "d", "e", "f", "g"],

  /**
   * Mapping of LED segment names to their numeric values.
   * See "segments" definition within shift-register-seven-segment for
   * functionally equivalent code.
   * @type {Object.<string,number>}
   */
  segments = _.mapValues(_.object(SEGMENTS, _.range(SEGMENTS.length)),
    function(seg) {
      return 1 << seg;
    }),

  /**
   * Array of digits used in our die-roller.
   * @type {number[]}
   */
  digits = (function() {
    var A = segments.a,
      B = segments.b,
      C = segments.c,
      D = segments.d,
      E = segments.e,
      F = segments.f,
      G = segments.g;

    /**
     * These two-value arrays represent numbers from 1 through 20.
     * By performing a bitwise OR on segments, we can combine them to find
     * the numeric value of a digit.
     *
     * Constructing the numbers in this manner may help you visualize how it
     * works.
     *
     * These arrays are logically reversed; the second digit is the first,
     * and the first is the second.  When 0 is present, it means the display
     * is unused.
     *
     * @type {number[]}
     */
    return [
      [F | E | D | C | B | A, 0],                 // 0
      [C | B, 0],                                 // 1
      [G | E | D | A | B, 0],                     // 2
      [G | D | C | B | A, 0],                     // 3
      [G | F | C | B, 0],                         // 4
      [G | F | D | C | A, 0],                     // 5
      [G | F | E | D | C | A, 0],                 // 6
      [C | B | A, 0],                             // 7
      [G | F | E | D | C | B | A, 0],             // 8
      [G | F | D | C | B | A, 0],                 // 9
      [F | E | D | C | B | A, C | B],             // 10
      [C | B, C | B],                             // 11
      [G | E | D | A | B, C | B],                 // 12
      [G | D | C | B | A, C | B],                 // 13
      [G | F | C | B, C | B],                     // 14
      [G | F | D | C | A, C | B],                 // 15
      [G | F | E | D | C | A, C | B],             // 16
      [C | B | A, C | B],                         // 17
      [G | F | E | D | C | B | A, C | B],         // 18
      [G | F | D | C | B | A, C | B],             // 19
      [F | E | D | C | B | A, G | E | D | A | B]  // 20
    ];

  })();

board.on("ready", function() {

  /**
   * While we may have multiple ShiftRegisters,
   * we only need one to control them both.
   * @type {exports.ShiftRegister}
   */
  var register = new ShiftRegister({
      pins: {
        data: 2,
        clock: 3,
        latch: 4
      }
    }),

    /**
     * Pressing this button will trigger the die roll.  Because we're using a
     * "momentary-off" switch, "invert: true" is appropriate here.  If you have
     * a "momentary-on" switch, set "invert: false".
     * @type {Button}
     */
    btn = new Button({
      pin: 8,
      invert: true
    }),

    /**
     * Clears both displays
     */
    clear = function clear() {
      register.send.apply(register, COMMON_ANODE ? [255, 255] : [0, 0]);
    },

    /**
     * Resets the storage of the shift register.  Helpful if you stuff something
     * weird in the IC's EEPROM.
     *
     * To reset a 74HC595, you must make a low-to-high transition on STCP
     * (clock) while the MR pin (reset) is low.  Return reset to high thereafter
     * to end the reset function.  At least, I'm pretty sure that's what the
     * datasheet is getting at...
     * @type {function(this:*)}
     */
    reset = function reset() {
      if (typeof RESET_PIN !== "undefined") {
        this.digitalWrite(register.pins.clock, this.io.LOW);
        this.digitalWrite(RESET_PIN, this.io.LOW);
        this.digitalWrite(register.pins.clock, this.io.HIGH);
        this.digitalWrite(RESET_PIN, this.io.HIGH);
      }
    }.bind(this),

    /**
     * Inverts the pin output if we are using common anode.
     * @param {number} num
     * @returns {number}
     */
    invert = function invert(num) {
      return ((~num << 24) >> 24) & 255;
    },

    /**
     * Prints a digit (0-20) on the display(s).
     * @param {(Array|number)} num If number, will look up in digits array.
     */
    digit = function digit(num) {
      if (!_.isArray(num)) {
        num = digits[num - 1] || [0, 0];
      }
      register.send.apply(register, COMMON_ANODE ? _.map(num, invert) : num);
    },

    /**
     * Sends a random number to the shift register.
     */
    randomNumber = function randomNumber() {
      return digit(_.sample(digits));
    },

    /**
     * This is an array of delays in ms.  When a button is pressed,
     * we'll iterate over this array and display a random number after the
     * delay.  This simulates a die bouncing on a table.
     * @type {Array.<number>}
     */
    delays = new Array(10).fill(16)
    .concat(new Array(8).fill(32))
    .concat(new Array(6).fill(64))
    .concat(new Array(4).fill(128))
    .concat(new Array(2).fill(256))
    .concat(512);

  this.repl.inject({
    digit: digit,
    digits: digits,
    clear: clear,
    randomNumber: randomNumber,
    reset: reset
  });

  reset();
  clear();

  btn.on("press", function() {
    console.log("Rolling...");
    clear();
    async.eachSeries(delays, function(delay, done) {
      randomNumber();
      setTimeout(function() {
        clear();
        done();
      }, delay);
    }, randomNumber);
  });

});

 

License

Copyright (c) 2012, 2013, 2014 Rick Waldron waldron.rick@gmail.com Licensed under the MIT license. Copyright (c) 2014, 2015 The Johnny-Five Contributors Licensed under the MIT license.