Retro Bubble Displays driven with 74HC595

Posted on
  • For one of my 'fun' project I needed a display for which 7 segment displays are sufficient... but it needs 'a lot of' them in a tight space... and that's where the Bubble Displays come in: Retro, small red LED segments behind a lens to make them readable.

    Surfing for supplies I found individuals, bars of 4, 8, and 12... The most affordable - and convenient to use ones - are the PCB carrier bonded ones - see attached photo.

    The displays / LEDs are small not only because they needed to be small (to make a bar without additional diffuser)... but they are small also because these are the first LED displays that became affordably available in the 70', at a time when the LEDs were red, tiny, and 'looked' dim. Such displays were used in first pocket calculators and watches with digital displays. In attached screenshot you can see that a single segment is actually made of multiple tiny tiny LED crystals on a die. The die is then placed on the carrier board with - for this display = the common cathodes contacting the cathode/digit traces. The Anodes on the die are bonded to the anode traces of the carrier board. A plastic with lenses 'protects' the delicate wiring and magnifies the digit... Legibility is usually enhanced by lenses from red, transparent plastic or by adding an extra sheet of similar material. The purple marked pins are the segment pins which match also the horizontal segment traces. In the most right bubble, the ambient light focuses nicely on the die and makes the individual LEDs also visible when not lit (a single LED consumes about 1mA, max 1.5mA - tbv).

    The National Semiconductor NS A5120A is a 12 digit 7-segment display with common cathode for each of the digits, and thus common same segments A..H are connected with each other.

    The display can be looked as a matrix of 8 columns (segments) and 12 row (digits) of (144) LEDs (with the columns of one row lit at a time / all rows displayed time multiplexed). The Anodes defining a columns of a matrix, and the cathodes are the row... Something like that is easy to drive with shift registers, as we see in other conversations, such as Driving LED matrix with shift registers such as 74HC595 and Is it possible to connect this 8x8 RGB matrix to espruino boards?. The project of this conversation was first, but got 'intercepted' by previous ones before being published. But now is the time, and it turned out even enhanced with the input from the other conversations.

    Here is the depiction of the display in character graphics

    7 Segment LED Bubble display array - segments and pins
    National Semiconductor
    NS A5120A                           .-A-.
    12 x 7-Segment, 14 Bubbles          F   B
    a=anode/segments _A.._H LED ->|-    :-G-:
    c=cathode/digit  c0..cB LED -|<-    E   C
                                        '-D-' H <- DecimalPoint
    BubbleNo   01 02 03 04 05 06 07 08 09 10 11 12 13 14
    7 SegmentNo    00 01 02 03 04 05 06 07 08 09 0A 0B
    PinNo (only 20 - 2..21 - are used):
    1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 
       1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

    Here is how the pins of the carrier board are mapped to the digits - the common cathodes c0..cB and anodes/segments _A .. _H:

    Segments  .++++..++++..++++..++++..++++..++++..+++­+..++++.
    in Digit/ :    ::    ::____::    ::    ::    ::    ::    :
    Bubble:   :    ::    ::  A ::    ::    ::    ::  B|::|F  :
              :    ::    ::    ::    ::    ::____::   |::|   :
              :   |::    ::    ::|   ::    ::  G ::    ::    :
              :  C|::   .::    ::|E  ::____::    ::    ::    :
    Pins:     :    ::   H::    ::    ::  D ::    ::    ::    :
     Segment/ '++++''++++''++++''++++''++++''++++''+++­+''++++'
     Digits/Fnct:     DP
    nc c0 c1 c2 _C c3 _H c4 _A c5 _E c6 _D c7 _G c8 _B c9 _F cA cB nc
     1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22

    Segments A..H that have to lighten up by being driven high, and because they are all same segments are connected with each other, only the common cathode for one digit at a time can be low. Therefore, the display has to be driven time multiplexed: one digit after the other has to be displayed in a fast manner, that the human eye still perceives them as lit at all times... The previously mentioned conversations elaborate in more detail about why all digits are displayed - one right after the in one 'shot' - in intervals of 10..20ms. Interesting is that a the bars of one digit are on just for the code execution time to prep lighting of the next one... the delay is not between the display of the digits, but between the lightening of all. Of course, this gives a lower duty cycle - and dimmer display - but proofs to be less susceptive to flickering when something else is going on and the interval cannot be honored in a timely manner (the whole display gets dimmer instead of one digit becoming brighter, which is choosing the lesser 'evil' / shortcoming).

    Below the coding of the digits:

    Coding of the digits
    - digit:            - supported characters (digits):
    segment and values  digit, Anode composition & values (dec, bin)
    | binary & decimal  | AnodesOn   dec binary
    | |            |    | |            | |  
    b 0b00000000   0   " "  (space)    0 0b00000000
    A 0b00000001   1    0 ABCDEF      63 0b00111111
    B 0b00000010   2    1 BC           6 0b00000110
    C 0b00000100   4    2 ABDEG       91 0b01011011 
    D 0b00001000   8    3 ABCDG       79 0b01001111
    E 0b00010000  16    4 BCFG       102 0b01100110
    F 0b00100000  32    5 ACDFG      109 0b01101101
    G 0b01000000  64    6 ACDEFG     125 0b01111101
    H 0b10000000 128    7 ABC          7 0b00000111
                        8 ABCDEFG    127 0b01111111
                        9 ABCDFG     111 0b01101111
                        . H (decPt)  128 0b10000000

    Having to drive 20 lines for 12 digits times 7 segments and decimal point being the 8, we need at least 3 74HC595:

    • 2 for driving (sinking, 0V, GND, grounding) the common (digit) cathodes
    • 1 for driving (sourcing, 3.3V, 5V) the 'common' (segment) anodes.

    We have 4 bits left that we can use for something else.

    The 3 595s are cascaded, the way that first the digit high-byte has to be feed first to sink one of the digits 5..12 (counted from the left) is shifted in with MSB first, and then the digit low-byte for the digits 1..4 (counted from the left). As last byte the segment byte is shifted in w/ MSB (segment H, decimal point) first. In the picture, the cascading is from the right to the left (the 595 are upside down (top left pins on picture are pins no 7, GND). This way I could use the same wiring as described in the previous conversation. I had just to relocate the Espruino-WiFi to the other side of the board. In the picture, only the first 3 digits are wired up... (cheap excuse - literally cheap: I ran out of resistors with same value in needed range in my stash of scavenged/salvaged components...) ...but it proofs the circuitry and code.

    Setup of Shift Registers:
    3 x M74HC595 8-bit shift register w/ latched three state output:
    The 595s are cascaded H' of previous is input to next's SI;
    (in picture arranged from right to left and on their heads)
    pin and pin labels (input and control       ouputs 
    - 14 SI    Serial In                        - 15 QA LSB
    - 11 /SCK  Schift Clock                     -  1 QB 
    - 12 /RCK  Register Clock                   -  2 QC
    - 13 -OE   Output Enable (PWM brightness)   -  3 QD
    -  9 QH'   H Stage for cascaded/next SI     -  4 QE
    - 10 -SCLR Shift Register Clear (CR Reset)  -  5 QF
    -  8 GND   Ground                           -  6 QG
    - 16 VCC   +2..+6V                          -  7 QH MSB
    -    QA..H to segments: (12) cathodes: -----; (8)anodes:
       calc:   3.3V 2.5mA: --|560R|-- / 5.0V_2.3mA: --|1.5K|-
       tested: 3.3V 220R (595 does obviously current limit
               because it can only drive 4..6mA on 5V supply

    Basic operation (every 10..20 milliseconds) is:

    - _OE - output enable
    - shift data (in 3 bytes):
      12 cathodes (in 2 bytes) and 8 anodes (in 1 byte) for:
      - 12 digits (w/ 1 only 0/'off' to sink common cathode);
        0b11111111 0b111111110 to drive first digit from left
        0b11110111 0b111111111 to drive most right digit
      - 8 segments (on/off to source or not to source anodes);
          0b00000110 (6) to source segments A..F for a 0 (zero)
      - option: since only 12 of 16 bits are needed for digits,
                4 left over bits can drive something additonal
                by composing high-byte (first byte) for digits
                with digit 9..12 and 4 extra information bits.
      - | &80 to ditit's segement bits to turn decimal point on
        (as many as 12 decimal points can be turned on... and
        therefore, the decimal points can be used for all kinds
        of indicators.
    - /RCK - latch (latches.shows data, rasing flank of SPI write's nns)
    - _OE - output disable

    So much for description... now the code (with display register preloaded with 25(degrees):

    var chrs = // supported chars: " 0123456789.cde" (Cursor, Degrees, Err)
    {" ":  0,"0":63,"1":  6,"2": 91,"3": 79,"4":102,"5":109
    ,"6":125,"7": 7,"8":127,"9":111,".":128,"c":  4,"d": 99,"e": 73
    }, cher = 73; // E-rror/non-displayable char (3 horizontal bars)
    var // connect definitions (Espruino -> 595 wiring): 
    //        SPI1 and other GPIOs          74HC595 (pin) 
      dspiDat=B5 // a) SDA ser data MOSI -> SER    (14)
    , dspiClk=B3 // b) SCK ser clock     -> SRCLK  (11)
    , dnss   =A0 // c) NSS to go w/ SPI  -> RCLK   (12)
    , dnoe   =A1 // d) 595 output enable -> _OE    (13)
    ;            // e) 595 _clear fix 1  -> _SRCLR (10)
    // A0 dnss is co-used for output register latch.
    //    Latch prevents blur while shifting.
    // A1 can do PWM when danger of over powering LEDs. 
    pinMode(dspiDat, "output"); dspiDat.set(); 
    pinMode(dspiClk, "output"); dspiClk.set();
    pinMode(dnss   , "output"); dnss.set();
    pinMode(dnoe   , "output"); dnoe.set();
    SPI1.setup({ mosi: dspiDat, sck: dspiClk });
    // display buffer: 12 Displayed char + Cursor = 13
    var dbf = ["2","5","d","","","","","","","","","",­""];
    function scan() { // drive display
      var l=12,d=2048,c;
      while (l--) {
        if ((c=chrs[dbf[l]])===undefined) c=cher;
    var iid; // interval id
    function r() { // run display
      iid = setInterval(scan,10);
    function h() { // halt display
      iid = clearInterval(iid);

    To be continued...

    2 Attachments

    • displayOnlyR.jpg
    • allSetupR.jpg
  • In scan details pictures below you can see some 'bleeding' - 0ff-segments that are very dimly lit. Bleeding can only be reduced by different scan logic: disable output while shifting data in. Unfortunately, this lowers the duty cycle even more and the display gets even dimmer (barely readable in ambient light - as you see by the lighter picture - longer shutter time). Scan time has already been increased from (theoretically) 100 to 125 times per second (every 10ms increased to every 8ms). ...may the bleeding come from shutter time / camera sensor scan?

    function scanVariant() { // drive display, display off while shifting
      var l=12,d=2048,c;
      while (l--) {
        if ((c=chrs[dbf[l]])===undefined) c=cher;

    Current pictures show the 595s already powered by 5V... to get some more brightness (less dimness). There are displays out there that compensate the issues, and some they have registers to hold on to the value and do not need to be scanned... What you pay is what you get...

    I added some code that plays continuously the display buffer with values from 0..25...0...25... etc. The value changes - increases or decreases by 1 - 3 times a second. This is the code:

    var dVal = -1, dInc = +1, dIntId, dStr;
    function v() { // continuously changing display value
      dIntId = setInterval(function(){
        dVal += dInc;
          = (dVal >= 25)
          ? -1
          : (dVal <= 0)
            ? +1
            : dInc;
        dStr = (" " + dVal).substr(-2);
        dbf[0] = dStr.charAt(0);
        dbf[1] = dStr.charAt(1);
      }, 333);

    The attached clip shows some issues of the setup: the 595 has source and sink limitations and the shared resistors in the common cathode aren't helpful either to mitigate that floating current: 1s are displayed much brighter, because there are only two segments on (8 LEDs for a '1' vs. 20 for a '2'). Since most of the characters/digits consist of more than just (1) or 2 segments, it is not that big a deal... but it is clearly noticeable... What is also noticeable is that flickering is setting in: the (theoretical) scan rate as currently (re)set to 100 times a second (10ms) seems not happening all the times anymore. From the flicker I conclude that the 'intervalled' function that does the display buffer change is already interfering with the scan interval.

    3 Attachments

  • That looks great! Shame about the bleeding/duty cycle... I'm always amazed at how much brighter modern LEDs are than the older ones - I guess it makes it much easier to work with lower duty cycles.

    If you needed to get it faster you could actually try unrolling your while loop? It's only 11 iterations?

  • ...12, but I did not think of that...

    After I noticed how much 'work' it is - in regard of code to execute and hardware to wire, I'm leaning now more towards using an OLED...

    It was an interesting experience... and satisfying childhood memory...

    I guess it was outgoing 60' (very early 70), when I bought a pocket calculator with such a display, but only 8 digits. The calculator chip could only do integer math. The calculator was pretty bulky, may be the volume of 4..5 9V block batteries. So my goal was to remake it as small as possible to fit in a small match box... I would reuse the carrier board with the display that also carried the calculator chip on the back of it's carrier board, rebuild a tiny key board and rewire it...

    Using a Pico I would now get a full 32 bit scientific calculator... the remaining 4 bit of the digit driving 595 and the plenty of pins would give me a comfortable keyboard scanner... I could even think of a Puck to make the anachronism perfect...

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

Retro Bubble Displays driven with 74HC595

Posted by Avatar for allObjects @allObjects