Multiplexing 3 digit 7seg with shiftregister?

Hi
I’ve bumped into problems with my project.
I followed this guide (WordPress › Error) to set-up a 7segment display counting from 0-9a-f and now I want to extend this with 2 more digits.
If I have that same code and 3 digits hooked up in my breadboard it’s showing 111, 222, 333, 444…
The code is only for one digit, so I didn’t expect so much. However, why is it showing 3 digits, I expected it to only show one, because only one where shifted out?

Anyway, I’ve tried to understand how to extend this code to 3 digits, but the bit-thing gets over my head. I think I understand the bitwise operations for the 1 digit display, but extending it gets me stuck.
From what I understand I need to send the rightmost digit first, then the middle and then the leftmost. (ofcourse that depends on my wiring, but the first digit is the first shiftregister)

Where do I begin if I want to make this code for 3 digit displays?

const int latchPin = 5;  // Pin connected to Pin 12 of 74HC595 (Latch)
const int dataPin  = 6;  // Pin connected to Pin 14 of 74HC595 (Data)
const int clockPin = 7;  // Pin connected to Pin 11 of 74HC595 (Clock)

unsigned long t1;
unsigned long t2;
int i = 0;

// Describe each digit in terms of display segments
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
const byte numbers[16] = {
                    0b11111100,
                    0b01100000,
                    0b11011010,
                    0b11110010,
                    0b01100110,
                    0b10110110,
                    0b10111110,
                    0b11100000,
                    0b11111110,
                    0b11100110,
                    0b11101110,
                    0b00111110,
                    0b10011100,
                    0b01111010,
                    0b10011110,
                    0b10001110
};

