Controlling Multiple 7 Seg Displays Doing Different Things Based on Different Inputs

Hey Arduino ninjas!

I have one main question and then a smaller much easier question if i may.

Im working with a little project to create some 7 segment displays. Overall there will be 4 or 5 one or two digit displays doing some very basic counting tasks.

Ive got the two segment display working after adjusting some code i found online and it works perfectly.

I'm now at the stage where i want to add the second one segment display.
My main question is can i get this to work using the same shift register and outputs on the Arduino or will i need another set of outputs and shift register?

Here's a diagram of what im trying to do..... The display on the right works perfectly.
The one on the left is the one i need to get working with the additional buttons.

The idea being i want to add a few more to this as well so it needs to be scalable.
This is my code i'm using.... (modified from the original on Simple Circuit) which works fine for the 2 digit display on the right.

/*
 * 7-segment display with 74HC595 shift register
 * 2-Digit counter example.
 * Common anode 7-segment display is used.
 * This is a free software with NO WARRANTY.
 * https://simple-circuit.com/
 */
 
// counter button definition
#define button1    A0 // increase
#define button2    A1 // decrease
#define button3    A2 // reset
#define button4    A3 // increase
#define button5    A4 // decrease
#define button6    A5 // reset
 
// shift register pin definitions
#define clockPin  7   // clock pin
#define dataPin   6   // data pin
 
// common pins of the four digits definitions
#define Dig1    2
#define Dig2    3
#define Dig3    4
 
// variable declarations
byte current_digit;
int  count = 0;
void disp(byte number, bool dec_point = 0);
 
void setup()
{
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
  pinMode(Dig1, OUTPUT);
  pinMode(Dig2, OUTPUT);
  pinMode(Dig3, OUTPUT);  
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
 
  disp_off();  // turn off the display
 
  // Timer1 module overflow interrupt configuration
  TCCR1A = 0;
  TCCR1B = 1;  // enable Timer1 with prescaler = 1 ( 16 ticks each 1 µs)
  TCNT1  = 0;  // set Timer1 preload value to 0 (reset)
  TIMSK1 = 1;  // enable Timer1 overflow interrupt
}
 
ISR(TIMER1_OVF_vect)   // Timer1 interrupt service routine (ISR)
{
  disp_off();  // turn off the display
 
  switch (current_digit)
  {
    case 1:
      disp( (count / 10) % 10 );   // prepare to display digit 2 (left)
      digitalWrite(Dig2, LOW);    // turn on digit 2
      break;
 
    case 2:
      disp(count % 10);   // prepare to display digit 1 (right)
      digitalWrite(Dig1, LOW);  // turn on digit 1
  }
 
  current_digit = (current_digit % 4) + 1;
}
 
// main loop
void loop()
{
  if(digitalRead(button1) == 0)
  {
    count++;  // increment 'count' by 1
    if(count > 9999)
      count = 0;
    delay(200);  // wait 200 milliseconds
  }
  if(digitalRead(button2) == 0)
  {
    count--;  // decrement 'count' by 1
    if(count > 9999)
      count = 0;
    delay(200);  // wait 200 milliseconds
  }
  if(digitalRead(button3) == 0)
  count = 0;
  delay(200);  // wait 200 milliseconds
}
 
