• No results found

Circuit฀Design

Figure฀7-3฀shows฀our฀test฀circuit.฀It’s฀a฀familiar฀mix฀of฀high-side฀and฀low-side฀drivers฀as฀discussed฀in฀Chap-ter฀3.฀Let’s฀look฀at฀it฀in฀detail.

Pins฀A0…A3฀are฀connected฀to฀2N4403฀PNP฀transistors฀set฀up฀as฀high-side฀digit฀switches.฀When฀A0…A3฀go฀

low,฀Q1…Q4฀are฀forward฀biased,฀and฀+5V,฀less฀VSAT,฀appears฀on฀the฀common฀anodes฀of฀Digits฀3…0.฀We’ve฀

selected฀R8…R11฀(2.2K฀ohm)฀to฀limit฀the฀base฀current฀drive฀to฀around฀2.5mA.

Each฀digit฀has฀its฀cathode฀segments฀A…G฀connected฀in฀parallel,฀with฀each฀segment฀going฀to฀an฀output฀pin฀

of฀a฀ULN2003A฀NPN฀Darlington฀array.฀The฀corresponding฀inputs฀of฀the฀ULN2003A฀are฀connected฀to฀pins฀

B0…B6.฀(The฀decimal฀point฀isn’t฀connected,฀as฀it฀isn’t฀needed฀in฀our฀application.฀But,฀don’t฀worry;฀we’ll฀see฀

a฀seven-segment฀display฀with฀automatic฀decimal฀point฀setting฀in฀Chapter฀11.)฀

When฀any฀pin฀B0…B6฀goes฀high,฀the฀attached฀Darlington฀is฀saturated฀and฀thus฀completes฀the฀circuit.฀฀

Thus,฀to฀turn฀on฀Segment(i)฀of฀Digit(j),฀the฀associated฀B0…B6฀pin฀must฀be฀high฀and฀the฀associated฀digit฀

selection฀pin฀A0…A3฀must฀be฀low.฀This฀permits฀us฀to฀individually฀control฀each฀segment฀of฀each฀digit.฀To฀

give฀the฀appearance฀of฀simultaneous฀operation,฀we฀cycle฀through฀all฀four฀digits฀quicker฀than฀our฀persistence฀

of฀vision฀recognizes฀flicker.฀

Each฀segment฀of฀a฀HDSP-5721฀is฀rated฀for฀60฀mA฀peak฀current,฀and฀20฀mA฀continuous฀current.฀Since฀we฀

are฀multiplexing฀four฀digits,฀we’ll฀be฀limited฀by฀the฀peak฀current฀rating.฀The฀HDSP-5721’s฀forward฀voltage฀

is฀specified฀as฀2.1V฀typical.฀As฀we’ll฀see฀below,฀we฀may฀expect฀around฀1.0฀V฀drop฀across฀the฀ULN2003A฀

driver.฀From฀Chapter฀3,฀we฀know฀the฀2N4403’s฀VSAT฀is฀around฀0.4V.฀Hence,฀the฀voltage฀across฀the฀limiting฀

Figure฀7-3:฀Four-digit฀LED฀

display฀setup.

Figure฀7-4:฀Typical฀ULN2003A฀driver.

resistor฀is฀(5.0฀–฀2.1฀–1.0฀–0.4)฀V฀or฀1.5฀V.฀To฀achieve฀60฀mA,฀the฀current฀limiting฀resistor฀would฀be฀(1.5฀

V/0.060A),฀or฀25฀ohms.฀I฀found฀60฀mA฀resulted฀in฀a฀brighter฀display฀than฀I฀wanted,฀so฀by฀experimentation฀I฀

found฀the฀desired฀brightness฀required฀68฀ohms.฀The฀resultant฀peak฀current฀is฀approximately฀25฀mA.

The฀ULN2003A฀is฀an฀interesting฀and฀useful฀device;฀so฀let’s฀take฀

a฀quick฀look฀at฀what’s฀inside.฀It฀consists฀of฀seven฀identical฀NPN฀

Darlington฀transistor฀drivers,฀and฀associated฀snubbing฀diodes฀

in฀a฀16-pin฀DIP฀package.฀Each฀Darlington฀is฀rated฀at฀500฀mA฀

current,฀and฀50฀V฀output฀voltage.฀(You฀can’t฀run฀all฀seven฀drivers฀

each฀sinking฀500฀mA฀without฀grossly฀

exceeding฀the฀ULN2003A’s฀power฀

ratings,฀of฀course.)฀A฀typical฀driver฀ap-pears฀in฀Figure฀7-4.฀Again,฀this฀should฀

be฀familiar฀from฀Chapter฀3.฀The฀driver฀

transistor฀has฀a฀built-in฀base฀resistor,฀

so฀we฀may฀directly฀connect฀it฀to฀a฀PIC฀

output฀pin.฀Like฀all฀Darlingtons,฀it฀

suffers฀in฀VSAT฀compared฀with฀a฀single฀

transistor,฀being฀rated฀at฀around฀1.0฀V฀

