Go Down

Topic: 4 Digit 7 Segment Serial LED Module - TM74HC595 (Read 39437 times) previous topic - next topic

aisc

I bought a 4 Digit 7 Segment serial led module with 2 x TM74HC595 shift registers.
I have the data sheet and some sample code.
I am able to get the sample code to display - which is simply the digits in the void loop.

The connections are 3 wires as listed in the code plus power.

I am trying to understand the meaning/limitations of first 3 lines of the code, which appears to be written in hex format and which I thought would determine the scope of what could be displayed.
However, the second line does not appear to correlate with the third line - so I am a bit lost.

For example I figure 0xC0 (hex) to represent 192 (decimal) - or have I got the wrong end of the stick?

1. An explanation of what line 3 represents would be appreciated.

2. How would I display a decimal point - lets say I wanted to display 999.5?


Code: [Select]
    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; //这里定义了那三个脚
    void setup ()
    {
      pinMode(SCLK,OUTPUT);
      pinMode(RCLK,OUTPUT);
      pinMode(DIO,OUTPUT); //让三个脚都是输出状态
    }
    void loop()
    {
        LED[0]=3;
    LED[1]=2;
    LED[2]=1;
    LED[3]=0;
    while(1)
    {
    LED4_Display ();
    }
     
    }
   
    void LED4_Display (void)
    {
    unsigned char *led_table;          // 查表指针
    unsigned char i;
    //显示第1位 - Digit 1
    led_table = LED_0F + LED[0];
    i = *led_table;
    LED_OUT(i);
    LED_OUT(0x01);
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    //显示第2位 - Digit 2
    led_table = LED_0F + LED[1];
    i = *led_table;
    LED_OUT(i);
    LED_OUT(0x02);
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    //显示第3位 - Digit 3
    led_table = LED_0F + LED[2];
    i = *led_table;
    LED_OUT(i);
    LED_OUT(0x04);
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    //显示第4位 - Digit 4
    led_table = LED_0F + LED[3];
    i = *led_table;
    LED_OUT(i);
    LED_OUT(0x08);
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    }
   
    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;
                digitalWrite(SCLK,LOW);
                digitalWrite(SCLK,HIGH);
    }
    }
   

aarg

Code: [Select]

    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
    };


Each byte is a segment list. The high bit is set for all of them, that controls the decimal point. 0xC0 is 0b11000000. The segments are ordered from the bottom up ABCDEFG, bit 01234567, bit 8 is the decimal point as I mentioned. A zero bit illuminates the digit. So 0xC0 represents digit zero, because the decimal point is extinguished and all segments are on except for bit 7, segment G, which is the middle segment. Compare with the digit 8, which is of course 0x80 since all the segments are turned on.

I don't like the code.
Code: [Select]

  led_table = LED_0F + LED[0];
  i = *led_table;
  LED_OUT(i);

could be simply
Code: [Select]

  LED_OUT( LED_0F[ LED[0] ] );

unless I am confused (however that may have been the purpose).
Also this function:
Code: [Select]

    void LED4_Display (void)
    {
    unsigned char *led_table;          // 查表指针
    unsigned char i;
    //显示第1位 - Digit 1
    led_table = LED_0F + LED[0];
    i = *led_table;
    LED_OUT(i);
    LED_OUT(0x01);
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    //显示第2位 - Digit 2
    led_table = LED_0F + LED[1];
    i = *led_table;
    LED_OUT(i);
    LED_OUT(0x02);
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    //显示第3位 - Digit 3
    led_table = LED_0F + LED[2];
    i = *led_table;
    LED_OUT(i);
    LED_OUT(0x04);
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    //显示第4位 - Digit 4
    led_table = LED_0F + LED[3];
    i = *led_table;
    LED_OUT(i);
    LED_OUT(0x08);
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    }

can be rewritten as:
Code: [Select]

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);
    digitalWrite(RCLK, LOW);
    digitalWrite(RCLK, HIGH);
    ++digit;
  }
}

However I have no hardware to test.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

aisc

Thanks for the explanation and the code review.

Before I start trying to use the provided sample code, I think I will try to use an existing library.

My sketch has an ultrasonic sensor which measures distance and prints the distance to a 1602 LCD screen with a backpack i.e. via I2C serial.

Instead of printing to the LCD screen I want to print the distances to the LED tube display.

Given I have a 3-wire connection, I assume I need an SPI library.
So I will try to make it work with the Arduino Standard SPI library.

I am still searching for some sample code.


aisc

I'm stumped.
Could someone please suggest a library with some sample code to get started.
Basically I have an LCD screen displaying the distances from an ultrasonic sensor, and I simply want to replace the LCD screen with the led tube display.

aisc

