## 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...

## Hookup

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.

## Software

BubbleDisplay.spin

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      
    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.

  repeat
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
waitcnt(clkfreq/delay+cnt)

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.