for฀100฀mA฀collector฀current,฀com-pared฀with฀the฀0.4฀V฀or฀so฀we฀find฀for฀a฀

2N4401.฀Each฀driver฀has฀an฀associated฀clamp฀diode฀to฀prevent฀damage฀when฀driving฀inductive฀loads.฀We’ve฀

connected฀it฀to฀VDD,฀although฀this฀connection฀could฀be฀omitted,฀if฀desired,฀since฀there’s฀no฀inductive฀spike฀

when฀driving฀an฀LED.฀We’ll฀use฀this฀chip฀for฀later฀projects,฀such฀as฀driving฀small฀stepper฀motors,฀where฀it’s฀

important฀to฀connect฀the฀clamp฀to฀VDD.

We’re฀using฀a฀ULN2003A฀instead฀of฀seven฀separate฀2N4401s฀or฀2N7000s฀purely฀as฀a฀matter฀of฀wiring฀

convenience.฀So,฀if฀you฀don’t฀have฀a฀ULN2003A,฀wire฀up฀the฀circuit฀with฀seven฀2N7000s,฀or฀2N4401s฀with฀

2.2K฀base฀resistors.฀It฀isn’t฀necessary฀to฀use฀a฀Darlington฀configuration฀at฀the฀current฀levels฀necessary฀to฀

drive฀small฀LED฀displays.฀If฀your฀LED฀display฀provides฀adequate฀brightness฀with฀25mA฀or฀less฀current,฀

omit฀any฀external฀segment฀driver฀and฀connect฀the฀current฀limiting฀resistor฀directly฀to฀pins฀B0…B6.฀In฀this฀

case,฀you฀will฀need฀to฀recalculate฀the฀dropping฀resistor,฀and฀invert฀the฀sense฀of฀the฀B0…B6฀pins.฀(With฀an฀

external฀driver,฀a฀high฀on฀B0…B6฀activates฀a฀segment,฀but฀with฀a฀direct฀connection฀a฀low฀on฀B0…B6฀is฀

required.฀The฀current฀limiting฀resistor฀should฀be฀around฀100ohms.) I฀developed฀the฀circuit฀of฀Figure฀7-3฀as฀part฀of฀a฀larger฀project,฀

and฀Figure฀7-5฀shows฀the฀resulting฀prototype฀PCB.฀The฀device฀

that฀looks฀like฀an฀integrated฀circuit฀above฀the฀displays฀are฀68-ohm฀

current฀limiting฀resistors฀in฀the฀form฀of฀a฀Bournes฀4116R-1-680฀

resistor฀network.฀Separate฀resistors฀would฀work฀just฀as฀well.฀Of฀

course,฀you฀don’t฀need฀to฀design฀and฀build฀a฀PCB฀to฀experiment฀

with฀this฀circuit;฀a฀solderless฀plugboard฀works฀perfectly฀well.฀You฀

can’t฀squeeze฀all฀the฀parts฀onto฀the฀small฀2840฀development฀board’s฀

plugboard,฀however,฀so฀a฀second,฀larger,฀plugboard฀is฀required.

Test฀Program

Now฀that฀you’ve฀wired฀up฀the฀circuit฀of฀Figure฀7-3,฀let’s฀test฀it.

Figure฀7-5:฀Completed฀4-digit฀LED฀display.

Program฀7-1

;Program฀to฀demonstrate฀4฀digit฀7-segment฀LED฀display

;Sequentially฀show฀each฀segment฀and฀each฀digit

i฀ Var฀ Byte

j฀ Var฀ Byte

For฀i฀=฀B0฀to฀B6฀

฀฀฀฀Output฀i

฀฀฀฀Low฀i Next

For฀i฀=฀A0฀to฀A3

฀฀฀฀Output฀i

฀฀฀฀High฀i Next฀฀฀฀฀฀฀

฀฀฀฀

Main

For฀i฀=฀A0฀to฀A3

Low฀i

฀฀฀฀ For฀j฀=฀B0฀to฀B6

฀฀฀฀ High฀j฀

GoTo฀Main฀฀฀฀฀฀฀฀

Program฀7-1฀exercises฀each฀segment฀and฀each฀digit.฀The฀segments฀illuminate,฀one฀at฀a฀time฀in฀order฀A…G฀

for฀250฀ms฀each,฀starting฀with฀Digit฀0฀(rightmost)฀and฀moving฀left.

The฀code฀itself฀requires฀little฀analysis;฀after฀setting฀B0…B6฀and฀A0…A3฀for฀output,฀we฀sequentially฀scan฀the฀

digits฀and฀segments.฀The฀outer฀loop,฀i,฀steps฀through฀the฀digits.฀For฀each฀digit,฀the฀inner฀j฀loop฀scans฀the฀

segments.฀Recall฀that฀in฀order฀to฀be฀illuminated,฀a฀segment฀requires฀its฀digit฀line฀(A0…A3)฀to฀be฀low฀(applies฀

+5฀V฀to฀the฀display฀common)฀and฀its฀segment฀pin฀(B0…B6)฀to฀be฀high฀(saturates฀the฀ULN2003฀Darlington).฀