@aarg : I have started using the sample code.
I have tested both code revisions u provided and both work.
This is the current revised code.
Code: [Select]
    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; //这里定义了那三个脚
    void setup ()
    {
      pinMode(SCLK,OUTPUT);
      pinMode(RCLK,OUTPUT);
      pinMode(DIO,OUTPUT); //让三个脚都是输出状态
    }
    void loop()
    {
        LED[0]=5;
    LED[1]=1;
    LED[2]=0;
    LED[3]=2;

//        LED[] = {5,1,0,2};
    while(1)
    {
    LED4_Display ();
    }
     
    }
   
    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);
            digitalWrite(RCLK, LOW);
            digitalWrite(RCLK, HIGH);
            ++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;
        digitalWrite(SCLK,LOW);
        digitalWrite(SCLK,HIGH);
    }
    }
   


I would appreciate some help making a revision to enter the number to be displayed simply as "x=n".
Let's say I want to display "2015".

Presently I have to specify the array values as follows:
    LED[0]=5;
    LED[1]=1;
    LED[2]=0;
    LED[3]=2;

What I would like to do is specify "x=2015;"
and have the code create the array from that.

I have tried applying my knowledge of PHP arrays and functions, but it appears C++ is a bit different.


aarg

How about I give you a clue and see if you can run with it?

To get the 1's digit:
Code: [Select]

LED[0] = x % 10;

Also a hint: integer division has no remainder. :)
Also thanks for testing my code.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

PaulMurrayCbr

#6
Apr 19, 2015, 05:21 am Last Edit: Apr 19, 2015, 05:23 am by PaulMurrayCbr
What does the function LED4 do? It displays four digits.

How does it know which four digits to display? They are put in LED[0] to LED[3].

So, one way to go (not the best way, but one that uses code you already have) is to write a function

Code: [Select]

void putDecimalIntoLED(int n) {
  // put the ones digit of n into LED[0], the tens digit into LED[1], and so on
}


A better way, IMO, is to work out the series of what actually happens in order to get the number out.

A sequence of 8 bytes is written out in four pairs.

each pair is first, the map of what segments should be lit - LED_0F[digit], then 1,2,4,or 8 to select which digit display the segment map goes to. Then RCLK is toggled LOW-HIGH.

(question - do all four digits have to go out? Or can you just output the digits in any sequence by sending these byte pairs? Is that second byte also a bit map? Can you clear the display to zero by sending LED_0F[0] and then 0x0F ?)

To send a byte (8 bits), send each bit to DIO (most significant bit first), and toggle SCLK from LOW to HIGH.

Given that, it becomes possible to write a function

Code: [Select]

void putDigitIntoDisplay(int digit, int position) {
  // Digit should be 0-15, position 0-3
}


If you understand what the code is doing , then you can do fun stuff like making the display animate a loop.

http://paulmurraycbr.github.io/ArduinoTheOOWay.html

aisc

@aarg : No need to thank me, it is much more elegant than the original - I should thank you.

Fair enough to work on clues... but - I am wondering why use division.

Just so u know my thought process and can guide me, my knowledge of arrays and functions comes from PHP.

So with a PHP mindset, I would look to do something like this :

$x = "2015";

LED[0] = $x[3];
LED[1] = $x[2];
LED[2] = $x[1];
LED[3] = $x[0];

Also I would get length of $x, and only build the array with the number of digits in $x.

Switching to C++.

I have tried using the above with appropriate adjustments for C++, but it wont compile.

First I declared x as a variable : int x;

And I added this in the void loop :
     x = 2015;
     LED[0]=x[3];
     LED[1]=x[2];
     LED[2]=x[1];
     LED[3]=x[0];

I got this error : _4bitLED_V12.ino:24:17: error: invalid types 'int[int]' for array subscript

Anyway that was before I posted and u replied. My thinking was I effectively have a value and I need to split it into single digits.

So now I am trying to get my head around the division...

I can see that taking 2015 and dividing by 1000 will give me 2, which would be LED[3].
i.e. LED[3]=x % 1000;
Now I actually tested this and LED[3]  does not illuminate, LED[0 thru 2] show zero as expected.

However, running your code of LED[0] = x % 10; gives me 5 i.e. the first digit.

Now mathematically that does not make sense, since 2015/10 = 201.
Hmmm so I am obviously missing something.
The divisor would appear to be representing something other than a decimal value.
On face value it is discarding everything but the first digit....
Sorry I need another clue for the next digit.

aisc

What does the function LED4 do? It displays four digits.

How does it know which four digits to display? They are put in LED[0] to LED[3].

So, one way to go (not the best way, but one that uses code you already have) is to write a function
This is in essence what I wish to do - see post #4.

(question - do all four digits have to go out? Or can you just output the digits in any sequence by sending these byte pairs? Is that second byte also a bit map? Can you clear the display to zero by sending LED_0F[0] and then 0x0F ?)
The output will eventually be a readout from a sensor. See post #2.
From what I can see, any digit that is NOT output, will automatically display a zero. I will probably not want it to display at all. I have not yet tried to deliberately clear it to zero with code.

