4 Digit 7 Segment Serial LED Module - TM74HC595

aarg:
Try removing the 3.3V power and check the display while the power pin on the module is disconnected. It might be lit! :slight_smile:

Correct - it stayed lit.

If it is the case, that in itself would be a good reason to power it on 5V, but also there are also logic level matching issues between the two voltages. So I advise you to leave the 3.3V supply out of it.

Noted. I will run it on 5V.

Thanks for the help.
Could u please provide a link to the "sweet" units you described in your earlier post.

Exactly what I was getting at. You can't push 5V logic into a 3V system unless it is designed for it. If you must use 3.3V (although why, I wonder?) you need logic level shifters for the logic lines.

aisc:
Could u please provide a link to the "sweet" units you described in your earlier post.

There's more than one. So search for "TM1638 display" on Ebay and you get mostly the board I'm using, and some other interesting ones.

@aisc, I have written an improved driver. Can you test it for me?

// controls 4 digit 7 segment display
// test version 1
// 2015-04-22 by aarg

const byte numDigits = 4;
byte testDigit[numDigits];

// control pin definitions:
int shiftClock = 5;
int latchClock = 6;
int DIO = 7;

void setup ()
{
  // configure outputs:
  pinMode(shiftClock, OUTPUT);
  pinMode(latchClock, OUTPUT);
  pinMode(DIO, OUTPUT);

  for (int i = 0; i < numDigits; i++)
    testDigit[i] = i;
}

void loop()
{
  displayUpdate(testDigit);
  //
  // any code in here that is non-blocking for more than the display
  // refresh interval should not
  // interfere with the display.
}

// non-blocking display update
//
// every time this routine is called, 16 bits are shifted into the display
// and latched.
// The first 8 bits is the segment data, and first 4 bits of the second byte are
// the segment select.

void displayUpdate(byte displayDigit[])
{
  unsigned long lastDisplayUpdate;
  const int scanRateHz = 60;
  const int displayScanPeriod = 1000000L / numDigits / 8 / scanRateHz;

  byte segmentList[] =
  { // 0123456789AbCdEF-
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x8C, 0xBF, 0xC6, 0xA1, 0x86, 0xFF, 0xbf
  };

  static byte digit = 0;
  static byte segment = 0x80;

  if (micros() - lastDisplayUpdate > displayScanPeriod)
  {

    // send segment data
    for (byte i = 8; i >= 1; i--)
    {
      if ( i & (~segment | segmentList[displayDigit[digit]]) != 0)
        digitalWrite(DIO, HIGH);
      else
        digitalWrite(DIO, LOW);
      bitClock();
    }

    // send digit select data
    for (byte i = 0; i < 8; i++)
    {
      if (i == digit)
        digitalWrite(DIO, HIGH);
      else
        digitalWrite(DIO, LOW);
      bitClock();
    }

    // data is now in the display shift register, so latch to LEDs
    //
    latchData();

    // increment variables to select the next segment and possibly the next digit:
    //
    segment = segment >> 1;
    if (segment == 0)
    {
      segment = 0x80;
      digit++;

      if (digit >= numDigits)
        digit = 0;
    }

    // reset timer for next update:
    lastDisplayUpdate = micros();
  }
  // else just return without doing anything
}

void bitClock()
{
  digitalWrite(shiftClock, LOW);
  digitalWrite(shiftClock, HIGH);
}

void latchData()
{
  digitalWrite(latchClock, LOW);
  digitalWrite(latchClock, HIGH);
}

Hi aarg, I have also this kind of display. I have uploaded you sketch, but the display stays empty (nothing on it).

aarg:
@aisc, I have written an improved driver. Can you test it for me?

// controls 4 digit 7 segment display

// test version 1
// 2015-04-22 by aarg

const byte numDigits = 4;
byte testDigit[numDigits];

// control pin definitions:
int shiftClock = 5;
int latchClock = 6;
int DIO = 7;