Hence,฀the฀i฀loop฀sets฀one฀of฀A0…A3฀low฀and฀the฀j฀loop฀sets฀one฀of฀B0…B6฀high.฀To฀turn฀off฀unwanted฀digits,฀

all฀other฀A0…A3฀pins฀are฀set฀high฀and฀to฀turn฀off฀unwanted฀segments,฀all฀other฀B0…B6฀pins฀are฀set฀low.

Now,฀let’s฀look฀at฀displaying฀actual฀digits,฀0…9.฀Program฀7-2฀continuously฀cycles฀the฀rightmost฀digit฀from฀

0…9,฀displaying฀each฀digit฀for฀one฀second.

Program฀7-2

;Shows฀digits฀0...9฀in฀last฀place

;LED฀display฀is฀commmon฀anode

;cathodes฀through฀ULN2003A฀array

;Variables

;---i฀ Var฀ Byte

Decode฀ ฀ Var฀ Byte(11)฀ ;holds฀segment฀pattern

;Digit฀values฀[D3][D2][D1][D0]

;Initialization

;---฀ Clear

DeCode(0)฀=฀%00111111฀฀฀฀฀holds฀segments

DeCode(1)฀=฀%00000110฀ ฀ +---A---+

DeCode(2)฀=฀%01011011฀ ฀ |฀฀฀฀฀฀฀|

DeCode(3)฀=฀%01001111฀ ฀ F฀฀฀฀฀฀฀B

DeCode(4)฀=฀%01100110฀ ฀ |฀฀฀฀฀฀฀|

DeCode(5)฀=฀%01101101฀ ฀ +---G---+

DeCode(6)฀=฀%01111100฀ ฀ |฀฀฀฀฀฀฀|

DeCode(7)฀=฀%00000111฀ ฀ E฀฀฀฀฀฀฀C

DeCode(8)฀=฀%01111111฀ ฀ |฀฀฀฀฀฀฀|

DeCode(9)฀=฀%01100111฀ ฀฀฀ +---D---+

DeCode(10)฀=฀%00000000฀฀;all฀blank฀฀ XGFEDCBA฀is฀bit฀order

TRISB฀=฀$00

TRISA฀=฀$F0

PortA.LowNib฀=฀%1111 Main

Low฀A0

For฀i฀=฀0฀to฀9

PortB฀=฀DeCode(i)

Pause฀1000

Next

GoTo฀Main฀฀฀฀฀฀฀฀

฀฀฀฀฀฀฀

฀฀฀฀฀฀

End

The฀major฀addition฀to฀this฀program฀is฀the฀array฀DeCode฀to฀map฀the฀desired฀digits฀to฀the฀segments฀that฀must฀

be฀illuminated฀to฀display฀that฀particular฀digit.฀

We’ve฀connected฀the฀segments฀in฀the฀order฀of฀segment฀A฀to฀B0…segment฀G฀to฀B6.฀Hence,฀to฀illuminate฀seg-ment฀A,฀we฀must฀write฀%00000001฀to฀Port฀B.฀To฀illuminate฀segment฀B,฀we฀write฀%00000010฀to฀Port฀B,฀and฀

so฀forth.฀If฀we฀wish฀to฀display฀the฀character฀“0”,฀for฀example,฀we฀must฀write฀%00111111฀to฀Port฀B,฀thereby฀

illuminating฀all฀segments฀except฀G.฀(If฀we฀directly฀connect฀the฀display฀cathodes฀to฀pins฀B0…B6,฀we฀must฀

invert฀this฀mapping,฀for฀example,฀to฀display฀the฀character฀“0”฀we฀would฀write฀%11000000฀to฀Port฀B.) Another฀change฀we’ve฀made฀is฀to฀initialize฀Port฀A฀and฀Port฀B฀through฀TRISA฀and฀TRISB฀statements,฀as฀

outlined฀in฀Chapter฀2.฀This฀change฀illustrates฀that฀MBasic฀often฀gives฀us฀several฀ways฀of฀accomplishing฀

the฀same฀task.฀(We฀could฀omit฀the฀TRISA฀statement,฀as฀the฀Low฀and฀High฀statements฀automatically฀change฀

the฀pin฀to฀output,฀but฀it’s฀cleaner฀to฀initialize฀our฀input฀and฀output฀pins฀directly.)฀We฀also฀set฀all฀digits฀to฀off฀

through฀PortA.LowNib฀=฀%1111.

Main

Low฀A0

For฀i฀=฀0฀to฀9

PortB฀=฀DeCode(i)

Pause฀1000

Next

GoTo฀Main฀฀฀฀฀฀฀฀

The฀main฀loop฀first฀activates฀Digit฀0฀by฀dropping฀A0฀low.฀Then,฀we฀

loop฀through฀the฀digits฀0…9.฀Each฀digit฀is฀displayed฀through฀the฀

assignment฀PortB฀=฀DeCode(i).

Now฀that฀we’ve฀verified฀that฀all฀segments฀illuminate฀and฀that฀the฀

