Wednesday, December 3, 2014

Bubble Display, Propeller

HP 4-Digit Bubble Display (7-Segment LED)
I used a Sparkfun coupon to buy a couple of vintage, 4-digit, 7-segment, HP LED bubble displays. The kind you'd find in really old calculators. Their guide is most helpful.

I used one of my spare eeZee Propellers to play around with the display and create driver code. I haven't played in Spin in awhile, so it was a good refresher. Here's what I did...


The LED module uses four cathodes, one for each digit, and common anodes for the 7 segments. The underside of the LED has a little dot to indicate pin 1. The rest of the pinout tips you can find in the image below.

Since you want no more than 5mA running through these LEDs, I calculated that a 1K resistor would be about right. At 3.3V, with 1.6V forward drop, that's about 2mA, max.

Propeller pins P16 - P19 are connected to Cathode 4 through 1, respectively. Pins P20 - P26 are connected to Anode A through G, respectively. The decimal point is hooked up to P27.

Driving 7-Segment Displays

So, how do you drive one of these displays? To turn on an LED segment its anode must be high, and cathode low. These HP bubble displays are common-anode, meaning there's a unique cathode for each display digit, but each anode is shared across all the display digits.

You could simply drive the display one digit at a time, setting all the required anodes high and the corresponding cathode low.

The problem is, each digit requires a different number of LED segments. Compare 1, which requires 2 LED segments, with 8 which requires 6 segments. If you used a resistor per cathode, 1 would look brightest and 8 would appear dimmest.

A common cathode display is intended to be controlled with the cathodes. Use 4 resistors, one for each cathode, power each of the common anodes sequentially, and use the cathodes to individually turn segments on or off for each display digit position.



The display driver runs on a cog and displays digits stored in hub memory. The main cog simply counts from 0 to 9999 over and over again, extracting the 1's, 10's, 100's and 1000's place and storing the numbers for display, as follows, where b1, b2, b3, b4 are each of the digits.

PUB Start | i              
  b1 := b2 := b3 := b4 := 10 ' initial digit (>9 means off)
  cognew(display, @stack)    ' start display driver cog
    repeat i from 0 to 9999             
      b4 := i // 10            ' ones
      b3 := (i/10) // 10       ' tens
      b2 := (i/100) // 10      ' hundreds
      b1 := (i/1000) // 10     ' thousands
      waitcnt(clkfreq/10+cnt)  ' count at ~10Hz

I may refactor the program to use BCD; storing digits in each of the 4 bytes of a Propeller's 32-bit long int, but the code is more readable using one variable per digit.

Driver Details

As for the driver, a segment array, seg, stores the outa cathode pin bits required to turn on each segment (a through g) at each display digit position (1 through 4).

The main loop calls setdigit(digit, value) which sets the cathode bit in the seg array, for the specified display digit and the specified numeric value. For example, displaying "2" in position 3 requires the cathode for position 3 to be low (on) for segments a, b, d, e, g, and high (off) for segments c and f.

    setdigit(1, b1)      ' set 1's value
    setdigit(2, b2)      ' set 10's value
    setdigit(3, b3)      ' set 100's value
    setdigit(4, b4)      ' set 1000's value
    bit := AN_A          ' start with anode a
    repeat s from A to G ' loop thru segments
      outa := seg[s]|bit ' turn on cathodes, anode
      bit <<= 1    ' next anode bit

Then, the main loop iterates over the array, seg[A..G], sets the cathode pins, simply with outa := seg[s] and raises the corresponding segment anode pin for each array element by ORing outa with bit, which is the bit corresponding to the current anode pin. How does the array get set with the right cathode?

The setdigit(digit, value) function converts the display digit into the appropriate cathode bit to activate (set low) or deactivate (set high).

  case digit
    1: unset := CA_1
    2: unset := CA_2
    3: unset := CA_3
    4: unset := CA_4
    other: unset := 0

  set := !unset & (CA_1|CA_2|CA_3|CA_4)
It then uses the numeric value (0..9) in another case statement to select which segments should be on (seg[?] &= set) or off (seg[?] |= unset).

  case value
    0: seg[A] &= set
       seg[B] &= set
       seg[C] &= set
       seg[D] &= set
       seg[E] &= set
       seg[F] &= set
       seg[G] |= unset

And that's it. You can find the source code here: BubbleDisplay.spin

So, It's Been Awhile...

You would have heard from me sooner, but alas, there has not been much time for tinkering or blogging lately.

My paucity of spare time can be blamed on work, filling Tindie orders, and gearing up for the crowd funding campaign for OpenMV Cam.

No comments:

Post a Comment