void setup ()
{
 // configure outputs:
 pinMode(shiftClock, OUTPUT);
 pinMode(latchClock, OUTPUT);
 pinMode(DIO, OUTPUT);

for (int i = 0; i < numDigits; i++)
   testDigit[i] = i;
}

void loop()
{
 displayUpdate(testDigit);
 //
 // any code in here that is non-blocking for more than the display
 // refresh interval should not
 // interfere with the display.
}

// non-blocking display update
//
// every time this routine is called, 16 bits are shifted into the display
// and latched.
// The first 8 bits is the segment data, and first 4 bits of the second byte are
// the segment select.

void displayUpdate(byte displayDigit[])
{
 unsigned long lastDisplayUpdate;
 const int scanRateHz = 60;
 const int displayScanPeriod = 1000000L / numDigits / 8 / scanRateHz;

byte segmentList[] =
 { // 0123456789AbCdEF-
   0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x8C, 0xBF, 0xC6, 0xA1, 0x86, 0xFF, 0xbf
 };

static byte digit = 0;
 static byte segment = 0x80;

if (micros() - lastDisplayUpdate > displayScanPeriod)
 {

// send segment data
   for (byte i = 8; i >= 1; i--)
   {
     if ( i & (~segment | segmentList[displayDigit[digit]]) != 0)
       digitalWrite(DIO, HIGH);
     else
       digitalWrite(DIO, LOW);
     bitClock();
   }

// send digit select data
   for (byte i = 0; i < 8; i++)
   {
     if (i == digit)
       digitalWrite(DIO, HIGH);
     else
       digitalWrite(DIO, LOW);
     bitClock();
   }

// data is now in the display shift register, so latch to LEDs
   //
   latchData();

// increment variables to select the next segment and possibly the next digit:
   //
   segment = segment >> 1;
   if (segment == 0)
   {
     segment = 0x80;
     digit++;

if (digit >= numDigits)
       digit = 0;
   }

// reset timer for next update:
   lastDisplayUpdate = micros();
 }
 // else just return without doing anything
}

void bitClock()
{
 digitalWrite(shiftClock, LOW);
 digitalWrite(shiftClock, HIGH);
}

void latchData()
{
 digitalWrite(latchClock, LOW);
 digitalWrite(latchClock, HIGH);
}

Hi aarg
Sorry just saw your message.
I uploaded the code and nothing displays.

The code is a bit too advanced for me to really comment, however one thing I did notice is that you used the variable "i" in multiple loops e.g. calculating both the digits and the segments - just wondering if this was causing some sort of conflict.

Rats about it not working. I will look at the code again. But as far as I know (I will look again) my reuse of the variable "i" is permissible (even recommendable) because it is used inside small blocks of code where it is not visible from outside the block, where it is out of scope. At least that was the intention. I use that technique a lot, so I doubt it is the problem.

I will edit this post as I think of things, so it won't bumpety bump.
I think:

  unsigned long lastDisplayUpdate;

should be:

static unsigned long lastDisplayUpdate;

There is more, I will repost when I think it might work...

Noted.
Still nothing...

shouldn't this

  for (int i = 0; i < numDigits; i++)
    testDigit[i] = i;

be

  for (int i = 0; i < numDigits; i++)
    testDigit = i;

and won't it always give u 3 given it is in the setup? - or am I thinking "while" loop.
Why are my code blocks so big? Cannot get them smaller.

No.
How could it?

Okay, this one is tested with serial debugs.

// controls 4 digit 7 segment display
// test version 2
// 2015-05-01 by aarg

const byte numDigits = 4;  //digits in the LED display
byte testDigit[numDigits];  //display buffer to pass to the driver

// control pin definitions for the display module:
int shiftClock = 5;
int latchClock = 6;
int DIO = 7;

// blink variables:

const int ledPin =  13;      // on board LED
int ledState = LOW;
unsigned long previousMillis = 0;
const long interval = 1000;   // interval at which to blink (milliseconds)