digits฀0…9฀can฀be฀displayed,฀let’s฀do฀something฀more฀useful฀with฀

our฀display.฀Program฀7-3฀reads฀three฀momentary฀contact฀switches;฀

one฀increments฀the฀count,฀one฀decrements฀the฀count฀and฀one฀will฀

clear฀the฀count฀to฀zero.฀Making฀it฀a฀bit฀fancier,฀we’ll฀blank฀leading฀

zeros.฀We’ll฀debounce฀the฀switches฀and฀set฀up฀the฀read฀loop฀so฀that฀

the฀counter฀only฀increments฀once฀for฀each฀closure.

First,฀as฀shown฀in฀Figure฀7-6฀add฀three฀momentary฀contact,฀normal-ly฀open฀switches฀and฀pull-up฀resistors฀to฀the฀circuit฀of฀Figure฀7-3.฀ Figure฀7-6:฀Added฀switches.

Program฀7-3

;Program฀to฀demonstrate฀4฀digit฀7-segment฀LED฀display

;Counts฀up/down฀on฀4-digit฀LED฀Display

;LED฀display฀is฀commmon฀anode

;cathodes฀through฀ULN2003A฀array

;Constants

DelayCount฀ Con฀ 3

;Variables

;---i฀ Var฀ Byte

j฀ Var฀ Byte

Decode฀ ฀ Var฀ Byte(11)฀ ;holds฀segment฀pattern Digit฀฀฀฀ Var฀ Nib(4)฀ ฀ ;holds฀digit฀values฀

Counter฀฀ Var฀ Word฀ ;value฀to฀disply

TempWord฀฀ Var฀ Word

ArmUp฀ ฀ Var฀ Nib

ArmDown฀฀ Var฀ Nib

;Digit฀values฀[D3][D2][D1][D0]

;Initialization

;---฀ Clear

DeCode(0)฀=฀%00111111฀฀฀฀holds฀segments

DeCode(1)฀=฀%00000110฀ ฀ +---A---+

DeCode(2)฀=฀%01011011฀ ฀ |฀฀฀฀฀฀฀|

DeCode(3)฀=฀%01001111฀ ฀ F฀฀฀฀฀฀฀B

DeCode(4)฀=฀%01100110฀ ฀ |฀฀฀฀฀฀฀|

DeCode(5)฀=฀%01101101฀ ฀ +---G---+

DeCode(6)฀=฀%01111100฀ ฀ |฀฀฀฀฀฀฀|

DeCode(7)฀=฀%00000111฀ ฀ E฀฀฀฀฀฀฀C

DeCode(8)฀=฀%01111111฀ ฀ |฀฀฀฀฀฀฀|

DeCode(9)฀=฀%01100111฀ ฀฀฀ +---D---+

DeCode(10)฀=฀%00000000฀฀ ;all฀blank฀฀ XGFEDCBA฀is฀bit฀order

TRISB฀=฀$00฀ ;segments฀on฀PortB

TRISA฀=฀$F0฀ ;Digit฀drive฀on฀PortA

TRISC฀=฀%00000111฀ ;Switches฀on฀PortC

PortA.LowNib฀=฀0

PortB฀=฀DeCode(8)

Pause฀1000

PortB฀=฀DeCode(10)

Pause฀250

฀฀฀

;blank฀leading฀0s

Digit(3)฀=฀10

Digit(2)฀=฀10

Digit(1)฀=฀10 Main

;multiplex฀right฀to฀left

For฀i฀=฀A0฀to฀A3

;order฀is฀important฀to฀void฀ghosting

฀฀฀฀ PortB฀=฀DeCode(Digit(i-A0))

฀฀ Low฀i

฀฀ ;GetDigits฀is฀part฀of฀the฀delay

GoSub฀GetDigits

Pause฀1

High฀i฀ ฀

Next

GoTo฀Main฀฀฀฀฀฀฀฀

฀฀฀฀฀฀฀

฀฀฀฀฀฀

GetDigits

;Check฀the฀up฀count฀switch

฀฀฀฀If฀(PortC.Bit0฀=฀0)฀AND฀(ArmUp฀=฀0)฀Then

฀฀฀฀฀ Counter฀=฀Counter฀+1

฀฀฀฀฀ ArmUp฀=฀1

฀฀฀฀EndIf

฀฀฀฀฀฀฀

฀฀฀฀;Is฀it฀off?฀If฀so฀delay

฀฀฀฀If฀(ArmUp฀>฀0)฀AND฀(PortC.Bit0฀=฀1)฀Then

ArmUp฀=฀ArmUp฀+฀1

If฀ArmUp฀=฀DelayCount฀Then

ArmUp฀=฀0

EndIf฀฀฀

฀฀฀฀EndIf

฀฀฀฀

฀฀฀฀;Check฀the฀down฀count฀switch

฀฀฀฀If฀(PortC.Bit1฀=฀0)฀AND฀(ArmDown฀=฀0)฀Then

฀฀฀฀฀ Counter฀=฀Counter฀-1

฀฀฀฀฀ ArmDown฀=฀1

