ronbentley1
Published

Command & Control of Multiple Serial-In/Parallel-Out ICs

Design projects using many 100's of output pins, using banks of serial-parallel ICs, up to 255 ICs/2040 output pins, on minimal digital pins

IntermediateFull instructions provided2,587
Command & Control of Multiple Serial-In/Parallel-Out ICs

Things used in this project

Hardware components

Texas Instruments 74HC595 IC Shift Register
any 74HC595 clone should do
×1
Breadboard (generic)
Breadboard (generic)
×1
5 mm LED: Red
5 mm LED: Red
standard LEDs
×16
Resistor 220 ohm
Resistor 220 ohm
use with LEDs
×16
Jumper wires (generic)
Jumper wires (generic)
lots
×32
7 Segment LED Display, InfoVue
7 Segment LED Display, InfoVue
cathode 7 segment LED matrix display
×1
Arduino UNO
Arduino UNO
I developed the SIPO8 library on a UNO, but other boards should okay, check memory limits
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Single SIPO, wiring scheme

See story board/examples and tutorials

Cascaded SIPOs, wiring scheme

See story board/examples and tutorials

Code

SIPO8 library header file, ez_SIPO8.h

C/C++
SIPO8 library header file, ez_SIPO8.h
/*
   Ron D Bentley, Stafford, UK
   April 2021
   SIPO8 v1-00

   Serial/Parallel IC (SIPO) library supporting banking of multiple SIPOs
   of same/different bit sizes.
   Supports maximum of up to 255 8bit SIPOs (2040 individual output pins)
   and up to 255 indivual timers.

   This example and code is in the public domain and
   may be used without restriction and without warranty.

*/

#ifndef SIPO8_h
#define SIPO8_h

#include <Arduino.h>  // always include this lib otherwise wont compile!

class SIPO8
{
  public:


#define pins_per_SIPO        8 // global 'set in stone' macro for a base SIPO unit

    // failure macros...
#define create_bank_failure -1
#define pin_read_failure    -1
#define pin_invert_failure  -1
#define pin_set_failure     -1
#define bank_not_found      -1
#define SIPO_not_found      -2

    // timer macros...
#define timer0               0
#define timer1               1
#define timer2               2
#define timer3               3
#define timer4               4
#define timer5               5
#define timer6               6
#define timer7               7
#define elapsed            true
#define not_elapsed        !elapsed
#define active             true
#define not_active         !active

    uint16_t max_pins             = 0; // user accessible params
    uint16_t num_active_pins      = 0; // ...
    uint8_t  num_pin_status_bytes = 0; // ...
    uint8_t  num_banks            = 0; // ...
    uint8_t  max_SIPOs            = 0; // ...
    uint8_t  bank_SIPO_count      = 0; // ...
    uint8_t  max_timers           = 0; // ...

    struct SIPO_control {
      uint8_t  bank_data_pin;
      uint8_t  bank_clock_pin;
      uint8_t  bank_latch_pin;
      uint8_t  bank_num_SIPOs;
      uint16_t bank_low_pin;
      uint16_t bank_high_pin;
    }*SIPO_banks;

    uint8_t * pin_status_bytes;  // records current status of each pin

    // timer control struct(ure)
    struct timer_control {
      bool     timer_status;    // records status of a timer - active or not active
      uint32_t start_time;      // records the millis time when a timer is started
    } *timers;

    // ******* function declarations....

    SIPO8(uint8_t, uint8_t); // constructor function called when class is initiated

    int  create_bank(uint8_t, uint8_t, uint8_t, uint8_t);
    void set_all_array_pins(bool);
    void invert_all_array_pins();
    int  set_array_pin(uint16_t, bool);
    int  invert_array_pin(uint16_t);
    int  read_array_pin(uint16_t);

    void set_banks(uint8_t, uint8_t, bool);
    void set_banks(bool);
    void set_bank(uint8_t, bool);
    void invert_banks();
    void invert_banks(uint8_t, uint8_t);
    void invert_bank(uint8_t);

    int  set_bank_SIPO(uint8_t, uint8_t, uint8_t);
    int  invert_bank_SIPO(uint8_t, uint8_t);
    int  read_bank_SIPO(uint8_t, uint8_t);

    int  set_bank_pin(uint8_t, uint8_t, bool);
    int  invert_bank_pin(uint8_t, uint8_t);
    int  read_bank_pin(uint8_t, uint8_t);

    int  get_bank_from_pin(uint16_t);
    int  num_pins_in_bank(uint8_t);

    void xfer_banks(uint8_t, uint8_t, bool);
    void xfer_banks(bool);
    void xfer_bank(uint8_t, bool);
    void xfer_array(bool);

    void print_pin_statuses();
    void print_SIPO_data();

    void SIPO8_start_timer(uint8_t);
    void SIPO8_stop_timer(uint8_t);
    bool SIPO8_timer_elapsed(uint8_t, uint32_t);

    // ****** private declarations.....
  private:
    uint16_t _max_pins             = 0;
    uint16_t _num_active_pins      = 0;
    uint8_t  _num_pin_status_bytes = 0;
    uint8_t  _max_SIPOs            = 0;
    uint8_t  _bank_SIPO_count      = 0;
    uint8_t  _next_bank            = 0;
    uint8_t  _max_timers           = 0;

    void SIPO_lib_exit(uint8_t);
    void shift_out_bank(uint8_t, uint8_t, uint8_t, bool);



};

#endif

SIPO8 library cpp file, ez_SIPO8.cpp

C/C++
SIPO8 library cpp file, ez_SIPO8.cpp
/*
   Ron D Bentley, Stafford, UK
   April 2021
   SIPO8 v1-00

   Serial/Parallel IC (SIPO) library supporting banking of multiple SIPOs
   of same/different bit sizes.
   Supports maximum of up to 255 8bit SIPOs (2040 individual output pins)
   and up to 255 indivual timers.

   This example and code is in the public domain and
   may be used without restriction and without warranty.

*/


#include <Arduino.h>
#include <ez_SIPO8_lib.h>

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This function will be called when the class is initiated.
// The parameter is the maximum number of SIPOs that will be configured
// in the sketch.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SIPO8::SIPO8(uint8_t max_SIPO_ICs, uint8_t Max_timers ) {
  // Setup the SIPO_banks control data struture sized for the maximum number of
  // SIPO banks that could be defined
  SIPO_banks = (SIPO_control *) malloc(sizeof(SIPO_control) * max_SIPO_ICs);
  if (SIPO_banks == NULL) {
    SIPO_lib_exit(0);
  }
  // Determine how may pin_status_bytes of 'pins_per_SIPO' bit length are
  // needed to map the number of bank SIPOs defined
  _max_pins = max_SIPO_ICs * pins_per_SIPO;
  max_pins  = _max_pins;
  _num_pin_status_bytes = max_SIPO_ICs;
  num_pin_status_bytes  = _num_pin_status_bytes;
  pin_status_bytes =  (uint8_t *) malloc(sizeof(pin_status_bytes) * _num_pin_status_bytes); // 1 byte per 8-bit SIPO
  if (pin_status_bytes == NULL) {
    SIPO_lib_exit(1);
  }
  // clear down pin_status_bytes to LOW (0)
  for (uint8_t pin_status_byte = 0; pin_status_byte < _num_pin_status_bytes; pin_status_byte++) {
    pin_status_bytes[pin_status_byte] = 0;
  }
  // create timer struct(ure) of required size
  if (Max_timers > 0){
    timers = (timer_control *) malloc(sizeof(timer_control) * Max_timers);
    if (timers == NULL) {
      SIPO_lib_exit(2);
   }
  }
  _max_timers = Max_timers;
  max_timers  = Max_timers;
  // initialise other working variables, private and user accessible
  for (uint8_t timer = 0; timer < max_timers; timer++) {
    timers[timer].timer_status = not_active;
    timers[timer].start_time = 0; // elapsed time
  }
  _num_active_pins = 0; // no pins yet declared
  num_active_pins  = 0;
  _max_SIPOs = max_SIPO_ICs;
  max_SIPOs  = _max_SIPOs;
  _bank_SIPO_count = 0;
  bank_SIPO_count  = 0;
  _next_bank = 0;
  num_banks  = 0;
}