void setup ()
{
  // configure outputs:
  pinMode(shiftClock, OUTPUT);
  pinMode(latchClock, OUTPUT);
  pinMode(DIO, OUTPUT);
  pinMode(ledPin, OUTPUT);

// spell "face" on the display :)

    testDigit[0] = 0x0f;
    testDigit[1] = 0x0a;
    testDigit[2] = 0x0c;
    testDigit[3] = 0x0e;
}

void loop()
{
  // update the display
  //
  displayUpdate(testDigit);

  // any code in here that is non-blocking for more than the display
  // refresh interval should not interfere with the display.

  // now do the blink
  //
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;
    digitalWrite(ledPin, ledState);
  }
  
} // end of loop()

// non-blocking display update
//
// every time this routine is called, 16 bits are shifted into the display
// and latched.
// The first 8 bits is the segment data, and lower 4 bits of the second byte are
// the segment select.

void displayUpdate(byte displayDigit[])
{
  static unsigned long lastDisplayUpdate;
  const int scanRateHz = 60;
  const long displayScanPeriod = 1000000L / numDigits / 8 / scanRateHz;

  const byte segmentList[] =
  { // 0123456789AbCdEF-<space>
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x8C, 0xBF, 0xC6, 0xA1, 0x86, 0xFF, 0xbf, 0x7f
  };

  static byte digit = 0;
  static byte segment = 0x80;

  if (micros() - lastDisplayUpdate > displayScanPeriod)
  {
    // send segment data
    //
    // each time the routine is called only one segment is displayed
    // however, in order to do this, all the segment and digit
    // data must be written to the display.
    //
    for (byte mask = 0x80; mask >= 1; mask = mask >> 1)  // a "walking mask bit" to assemble the segment list
    {
      if ( (mask & segment) & ~segmentList[displayDigit[digit]])
      {
        digitalWrite(DIO, LOW);
      }
      else
      {
        digitalWrite(DIO, HIGH);
      }
      bitClock();
    }

    // send digit select data
    for (byte i = 0; i < 8; i++)
    {
      if (i == digit)
      {
        digitalWrite(DIO, HIGH);
      }
      else
      {
        digitalWrite(DIO, LOW);
      }
      bitClock();
    }

    // data is now in the display shift register, so latch to LEDs
    //
    latchData();

    // increment variables to select the next segment and possibly the next digit:
    //
    segment = segment >> 1;
    if (segment == 0)
    {
      segment = 0x80;
      digit++;

      if (digit >= numDigits)
        digit = 0;
    }

    // reset timer for next update:
    lastDisplayUpdate = micros();
  }
  // else just return without doing anything
}

void bitClock()
{
  digitalWrite(shiftClock, LOW);
  digitalWrite(shiftClock, HIGH);
}

void latchData()
{
  digitalWrite(latchClock, LOW);
  digitalWrite(latchClock, HIGH);
}

There may be a few odds and ends to clean up, but I just want to know if it works at this point.

AWOL:
No.
How could it?

Yeah. I let my enthusiasm get the better of me. It was half baked. When I posted it, there were people loudly debating in a language I only partly understand, a baby screaming, and I urgently needed to go shopping so I was rushing to finish and get out the door. So I will know better next time.

:slight_smile:
I finally realized you weren't talking to me.

aisc:
Noted.
Still nothing...

shouldn't this

  for (int i = 0; i < numDigits; i++)

testDigit[i] = i;



be


for (int i = 0; i < numDigits; i++)
    testDigit = i;



and won't it always give u 3 given it is in the setup? - or am I thinking "while" loop.
Why are my code blocks so big? Cannot get them smaller.

It's supposed to display "0123". I decided to change it to "FACE" just to test the hex characters. Maybe I don't understand your question about code block size. Sometimes the answer to that question is, by breaking parts of it down into functions.

Forget the question about the code blocks - it was relating to the appearance of my post - it has corrected itself.

My previous code comment : I realised afterwards u were building an array - been a while since I coded. Need a refresher course.

V2 Sketch Result: Pin 13 LED blinks. Display is completely unlit - not a single segment is lit.

Okay - I will try to get my hands on the display hardware and go from there. Thanks for your help in testing.