฀฀฀฀EndIf

฀฀฀฀฀฀฀

฀฀฀฀;If฀switch฀off?฀If฀so฀delay

฀฀฀฀If฀(ArmDown฀>฀0)฀AND฀(PortC.Bit1฀=฀1)฀Then

ArmDown฀=฀ArmDown฀+฀1

If฀ArmDown฀=฀DelayCount฀Then

฀฀฀ArmDown฀=฀0

EndIf฀฀฀

฀฀฀฀EndIf

฀฀฀฀

฀฀฀฀If฀PortC.Bit2฀=฀0฀Then

฀฀฀฀฀ Counter฀=฀0

฀฀฀฀EndIf

฀฀฀฀

฀฀฀฀;Avoid฀rollover

If฀Counter฀>฀9999฀Then

฀฀฀Counter฀=฀0

EndIf

;Digits฀are฀individual฀BCD

TempWord฀=฀Bin2BCD฀Counter

Digit(0)฀=฀TempWord.Nib0

Digit(1)฀=฀TempWord.Nib1

Digit(2)฀=฀TempWord.Nib2

Digit(3)฀=฀TempWord.Nib3

;Following฀is฀for฀blanking฀leading฀0s

If฀Counter฀<฀1000฀Then

฀฀฀Digit(3)฀=฀10

EndIf

If฀Counter฀<฀100฀Then

฀฀฀Digit(2)฀=฀10

EndIf

If฀Counter฀<฀10฀Then

฀฀฀Digit(1)฀=฀10

EndIf฀฀฀฀฀฀฀฀฀฀฀

Return End

We’ve฀declared฀several฀new฀variables,฀including:

Digit฀฀฀฀ Var฀ Nib(4)฀ ฀

Counter฀฀ Var฀ Word

Counter฀is฀the฀value฀that฀we฀increment,฀decrement฀or฀zero฀with฀the฀added฀switches.฀Since฀we฀have฀four฀display฀

digits,฀Counter฀may฀run฀from฀0…9999.฀We฀accordingly฀store฀it฀in฀a฀word฀length฀variable.฀The฀array฀Digit฀holds฀

the฀value฀of฀the฀individual฀digits฀to฀be฀displayed.฀If฀Counter฀is฀1234,฀for฀example,฀Digit(0)฀=฀4,฀Digit(1)฀

=฀3,฀Digit(2)฀=฀2฀and฀Digit(3)฀=฀1.฀(The฀digits฀are฀numbered฀with฀0฀at฀the฀rightmost฀position.)

PortA.LowNib฀=฀0

PortB฀=฀DeCode(8)

Pause฀1000

PortB฀=฀DeCode(10)

Pause฀250

฀฀฀

;blank฀leading฀0s

Digit(3)฀=฀10

Digit(2)฀=฀10

Digit(1)฀=฀10

As฀part฀of฀initialization,฀we฀illuminate฀all฀segments฀by฀displaying฀“8888”฀for฀250฀ms,฀followed฀by฀blanking฀

the฀leftmost฀three฀digits.฀(DeCode(10) ฀is฀a฀blank.)฀Briefly฀illuminating฀all฀segments฀is฀a฀common฀initial-ization฀feature,฀as฀it฀permits฀the฀user฀to฀detect฀failed฀segments.฀We฀don’t฀have฀to฀worry฀about฀operating฀all฀

segments฀simultaneously฀as฀we฀use฀robust฀drivers.฀If฀your฀segments฀are฀directly฀connected฀to฀pins฀B0…B6,฀

simultaneously฀operating฀all฀segments฀and฀all฀digits฀may฀overstress฀the฀PIC’s฀ability฀to฀sink฀current,฀in฀

which฀case฀modify฀your฀code฀to฀sequence฀the฀digits.

Main

;multiplex฀right฀to฀left

For฀i฀=฀A0฀to฀A3

;order฀is฀important฀to฀void฀ghosting

฀฀฀฀ PortB฀=฀DeCode(Digit(i-A0))

฀฀ Low฀i

฀฀ ;GetDigits฀is฀part฀of฀the฀delay

GoSub฀GetDigits

Pause฀1

High฀i฀ ฀

Next

GoTo฀Main฀฀฀฀฀฀฀฀

The฀main฀loop฀sequentially฀steps฀through฀the฀digits,฀from฀right฀to฀left,฀with฀the฀For฀i…Next ฀loop฀and฀il-luminates฀the฀segments฀required฀to฀display฀the฀associated฀value฀held฀in฀Digit(i).฀We’ll฀assume฀for฀the฀

moment฀that฀Digit(i)฀holds฀the฀correct฀value฀(0…9)฀for฀each฀of฀the฀four฀displayed฀digits.฀After฀Port฀B฀

is฀set฀to฀the฀proper฀bit฀sequence฀to฀display฀the฀correct฀digit,฀execution฀branches฀to฀subroutine฀GetDigits,฀

following฀which฀an฀extra฀1ms฀pause฀is฀executed.฀(As฀we’ll฀see฀shortly,฀subroutine฀GetDigits฀fills฀Digit()฀ with฀the฀correct฀values.)