void setup()
{

  // initialisation time
  t1 = millis();

  //set pins to output 
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop()
{
    // update digit every two seconds
    t2 = millis();
    if(t2 - t1 > 2000)
    {
      i++;
      t1 = t2;
      if(i > 15) { i = 0; }
    }
    // display the current digit
    show(numbers[i]);
}

void show( byte number)
{
  // Use a loop and a bitwise AND to move over each bit that makes up
  // the seven segment display (from left to right, A => G), and check
  // to see if it should be on or not
  for(int j = 0; j <= 7; j++)
  {
    byte toWrite = number & (0b10000000 >> j); 

    // If all bits are 0 then no point writing it to the shift register,
    // so break out and move on to next segment.
    if(!toWrite) { continue; }

    // Otherwise shift it into the register
    shiftIt(toWrite);
  }
}

void shiftIt (byte data)
{
    // Set latchPin LOW while clocking these 8 bits in to the register
    digitalWrite(latchPin, LOW);

    for (int k=0; k <= 7; k++)
    {
      // clockPin LOW prior to sending a bit
      digitalWrite(clockPin, LOW); 

      // Note that in our case, we need to set pinState to 0 (LOW) for
      // “On” as the 74HC595 is sinking current when using a common
      // anode display. If you want to use a common cathode display then
      // switch this around.
      if ( data & (1 << k) )
      {
        digitalWrite(dataPin, LOW); // turn “On”
      }
      else
      {
        digitalWrite(dataPin, HIGH); // turn “Off”
      }

      // and clock the bit in
      digitalWrite(clockPin, HIGH);
    }

    //stop shifting out data
    digitalWrite(clockPin, LOW); 

    //set latchPin to high to lock and send data
    digitalWrite(latchPin, HIGH);

    // put delay here if you want to see the multiplexing in action!
    //delay(100);
}

Best Regards
Niclas Gustafsson

What is your hardware plan? A shift register for each digit? Software seems overly complicated to me. Use SPI pins instead with 3 daisy chained shift registers: digitalWrite (Latch, LOW); // D10 SPI.transfer (numbers[digit0]); // digit0,1,2 are the three numbers to be displayed SPI.transfer (numbers[digit1]); // D13 is SCK, D11 is MOSI SPI.transfer (numbers[digit2]); digitalWrite (Latch, HIGH); // D10

Hi My hardware is one shiftregister 74hc595 for each digit. I know the software is overly complicated. I've already managed to make a counting 3digit without multiplexing and now I want to use the 1digit multiplexing code for 3 digits.

/Niclas

And you said common anode in the code. Ok, so you will have 3 outputs from the Arduino that will be (or will control 3 transistors) that will be the 3 common anodes. So your multiplexing will require something like: anode 1 high, cathode a low if on anode 1 high, cathode b low if on anode 1 high, cathode c low if on anode 1 high, cathode d low if on anode 1 high, cathode e low if on anode 1 high, cathode f low if on anode 1 high, cathode g low if on anode 1 high, cathode dp low if on then anode 2 high, cathode a low if on anode 2 high, cathode b low if on anode 2 high, cathode c low if on anode 2 high, cathode d low if on anode 2 high, cathode e low if on anode 2 high, cathode f low if on anode 2 high, cathode g low if on anode 2 high, cathode dp low if on then anode 3 high, cathode a low if on anode 3 high, cathode b low if on anode 3 high, cathode c low if on anode 3 high, cathode d low if on anode 3 high, cathode e low if on anode 3 high, cathode f low if on anode 3 high, cathode g low if on anode 3 high, cathode dp low if on

Yes? So make your existing code run inside an outer loop that cycles thru the three anodes and supports the 3 digits to be displayed.

skorpion_: Hi My hardware is one shiftregister 74hc595 for each digit. I know the software is overly complicated. I've already managed to make a counting 3digit without multiplexing and now I want to use the 1digit multiplexing code for 3 digits.

/Niclas

Hi Niclas,

I think the thing that is making you confused is the meaning of "multiplexing". If you have one 595 driving each digit, then no multiplexing is taking place. All 8 segments of all 3 digits can be lit at once, because you have 24 outputs from the 3 x 595s. If you have fewer outputs than segments, then you use multiplexing. This means only lighting some of the segments at once, but switching them around at high speed so that the eye is fooled.

Paul

Thanks for the replies! CrossRoads: Your example looks to be for separate control for each 74hc595, I have them connected in a chain. So I send 8 bits, 8 more bits, and finally 8 more bits, to push data in the shiftregisters.

PaulRB: You are right, this is not "real" multiplexing, but I think you could call it software-multiplexing. I want to use it because it would draw less current from my final project.

I believe I'm getting a little bit more clever regarding this so I hope I can make a update to this thread with working example soon :)

This code did it:

const int latchPin=12;  //latch ST_CP of 74HC595
const int clockPin=13;  //clock SH_CP of 74HC595
const int dataPin=11;  //data DS of 74HC595

void setup(){
    pinMode(latchPin, OUTPUT);  //set pins to output
    pinMode(clockPin, OUTPUT);
    pinMode(dataPin, OUTPUT);
  }

void loop(){
  const byte numbers[10] = {
                    0b11111100,
                    0b01100000,
                    0b11011010,
                    0b11110010,
                    0b01100110,
                    0b10110110,
                    0b10111110,
                    0b11100000,
                    0b11111110,
                    0b11111110
  };   

  show(numbers[3],3);
  show(numbers[2],2);
  show(numbers[1],1);
}

//Software multiplexing
void show(byte number, int digit){
  // Use a loop and a bitwise AND to move over each bit that makes up
  // the seven segment display (from left to right, A => G), and check
  // to see if it should be on or not
  for(int j=0;j<=7;j++){
    //byte toWrite = number & (0b10000000 >> j);
    //numfromarray returnera HIGH om både är HIGH ( utgångspunkt SKIFTA 5 )
    //0b11000000
    byte toWrite = number & (0b10000000 >> j);
    // If all bits are 0 then no point writing it to the shift register,
    // so break out and move on to next segment.
    if(!toWrite) { continue; }
    shiftIt(toWrite, digit);    // Otherwise shift it into the register
  }
}

void shiftIt (byte data, int digit){
    digitalWrite(latchPin, LOW);    // Set latchPin LOW while clocking these 8 bits in to the register
    for (int k=0; k < (8*digit); k++){
      digitalWrite(clockPin, LOW);       // clockPin LOW prior to sending a bit
      // Note that in our case, we need to set pinState to 0 (LOW) for
      // “On” as the 74HC595 is sinking current when using a common
      // anode display. If you want to use a common cathode display then
      // switch this around.
      // 0b01010101 & ()
      if ( data & (1 << k) ){
        digitalWrite(dataPin, LOW); // turn “On”
      }
      else{
        digitalWrite(dataPin, HIGH); // turn “Off”
      }

      digitalWrite(clockPin, HIGH);      // and clock the bit in
    }
    digitalWrite(clockPin, LOW);     //stop shifting out data
    digitalWrite(latchPin, HIGH);    //set latchPin to high to lock and send data
    delay(20);
}

The changes I had to make was in shiftIt() function, pretty easy change when I think about it, but earlier I was so stuck. What helped me was to print out the 1-digit multiplexing code and read and comment it thoroughly. The bitwise operations are not fully crystal-clear for me yet, so thats a bit hard to just look at it and understand it at once, but its getting better!
In the code above there is a delay(50), is to show how the segments lit up one at a time, comment that out and no flickering will be showed

Code is not finished yet. I will make a function to break down a 3-digit into separate functions-calls instead of the three show() with 8bits and digitnumber passed to. Will put that code in here aswell later! :slight_smile:

Best regards
Niclas Gustafsson

skorpion_: CrossRoads: Your example looks to be for separate control for each 74hc595, I have them connected in a chain.

No, I'm certain CrossRoads assumes you are connecting them in a chain. There is only one SPI port and he is suggesting using that, so chaining them would be the only option. You should follow his advice and greatly simplify your code.

skorpion_: PaulRB: You are right, this is not "real" multiplexing, but I think you could call it software-multiplexing. I want to use it because it would draw less current from my final project.

Note sure what you mean by "software multiplexing" and I can't see how it will draw less current. Can you explain further please? If you want to draw less current, increase the value of your series resistors. This will make the display less bright, just as multiplexing would.

EDIT: I suppose you could be attempting "software dimming" by flashing the displays on and off rapidly, effectively dimming via PWM in software....

Paul

Driving a shift register per digit is not multiplexing at all. Not software, not hardware. You are just putting data into 3 registers. This is all you need then:

digitalWrite (Latch, LOW); // D10
SPI.transfer (numbers[digit0]); // digit0,1,2 are the three numbers to be displayed
SPI.transfer (numbers[digit1]); // D13 is SCK, D11 is MOSI
SPI.transfer (numbers[digit2]);
digitalWrite (Latch, HIGH); // D10

Just call it when you need the outputs to change. Connect a PWM pin to the OE/ pins if you want software controlled dimming/current limiting with max current set by the current limit resistors (7 per digit, 1 per segment):

analogWrite (PWMpin, 0); // full bright analogWrite (PWMpin, 255); // full off

PaulRB:

skorpion_:
CrossRoads: Your example looks to be for separate control for each 74hc595, I have them connected in a chain.

No, I’m certain CrossRoads assumes you are connecting them in a chain. There is only one SPI port and he is suggesting using that, so chaining them would be the only option. You should follow his advice and greatly simplify your code.

skorpion_:
PaulRB: You are right, this is not “real” multiplexing, but I think you could call it software-multiplexing. I want to use it because it would draw less current from my final project.

Note sure what you mean by “software multiplexing” and I can’t see how it will draw less current. Can you explain further please? If you want to draw less current, increase the value of your series resistors. This will make the display less bright, just as multiplexing would.

EDIT: I suppose you could be attempting “software dimming” by flashing the displays on and off rapidly, effectively dimming via PWM in software…

Paul

First I thought that multiplexing was a technique to lit up one segment at a time so rapidly that the user didn’t notice any flicker. But maybe this is something that “comes in hand” with multiplexing, rather then the real point of multiplexing?
Your EDIT is exactly what I was trying to do with the code. However I’m not sure if it’s a good way of doing it.
My code that makes, what you refer to as “software dimming”, lit up one segment at a time, so that should draw less current I thought. Instead of lit up all segment at the same time.
Using other resistors for making the segments less bright is something that I also will use, but my idea was to use both “software dimming” and resistors.

CrossRoads:
Driving a shift register per digit is not multiplexing at all. Not software, not hardware.
You are just putting data into 3 registers. This is all you need then:

digitalWrite (Latch, LOW); // D10

SPI.transfer (numbers[digit0]); // digit0,1,2 are the three numbers to be displayed
SPI.transfer (numbers[digit1]); // D13 is SCK, D11 is MOSI
SPI.transfer (numbers[digit2]);
digitalWrite (Latch, HIGH); // D10



Just call it when you need the outputs to change.
Connect a PWM pin to the OE/ pins if you want software controlled dimming/current limiting with max current set by the current limit resistors (7 per digit, 1 per segment):

analogWrite (PWMpin, 0); // full bright
analogWrite (PWMpin, 255); // full off

I didn’t understand the use of SPI first, but now I do :slight_smile: Good tip with the use of PWM.
I put together a new piece of code based on your, and it’s working well.

Thanks for all the help!

Best regards
Niclas