@Aarg : Since it seems u are determined to get your teeth stuck into this... I hope u don't mind if I put in a request.

At the moment I have an LCD (with I2C backpack) hooked up to an Pro Mini (SDA/SCL) and an HC-SR04 ultrasonic sensor. The LCD displays the distance measured by the sensor. There is a one second delay between each measurement.

I would like to use the tube display to replace the LCD. I messed with the code we did previously, but could not get it to work as a replacement for the LCD.

In your quest to conquer this display, if it is not too make hassle and if it is indeed possible, could you make the code easy to adapt to drive the LED display to operate in the same way as my LCD screen is working at the moment.

Once I start a game, I hate to lose. I found the module online and can have it in my hands in a few days. I think it should be relatively easy to convert to a library with a few simple numeric functions.

I now the feeling - and it motivated me to have another go.

I know u are reworking the code, but can u tell me if I am trying to use the code in a way it is not meant to work.

I basically took what u created before, which will display a "fixed" value and tried to adapt it to display a changing value.

When I leave the "while" loop uncommented, the LED (pin 13) does not light up and a fixed value is displayed (0143 if I recall). With the while loop commented out, the LED does light, the display starts with 0 (only 1 digit) and I can see very brief flashes of the display changing, but the display does not "hold" the measured value. I thought the "delay" would hold the value, but maybe I need something else.

/* Sensor : HC-SR04 + Arduino with LED Ouput + Serial LED Module Output*/

/* Sensor Settings:
   ----------------*/
#define trigPin 2
#define echoPin 3
#define datPin 13 // onboard LED

float maxrange; // maximum detection range
long duration;
float distance;

/* LED Module Settings:
   --------------------*/
unsigned char LED_0F[] = 
  {// 0   ,1	 ,2   ,3   ,4	,5   ,6   ,7   ,8   ,9   ,A   ,b   ,C   ,d   ,E   ,F   ,-
      0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x8C,0xBF,0xC6,0xA1,0x86,0xFF,0xbf
  };

  unsigned char LED[4];	//用于LED的4位显示缓存
  int SCLK = 5;
  int RCLK = 6;
  int DIO = 7; //这里定义了那三个脚
  int x;
 

void setup()
  {
/* Sensor:*/
   pinMode(trigPin, OUTPUT);
   pinMode(echoPin, INPUT);
   pinMode(datPin, OUTPUT);
   digitalWrite(trigPin, LOW); 	// ensure low state at start
   delay(3000);

/* LED Module:*/
   pinMode(SCLK,OUTPUT);
   pinMode(RCLK,OUTPUT);
   pinMode(DIO,OUTPUT); //让三个脚都是输出状态
  }

void loop()
  {
   digitalWrite(trigPin, HIGH); 	// initiate pulse
   delayMicroseconds(10); 		// maintain pulse for 10�s
   digitalWrite(trigPin, LOW);  	// terminate pulse

   maxrange = 50;
   duration = pulseIn(echoPin, HIGH);  // measures delay till echo heard
   distance = duration / 29.1 / 2;  // calculates distance in cm

   if (distance < maxrange)
     {
      digitalWrite(datPin, HIGH);
     }
   else
     {
      digitalWrite(datPin, LOW);
     }

      x = distance/1;
      LED[0] = x % 10;
      LED[1] = x/10 % 10;
      LED[2] = x/100 %10;
      LED[3] = x / 1000;

//      while(1)
//   	{
    	 LED4_Display ();
//    	} 

      delay(3000);  			// maintain terminates state for 100ms
  }

    
    void LED4_Display (void)
    {
        byte digit = 0;
        for (byte dselect = 1; dselect < 0x10; dselect = dselect << 1)
        {
            LED_OUT(LED_0F[LED[digit]]);
            LED_OUT(dselect);
            RCLK_SET();
            ++digit;
        }
    }

    void LED_OUT(unsigned char X)
    {
    	unsigned char i;
    	for(i=8;i>=1;i--)
    	{
    	    if (X&0x80) 
            {
                digitalWrite(DIO,HIGH);
            }  
            else 
            {
                 digitalWrite(DIO,LOW);
            }
        X<<=1;
        SCLK_SET();
    	}
    }

    void RCLK_SET()
    {
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    }    

    void SCLK_SET()
    {
        digitalWrite(SCLK,LOW);
        digitalWrite(SCLK,HIGH);
    }