Several฀points฀are฀worth฀mentioning฀if฀you฀modify฀this฀code฀for฀other฀programs.฀First,฀in฀order฀to฀avoid฀

flicker,฀the฀loop฀must฀execute฀relatively฀fast.฀As฀written,฀each฀digit฀is฀illuminated฀for฀about฀4฀ms,฀so฀all฀four฀

digits฀are฀updated฀about฀60฀times฀per฀second.฀If฀this฀update฀rate฀significantly฀slows,฀due฀to฀added฀code,฀the฀

display฀will฀start฀to฀flicker.฀Different฀people฀perceive฀flicker฀differently,฀but฀I฀find฀flicker฀objectionable฀if฀the฀

update฀rate฀falls฀below฀40฀per฀second.฀Second,฀it’s฀important฀to฀switch฀between฀digits฀as฀fast฀as฀possible฀to฀

avoid฀“ghosting”฀where฀false฀segments฀are฀briefly฀illuminated.฀To฀avoid฀ghosting฀we฀first฀write฀the฀segment฀

information฀to฀Port฀B฀and฀then฀take฀the฀associated฀A฀pin฀low฀to฀activate฀the฀digit.฀Reversing฀this฀order฀causes฀

objectionable฀ghosting.฀Finally,฀there’s฀a฀trade-off฀between฀the฀digit-on฀time฀and฀perceived฀brightness,฀of฀

course,฀so฀some฀tinkering฀with฀the฀current฀limiting฀resistor฀may฀be฀necessary฀after฀your฀code฀is฀finished.฀

Let’s฀look฀at฀GetDigits.฀This฀subroutine฀is฀called฀every฀4฀ms฀or฀so.

If฀(PortC.Bit0฀=฀0)฀AND฀(ArmUp฀=฀0)฀Then

฀฀฀฀฀ Counter฀=฀Counter฀+1

฀฀฀฀฀ ArmUp฀=฀1 EndIf

฀฀฀฀฀฀฀

;Is฀it฀off?฀If฀so฀delay

฀If฀(ArmUp฀>฀0)฀AND฀(PortC.Bit0฀=฀1)฀Then

ArmUp฀=฀ArmUp฀+฀1

If฀ArmUp฀=฀DelayCount฀Then

฀฀฀ArmUp฀=฀0

EndIf฀฀฀

฀EndIf

All฀switches฀are฀wired฀with฀the฀normal฀(unpressed)฀position฀open,฀so฀that฀a฀0฀represents฀switch฀pressed฀and฀1฀

is฀switch฀un-activated.฀We฀have฀two฀almost฀identical฀routines฀to฀handle฀the฀up฀and฀down฀switch฀inputs.฀We’ll฀

only฀look฀at฀the฀“up”฀switch฀code.฀

Assume฀for฀the฀moment฀that฀ArmUp฀=฀0฀(it’s฀initialized฀that฀way)฀and฀that฀the฀“up”฀switch฀is฀pressed.฀The฀

conditional฀If฀(PortC.Bit0฀=฀0)฀AND฀(ArmUp฀=฀0)฀thus฀evaluates฀as฀true,฀Counter฀is฀incremented฀

and฀ArmUp฀is฀assigned฀as฀1.฀฀

As฀long฀as฀the฀“up”฀switch฀continues฀to฀be฀pressed฀the฀second฀conditional฀If฀(ArmUp฀>฀0)฀AND฀

(PortC.Bit0฀=฀1)฀fails,฀as฀PortC.Bit0฀=฀0.฀ArmUp ฀thus฀continues฀to฀equal฀1฀and฀the฀earlier฀condi-tional฀will฀fail,฀thus฀preventing฀Counter฀from฀being฀updated฀due฀to฀continued฀switch฀activation.฀

When฀the฀“up”฀switch฀is฀released,฀the฀second฀conditional฀If฀(ArmUp฀>฀0)฀AND฀(PortC.Bit0฀=฀1)฀ evaluates฀true,฀so฀ArmUp฀is฀incremented.฀If฀the฀switch฀bounces,฀it’s฀possible฀that฀the฀first฀conditional฀may฀

evaluate฀PortC.Bit0฀=฀0฀as฀true.฀However,฀ArmUp฀continues฀to฀be฀greater฀than฀0,฀so฀the฀ArmUp฀=฀0฀part฀

of฀the฀first฀conditional฀fails฀and฀Counter฀is฀not฀incremented.฀Eventually,฀however,฀assuming฀the฀“up”฀switch฀

is฀not฀pressed,฀ArmUp฀reaches฀DelayCount฀at฀which฀time฀it฀is฀reset฀to฀0฀and฀we฀are฀ready฀for฀another฀press฀

of฀the฀“up”฀switch.฀DelayCount฀is฀a฀constant฀that฀I’ve฀set฀at฀3,฀thus฀representing฀two฀passes฀of฀GetDigits,฀

or฀about฀8฀ms.฀You฀may฀need฀to฀vary฀DelayCount,฀depending฀on฀the฀bounce฀performance฀of฀your฀switches,฀