//
// General error reporting routine with termination
//
void SIPO8::SIPO_lib_exit(uint8_t reason) {
  Serial.begin(9600);
  switch  (reason) {
    case 0:
      Serial.println(F("Exit:out of memory for setup-SIPO banks"));
      break;
    case 1:
      Serial.println(F("Exit:out of memory for setup-status bytes"));
      break;
    case 2:
      Serial.println(F("Exit:out of memory for setup-timers"));
      break;
    default:
      Serial.println(F("Exit:unspecified"));
      break;
  }
  Serial.flush();
  exit(reason);
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The function will try to create a bank of SIPOs if possible.  The create process
// will fail if the more SIPOs for a bank are requested than remain unallocated.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int SIPO8::create_bank(uint8_t data_pin, uint8_t clock_pin, uint8_t latch_pin,
                       uint8_t num_SIPOs) {
  if (_bank_SIPO_count + num_SIPOs <= _max_SIPOs  && num_SIPOs > 0) {
    // still enough free SIPOs available to assign to a new bank
    pinMode(data_pin,  OUTPUT);
    digitalWrite(data_pin, LOW);
    pinMode(clock_pin, OUTPUT);
    digitalWrite(clock_pin, LOW);
    pinMode(latch_pin, OUTPUT);
    digitalWrite(latch_pin, LOW);
    SIPO_banks[_next_bank].bank_data_pin  = data_pin;
    SIPO_banks[_next_bank].bank_clock_pin = clock_pin;
    SIPO_banks[_next_bank].bank_latch_pin = latch_pin;
    SIPO_banks[_next_bank].bank_num_SIPOs = num_SIPOs;
    SIPO_banks[_next_bank].bank_low_pin   = _num_active_pins;
    uint16_t num_pins_this_bank = num_SIPOs * pins_per_SIPO;
    SIPO_banks[_next_bank].bank_high_pin  = _num_active_pins + num_pins_this_bank - 1;// inclusive pin numbers
    _num_active_pins = _num_active_pins + num_pins_this_bank;
    num_active_pins  = _num_active_pins;
    _bank_SIPO_count = _bank_SIPO_count + num_SIPOs;
    bank_SIPO_count  = _bank_SIPO_count;
    _next_bank++;             // next bank struct(ure) entry
    num_banks = _next_bank;   // user accessible number of banks defined
    return _next_bank - 1;    // return the bank number of this bank in the SIPOs struct(ure)
  }
  return create_bank_failure; // cannot provide number of SIPOs asked for, for this bank request
}