aisc

Thanks aarg.

  x = 2015;

      LED[0] = x % 10;
      LED[1] = (x % 100 - LED[0]) / 10;
      LED[2] = (x % 1000 - LED[1]*10 - LED[0])/100;
      LED[3] = (x % 10000 - LED[2]*100 - LED[1]*10 - LED[0])/1000;

I was confused when u mentioned division and assume "%" was division in C==.
Found out it was modulo giving remainder.

Not elegant I know, but it gives me what I want.

Thanks

aarg

#10
Apr 19, 2015, 08:59 am Last Edit: Apr 19, 2015, 09:02 am by aarg
Thanks aarg.

  x = 2015;

      LED[0] = x % 10;
      LED[1] = (x % 100 - LED[0]) / 10;
      LED[2] = (x % 1000 - LED[1]*10 - LED[0])/100;
      LED[3] = (x % 10000 - LED[2]*100 - LED[1]*10 - LED[0])/1000;

I was confused when u mentioned division and assume "%" was division in C==.
Found out it was modulo giving remainder.

Not elegant I know, but it gives me what I want.

Thanks
Well, what I said was confusing, actually I meant that the remainder is discarded with integer division.
I only realized it a half hour after I left the computer.

But I think there is a slightly easier way:
Code: [Select]

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

Only if x<10000.
Also, can you see how this could be made into a loop? :)
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

aisc

Much more elegant - thanks.

I will wrap my head around a loop another day, but for the moment my object is to print out the value of a calculation, which I can now call x :)

Three follow-on questions.
1. This module has no resistors on board. I have been doing my testing with 3.3V output from the arduino. Just wondering I need to add a resistor. FWIW the only resistor shown on the datasheet is for an external led.

2. I notice the fewer segments lit, the brighter the digit, so 1 will always be brighter than 8.
I have come across brightness topics in other threads, so just wondering if this module's brightness can be controlled.

3. The hard one - at the moment x is an integer. What if I wanted x to be a float i.e. if I wanted to display the decimal point. A clue please....



aarg

#12
Apr 19, 2015, 09:49 am Last Edit: Apr 19, 2015, 09:51 am by aarg
Much more elegant - thanks.

I will wrap my head around a loop another day, but for the moment my object is to print out the value of a calculation, which I can now call x :)

Three follow-on questions.
1. This module has no resistors on board. I have been doing my testing with 3.3V output from the arduino. Just wondering I need to add a resistor. FWIW the only resistor shown on the datasheet is for an external led.

2. I notice the fewer segments lit, the brighter the digit, so 1 will always be brighter than 8.
I have come across brightness topics in other threads, so just wondering if this module's brightness can be controlled.

3. The hard one - at the moment x is an integer. What if I wanted x to be a float i.e. if I wanted to display the decimal point. A clue please....
Answers:
1. Normally you have resistors in series with an LED. But in this application, the LED is not on all the time. An LED can exceed its rated maximum continuous current for short periods of time, if the average current is the same. This is how they can get away without resistors. Indeed, you should avoid any code that leaves a segment on continuously.

2. Yes. Usually each digit has a high current driver such as a transistor. But this board doesn't have any. So one digit driver has to share the current for 8 LEDs and they will dim. That is, if the segments are enabled simultaneously. The solution is to modify the software so that only one segment in the entire display is on at any given time.

3. That one is no fun. :) I don't have any ideas other than sluggo coding.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

nickgammon

Quote
Normally you have resistors in series with an LED. But in this application, the LED is not on all the time. An LED can exceed it's rated maximum continuous current for short periods of time, if the average current is the same.
I'm tempted to get Grumpy Mike involved here. He would disagree that exceeding the chip's maximum ratings is a good idea for any length of time. I know, because I tried it once.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

aarg

#14
Apr 19, 2015, 09:58 am Last Edit: Apr 19, 2015, 10:12 am by aarg
I'm tempted to get Grumpy Mike involved here. He would disagree that exceeding the chip's maximum ratings is a good idea for any length of time. I know, because I tried it once.
Feel free. I noticed a recent thread on the subject in passing. It is true that what is okay for the LED is not necessarily good for the chip that is driving it. I think it is thermal dissipation ability of the 74HC595 that determines the safety in this case. I would be the last to vouch for the engineering of a cheap display module from China. But I guess you'd have to look up the 595 specs. @aisc, how warm do they get?

Also, before you sic @Grumpy on me, consider that in this circuit, the LED is connected to a gate output at both terminals. Whereas the other thread discussed the case where one end is connected directly to a ground or supply voltage. Thus the series resistance of the FET driver circuit is almost doubled, giving more leeway. The specified maximum current for the 595 is 35mA and the max power dissipation for an SO package is 500mW.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

Go Up