but฀3฀is฀a฀good฀starting฀point.

If฀PortC.Bit2฀=฀0฀Then

฀฀฀ Counter฀=฀0 EndIf

We฀need฀not฀complicate฀reading฀the฀reset฀switch฀connected฀to฀pin฀C2;฀it฀doesn’t฀matter฀if฀bounce฀causes฀two฀

or฀three฀consecutive฀resets.

;Avoid฀rollover If฀Counter฀>฀9999฀Then

฀฀฀Counter฀=฀0 EndIf

Since฀our฀four-digit฀display฀only฀shows฀0…9999,฀we฀restrict฀Counter฀to฀those฀values฀that฀are฀capable฀of฀

being฀displayed.

TempWord฀=฀Bin2BCD฀Counter Digit(0)฀=฀TempWord.Nib0 Digit(1)฀=฀TempWord.Nib1 Digit(2)฀=฀TempWord.Nib2 Digit(3)฀=฀TempWord.Nib3

Let’s฀look฀at฀how฀we฀get฀the฀individual฀digit฀values฀out฀of฀Counter฀and฀into฀the฀Digit฀array.฀It฀turns฀out฀

that฀this฀is฀a฀rather฀common฀activity฀and฀MBasic฀includes฀a฀function,฀Bin2BCD฀to฀perform฀it฀for฀us.฀(There’s฀

an฀error฀in฀some฀editions฀of฀the฀User’s฀Guide฀where฀this฀function฀is฀incorrectly฀identified฀as฀Dec2BCD.)฀Let’s฀

look฀at฀“binary฀coded฀decimal”฀a฀bit฀more฀closely.

Suppose฀Counter฀holds฀1234.฀The฀value฀1234฀(decimal)฀is฀held฀in฀MBasic฀as฀hex฀$4D2.฀If฀we฀wanted฀our฀

display฀to฀show฀hexadecimal฀(assuming฀we฀defined฀DeCode($A)…DeCode($F)),฀we฀could฀very฀easily฀find฀

our฀digits.฀We’ve฀declared฀Counter฀as฀a฀word฀length฀variable,฀so฀it฀has฀four฀nibbles:

Word฀Value Nibble฀3 Nibble฀2 Nibble฀1 Nibble฀0

$4D2 $0 $4 $D $2

Hence,฀Digit(0)฀=฀Counter.Nib0,฀etc.฀But,฀you฀say,฀we฀want฀a฀decimal฀display,฀not฀a฀hexadecimal฀one.฀

Fair฀enough.฀First,฀however,฀what฀is฀1234?฀It’s฀1฀×฀1000฀+฀2฀×฀100฀+฀3฀×฀10฀+฀4.฀With฀this฀understanding,฀we฀

can฀devise฀an฀algorithm฀to฀fill฀Digit():

Digit(3)฀=฀Counter฀/฀1000 Remainder฀=฀Counter฀//฀1000

Digit(2)฀=฀Remainder฀/฀100 Remainder฀=฀Counter฀//฀100 Digit(1)฀=฀Remainder฀/฀10 Digit(0)฀=฀Remainder฀//฀10

In฀MBasic฀“/”฀is฀integer฀division฀and฀“// ”฀is฀the฀remainder,฀or฀modulus,฀function.฀Let’s฀see฀how฀this฀algo-rithm฀works฀if฀Counter฀is฀1234.

1234฀/฀1000฀=฀1฀฀ ->฀Digit(3) 1234฀//฀1000฀=฀234

234/100฀=฀2฀ ->฀Digit(2) 234//100฀=฀34

34/10฀=฀3฀ ->฀Digit(1) 34//10฀=฀4฀ ->฀Digit(0)

But,฀there’s฀a฀faster฀way฀to฀accomplish฀this฀conversion฀than฀invoking฀six฀integer฀division฀and฀remainder฀op-erations฀in฀MBasic.฀First,฀a฀definition:฀binary฀coded฀decimal,฀or฀BCD,฀uses฀four฀bits฀(one฀nibble)฀to฀represent฀

each฀decimal฀digit.฀Since฀a฀word฀is฀16฀bits฀long,฀we฀can฀“pack”฀four฀BCD฀digits,฀each฀four฀bits฀long,฀into฀

each฀word.฀This฀arrangement,฀often฀called฀“packed฀BCD,”฀is฀shown฀below,฀supposing,฀somehow,฀we฀are฀able฀

to฀get฀each฀nibble฀to฀hold฀the฀desired฀digits.

Word฀Value Nibble฀3 Nibble฀2 Nibble฀1 Nibble฀0 4660

$1234 1 2 3 4

The฀decimal฀value฀of฀$1234฀is฀4660,฀but฀looking฀at฀each฀nibble฀individually฀we฀immediately฀see฀what฀we฀are฀

looking฀for,฀[1][2][3][4].

MBasic’s฀Bin2BCD฀function฀converts฀a฀binary฀variable฀to฀packed฀BCD.฀Hence,฀filling฀Digit()฀is฀simply฀a฀