//
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will set the entire array of pins to the given status value.  Note that
// this function operates on an entire array basis rather than bank by bank.
// The parameter pin_status should be HIGH or LOW
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::set_all_array_pins(bool pin_status) {
  uint8_t mask = pin_status * 255; // either 0 (all pins set low), or 255 (all pins set high)
  for (uint8_t pin_status_byte = 0; pin_status_byte < _num_pin_status_bytes; pin_status_byte++) {
    pin_status_bytes[pin_status_byte] = mask;
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will invert the status of each pin the the array.   Note that
// this function operates on an entire array basis rather than bank by bank.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::invert_all_array_pins() {
  for (uint8_t pin_status_byte = 0; pin_status_byte < _num_pin_status_bytes; pin_status_byte++) {
    pin_status_bytes[pin_status_byte] = ~pin_status_bytes[pin_status_byte];
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function sets the given pin (absolute pin reference) to the given status value.
// The success or otherwise of the process may be tested by the calling code using
// the return function value.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int SIPO8::set_array_pin(uint16_t pin, bool pin_status) {
  if (pin < _num_active_pins) {
    // pin is in the defined pin range
    uint8_t pin_status_byte = pin / pins_per_SIPO;
    uint8_t pin_bit = pin % pins_per_SIPO;
    bitWrite(pin_status_bytes[pin_status_byte], pin_bit, pin_status);
    return pin;
  }
  return pin_set_failure;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function inverts the existing pin status. Note that pin is given as an absolute
// pin reference.
// The success or otherwise of the process may be tested by the calling code using
// the return function value.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int SIPO8::invert_array_pin(uint16_t pin) {
  if (pin < _num_active_pins) {
    // pin is in the defined pin range
    uint8_t pin_status_byte = pin / pins_per_SIPO;
    uint8_t pin_bit = pin % pins_per_SIPO;
    bool inverted_status = !bitRead(pin_status_bytes[pin_status_byte], pin_bit);
    bitWrite(pin_status_bytes[pin_status_byte],
             pin_bit,
             inverted_status);
    return inverted_status;  // high or low status
  }
  return pin_invert_failure;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Funcion will read the status value of the specified pin and return to the calling
// code. Note that pin is given as an absolute pin reference.
// The success or otherwise of the process may be tested by the calling code using
// the return function value.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int SIPO8::read_array_pin(uint16_t pin) {
  if (pin < _num_active_pins) {
    // pin is in the defined pin range
    uint8_t pin_status_byte = pin / pins_per_SIPO;
    uint8_t pin_bit = pin % pins_per_SIPO;
    return bitRead(pin_status_bytes[pin_status_byte], pin_bit);  // high or low status
  }
  return pin_read_failure;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function is equivalent to the set_all_array_pins function and is prvided as an
// alternative choice.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::set_banks(bool pin_status) {
  set_all_array_pins(pin_status);
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will set every pin in each bank, from_bank - to_bank, to the
// specified pin status.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::set_banks(uint8_t from_bank, uint8_t to_bank, bool pin_status) {
  if (from_bank <= to_bank && to_bank < _next_bank) {
    for (uint8_t bank = from_bank; bank <= to_bank; bank++) {
      set_bank(bank, pin_status);
    }
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will set every pin in given bank to the specified pin status.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::set_bank(uint8_t bank, bool pin_status) {
  if (bank < _next_bank) {
    // start with the first pin of the first SIPO in the bank
    uint16_t first_pin = SIPO_banks[bank].bank_low_pin; // first pin in this bank
    uint8_t mask = pin_status * 255; // either 0 (all pins set low), or 255 (all pins set high)
    // determine the first pin status byte for the first pin in the bank
    uint8_t pin_status_byte = first_pin / pins_per_SIPO;
    // now deal with each SIPO declared in the bank, setting the
    // associated pin status bytes accordingly
    for (uint8_t SIPO = 0; SIPO < SIPO_banks[bank].bank_num_SIPOs; SIPO++) {
      pin_status_bytes[pin_status_byte + SIPO] = mask; // reset all pin bits
    }
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function is equivalent to the invert_all_array_pins function and is prvided as an
// alternative choice.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::invert_banks() {
  invert_all_array_pins();
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will invert the existing pin status of every pin in the specified banks.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::invert_banks(uint8_t from_bank, uint8_t to_bank) {
  if (from_bank <= to_bank && to_bank < _next_bank) {
    for (uint8_t bank = from_bank; bank <= to_bank; bank++) {
      invert_bank(bank);
    }
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will invert the existing pin status of every pin in the specified bank.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::invert_bank(uint8_t bank) {
  if (bank < _next_bank) {
    // start with the first pin of the first SIPO in the bank
    uint16_t first_pin = SIPO_banks[bank].bank_low_pin; // first pin in this bank
    // determine the first pin status byte for the first pin in the bank
    uint8_t pin_status_byte = first_pin / pins_per_SIPO;
    // now deal with each SIPO declared in the bank, setting the
    // associated pin status bytes accordingly
    for (uint8_t SIPO = 0; SIPO < SIPO_banks[bank].bank_num_SIPOs; SIPO++) {
      pin_status_bytes[pin_status_byte + SIPO] =
        ~pin_status_bytes[pin_status_byte + SIPO]; // invert the status byte bits
    }
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function for setting specific pin within a given bank.
// Note that these functions operate relative to the pins defined
// in a bank - set_bank_pin, invert_bank_pin, read_bank_pin.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int  SIPO8::set_bank_pin(uint8_t bank, uint8_t pin, bool pin_status) {
  if (bank < _next_bank) {
    pin = pin + SIPO_banks[bank].bank_low_pin;  // absolute pin number in the array
    return set_array_pin(pin, pin_status);      // returns failure or the absolute pin muber if successful
  }
  return pin_set_failure;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function for inverting specific existing pin status within a given bank.
// Note that these functions operate relative to the pins defined
// in a bank - set_bank_pin, invert_bank_pin, read_bank_pin.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int  SIPO8::invert_bank_pin(uint8_t bank, uint8_t pin) {
  if (bank < _next_bank) {
    pin = pin + SIPO_banks[bank].bank_low_pin;  // absolute pin number in the array
    return invert_array_pin(pin);      // returns failure or the new status of the pin if successful
  }
  return pin_invert_failure;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will read the given pin's status and return its value to the calling code.
// Note that these functions operate relative to the pins defined
// in a bank - set_bank_pin, invert_bank_pin, read_bank_pin.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int  SIPO8::read_bank_pin(uint8_t bank, uint8_t pin) {
  if (bank < _next_bank) {
    pin = pin + SIPO_banks[bank].bank_low_pin;  // absolute pin number in the array
    return read_array_pin(pin);     // returns failure or the pin status if successful
  }
  return pin_read_failure;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will set the given bank SIPO (8bits) to the secified value
// Note that this functions operate relative to the SIPOs/pins defined
// in a bank.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int  SIPO8::set_bank_SIPO(uint8_t bank, uint8_t SIPO_num, uint8_t SIPO_value) {
  if (bank < _next_bank) {
    // bank is valid
    uint8_t SIPOs_this_bank = SIPO_banks[bank].bank_num_SIPOs;
    if (SIPO_num < SIPOs_this_bank) {
      // specified SIPO number is valid for this bank
      // now determine the pin_staus_byte entry for the SIPO
      uint8_t status_byte = SIPO_banks[bank].bank_low_pin / pins_per_SIPO;// first status byte for this bank
      status_byte = status_byte + SIPO_num; // actual status byte to be set
      pin_status_bytes[status_byte] = SIPO_value;// set required status byte
      return status_byte;
    }
    return SIPO_not_found;
  }
  return bank_not_found;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will invert the current contents of the given bank SIPO (8bits) to the
// secified value.
// Note that this functions operate relative to the SIPOs/pins defined
// in a bank.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int  SIPO8::invert_bank_SIPO(uint8_t bank, uint8_t SIPO_num) {
  if (bank < _next_bank) {
    // bank is valid
    uint8_t SIPOs_this_bank = SIPO_banks[bank].bank_num_SIPOs;
    if (SIPO_num < SIPOs_this_bank) {
      // specified SIPO number is valid for this bank
      // now determine the pin_staus_byte entry for the SIPO
      uint8_t status_byte = SIPO_banks[bank].bank_low_pin / pins_per_SIPO;// first status byte for this bank
      status_byte = status_byte + SIPO_num; // actual status byte to be inverted
      pin_status_bytes[status_byte] = ~pin_status_bytes[status_byte]; // invert current contents
      return status_byte;
    }
    return SIPO_not_found;
  }
  return bank_not_found;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function will read the current contents of the given bank SIPO (8bits) and return
// the 8bit status value for the SIPO.
// Note that this functions operate relative to the SIPOs/pins defined
// in a bank.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int  SIPO8::read_bank_SIPO(uint8_t bank, uint8_t SIPO_num) {
  if (bank < _next_bank) {
    // bank is valid
    uint8_t SIPOs_this_bank = SIPO_banks[bank].bank_num_SIPOs;
    if (SIPO_num < SIPOs_this_bank) {
      // specified SIPO number is valid for this bank
      // now determine the pin_staus_byte entry for the SIPO
      uint8_t status_byte = SIPO_banks[bank].bank_low_pin / pins_per_SIPO;// first status byte for this bank
      status_byte = status_byte + SIPO_num; // actual status byte to be read
      return pin_status_bytes[status_byte];
    }
    return SIPO_not_found;
  }
  return bank_not_found;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Given an absolute array pin number, the function determines which
// bank it resides within and returns the bank number.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int  SIPO8::get_bank_from_pin(uint16_t pin) {
  if (pin < _num_active_pins) {
    for (uint8_t bank = 0; bank < _next_bank; bank++) {
      if (SIPO_banks[bank].bank_low_pin <= pin &&
          pin <= SIPO_banks[bank].bank_high_pin)return bank;
    }
  }
  return bank_not_found;
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Determines the number of pins in the given bank
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int SIPO8::num_pins_in_bank(uint8_t bank) {
  if (bank < _next_bank) {
    // valid bank, so return number of pins in this bank
    return SIPO_banks[bank].bank_num_SIPOs * pins_per_SIPO;
  }
  return bank_not_found;
}


// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Selective transfer of array pin satuses based on bank transfers,
// rather than the entire array of banks.
// Transfers specified banks' pin statuses to the hardware SIPOs,
// starting with from_bank and continuing to to_bank.
// The direction of transfer is determined by the msb_or_lsb parameter
// which must be either LSBFIRST  or MSBFIRST.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::xfer_banks(uint8_t from_bank, uint8_t to_bank, bool msb_or_lsb) {
  if (from_bank <= to_bank && to_bank < _next_bank) {
    // examine each bank in turn and deal with as many SIPOs as
    // are configured in each bank
    for (uint8_t bank = from_bank; bank <= to_bank; bank++) {
      uint16_t low_pin  = SIPO_banks[bank].bank_low_pin;   // start pin number for this bank
      uint16_t high_pin = SIPO_banks[bank].bank_high_pin;  // end inclusive pin number for this bank
      uint8_t latch_pin = SIPO_banks[bank].bank_latch_pin;
      uint8_t clock_pin = SIPO_banks[bank].bank_clock_pin;
      uint8_t data_pin  = SIPO_banks[bank].bank_data_pin;
      uint8_t num_SIPOs_this_bank = SIPO_banks[bank].bank_num_SIPOs;
      uint8_t SIPO_first_status_byte = SIPO_banks[bank].bank_low_pin  / pins_per_SIPO;
      uint8_t SIPO_last_status_byte  = SIPO_banks[bank].bank_high_pin / pins_per_SIPO;
      uint8_t SIPO_status_byte = 0;
      digitalWrite(latch_pin, LOW);   //  tell IC data transfer to start
      for (uint8_t SIPO = 0; SIPO < num_SIPOs_this_bank; SIPO++) {
        if (msb_or_lsb == LSBFIRST) {
          SIPO_status_byte = SIPO_first_status_byte + SIPO;
        } else {
          SIPO_status_byte = SIPO_last_status_byte - SIPO;
        }
        shift_out_bank(data_pin, clock_pin, pin_status_bytes[SIPO_status_byte], msb_or_lsb);
      }
      digitalWrite(latch_pin, HIGH);   //  tell IC data transfer is finished
    }
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Equivalent to xfer_array, and transfers all array pin statuses to the
// the hardware SIPOs. Provided as an alternative method.
// The direction of transfer is determined by the msb_or_lsb parameter
// which must be either LSBFIRST  or MSBFIRST.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::xfer_banks(bool msb_or_lsb) {
  if (_next_bank > 0) {
    xfer_banks(0, _next_bank - 1, msb_or_lsb);
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Specific transfer of the given bank pin statuses to the bank's associated
// hardware SIPOs. The direction of transfer is determined by the msb_or_lsb
// parameter which must be either LSBFIRST  or MSBFIRST.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::xfer_bank(uint8_t bank, bool msb_or_lsb) {
  if (_next_bank > 0) {
    xfer_banks(bank, bank, msb_or_lsb);
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Transfers all array pin statuses to the hardware SIPOs.
// The direction of transfer is determined by the msb_or_lsb parameter
// which must be either LSBFIRST  or MSBFIRST.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::xfer_array(bool msb_or_lsb) {
  xfer_banks(msb_or_lsb);
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Based on the standard Arduino shiftout function.
// Moves out the given set of pin statuses, status_bits, to the specified SIPO.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::shift_out_bank(uint8_t data_pin, uint8_t clock_pin, uint8_t status_bits, bool msb_or_lsb)
{ // Shuffle each bit of the val parameter (0 or 1) one bit left
  // until all bits written out.
  for (uint8_t  i = 0; i < pins_per_SIPO; i++)  {
    if (msb_or_lsb == LSBFIRST) {
      digitalWrite(data_pin, !!(status_bits & (1 << i)));
    }
    else
    {
      digitalWrite(data_pin, !!(status_bits & (1 << (7 - i))));
    }
    digitalWrite(clock_pin, HIGH);
    digitalWrite(clock_pin, LOW);
  }
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function is provided to assist end user to provide debug data during development.
// Prints all active pin status bytes, in bit form, starting with pin 0 and
// progressing to the last active end pin defined.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::print_pin_statuses() {
  if (_next_bank > 0) {
    Serial.println(F("\nActive pin array, pin statuses:"));
    // there is at least 1 bank
    uint8_t SIPO_count = 0;
    for (uint8_t bank = 0; bank < _next_bank; bank++) {
      Serial.print(F("Bank "));
      if (bank < 10)Serial.print(F(" "));
      Serial.print(bank);
      Serial.print(F(": MS"));
      uint8_t start_byte = SIPO_count;
      uint8_t last_byte = start_byte + SIPO_banks[bank].bank_num_SIPOs - 1;
      SIPO_count = SIPO_count + SIPO_banks[bank].bank_num_SIPOs;
      for (int16_t next_byte = last_byte; next_byte >= start_byte; next_byte--) {
        uint8_t SIPO_status = pin_status_bytes[next_byte];
        for (int pos = 7; pos >= 0; pos--) {
          uint8_t pin_status = bitRead(SIPO_status, pos);
          Serial.print(pin_status);
        }
      }
      Serial.println(F("LS"));
    }
  } else {
    Serial.println(F("\nNo banks defined"));
  }
  Serial.flush();
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function is provided to assist end user to display debug data during development.
// Prints all setup data as a confirmation that the end user has correctly configured
// all key data.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::print_SIPO_data() {
  Serial.println(F("\nSIPO global values:"));
  Serial.print(F("pins_per_SIPO   = "));
  Serial.println(pins_per_SIPO);
  Serial.print(F("max_SIPOs       = "));
  Serial.println(_max_SIPOs);
  Serial.print(F("bank_SIPO_count = "));
  Serial.println(_bank_SIPO_count);
  Serial.print(F("num_active_pins = "));
  Serial.println(_num_active_pins);
  Serial.print(F("num_pin_status_bytes = "));
  Serial.println(_num_pin_status_bytes);
  Serial.print(F("next_free bank = "));
  if (_bank_SIPO_count < _max_SIPOs) {
    Serial.print(_next_bank);
    Serial.println();
  } else {
    Serial.println(F(" all SIPOs used"));
  }
  Serial.print("Number timers  =  ");
  Serial.println(_max_timers);
  Serial.println(F("\nBank data:"));
  for (uint8_t bank = 0; bank < _next_bank; bank++) {
    // still SIPOs available to assign to a new bank
    Serial.print(F("bank = "));
    Serial.println(bank);
    Serial.print(F("  num SIPOs =\t"));
    Serial.println(SIPO_banks[bank].bank_num_SIPOs);
    Serial.print(F("  latch_pin =\t"));
    Serial.print(SIPO_banks[bank].bank_latch_pin);
    Serial.print(F("  clock_pin =\t"));
    Serial.print(SIPO_banks[bank].bank_clock_pin);
    Serial.print(F("  data_pin  =\t"));
    Serial.println(SIPO_banks[bank].bank_data_pin);
    Serial.print(F("  low_pin   =\t"));
    Serial.print(SIPO_banks[bank].bank_low_pin);
    Serial.print(F("  high_pin  =\t"));
    Serial.println(SIPO_banks[bank].bank_high_pin);
  }
  Serial.flush();
}

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Function sets the given timer as active and records the start time.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void SIPO8::SIPO8_start_timer(uint8_t timer) {
  if (timer < _max_timers) {
    timers[timer].timer_status = active;
    timers[timer].start_time = millis();
  }
}

//
// Function cancels the given timer.
//
void SIPO8::SIPO8_stop_timer(uint8_t timer) {
  if (timer < _max_timers) {
    timers[timer].timer_status = not_active;
  }
}

//
// Function determines if the time has elapsed for the given timer, if active.
//
bool SIPO8::SIPO8_timer_elapsed(uint8_t timer, uint32_t elapsed_time) {
  if (timer < _max_timers) {
    if (timers[timer].timer_status == active) {
      if (millis() - timers[timer].start_time >= elapsed_time) {
        timers[timer].timer_status = not_active; // mark this timer no longer active
        return elapsed;
      }
    }
  }
  return not_elapsed;
}

SIPO8 library keyword file, keywords.txt

C/C++
SIPO8 library keyword file, keywords.txt
#
#   Ron D Bentley, Stafford, UK
#   April 2021
#   SIPO8 v1-00
#
#   This example and code is in the public domain and
#   may be used without restriction and without warranty
#

# class
SIPO8	KEYWORD1

# macros...    
pins_per_SIPO	LITERAL1 
create_bank_failure	LITERAL1 
pin_read_failure	LITERAL1 
pin_invert_failure	LITERAL1 
pin_set_failure	LITERAL1 
bank_not_found	LITERAL1 
SIPO_not_found	LITERAL1 
timer0	LITERAL1 
timer1	LITERAL1 
timer2	LITERAL1 
timer3	LITERAL1 
timer4	LITERAL1 
timer5	LITERAL1 
timer6	LITERAL1 
timer7	LITERAL1 
elapsed	LITERAL1 
not_elapsed	LITERAL1 
active	LITERAL1 
not_active	LITERAL1 

# user accessible variables...
max_pins	KEYWORD2
num_active_pins	KEYWORD2
num_pin_status_bytes	KEYWORD2
num_banks	KEYWORD2
max_SIPOs	KEYWORD2
bank_SIPO_count	KEYWORD2
max_timers	KEYWORD2
bank_data_pin	KEYWORD2
bank_clock_pin	KEYWORD2
bank_latch_pin	KEYWORD2
bank_num_SIPOs	KEYWORD2
bank_low_pin	KEYWORD2
bank_high_pin	KEYWORD2
SIPO_banks	KEYWORD2
pin_status_bytes	KEYWORD2
timer_status	KEYWORD2
start_time	KEYWORD2
timers	KEYWORD2

# functions...
create_bank	KEYWORD2
set_all_array_pins	KEYWORD2
invert_all_array_pins	KEYWORD2
set_array_pin	KEYWORD2
invert_array_pin	KEYWORD2
read_array_pin	KEYWORD2
set_banks	KEYWORD2
set_banks	KEYWORD2
set_bank	KEYWORD2
invert_banks	KEYWORD2
invert_banks	KEYWORD2
invert_bank	KEYWORD2
set_bank_SIPO	KEYWORD2
invert_bank_SIPO	KEYWORD2
read_bank_SIPO	KEYWORD2
set_bank_pin	KEYWORD2
invert_bank_pin	KEYWORD2
read_bank_pin	KEYWORD2
get_bank_from_pin	KEYWORD2
num_pins_in_bank	KEYWORD2
xfer_banks	KEYWORD2
xfer_banks	KEYWORD2
xfer_bank	KEYWORD2
xfer_array	KEYWORD2
print_pin_statuses	KEYWORD2
print_SIPO_data	KEYWORD2
SIPO8_start_timer	KEYWORD2
SIPO8_stop_timer	KEYWORD2
SIPO8_timer_elapsed	KEYWORD2

# private variables
_max_pins	KEYWORD2
_num_active_pins	KEYWORD2
 _num_pin_status_bytes	KEYWORD2
_max_SIPOs	KEYWORD2
 _bank_SIPO_count	KEYWORD2
_next_bank	KEYWORD2
_max_timers	KEYWORD2

# private functions...
SIPO_lib_exit	KEYWORD2
shift_out_bank	KEYWORD2

Story Board Example 1

C/C++
Story board example 1 - toggle switches status display
/*
   Ron D Bentley, Stafford, UK
   April 2021

   Example sketch 1 for Arduino Community story board
   The sketch reads a number of toggle switches which are in one of two
   states - on(HIGH) or off(LOW).
   Each switch is mapped to a SIPO bank pin as follows:
   switches 0-5 -> bank pins 0-5
                   bank pin 6 is not used
                   bank pin 7 is used as a sketch running indicator,
                              or 'heart beat'

   Note that the switches are 'read' periodically sampled by a dummy
   function using the SIPO8 timers feature. The code is non-blocking.

   This example and code is in the public domain and
   may be used without restriction and without warranty.

*/

#include <ez_SIPO8_lib.h>

#define Max_SIPOs        1
#define Max_timers       1
#define sample_time    500  // interval period between sampling sensors
#define num_switches     6  // number of sensors requiring testing/reading in each cycle

// initiate the class for max SIPOs/timers required
SIPO8 my_SIPOs(Max_SIPOs, Max_timers); 

uint8_t bank_id;

// dummy read switch function - returns a random staus for given switch
// number, based on switch's current status
bool read_switch_status(uint8_t Switch) {
  randomSeed(analogRead(A0)*analogRead(A2)); // keep changing seed
  if (random(0, 4) == 0)return !my_SIPOs.read_bank_pin(bank_id, Switch);
  return my_SIPOs.read_bank_pin(bank_id, Switch);
}

void setup() {
  Serial.begin(9600);
  // create 1 bank of Max_SIPOs
  // params are data pin, clock pin anf latch pin, number SIPOs
  bank_id = my_SIPOs.create_bank(8, 10, 9, Max_SIPOs);
  if (bank_id == create_bank_failure) {
    my_SIPOs.print_SIPO_data();
    Serial.println(F("failed to create bank"));
    Serial.flush();
    exit(0);
  }
my_SIPOs.print_SIPO_data();  // report on global SIPO8 params
}
// Scan all defined switches and set their respective bank pin status, LOW/HIGH.
// Switches/bank pins layout:
// Switch associated bank pins run from 0 to num_switches-1, inclusive (5) and
// are used to indicate respective switch status,
// bank pin 6 is not used and bank pin 7 is used to provide
// a 'heart beat' to indicate that the sketch is running.
void loop() {
  my_SIPOs.set_all_array_pins(LOW); // ensure we start with clear array pool/bank
  my_SIPOs.xfer_array(MSBFIRST);
  my_SIPOs.SIPO8_start_timer(timer0); // start the sample timer
  do {
    if (my_SIPOs.SIPO8_timer_elapsed(timer0, sample_time) == elapsed) {
      my_SIPOs.SIPO8_start_timer(timer0); // reset/restart the timer
      // read each switch and set its virtual SIPO pin in the bank
      my_SIPOs.invert_bank_pin(bank_id, 7); // flash 'heart beat'
      for (uint8_t Switch = 0; Switch < num_switches; Switch ++) {
        my_SIPOs.set_bank_pin(bank_id, Switch, read_switch_status(Switch));
      }
      my_SIPOs.xfer_bank(bank_id, MSBFIRST); // update the physical SIPO pins
    }
  } while (true);
}

Story Board Example 2

C/C++
Story board example 2 - strobing LEDs
/* Ron D Bentley, Stafford, UK
   April 2021

   Example sketch 2 for Arduino Community story board
   This sketch strobes a number of LEDs driven by a SIPO IC, 8 output pins
   forwards and backwards at a defined frequency.

   This example uses relative bank addressing with pin set and invert functions.

   Ron D Bentley, Stafford, UK
   April 2021

   This example and code is in the public domain and
   may be used without restriction and without warranty.

*/

#include <ez_SIPO8_lib.h>

#define Max_SIPOs  1 // 1 x SIPOs - provides 8 output pins
#define Max_timers 0 // no timers required, will use delay 

#define data_pin    8
#define clock_pin  10
#define latch_pin   9

#define strobe_frequency     50  // milli seconds, delay frequency

// initiate the class for max SIPOs/timers required
SIPO8 my_SIPOs(Max_SIPOs, Max_timers);

int bank_id;  // used to keep the SIPO bank id

void setup() {
  Serial.begin(9600);
  // create a bank of 'Max_SIPOs' using create_bank function:
  bank_id = my_SIPOs.create_bank(data_pin, clock_pin, latch_pin, Max_SIPOs);
  if (bank_id == create_bank_failure) {
    Serial.println(F("\nfailed to create bank, terminated"));
    Serial.flush();
    exit(0);
  }
  my_SIPOs.print_SIPO_data();  // report on global SIPO8 params
  // start by setting all SIPO outputs to low (off) in the SIPO bank
  my_SIPOs.set_bank_SIPO(bank_id, 0, 0b00000000); // only 1 SIPO in bank,index 0
  my_SIPOs.xfer_bank(bank_id, MSBFIRST);
}

void loop() {
  //strobe example, strobe bank_id pins forward and back
  bool msb_or_lsb = MSBFIRST; // pick an initial strobe direction
  uint8_t num_pins = Max_SIPOs * pins_per_SIPO;  // lots of ways to do this
  do {
    for (uint16_t pin = 0; pin < num_pins; pin++) {
      my_SIPOs.set_bank_pin(bank_id, pin, HIGH); // set strobe pin
      my_SIPOs.xfer_bank(bank_id, msb_or_lsb);   // update physical SIPO bank
      delay(strobe_frequency);
      my_SIPOs.invert_bank_pin(bank_id, pin);    // invert strobe pin - alternative to set_bank_pin(low)
      my_SIPOs.xfer_bank(bank_id, msb_or_lsb);   // update physical SIPO bank
    }
    msb_or_lsb = !msb_or_lsb; // switch strobe direction
  } while (true);
}

ez_SIPO8_lib - Tutorial 1 - Setup & Absolute Pin Addressing

C/C++
Accompanying sketch for Tutorial 1 - Setup & Absolute Pin Addressing
//
//   Tutorial 1 - use of ez_SPI8 library
//   Setup and absolute addressing.
//
//   Ron D Bentley, Stafford, UK
//   April 2021
//
//   This example and code is in the public domain and
//   may be used without restriction and without warranty.
//

#include <ez_SIPO8_lib.h>

#define max_SIPOs  1  // one 1 SIPO for this tutorial
#define max_timers 0  // no timers required

// initiate the class for max SIPOs/timers required
SIPO8 my_SIPOs(max_SIPOs, max_timers);

int my_LEDs;  // used to keep the SIPO bank id

void setup() {
  Serial.begin(9600);
  // create a bank of 1 SIPO using create_bank function:
  // data pin, clock pin, latch pin, number of SIPO this bank
  my_LEDs = my_SIPOs.create_bank(8, 10, 9, 1); // data pin, clock pin, latch pin, number of SIPO this bank
  if (my_LEDs == create_bank_failure) {
    Serial.println(F("\nfailed to create bank"));
    Serial.flush();
    exit(0);
  }
  // print the bank data for confirmation/inspection
  my_SIPOs.print_SIPO_data();
}

void loop() {
  // start by setting all SIPO outputs to low (off)
  my_SIPOs.set_all_array_pins(LOW);
  my_SIPOs.xfer_array(LSBFIRST);
  do {
    //
    // assemble pattern 0b01010101 into the array
    my_SIPOs.set_array_pin(0, HIGH);
    my_SIPOs.set_array_pin(1, LOW);
    my_SIPOs.set_array_pin(2, HIGH);
    my_SIPOs.set_array_pin(3, LOW);
    my_SIPOs.set_array_pin(4, HIGH);
    my_SIPOs.set_array_pin(5, LOW);
    my_SIPOs.set_array_pin(6, HIGH);
    my_SIPOs.set_array_pin(7, LOW);
    my_SIPOs.xfer_array(MSBFIRST);  // now move array to physical SIPO and light up the LEDs
    delay(500);
    // assemble inverted pattern 0b10101010 into the array
    my_SIPOs.set_array_pin(0, LOW);
    my_SIPOs.set_array_pin(1, HIGH);
    my_SIPOs.set_array_pin(2, LOW);
    my_SIPOs.set_array_pin(3, HIGH);
    my_SIPOs.set_array_pin(4, LOW);
    my_SIPOs.set_array_pin(5, HIGH);
    my_SIPOs.set_array_pin(6, LOW);
    my_SIPOs.set_array_pin(7, HIGH);
    my_SIPOs.xfer_array(MSBFIRST);  // now move array to physical SIPO and light up the LEDs
    delay(500);
  } while (true);
}

ez_SIPO8_lib - Tutorial 2 - Relative Pin Addressing

C/C++
Accompanying sketch for Tutorial 2 - Relative Pin Addressing
//
//   Tutorial 2 - use of ez_SPI8 library,
//   Relative addressing - 1 x physical SIPO IC
//   The sketch demonstrates two ways in which SIPO output pins
//   may be updated, individually (set_bank_pin) or in a group
//   of 8 pins (set_bank_SIPO), representing a single 8it SIPO
//   within a bank.
//
//   Ron D Bentley, Stafford, UK
//   April 2021
//
//   This example and code is in the public domain and
//   may be used without restriction and without warranty.
//

#include <ez_SIPO8_lib.h>

#define Max_SIPOs  1  // two virtual SIPOs for this tutorial
#define Max_timers 0  // no timers required

// initiate the class for Max SIPOs/timers required
SIPO8 my_SIPOs(Max_SIPOs, Max_timers);

int bank_id;  // used to keep the SIPO bank id

void setup() {
  Serial.begin(9600);
  bank_id = my_SIPOs.create_bank(8, 10, 9, 1); // absolute pin addresses 0-7, relative addresses 0-7
  if (bank_id == create_bank_failure) {
    Serial.println(F("\nfailed to create bank"));
    Serial.flush();
    exit(0);
  }
  // print the bank data for confirmation/inspection
  my_SIPOs.print_SIPO_data();
}

void loop() {
  // start by setting the only SIPO (0) in the bank to all outputs off/LOW
  my_SIPOs.set_bank_SIPO(bank_id, 0, LOW);
  my_SIPOs.xfer_bank(bank_id, LSBFIRST);   // move virtual pin statuses to real SIPO output pins
  do {
    //
    // setup pattern for first cycle: 0b01010101
    // note that set_bank_pin uses relative addressing
    my_SIPOs.set_bank_pin(bank_id, 0, HIGH); // least significant bit/pin
    my_SIPOs.set_bank_pin(bank_id, 1, LOW);
    my_SIPOs.set_bank_pin(bank_id, 2, HIGH);
    my_SIPOs.set_bank_pin(bank_id, 3, LOW);
    my_SIPOs.set_bank_pin(bank_id, 4, HIGH);
    my_SIPOs.set_bank_pin(bank_id, 5, LOW);
    my_SIPOs.set_bank_pin(bank_id, 6, HIGH);
    my_SIPOs.set_bank_pin(bank_id, 7, LOW); // most significant bit/pin
    my_SIPOs.xfer_bank(bank_id, MSBFIRST);
    delay(500);
    //
    // setup reverse pattern using 8bit write function: 0b10101010
    // note that set_bank_SIPO uses relative addressing for SIPOs in the bank
    my_SIPOs.set_bank_SIPO(bank_id, 0, 0b10101010);
    my_SIPOs.xfer_bank(bank_id, MSBFIRST);
    delay(500);
  } while (true);
}

ez_SIPO8_lib - Tutorial 3 - SIPO8 Timers (flashing LEDs)

C/C++
Accompanying sketch for Tutorial 3 - SIPO8 Timers (flashing LEDs)
//
//   Tutorial 3 - use of ez_SIPO8 library,
//   1x physical SIPO, and use of SIPO8 timers to control SIPO outputs with time
//
//   Ron D Bentley, Stafford, UK
//   April 2021
//
//   This example and code is in the public domain and
//   may be used without restriction and without warranty.
//

#include <ez_SIPO8_lib.h>

#define Max_SIPOs  1  // one virtual SIPO for this tutorial
#define Max_timers 8  // 8 timers required to map a complete 8bit SIPO

// initiate the class for Max SIPOs/timers required
SIPO8 my_SIPOs(Max_SIPOs, Max_timers);

int bank0_id;  // used to keep the SIPO bank id
//
// setup pin/LED flash data
uint8_t timer;
uint32_t timer_intervals[Max_timers] = {
  300, 400, 500, 600, 700, 800, 900, 1000 // millisecond elapse timer values
};
uint8_t timer_pins[Max_timers] = {
  0, 1, 2, 3, 4, 5, 6, 7 // SIPO output pin absolute addresses
};

void setup() {
  Serial.begin(9600);
  // create bank, params are:
  // data pin, clock pin, latch pin, number of SIPOs this bank
  bank0_id = my_SIPOs.create_bank(8, 10, 9, 1); 
  if (bank0_id == create_bank_failure) {
    Serial.println(F("\nfailed to create bank"));
    Serial.flush();
    exit(0);
  }
  // print the bank data for confirmation/inspection
  my_SIPOs.print_SIPO_data();
}

void loop() {
  // start by setting all SIPO outputs to low (off)
  my_SIPOs.set_all_array_pins(LOW);// set all declared virtual output pins LOW/off
  my_SIPOs.xfer_array(LSBFIRST);   // move virtual pin statuses to real SIPO output pins
  //
  // start all timers
  for (timer = 0; timer < Max_timers; timer++) {
    my_SIPOs.SIPO8_start_timer(timer);
  }
  timer = 0; // start checking at first timer
  do {
    // check each timer for elapse and, if elapsed, invert the timer's output pin
    // and reset the timer
    if (my_SIPOs.SIPO8_timer_elapsed(timer, timer_intervals[timer]) == elapsed) {
      my_SIPOs.invert_array_pin(timer_pins[timer]); // invert the pin associated with this timer
      my_SIPOs.xfer_array(MSBFIRST);                // update physical SIPO outputs
      my_SIPOs.SIPO8_start_timer(timer);            // reset/restart the current timer
    }
    timer++;  // move on to next timer
    if (timer >= Max_timers) timer = 0;  // wrap around to beginning
  } while (true);
}

ez_SIPO8_lib - Tutorial 4 - Cascaded SIPOs, Rotating LED Patterns

C/C++
Accompanying sketch for Tutorial 4 - Cascaded SIPOs, Rotating LED Patterns
//
//   Tutorial 4 - use of ez_SPI8 library,
//   2 x physical SIPOs, cascaded into a single SIPO bank
//
//   Ron D Bentley, Stafford, UK
//   April 2021
//
//   This example and code is in the public domain and
//   may be used without restriction and without warranty.
//

#include <ez_SIPO8_lib.h>

int bank_id;

#define Num_SIPOs   2
#define Num_timers  0

SIPO8 my_SIPOs(Num_SIPOs, Num_timers); // initiate the class for the tutorial

void setup() {
  Serial.begin(9600);
  // create a single bank of required number of SIPOs in the bank,
  // params are: data pin, clock pin, latch pin, number of SIPOs this bank
  bank_id = my_SIPOs.create_bank(8, 10, 9, 2);
  if (bank_id == create_bank_failure) {
    my_SIPOs.print_SIPO_data();
    Serial.println(F("failed to create bank"));
    Serial.flush();
    exit(0);
  }
  // print the bank data for confirmation/inspection
  my_SIPOs.print_SIPO_data();
}

// patterns to be xferred / shifted out to the SIPO bank
#define num_patterns 7
uint8_t patterns[num_patterns] = {// starting patterns
  0b11111111,
  0b00001111,
  0b00110011,
  0b01010101,
  0b11000011,
  0b01000010,
  0b00111100
};

void loop() {
  // scroll through every SIPO bank and assert the given pattern to every
  // SIPO defined by the bank.
  do {
    // setup each pattern in turn
    for (uint8_t pattern = 0; pattern < num_patterns; pattern++) {
      // perform 2 passes for each pattern - first, as defined, second inverted
      for (uint8_t cycle = 0; cycle < 2; cycle++) {
        // consider each SIPO in this 2 SIPO cascaded bank
        for (uint8_t sipo = 0; sipo < my_SIPOs.SIPO_banks[bank_id].bank_num_SIPOs; sipo++) {
          my_SIPOs.set_bank_SIPO(bank_id, sipo, patterns[pattern]); // set all pins of this SIPO in this bank_id
        }
        my_SIPOs.xfer_bank(bank_id, MSBFIRST);  // update physical SIPOs for this bank_id
        delay(500);
        patterns[pattern] = ~patterns[pattern]; // invert current pattern byte for next pass
      }
    }
  } while (true);
}

ez_SIPO8_lib - Tutorial 5 - Bank Interleaving (Strobing LEDs)

C/C++
Accompanying sketch for Tutorial 5 - Bank Interleaving (Strobing LEDs)
//
//   Tutorial 5 - use of ez_SPI8 library,
//   Demonstration of SIPO bank interleaving - 8 x physical SIPOs
//   each mapped to an individual bank but with the same 3-wire digital
//   pin microcontroller interface.
//
//   The sketch sets up each single SIPO bank with a different binary 8bit
//   (8 pin) pattern which is then xferred to the physical single SIPO IC.
//   The sketch mimics the strobe sketch by using bank interleaving.
//   To note is the small amount of SIPO8 library code that is used.
//
//   Ron D Bentley, Stafford, UK
//   April 2021
//
//   This example and code is in the public domain and
//   may be used without restriction and without warranty.
//

#include <ez_SIPO8_lib.h>

int bank_id;

#define Num_SIPOs   8
#define Num_timers  0

SIPO8 my_SIPOs(Num_SIPOs, Num_timers); // initiate the class for the tutorial

uint8_t bank_ids[Num_SIPOs]; // one bank_id per SIPO bank
uint8_t bank;

void setup() {
  Serial.begin(9600);
  // create banks of 1 x SIPO, all of same 3-wire interface and initialise
  // with each strobe pattern - 0b00000001, 0b00000010, 0b00000100, etc.
  // create_bank params are: data pin, clock pin, latch pin, number of SIPOs this bank
  for (bank = 0; bank < Num_SIPOs; bank++) {
    bank_ids[bank] = my_SIPOs.create_bank(8, 10, 9, 1);
    if (bank_ids[bank] == create_bank_failure) {
      Serial.println(F("failed to create bank"));
      Serial.flush();
      exit(0);
    }
    // now set up the strobe patterns in the bank's single SIPO, relative SIPO address is 0
    // sliding pattern of 1's starting at 0b00000001
    my_SIPOs.set_bank_SIPO(bank_ids[bank], 0, 1 << bank); // set up this bank's strobe pattern
  }
  // print the bank data and pin statuses for confirmation/inspection
  my_SIPOs.print_SIPO_data();
  my_SIPOs.print_pin_statuses();
}

void loop() {
  // scroll through every SIPO bank (interleave) and xfer the bank's pins
  // according to the direction for shift out.
  bool msb_or_lsb = MSBFIRST; // starting direction
  do {
    for (bank = 0; bank < Num_SIPOs; bank++) {
      my_SIPOs.xfer_bank(bank, msb_or_lsb); // xfer out this bank's SIPO pins
      delay(50);
    }
    msb_or_lsb = !msb_or_lsb; // switch direction
  } while (true);
}

ez_SIPO8_lib - User Guide - Sketch Example – Flashing LEDs, Absolute Pin Addressing

C/C++
ez_SIPO8_lib - User Guide - Sketch Example – Flashing LEDs, Absolute Pin Addressing, see User Guide
//
//   Flash LEDs -
//   This sketch independently flashes eight LEDs each connected to a SIPO output pin,
//   each at a different frequency, using SIPO8 library timers.
//   The demonstration runs for a fixed time before terminating, again this being
//   controlled by a SIPO8 library timer.
//
//   This example uses absolute array pin addressing.
//
//   Ron D Bentley, Stafford, UK
//   April 2021
//
//   This example and code is in the public domain and
//   may be used without restriction and without warranty.
//
#include "ez_SPIC8_lib.h"

#define Max_SPICs  1
#define Max_timers 9 // we will create 9 timers, 0-7 for LEDs and 8 for our timimg
#define my_timer   8 // we will use SPIC8 timer 8 for timing our demonstration

#define number_banks     1 // demonstration using 1 bank
#define demo_time    20000 // time in milliseconds flash demonstration to run for

int bank0_id;

// setup pin/LED flash data
uint8_t timer;

uint32_t timer_intervals[Max_timers] = {
  // millisecond elapse timer values for each timer, 0 - 7
  300, 400, 500, 600, 700, 800, 900, 1000
};

uint8_t timer_pins[Max_timers] = {
  // SPIC output pin absolute addresses for timers 0 - 7
  0, 1, 2, 3, 4, 5, 6, 7
};

SPIC8 my_SPICs(Max_SPICs, Max_timers); // initiate the class for max SPICs/timers

void setup() {
  Serial.begin(9600);
  // create a single bank, params are:
  // data pin, clock pin, latch pin, number of SPICs this bank
  bank0_id = my_SPICs.create_bank(8, 10, 9, number_banks);
  if (bank0_id == create_bank_failure) {
    Serial.println(F("\nfailed to create bank"));
    Serial.flush();
    exit(0);
  }
  // print the bank data for confirmation/inspection
  my_SPICs.print_SPIC_data();
}

void loop() {
  //
  // now lets add in the flashing LED code from Tutorial 3
  // start by setting all SPIC outputs to low (off)
  my_SPICs.set_all_array_pins(LOW);// set all declared virtual output pins LOW/off
  my_SPICs.xfer_array(LSBFIRST);   // move virtual pin to real SPIC output pins
  my_SPICs.SPIC8_start_timer(my_timer);  // start my_timer
  // keep processing until our my_timer has elapsed
  // start all timers
  for (timer = 0; timer < Max_timers - 1; timer++) {
    my_SPICs.SPIC8_start_timer(timer);
  }
  timer = 0; // start checking at first timer
  do {
    // check each timer for elapse and, if elapsed, invert the timer's output pin
    // and reset the timer
    if (my_SPICs.SPIC8_timer_elapsed(timer, timer_intervals[timer]) == elapsed) {
      my_SPICs.invert_array_pin(timer_pins[timer]); // invert the pin associated with this timer
      my_SPICs.xfer_array(MSBFIRST);                // update physical SPIC outputs
      my_SPICs.SPIC8_start_timer(timer);            // reset/restart the current timer
    }
    timer++;  // move on to next timer
    if (timer >= Max_timers - 1) timer = 0; // wrap around to beginning
  } while (my_SPICs.SPIC8_timer_elapsed(my_timer, demo_time) != elapsed);
  //
  // end of LED flash demonstration - now clear down LEDs to off(LOW)
  my_SPICs.set_all_array_pins(LOW);// set all declared virtual output pins LOW/off
  my_SPICs.xfer_array(LSBFIRST);   // move virtual pin statuses to real SPIC output pins
  exit(0);
}

ez_SIPO8_lib - User Guide - Sketch Example – Strobing LEDs, Relative Pin Addressing

C/C++
ez_SIPO8_lib - User Guide - Sketch Example – Strobing LEDs, Relative Pin Addressing, see User Guide
//
//   Strobe -
//   This sketch strobes a number of LEDs driven by a SIPO IC, 8 output pins
//   forwards and backwards at a defined frequency.
//
//   This example uses relative bank addressing.
//
//   Ron D Bentley, Stafford, UK
//   April 2021
//
//   This example and code is in the public domain and
//   may be used without restriction and without warranty.
//

#include <ez_SIPO8_lib.h>

#define Max_SIPOs  1 // 1 x SIPOs - provides 8 output pins
#define Max_timers 0 // no timers required, will use delay 

#define data_pin    8
#define clock_pin  10
#define latch_pin   9

#define strobe_frequency     50  // milli seconds, delay frequency

// initiate the class for max SIPOs/timers required
SIPO8 my_SIPOs(Max_SIPOs, Max_timers);

int bank_id;  // used to keep the SIPO bank id

void setup() {
  Serial.begin(9600);
  // create a bank of 'Max_SIPOs' using create_bank function:
  bank_id = my_SIPOs.create_bank(data_pin, clock_pin, latch_pin, Max_SIPOs);
  if (bank_id == create_bank_failure) {
    Serial.println(F("\nfailed to create bank, terminated"));
    Serial.flush();
    exit(0);
  }
  // start by setting all SIPO outputs to low (off) in the SIPO bank
  my_SIPOs.set_bank_SIPO(bank_id, 0, 0b00000000); // only 1 SIPO in bank,index 0
  my_SIPOs.xfer_bank(bank_id, MSBFIRST);
  // print the bank data for confirmation/inspection
  my_SIPOs.print_SIPO_data();
}

void loop() {
  //strobe example, strobe bank_id pins forward and back
  bool msb_or_lsb = MSBFIRST; // pick an initial strobe direction
  int pins_in_bank = my_SIPOs.num_pins_in_bank(bank_id); // number SIPO output pins in this bank
  if (pins_in_bank > 0) {
    do {
      for (uint16_t pin = 0; pin < pins_in_bank; pin++) {
        my_SIPOs.set_bank_pin(bank_id, pin, HIGH); // set next strobe pin 
        my_SIPOs.xfer_bank(bank_id, msb_or_lsb);   // update physical SIPO bank
        delay(strobe_frequency);
        my_SIPOs.set_bank_pin(bank_id, pin, LOW);  // clear next strobe pin 
        my_SIPOs.xfer_bank(bank_id, msb_or_lsb);   // update physical SIPO bank
      }
      msb_or_lsb = !msb_or_lsb; // switch strobe direction
    } while (true);
  } else {
    Serial.println(F("\nbank not found, terminated"));
    Serial.flush();
    exit(0);
  }
}

ez_SIPO8_lib - User Guide - Sketch Example – Chasing LEDs, using 8 x SIPO ICs in a Single Cascade

C/C++
ez_SIPO8_lib - User Guide - Sketch Example – Chasing LEDs, using 8 x SIPO ICs in a Single Cascade, see User Guide
//
//   Chasing LEDs -
//   This sketch illuminates a defined sequential array of LEDs connected to
//   SIPO output pins, at a specified frequency (milliseconds).
//   At the end of the LED sequence, the LEDs are reset and the
//   cycle repeated.
//
//   The sketch can be configured for a SIPO array of any length, but this
//   example is configured to demonstrate the chasing LEDs of a clock dial.
//   In this instance the SIPO array length will be 64 output pins (8 x SIPOs)
//   but only the first 60 outputs will be used (0-59 seconds). The frequency
//   will be set to 1 second (1000 millisecs).
//
//   This example uses absolute addressing of the defined SIPO array.
//
//   Ron D Bentley, Stafford, UK
//   April 2021
//
//   This example and code is in the public domain and
//   may be used without restriction and without warranty.
//

#include <ez_SIPO8_lib.h>

#define Max_SIPOs  8 // 8 x SIPOs - provides 64 output pins
#define Max_timers 1 // used to count 1 second elapsing 'beats'

#define data_pin    8
#define clock_pin  10
#define latch_pin   9

#define chase_length  60   // chasing seconds on a clock
#define frequency     1000 // milli seconds - 1 second frequency

// initiate the class for max SIPOs/timers required
SIPO8 my_SIPOs(Max_SIPOs, Max_timers);

int my_LEDs;  // used to keep the SIPO bank id

void setup() {
  Serial.begin(9600);
  // create a bank of 'Max_SIPOs' using create_bank function:
  my_LEDs = my_SIPOs.create_bank(data_pin, clock_pin, latch_pin, Max_SIPOs);
  if (my_LEDs == create_bank_failure) {
    Serial.println(F("\nfailed to create bank"));
    Serial.flush();
    exit(0);
  }
  // start by setting all SIPO outputs to low (off)
  my_SIPOs.set_all_array_pins(LOW);
  my_SIPOs.xfer_array(MSBFIRST);
  // print the bank data for confirmation/inspection
  my_SIPOs.print_SIPO_data();
}

void loop() {
  uint8_t  next_pin = 0;
  my_SIPOs.SIPO8_start_timer(timer0); // kick off the timer
  do {
    if (my_SIPOs.SIPO8_timer_elapsed(timer0, frequency) == elapsed) {
      // 1 second time elapsed, so update next pin in the array
      my_SIPOs.SIPO8_start_timer(timer0); // restart 1 second count for next cycle
      if (next_pin == chase_length) { // wrap around
        my_SIPOs.set_all_array_pins(LOW); // clear all pins
        next_pin = 0;
      } else {
        my_SIPOs.set_array_pin(next_pin, HIGH); // set absolute next_pin pin status
        next_pin++;
      }
      my_SIPOs.xfer_array(MSBFIRST); // update physical SIPOs
    }
  } while (true);
}

ez_SIPO8_lib - User Guide - Sketch Example – Driving a 7 Segment LED Matrix Display

C/C++
ez_SIPO8_lib - User Guide - Sketch Example – Driving a 7 Segment LED Matrix Display, see User Guide
//
//   7 Segment LED Matrix -
//   Sketch uses a single SIPO IC to map the 7 segment matrix and creates
//   a single bank comprising one SIPO IC.
//
//   This sketch drives a single 7 segment LED matrix, displaying digits
//   from 0 to hex F, in two repeating cycles:
//   1. cycle 1 - shifting out the most significant bit first (MSBFIRST), with each
//      character appended with the DP character ".", eg "3."
//   2. cycle 2 - shifting out the least significant bit first (LSBFIRST), without the DP
//      character appended.
//
//   The sketch offers two methods for updating a 7 Segment LED Matrix, choice
//   is a simple matter a preference/familiarity.
//
//   This example uses relative bank addressing.
//
//   Ron D Bentley, Stafford, UK
//   April 2021
//
//   This example and code is in the public domain and
//   may be used without restriction and without warranty.
//

#include <ez_SIPO8_lib.h>

#define max_SIPOs  1  // one 1 SIPO for this example
#define max_timers 0  // no timers required

// initiate the class for max SIPOs/timers required
SIPO8 my_SIPOs(max_SIPOs, max_timers);

int my_matrix7;  // used to keep the SIPO bank id

#define num_digits   16
uint8_t LSB_matrix_chars[num_digits] = {
  252, 96, 218, 242, 102, 182, 190, 224, 254, 246, 238, 62, 156, 122, 158, 142
};
#define LSB_DP_char 0b00000001  // "." value for LSBFIRST shiftout

uint8_t MSB_matrix_chars[num_digits] = {
  63, 6, 91, 79, 102, 109, 125, 7, 127, 111, 119, 124, 57, 94, 121, 113
};
#define MSB_DP_char 0b10000000  // "." value for MSBFIRST shiftout

void setup() {
  Serial.begin(9600);
  // create a bank of 1 SIPO using create_bank function:
  // data pin, clock pin, latch pin, number of SIPO this bank
  my_matrix7 = my_SIPOs.create_bank(8, 10, 9, 1); // data pin, clock pin, latch pin, number of SIPO this bank
  if (my_matrix7 == create_bank_failure) {
    Serial.println(F("\nfailed to create bank"));
    Serial.flush();
    exit(0);
  }
  // print the bank data for confirmation/inspection
  my_SIPOs.print_SIPO_data();
}

void loop() {
  // keep running through the digits 0 to hex F, as defined by the
  // bit patterns in the preset array MSB_/LSB_matrix_chars
  do {
    // cycle 1 - MSBFIRST, with appended DP character "."
    my_SIPOs.set_bank_SIPO(my_matrix7, 0, 0b00000000);  // reset all LEDs in the matrix to off/LOW
    my_SIPOs.xfer_bank(my_matrix7, MSBFIRST);
    delay(50);
    for (uint8_t digit = 0; digit < num_digits; digit++) {
      my_SIPOs.set_bank_SIPO(my_matrix7, 0, MSB_matrix_chars[digit] + MSB_DP_char); // append "."
      my_SIPOs.xfer_bank(my_matrix7, MSBFIRST);
      delay(500);
    }
    // cycle 2 - LSBFIRST, no appended DP character
    my_SIPOs.set_bank_SIPO(my_matrix7, 0, 0b00000000);  // reset all LEDs in the matrix to off/LOW
    my_SIPOs.xfer_bank(my_matrix7, LSBFIRST);
    delay(50);
    for (uint8_t digit = 0; digit < num_digits; digit++) {
      // if DP char "." required to be appended to a char, then add 'LSB_DP_char'
      // to the 'LSB_matrix_chars[digit]' parameter
      my_SIPOs.set_bank_SIPO(my_matrix7, 0, LSB_matrix_chars[digit]);
      my_SIPOs.xfer_bank(my_matrix7, LSBFIRST);
      delay(500);
    }
  } while (true);
}

Credits

ronbentley1

ronbentley1

25 projects • 13 followers

Comments