void disp(byte number, bool dec_point)
{
  switch (number)
  {
    case 0:  // print 0
      shiftOut(dataPin, clockPin, MSBFIRST, 0x02 | !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
 
    case 1:  // print 1
      shiftOut(dataPin, clockPin, MSBFIRST, 0x9E | !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
 
    case 2:  // print 2
      shiftOut(dataPin, clockPin, MSBFIRST, 0x24 | !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
 
    case 3:  // print 3
      shiftOut(dataPin, clockPin, MSBFIRST, 0x0C | !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
 
    case 4:  // print 4
      shiftOut(dataPin, clockPin, MSBFIRST, 0x98 | !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
 
    case 5:  // print 5
      shiftOut(dataPin, clockPin, MSBFIRST, 0x48 | !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
 
    case 6:  // print 6
      shiftOut(dataPin, clockPin, MSBFIRST, 0x40 | !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
    
    case 7:  // print 7
      shiftOut(dataPin, clockPin, MSBFIRST, 0x1E | !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
 
    case 8:  // print 8
      shiftOut(dataPin, clockPin, MSBFIRST, !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
      break;
 
    case 9:  // print 9
      shiftOut(dataPin, clockPin, MSBFIRST, 0x08 | !dec_point);
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
  }
}
 
void disp_off()
{
   digitalWrite(Dig1, HIGH);
   digitalWrite(Dig2, HIGH);
   digitalWrite(Dig3, HIGH);
}
 
// end of code.

How do i modify the loop to control the 2 displays separately?
Once i know how to do it for one i can crack on and do it for others myself.

And finally my second easier question is how can i modify the code to increase the button presses by 5 instead of just 1.

Many thanks

learn how the IDE Example "Blink Without Delay" works and get rid of all your delays(). Than you can do several things in "near parallel".

Personally I'm convinced that the usage of a LED driver like a MAX7219 or a HT16K33 will it make far easier for you than using a shift register which have to be driven permanentely by the Arduino.

below is code for reading multiple buttons. i post this an example of using sub-functions to monitor multiple things instead of having separate pieces of code for each input

the same is needed for the displays. some piece of code repeatedly updates the code displays. other code simply updates some data values with what (if anything) to display)

multiple 7-segment displays are often multiplexed. the displays are disabled, the 7 output bits changed, a display enabled for a brief period of time and this repeated for each display. doing a complete cycle of all displays 50 times a seconds is probably fast enough.

// check multiple buttons and toggle LEDs

enum { Off = HIGH, On = LOW };

byte pinsLed [] = { 10, 11, 12 };
byte pinsBut [] = { A1, A2, A3 };
#define N_BUT   sizeof(pinsBut)

byte butState [N_BUT];

// -----------------------------------------------------------------------------
int
chkButtons ()
{
    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        byte but = digitalRead (pinsBut [n]);

        if (butState [n] != but)  {
            butState [n] = but;

            delay (10);     // debounce

            if (On == but)
                return n;
        }
    }
    return -1;
}

// -----------------------------------------------------------------------------
void
loop ()
{
    switch (chkButtons ())  {
    case 2:
        digitalWrite (pinsLed [2], ! digitalRead (pinsLed [2]));
        break;

    case 1:
        digitalWrite (pinsLed [1], ! digitalRead (pinsLed [1]));
        break;

    case 0:
        digitalWrite (pinsLed [0], ! digitalRead (pinsLed [0]));
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        pinMode (pinsBut [n], INPUT_PULLUP);
        butState [n] = digitalRead (pinsBut [n]);
    }

    for (unsigned n = 0; n < sizeof(pinsLed); n++)  {
        digitalWrite (pinsLed [n], Off);
        pinMode      (pinsLed [n], OUTPUT);
    }
}

Thanks for this ill take a look at the "without delay" example.

I dont think the drivers will work for me due to the way i want to us them as a bit of additional info included this diagram of what it will look like as an end result. independent displays with independent buttons and a master reset button.

Thanks gcjr ill have a look at this and see if i can get my head around it :slight_smile:

No, the max7219 or ht16k33 will work fine for your use. Better in fact, and simpler.

One Max 7219 chip can drive all 7 digits. No need for transistors or series resistors. One resistor and a couple of caps are the only other components needed, and only 3 Arduino pins.

One ht16k33 chip, these normally come on a breakout board, can not only drive all your digits, but scan all your buttons for you too. No extra components and only 2 Arduino pins.

Many thanks Paul,

Seems i need to take a look at the two chips and reassess my approach!
I'm all about simplifying here if i can!

Looking online at the configurations for these most seem to come in 2 x 4 7 seg display configurations inc the driver, im guessing you can break out the display configurations into what ever you like after the chip so my layout above would be fine?

Yes, any combination of single, dual, triple or quad digit displays you like. Max7219 can drive up to 8 digits and ht16k33 up to 16 digits.

However, while you can use common anode displays, both chips are designed for common cathode displays. Some of their convenient features won't be available if you use common anode, which might make you code less simple, and with the ht16k33 I think the max 16 digits can only be reached with common cathode, otherwise the limit is 8 digits.

1 Like

basically yes. either you use bare bone ICs and make your own PCB (the MAX7219 is available in DIL) or you could chose this premade PCBs. The MAX7219 modules with green PCB come mostly with sockets, so you can pull out the two 4digit displays very easily and use "single" digit displays also. As already mentioned - these ICs are best used with Common Cathode.

I would just use the displays as they come, so the MAX7219 in groups of 4 digits and the HT16K33 in groups of 2 digits (although unsoldering these modules might need a calm hand ... I would just use them as 4digit modules). If you stil want to wire your exact 1 / 2 / 2 digits ... the HT16K33 is SMD only, but you will find also breakout boards (without LEDs).

If you need libraries for these two display drivers have look on my page:
https://werner.rothschopf.net/201904_arduino_ledcontrol_max7219.htm and
https://werner.rothschopf.net/201909_arduino_ht16k33.htm

1 Like

Thanks Noiasca,

Already had a look and added the libraries and some youtube on the chip,
I dont want the 2x4 config as that doesnt really fit into the design. ill have to customise that a bit but that should be fairly easy its just more wires/tracks rather than being neat and tidy in one unit :slight_smile:

@PheonixUK Sorry, this is what I meant to suggest if using max 7219. Not canibalising some module that comes with displays already attached.
$_1

Sorry, again, I was not suggesting to canibalise some ht16k33 module that comes with displays attached either. I meant to suggest an ht16k33 chip on a simple breakout board:

Many thanks guys, ive ordered myself an all in once max for now so i can prove my code out on the display as it was cheap.
Once thats done ill look into the logistics of using the ICs and the display configs i want.
Many thanks!
A

1 Like

Hey again guys!

Just wanted to say thanks for the heads up on the Max7219. @PaulRB @noiasca
Got one here at home now, redid my code and this is doing everything I wanted.
design

I still have the one last thing which was the second question in my original post.
At present on button press it increases the display by 1 how can I get this to increase by 5 every time instead? Its probably quite obvious but my brain isnt computing!
Current code again....

/* Arduino 7 Segment MultiCounter 
 * Using the MAX7219CNG LED Driver

*/

#include "LedControl.h"  // Library used for communcation with 7 segment

LedControl lc=LedControl(12,11,13,1);  //  (DIN, CLK, LOAD, number of Max7219 chips)

// Variable to hold current scores
int displayone=0;
int displaytwo=0;
int displaythree=0;
int displayfour=0;

// Variables to split whole number into single digits
int rightdigit;
int leftdigit;

// Switches pin connection to Arduino UNO
#define switchone 2
#define switchtwo 3
#define switchthree 4
#define switchfour 5
#define masterreset A1
#define downone 7
#define downtwo 8
#define downthree 9
#define downfour 10


void setup() {
  pinMode(switchone,INPUT_PULLUP);
  pinMode(switchtwo,INPUT_PULLUP);
  pinMode(switchthree,INPUT_PULLUP);
  pinMode(switchfour,INPUT_PULLUP);
  pinMode(masterreset,INPUT_PULLUP);
  pinMode(downone,INPUT_PULLUP);
  pinMode(downtwo,INPUT_PULLUP);
  pinMode(downthree,INPUT_PULLUP);
  pinMode(downfour,INPUT_PULLUP);
  
  lc.shutdown(0,false);  // Wake up MAX7219

  lc.setIntensity(0,7);  // Set brightness to medium

  lc.clearDisplay(0);  // Clear all displays connected to MAX7219 chip #

// Put zeros on the displays at startup
  
  lc.setDigit(0,0,0,false);  // (Max7219 chip #, Digit, value, DP on or off)
  lc.setDigit(0,1,0,false);
  
  lc.setDigit(0,2,0,false);
  lc.setDigit(0,3,0,false);

  lc.setDigit(0,4,0,false);
  lc.setDigit(0,5,0,false);

  lc.setDigit(0,7,0,false);
}


void loop() { 

  // If switch 1 is clicked
  if (!digitalRead(switchone)) {
    
    displayone++;  // Increase score by 1
  
    // convert whole number to single digits
    rightdigit=displayone%10;
    leftdigit=displayone%100/10;

    // Display extracted digits on the display
    lc.setDigit(0,1,leftdigit,false);
    lc.setDigit(0,0,rightdigit,false);

    
    // Wait until switch is released to continue
    while (!digitalRead(switchone)) { 
    }
    delay(5);  // Small delay to debounce the switch
  }

    if (!digitalRead(switchtwo)) {
      
    displaytwo++;
  
    rightdigit=displaytwo%10;
    leftdigit=displaytwo%100/10;

    lc.setDigit(0,3,leftdigit,false);
    lc.setDigit(0,2,rightdigit,false);

    while (!digitalRead(switchtwo)) { 
    }    
    delay(5);
  }

    if (!digitalRead(switchthree)) {
      
    displaythree++;
  
    rightdigit=displaythree%10;
    leftdigit=displaythree%100/10;

    lc.setDigit(0,5,leftdigit,false);
    lc.setDigit(0,4,rightdigit,false);

    while (!digitalRead(switchthree)) { 
    }    
    delay(5);
  }

    if (!digitalRead(switchfour)) {
      
    displayfour++;
  
    rightdigit=displayfour%10;

    lc.setDigit(0,7,rightdigit,false);

    while (!digitalRead(switchfour)) { 
    }    
    delay(5);
  }

  // If Down 1 is clicked
  if (!digitalRead(downone)) {
    
    displayone--;  // Decrease score by 1
  
    // convert whole number to single digits
    rightdigit=displayone%10;
    leftdigit=displayone%100/10;

    // Display extracted digits on the display
    lc.setDigit(0,1,leftdigit,false);
    lc.setDigit(0,0,rightdigit,false);

    
    // Wait until switch is released to continue
    while (!digitalRead(downone)) { 
    }
    delay(5);  // Small delay to debounce the switch
  }

    if (!digitalRead(downtwo)) {
      
    displaytwo--;
  
    rightdigit=displaytwo%10;
    leftdigit=displaytwo%100/10;

    lc.setDigit(0,3,leftdigit,false);
    lc.setDigit(0,2,rightdigit,false);

    while (!digitalRead(downtwo)) { 
    }    
    delay(5);
  }

    if (!digitalRead(downthree)) {
      
    displaythree--;
  
    rightdigit=displaythree%10;
    leftdigit=displaythree%100/10;

    lc.setDigit(0,5,leftdigit,false);
    lc.setDigit(0,4,rightdigit,false);

    while (!digitalRead(downthree)) { 
    }    
    delay(5);
  }

    if (!digitalRead(downfour)) {
      
    displayfour--;
  
    rightdigit=displayfour%10;

    lc.setDigit(0,7,rightdigit,false);

    while (!digitalRead(downfour)) { 
    }    
    delay(5);
  }

   if (!digitalRead(masterreset)) {

   //Resets the display back to first use.
   lc.clearDisplay(0);

   displayone=0;
   displaytwo=0;
   displaythree=0;
   displayfour=0;

   lc.setDigit(0,0,0,false);
   lc.setDigit(0,1,0,false);
  
   lc.setDigit(0,2,0,false);
   lc.setDigit(0,3,0,false);

   lc.setDigit(0,4,0,false);
   lc.setDigit(0,5,0,false);

   lc.setDigit(0,7,0,false);
   }
   delay(5);
}

Many thanks
Andy

when I read this:

I assume that:

displayone = displayone + 5; // increase score

1 Like

Ill give it a try thanks @noiasca

i did this....

    if (!digitalRead(switchtwo)) {
      
    displaytwo++;
      displaytwo++;  
          displaytwo++;
              displaytwo++;
                  displaytwo++;
  
    rightdigit=displaytwo%10;
    leftdigit=displaytwo%100/10;

which does work but is messy code! ha!

Confrimed all working @noiasca my messy code can now be removed :smiley:

you should have tried this:

Lots of mess yet to remove!

Suggest you learn to use arrays. Any variables or constants that have a number in their name, such as "switchfour" or "downtwo" should probably be elements of arrays. With this technique you should be able to reduce the length of your code by a factor of 4, or almost that.

So

// Variable to hold current scores
int displayone=0;
int displaytwo=0;
int displaythree=0;
int displayfour=0;

becomes

// Variable to hold current scores
int display[4] = {0, 0, 0, 0};

and

// Switches pin connection to Arduino UNO
#define switchone 2
#define switchtwo 3
#define switchthree 4
#define switchfour 5
#define masterreset A1
#define downone 7
#define downtwo 8
#define downthree 9
#define downfour 10

becomes

// Switches pin connection to Arduino UNO
const byte switch[4] = { 2, 3, 4, 5 };
#define masterreset A1
const byte down[4] = { 7, 8, 9, 10 };

and

  pinMode(switchone,INPUT_PULLUP);
  pinMode(switchtwo,INPUT_PULLUP);
  pinMode(switchthree,INPUT_PULLUP);
  pinMode(switchfour,INPUT_PULLUP);
  pinMode(masterreset,INPUT_PULLUP);
  pinMode(downone,INPUT_PULLUP);
  pinMode(downtwo,INPUT_PULLUP);
  pinMode(downthree,INPUT_PULLUP);
  pinMode(downfour,INPUT_PULLUP);

becomes

  for (byte s = 0; s < 4; s++) {
    pinMode(switch[s],INPUT_PULLUP);
    pinMode(down[s],INPUT_PULLUP);
  }
  pinMode(masterreset,INPUT_PULLUP);

@PheonixUK
if you rearange you variables you might see, that each counter consists of two pins, a counter variable and specific place on the display. So you should consider to put your data into a structure. Furthermore if you check your code - all your counters are doing the same:
reading buttons, increase/decrease counter and print something to the display.
These actions can be brought into one class. You just make 4 "objects" of counters.
At the end, you can reduce your sketch by over 100 lines of code and the loop is just a four liner.

This should give you an idea how I would start:

/* Arduino 7 Segment MultiCounter
   Using the MAX7219CNG LED Driver
   221 rows 4240/128
   124 rows 3822/144
*/

#include "NoiascaLedControl.h"         // download from: https://werner.rothschopf.net/201904_arduino_ledcontrol_max7219.htm
const byte maxCsPin = 13;              // LED CS or LOAD -  8 CS
const byte maxClkPin = 11;             // CLK on UNO/NANO - 52 MEGA
const byte maxDataPin = 12;            // MOSI on UNO/NANO - 51 MEGA
const byte noOfModules = 1;
LedControl lc = LedControl  (maxDataPin, maxClkPin, maxCsPin, noOfModules);  // Software Bitbang - use this if you can't use Hardware SPI

// Switches pin connection to Arduino UNO
constexpr byte switchone = 2;
constexpr byte switchtwo = 3;
constexpr byte switchthree = 4;
constexpr byte switchfour = 5;
constexpr byte masterreset = A1;
constexpr byte downone = 7;
constexpr byte downtwo = 8;
constexpr byte downthree = 9;
constexpr byte downfour = 10;

class Counter {
    byte steps = 5;           // steps for up/down
    const byte upPin;         // pin increase
    const byte downPin;       // pin decrease
    const byte startpos;      // start position on display
    const byte digits;        // 1 or two digits
    byte score = 0;           // the current count of this counter
  public:
    Counter (byte upPin, byte downPin, byte startpos, byte digits) :
      upPin{upPin}, downPin{downPin}, startpos{startpos}, digits{digits}
    {}
    void begin()
    {
      pinMode(upPin, INPUT_PULLUP);
      pinMode(downPin, INPUT_PULLUP);
      display();
      if (digits == 1) steps = 1;
    }
    void up()  // increase counter
    {
      if (digits == 1)
      {
        if (score < (byte)(9 - steps)) score += steps;
      }
      else
      {
        if (score < (byte)(99 - steps)) score += steps;
      }
    }
    void down()
    {
      if (score > steps) score -= steps; // decrease counter
    }
    void reset()
    {
      score = 0;
      display();
    }
    void display()
    {
      lc.setCursor(startpos);
      if (digits == 1)
      {
        lc.print(score % 10);
      }
      else
      {
        if (score < 10) lc.print(" ");
        lc.print(score % 100);
      }
    }
    void update()                          // read buttons and increase/decrease counter
    {

      if (digitalRead(upPin) == LOW)
      {
        up();
        display();
        delay(50); // dirty debounce
      }
      if (digitalRead(downPin) == LOW)
      {
        down();
        display();
        delay(50); // dirty debounce
      }
    }
};

Counter counter[] {
  {switchone, downone, 0, 1},  // initialize the first counter with pins, start position on display and used digits
  {switchtwo, downtwo, 2, 2},
  {switchthree, downthree, 4, 2},
  {switchfour, downfour, 6, 2},
};

void setup() {
  pinMode(masterreset, INPUT_PULLUP);
  lc.begin();
  lc.shutdown(0, false); // Wake up MAX7219
  lc.setIntensity(0, 7);
  lc.clearDisplay(0);
  for (auto &i : counter)
  {
    i.begin();
    i.display();
  }
}

void loop() {
  for (auto &i : counter) i.update();      // call each object to let its buttons checked

  if (digitalRead(masterreset) == LOW) {
    for (auto &i : counter) i.reset();
    delay(50); // dirty debounce
  }
}