matter฀of฀assigning฀each฀nibble฀to฀its฀Digit() ฀counterpart฀the฀following฀the฀BCD฀conversion.฀We฀accom-plish฀this฀with฀the฀following฀code฀snippet:

TempWord฀=฀Bin2BCD฀Counter Digit(0)฀=฀TempWord.Nib0 Digit(1)฀=฀TempWord.Nib1 Digit(2)฀=฀TempWord.Nib2 Digit(3)฀=฀TempWord.Nib3

Finally,฀for฀aesthetic฀reasons฀we฀blank฀leading฀zeros.

;Following฀is฀for฀blanking฀leading฀0s If฀Counter฀<฀1000฀Then

฀฀฀Digit(3)฀=฀10 EndIf

If฀Counter฀<฀100฀Then

฀฀฀Digit(2)฀=฀10 EndIf

If฀Counter฀<฀10฀Then

฀฀฀Digit(1)฀=฀10 EndIf฀฀฀฀฀฀฀฀฀฀฀

We’ve฀defined฀DeCode(10)฀as฀a฀blank฀digit฀(all฀segments฀off),฀so฀to฀blank฀a฀digit฀we฀set฀Digit()฀as฀10.฀

This฀task฀is฀easily฀accomplished฀by฀checking฀the฀value฀of฀Counter.฀If฀Counter฀is฀under฀1000,฀we฀know฀

that฀at฀least฀the฀leftmost฀digit฀must฀be฀0,฀and฀according฀assign฀Digit(3)฀as฀a฀blank.฀We฀repeat฀this฀test฀and฀

assignment฀process฀with฀100฀and฀10฀for฀Digit(2)฀and฀Digit(1).฀

After฀blanking฀leading฀zeros,฀the฀subroutine฀is฀finished฀and฀program฀execution฀returns฀to฀Main.

We฀can฀now฀test฀the฀program.฀Every฀time฀the฀“up”฀button฀is฀operated,฀the฀displayed฀count฀will฀increase฀by฀

one.฀Every฀time฀the฀“down”฀button฀is฀operated,฀the฀displayed฀count฀will฀decrease฀by฀one,฀with฀a฀minimum฀of฀

zero.฀Pressing฀the฀“clear”฀button฀resets฀the฀display฀to฀zero.฀

Program฀7-3฀calls฀the฀subroutine฀once฀every฀3.8฀ms฀as฀written.฀The฀anti-bounce฀delay฀limits฀the฀speed฀with฀

which฀the฀counter฀will฀increment฀and฀decrement฀to฀about฀20฀to฀30฀operations฀per฀second,฀more฀than฀fast฀

enough฀for฀hand-operated฀switches.

Ideas฀for฀Modifications฀to฀Program฀and฀Circuits

•฀ Add฀segment฀definitions฀for฀“a”,฀”b”,฀”c”,฀“d”,฀“E”฀and฀“F”฀to฀DeCode().฀฀Remove฀the฀TempWord฀=฀

Bin2BCD฀Counter฀statement฀and฀display฀the฀count฀in฀hexadecimal.฀

•฀ How฀does฀flicker฀change฀as฀you฀modify฀the฀Pause฀1฀statement?฀(I฀found฀that฀if฀I฀changed฀the฀pause฀

time฀to฀5,฀flicker฀is฀quite฀objectionable,฀and฀with฀4,฀flicker฀is฀barely฀noticeable.฀I฀could฀not฀detect฀any฀

flicker฀for฀a฀pause฀time฀of฀3฀or฀below.฀If฀you฀wish฀to฀disable฀the฀pause,฀comment฀it฀out฀or฀delete฀the฀

statement.฀Pause฀0฀actually฀executes฀Pause฀255.)

•฀ How฀does฀perceived฀brightness฀change฀as฀you฀modify฀

the฀Pause฀1฀statement?฀What฀is฀the฀ratio฀of฀digit-on฀to฀

digit-off฀time?฀If฀the฀overhead฀is฀zero,฀each฀digit฀should฀

be฀on฀25%฀of฀the฀time.฀(As฀Figure฀7-7฀shows,฀with฀the฀

Pause฀1฀statement฀operational,฀each฀digit฀is฀illuminated฀

22.1%฀of฀the฀time.฀The฀overhead฀is฀thus฀11.6%.)

•฀ Instead฀of฀triggering฀up/down฀counts฀with฀mechanical฀

switches,฀substitute฀a฀function฀generator’s฀square฀wave฀

output.฀How฀fast฀can฀you฀update฀the฀counter฀before฀

it฀misses฀input฀pulses?฀It฀is฀possible฀to฀increase฀the฀

response฀speed?฀(You฀don’t฀need฀to฀debounce฀a฀function฀

generator’s฀input,฀so฀the฀input฀software฀can฀be฀simpli-fied.)฀Is฀it฀possible฀to฀replace฀the฀Pause฀1฀statement฀

with฀repeated฀subroutine฀calls฀to฀GetDigits?

with฀repeated฀subroutine฀calls฀to฀GetDigits?