Hello, this is my first post.
I currently have an Arduino Uno on order and wish to control signals on my train layout depending on a train's position. I have been successfully doing this with TTL NAND gates, but future interlocking logic may require over 50 ICs on a single table.
So my first attempt will be to get a simple non-interlocking section containing 4 signals to work. At a high level, there are 12 inputs and 11 outputs. The inputs are sourced via current detectors for individual track blocks on the layout. Occupied track sends a HIGH voltage (~4.0VDC) while unoccupied track sends a LOW voltage (0VDC). The outputs are used to control the LEDs. In this case, there will only be 4 LEDs active (lighted) at any given time. Active LEDs require a LOW voltage because they are wired via a common anode.
Initially, my first course of action was to use the many inputs and outputs available on a Mega, but I wanted a multibyte solution because some of my implementations could exceed the total available.
So after careful research, I decided upon using the 74HC165 and 74HC595 shift registers. There are two of each daisy-chained.
While I am very successful at troubleshooting circuits, computer programs, etc., there are some great minds on this forum that could direct this Arduino newbie.
I have attached a schematic and source code for anyone who has the time to review my design. Your feedback is greatly appreciated.
#include <SPI.h>
SPISettings shiftRegisterSettings(2000000, MSBFIRST, SPI_MODE0);
// Standard SPI pins are used used for clock and data, but slave select can be any free pin
#define ST_CP_LATCH_PIN_IN 8
#define ST_CP_LATCH_PIN_OUT 9
// Input masks for all current detectors and switches
#define DA7_01 0b10000000
#define DA7_02 0b01000000
#define DA7_03 0b00100000
#define DA3_01 0b00010000
#define DA3_05 0b00001000
#define DA6_01 0b00000100
#define DA6_03 0b10000000
#define DA8_02 0b01000000
#define DA8_03 0b00100000
#define DA9_03 0b00010000
#define A135 0b00001000
#define DIRECTION_A 0b00000100
// Signal masks.
#define A1_1_GREEN 0b01111111
#define A1_1_YELLOW 0b10111111
#define A1_1_RED 0b11011111
#define A2_49_YELLOW 0b11110111
#define A2_49_RED 0b11111011
#define A3_1_GREEN 0b01111111
#define A3_1_YELLOW 0b10111111
#define A3_1_RED 0b11011111
#define A4_49_GREEN 0b11101111
#define A4_49_YELLOW 0b11110111
#define A4_49_RED 0b11111011
void setup()
{
pinMode(ST_CP_LATCH_PIN_IN, OUTPUT);
pinMode(ST_CP_LATCH_PIN_OUT, OUTPUT);
}
void loop()
{
// Read (2) bytes from 74HC165 shift registers.
byte bInputMask[2];
SPI.beginTransaction(shiftRegisterSettings);
digitalWrite(ST_CP_LATCH_PIN_IN, LOW);
digitalWrite(ST_CP_LATCH_PIN_OUT, HIGH);
bInputMask[0] = SPI.transfer(0);
bInputMask[1] = SPI.transfer(0);
SPI.endTransaction();
// Initialize bytes to send to send to shift registers for LED control.
// LEDS are wired using a common anode, so an active LED must have
// zero voltage. High voltage means an LED is not active.
byte bSignalMask[2] = { 0b11111111, 0b11111111 };
// Update all signals in the section.
bSignalMask[1] &= Signal_A1_1(bInputMask);
bSignalMask[1] &= Signal_A2_49(bInputMask);
bSignalMask[0] &= Signal_A3_1(bInputMask);
bSignalMask[0] &= Signal_A4_49(bInputMask);
// Send bytes to (2) 74HC595 shift registers.
SPI.beginTransaction(shiftRegisterSettings);
digitalWrite(ST_CP_LATCH_PIN_OUT, LOW);
SPI.transfer(bSignalMask[0]);
SPI.transfer(bSignalMask[1]);
digitalWrite(ST_CP_LATCH_PIN_OUT, HIGH);
SPI.endTransaction();
}
// Update signal A1-1.
byte Signal_A1_1(byte bInputMask[])
{
// Read inputs.
int iDA3_01 = bInputMask[1] & DA3_01 ? HIGH : LOW;
int iDA6_01 = bInputMask[1] & DA6_01 ? HIGH : LOW;
int iDA7_01 = bInputMask[1] & DA7_01 ? HIGH : LOW;
int iA135 = bInputMask[0] & A135 ? HIGH : LOW;
// Combinations.
boolean bCombo_1 = (iDA3_01 == LOW && A135 == HIGH);
// Determine signal color.
byte outByte;
if (iDA7_01 == LOW && iDA6_01 == LOW && bCombo_1) outByte = A1_1_GREEN;
if (iDA7_01 == LOW && iDA6_01 == LOW && !bCombo_1) outByte = A1_1_YELLOW;
if (iDA7_01 == HIGH || iDA6_01 == HIGH) outByte = A1_1_RED;
return outByte;
}
// Update signal A2-49.
byte Signal_A2_49(byte bInputMask[])
{
// Read inputs.
int iDA7_02 = bInputMask[1] & DA7_02 ? HIGH : LOW;
int iDA8_02 = bInputMask[0] & DA8_02 ? HIGH : LOW;
// Determine signal color.
return (iDA7_02 == LOW && iDA8_02 == LOW) ? A2_49_YELLOW : A2_49_RED;
}
// Update signal A3-1.
byte Signal_A3_1(byte bInputMask[])
{
// Read inputs.
int iDA7_03 = bInputMask[1] & DA7_03 ? HIGH : LOW;
int iDA3_05 = bInputMask[1] & DA3_05 ? HIGH : LOW;
int iDA6_03 = bInputMask[0] & DA6_03 ? HIGH : LOW;
int iA135 = bInputMask[0] & A135 ? HIGH : LOW;
int iDIRECTION_A = bInputMask[0] & DIRECTION_A ? HIGH : LOW;
// Combinations.
boolean bCombo_1 = (iDIRECTION_A == LOW && iDA7_03 == LOW && iDA6_03 == LOW);
boolean bCombo_2 = (iDA3_05 == LOW && A135 == HIGH);
// Determine signal color.
byte outByte;
if (bCombo_1 && bCombo_2) outByte = A3_1_GREEN;
if (bCombo_1 && !bCombo_2) outByte = A3_1_YELLOW;
if (!bCombo_1) outByte = A3_1_RED;
return outByte;
}
// Update signal A4-49.
byte Signal_A4_49(byte bInputMask[])
{
// Read inputs.
int iDA7_03 = bInputMask[1] & DA7_03 ? HIGH : LOW;
int iDA8_03 = bInputMask[0] & DA8_03 ? HIGH : LOW;
int iDA9_03 = bInputMask[0] & DA9_03 ? HIGH : LOW;
int iDIRECTION_A = bInputMask[0] & DIRECTION_A ? HIGH : LOW;
// Combinations.
boolean bCombo_1 = (iDIRECTION_A == HIGH && iDA7_03 == LOW && iDA8_03 == LOW);
// Determine signal color.
byte outByte;
if (bCombo_1 && iDA9_03 == LOW) outByte = A4_49_GREEN;
if (bCombo_1 && iDA9_03 == HIGH) outByte = A4_49_YELLOW;
if (!bCombo_1) outByte = A4_49_RED;
return outByte;
}