Well, I wasn't going to do this, but:

// controls 4 digit 7 segment display
// test version 3
// 2015-05-02 by aarg

const byte numDigits = 4;
byte testDigit[numDigits];

// control pin definitions:
int shiftClock = 5;
int latchClock = 6;
int DIO = 7;

// count variables:

unsigned long previousMillis = 0;        // will store last time of count
const long interval = 1000;           // interval at which to count (milliseconds)

void setup ()
{
  // configure outputs:
  pinMode(shiftClock, OUTPUT);
  pinMode(latchClock, OUTPUT);
  pinMode(DIO, OUTPUT);

  // initialize display buffer
  for (int i = 0; i < numDigits; i++)
    testDigit[i] = i;
}

void loop()
{
  // update the display
  //
  displayUpdate(testDigit);
  //
  // any code in here that is non-blocking for more than the display
  // refresh interval should not
  // interfere with the display.

  // see if it's time to increment the counter
  //
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    incrementDisplay();
  }
}

// non-blocking display update
//
// every time this routine is called, 16 bits are shifted into the display
// and latched.
// The first 8 bits is the segment data, and first 4 bits of the second byte are
// the segment select.

void displayUpdate(byte displayDigit[])
{
  static unsigned long lastDisplayUpdate;
  const int scanRateHz = 60;
  const int displayScanPeriod = 1000000L / numDigits / 8 / scanRateHz;

  const byte segmentList[] =
  { // 0123456789AbCdEF-<space>
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x8C, 0xBF, 0xC6, 0xA1, 0x86, 0xFF, 0xbf, 0x7f
  };

  static byte digit = 0;
  static byte segment = 0x80;

  if ((micros() - lastDisplayUpdate > displayScanPeriod))
  {
    // send segment data
    for (byte i = 0x80; i >= 1; i = i >> 1)
    {
      if ( (i & segment) & ~segmentList[displayDigit[digit]])
      {
        digitalWrite(DIO, LOW);
      }
      else
      {
        digitalWrite(DIO, HIGH);
      }
      bitClock();
    }

    // send digit select data
    for (byte i = 8; i > 0; i--)
    {
      if (i == digit + 1)
      {
        digitalWrite(DIO, HIGH);
      }
      else
      {
        digitalWrite(DIO, LOW);
      }
      bitClock();
    }

    // data is now in the display shift register, so latch to LEDs
    //
    latchData();

    // increment variables to select the next segment and possibly the next digit:
    //
    segment = segment >> 1;
    if (segment == 0)
    {
      segment = 0x80;
      digit++;

      if (digit >= numDigits)
        digit = 0;
    }

    // reset timer for next update:
    lastDisplayUpdate = micros();
  }
  // else just return without doing anything
}

void bitClock()
{
  digitalWrite(shiftClock, LOW);
  digitalWrite(shiftClock, HIGH);
}

void latchData()
{
  digitalWrite(latchClock, LOW);
  digitalWrite(latchClock, HIGH);
}

// very dumb counter (includes "-" and <space>)
//
void incrementDisplay()
{
  if (++testDigit[0] > 17)
  {
    testDigit[0] = 0;
    if (++testDigit[1] > 17)
    {
      testDigit[1] = 0;
      if (++testDigit[2] > 17)
      {
        testDigit[2] = 0;
        if (++testDigit[3] > 17)
        {
          testDigit[3] = 0;
        }
      }
    }
  }
}

Ok that codes does displays.
321 and right digit cycles thru all characters.
After each cycle the left 3 digits increases by 1 i.e. 322.

One thing - the display is "shaky" - the characters are not steady.
Does it need a higher scan rate maybe?

The 595 latches, it shouldn't be shaky.