HomeArticlesProjectsBlogContact
Articles
2-Digit up/down Counter
Colin Mitchell
Colin Mitchell
June 18, 2009
Make sure to subscribe to our newsletter and be the first to know the news.

  • P1 - 2 digit Up/Down Counter using PIC16F628 and with alarm

  • Page 2 Adding Dice and Random Number (PIC16F628)

  • Page 3 2 digit Up/Down Counter using PIC12F629

  • Page 4 Adding RF Link to PIC12F629 version

  • Page 5 Car Gears Up/Down counter

  • Instruction Set for PIC16F628

  • PIC16F628 PDF Data Sheet

  • BlankF628.asm

  • BlankF628.txt

  • Chip Programmer - PICkit2 from Modtronix (MPASM and MPLAB come with PICkit2)

  • PIC16F628A.inc

2 Digit Up/Down Counter with PIC16F628 and prototype PC board $25.00 plus $6.50 postage.
To order the kit, [send an email](mailto:colin@elechelp.com?Subject=Buying components for the 2 Digit Up/Down counter&Body=Please e-mail the cost of components for the 2 Digit Up/Down counter on prototype PC board for $25.00 plus postage by air mail to my country:**___**** and send details of how I can pay for it. My name is:____)** to us and we will reply with the details of how to order etc.

[components](mailto:colin@elechelp.com?Subject=Buying components for the 2 Digit Up/Down counter&Body=Please e-mail the cost of components for the 2 Digit Up/Down counter on prototype PC board by air mail to my country:****___**** and send details of how I can pay for it. My name is:____) are available from Talking Electronics.

See more projects using micros:

The files for PIC16F628: See P1

The files for Dice for PIC16F628: See Page 2

The files for UpDown/Dice/00-99 for PIC16F628: See Page 2

The files for UpDown for PIC12F629: See Page 3

This project comes via two circuits on 2 boards. The first circuit is designed around a PIC16F628A. It has been presented on an experimental PC board using surface-mount components and was built in less than 1 hour, with about 2 hours to write and finalise the program. See Page 1, Page 2
The second circuit uses a PIC12F629 to produce a 2-Digit Up/Down Counter (see Page 3).
Both use “In Circuit Programming” via PICkit-2.

Page 5 Describes the up/down counter displaying the “gear” for motorcycles and racing cars.

The project shows what can be done with a micro and you can modify it to set an alarm at any count-value or set a limit such as “count-to-60.” You can add a buzzer or relay or increase the display to 3 digits. You need to remember that each additional display will reduce the illumination of each digit as they are “multiplexed (time-sharing).”


The Basic 2-Digit Up/Down Counter Circuit


Full circuit diagram including programming socket
The displays do not affect the “In Circuit Programming” as they are not active when programming and do not load the outputs.


The “In Circuit Programming” Connections


The two 7-Segment displays are paralleled by jumpers at the top and bottom
Any 7-Segment displays will work in this circuit. You need to identify the pinout of any display you use.
(Common Anode displays can be used providing you invert the values in the display table. A PNP transistor will need to be used.)

2DigitCounterTop
Photo of the 2-Digit Up/Down Counter

2DigitCounterIso
2 Digit up/down COUNTER
The experimenter PC Board shows the five “In Circuit Programming” pins and a diode on the positive rail to drop the 6v supply to 5.4v. The board also has a 100n surface mount capacitor and two surface-mount transistors. The Up/Down buttons have 22k resistors.

TESTING THE CIRCUIT

Check the circuit by removing the chip and taking pins 6 and 18 to the 5v rail ad make sure segment “A” illuminates. Do the same for all the other segments.
The circuit diagram does not have any voltages marked on it as the circuit is DIGITAL.
All the “lines” or “wires” or pins of a microcontroller will have rail voltage (5v) on them when they are HIGH and when you come to a resistor, the resistor will drop a certain voltage. The voltage it will drop will be the difference between rail voltage and the voltage developed across the component it is driving. If it is driving a LED, the LED will drop a characteristic voltage of between 1.7v and 3.6v, depending on the colour.
If the component is a transistor, the voltage developed between the base and emitter will be about 0.7v.

;****************************************************************
;* 2 Digit UP / Down Counter    17/6/2009
;Port B drives two 7 segment displays
;Up Sw on RA2   Down Sw on RA3
;"Units" drive on RA0   "Tens" drive on RA1
;*                              *
;****************************************************************

    list P = 16F628A    ;microcontroller
    include         ;registers for F628A


    __Config _cp_off & _lvp_off & _pwrte_on &
            _wdt_off & _intRC_osc_noclkout & _mclre_off

;code protection - off
;low-voltage programming - off
;power-up timer -  on
;watchdog timer - off
;use internal RC for 4MHz - all pins for in-out


;****************************************************************
; variables - names and files
;****************************************************************


        ;Files for F628A start at 20h

temp1       equ     20h ;for delay
temp2       equ     21h ;for delay
SwUp        equ     22h ;
SwDwn       equ 23h ;
units       equ 24h ;
tens        equ 25h ;
Sw_Flag     equ 26h ;
FastCount   equ 27h ;counts loops fast incrementing

;****************************************************************
;Equates
;****************************************************************
status      equ 0x03
cmcon       equ 0x1F
rp1     equ 0x06
rp0     equ 0x05
portA       equ 0x05
portB       equ 0x06
trisA           equ     0x85
trisB           equ     0x86
 ;****************************************************************
;Beginning of program
;****************************************************************
reset   org 00  ;reset vector address
    goto    SetUp

table   addwf   PCL,F           ;02h,1  add W to program counter
        retlw   b'00111111'     ; "0"   -|F|E|D|C|B|A
        retlw   b'00000110'     ; "1"   -|-|-|-|C|B|-
        retlw   b'01011011'     ; "2"   G|-|E|D|-|B|A
        retlw   b'01001111'     ; "3"   G|-|-|D|C|B|A
        retlw   b'01100110'     ; "4"   G|F|-|-|C|B|-
        retlw   b'01101101'     ; "5"   G|F|-|D|C|-|A
        retlw   b'01111101'     ; "6"   G|F|E|D|C|-|A
        retlw   b'00000111'     ; "7"   -|-|-|-|C|B|A
        retlw   b'01111111'     ; "8"   G|F|E|D|C|B|A
        retlw   b'01101111'     ; "9"   G|F|-|D|C|B|A


;****************************************************************
;* port A and B initialisation                  *
;****************************************************************

SetUp   bsf status,rp0
    movlw   b'00001100' ;Make RA0,1 out   RA2,3 in
    movwf   05h     ;trisA clrf 06h     ;trisB Make all RB output
    bcf status,rp0  ;select programming area - bank0
    movlw   b'10000000' ;Turn off T0CKI
    movwf   option_reg
    clrf    portB ;Clear Port B of junk
    clrf    units       ;zero the units file
    clrf    tens        ;zero the tens file
    clrf    Sw_Flag
    movlw   07h     ;turn comparators off and enable
    movwf   cmcon       ;    pins for I/O functions
    goto    Main

   ;Delay 10mS      10 x 1,000uS

D_10mS  movlw   0Ah
    movwf   temp2
D_a nop
    decfsz  temp1,f
    goto    D_a
    decfsz  temp2,f
    goto    D_a
    retlw   00

Up  btfsc   Sw_Flag,2
    retlw   00
    bsf Sw_Flag,2
    incf    units,f
    movlw   0Ah     ;put 10 into w
    xorwf   units,w     ;compare units file with 10
    btfss   status,2    ;zero flag in status file. Set if units is 10
    retlw   00
    clrf    units
    incf    tens,f
    movlw   0Ah     ;put 10 into w
    xorwf   tens,w      ;compare units file with 10
    btfsc   status,2    ;zero flag in status file. Set if tens is 10
    clrf    tens
    retlw   00      ;display passes 99 but not below 0


Dwn btfsc   Sw_Flag,3
    retlw   00
    bsf Sw_Flag,3
    decf    units,f
    movlw   0FFh        ;put FFh into w
    xorwf   units,w     ;compare units file with FFh
    btfss   status,2    ;zero flag in status file. Set if units is 10
    retlw   00
    movlw   09
    movwf   units       ;put 9 into units file
    decf    tens,f
    movlw   0FFh        ;put 0FFh into w
    xorwf   tens,w      ;compare tens file with 0FFh
    btfsc   status,2    ;zero flag status file). Set if tens is 0FFh
    goto    $+2     ;tens file is 0FFh  Jump down 2 instructions
    retlw   00
    clrf    tens
    clrf    units
    retlw   00      ;display  not below 0


Main    btfss   portA,2     ;test switch-press for UP
    call    Up      ;UP switch pressed
    btfss   portA,3     ;test switch-press for Down
    call    Dwn     ;Down switch pressed
    movlw   b'00000001' ;Make RA0 HIGH for units drive
    movwf   portA movf  units,f     ;copy unit value into w
    call    table       ;unit display value will return in w
    movwf   portB ;output units value
    call    D_10mS      ;call delay
    clrf    portB ;clear display
    movlw   b'00000010' ;Make RA1 HIGH for tens drive
    movwf   portA movf  tens,f      ;copy tens value into w
    call    table       ;tens display value will return in w
    movwf   portB ;output tens value
    call    D_10mS      ;call delay
    clrf    portB ;clear display
    btfsc   portA,2     ;bit will be zero when sw is pressed
    bcf Sw_Flag,2
    btfsc   portA,3     ;bit will be zero when sw is pressed
    bcf Sw_Flag,3
    goto    Main

        END

If you want to suppress (remove) the first “0” when the digits are 1, 2, 3, etc, here are the lines of code to add:

In the files section, add

 ;Files for F628A start at 20h


compare         equ     28h     ;looks for "0" in first display

In Main, add the following lines shown in <--this:

Main    btfss   portA,2     ;test switch-press for UP
        call    Up          ;UP switch pressed
        btfss   portA,3     ;test switch-press for Down
        call    Dwn         ;Down switch pressed
        movlw   b'00111111' ;binary for "0" on the display   <--this
        movwf   compare     ;put the value into compare      <--this
        movlw   b'00000001' ;Make RA0 HIGH for units drive
        movwf   portA
        movf    units,f     ;copy unit value into w
        call    table       ;unit display value will return in w
        movwf   portB       ;output units value
        call    D_10mS      ;call delay
        clrf    portB       ;clear display
        movlw   b'00000010' ;Make RA1 HIGH for tens drive
        movwf   portA
        movf    tens,f      ;copy tens value into w
        call    table       ;tens display value will return in w
        subwf   compare,1                                     <--this
        btfsc   03,2        ;test zero flag for match         <--this
        movlw   0           ;remove the zero on the display   <--this
        movwf   portB       ;output tens value
        call    D_10mS      ;call delay
        clrf    portB       ;clear display
        btfsc   portA,2     ;bit will be zero when sw is pressed
        bcf     Sw_Flag,2
        btfsc   portA,3     ;bit will be zero when sw is pressed
        bcf     Sw_Flag,3
        goto    Main

                END

The program above uses very simple instructions and it is very easy to see how each line of code works. The only Boolean instruction is xorwf and this is an ex-or instruction (exclusive-or) which means two files are compared with each bit in one file compared with the same bit in the other file. The result is “1” when one (and only one) of the numbers is “1.” XOR detects a MATCH. If two identical numbers are compared, the answer for say the lowest bit will be “0” because only one of the numbers must be “1.” If both files contain the same value, the result of the XOR is “0.”
We now look at file 03, (the Status file) and check bit 2. This is the zero bit. The result of the XOR instruction is “0” and thus the zero bit is SET.
This is the the most complex of the instructions used in this program.

Now we come to a program written by a PROGRAMMER.
It performs the same as the program above and has the same number of instructions, but it contains a lot more Boolean instructions and this makes it a lot harder to understand.
However it is very interesting to see how to program at the “next level of understanding,” and we will cover some of the features (these are located after the program).

If you use any of the following .asm files and find they fail to produce a .hex file, make sure you have the .inc file in the same folder as the .asm.

To add RESET to the up/down counter, add the following instructions:

SetUp   bsf status,rp0
    movlw   b'00011100' ;Make RA0,1 out   RA2,3,4 in

Put reset on RA4 pin3
put 22k to positive and the switch between pin3 and 0v.
When sw is pressed the input will go low.

in main, the least few lines will be

    bcf     Sw_Flag,2   ;button not pressed. Clear Up flag
    bcf     Sw_Flag,7   ;Clear Up repeat flag
    clrf    FastCount
    btfss   portA,4     ;test reset
    goto    SetUp
    goto    Main

    END
;******************************************************************
;  2-Digit Up/Dn Counter, Isochronous Loop Example
;           Isochronous - to occur at equal time intervals.       *
;******************************************************************

        processor PIC16F628
        include "p16f628.inc"
        errorlevel -302

       __Config _cp_off & _lvp_off & _pwrte_on &
        _wdt_off & _intRC_osc_noclkout & _mclre_off

;code protection - off
;low-voltage programming - off
;power-up timer -  on
;watchdog timer - off
;use internal RC for 4MHz - all pins for in-out

ones    equ     0x20            ; 0..9
tens    equ     0x21            ; 0..9
number  equ     0x22            ; 00..99
swlatch equ     0x23            ; switch state latch variable
DelayHi equ     0x24            ; DelayCy() subsystem variable

#define DnSw    3               ; RA3

;******************************************************************
;
;  DelayCy() subsystem macro generates four instructions
;
        radix   dec
clock   equ     8               ; clock frequency in Megahertz
usecs   equ     clock/4         ; cycles/microsecond multiplier
msecs   equ     usecs*1000      ; cycles/millisecond multiplier

DelayCy macro   delay           ; 11..327690 cycle range
        movlw   high((delay-11)/5)+1
        movwf   DelayHi
        movlw   low ((delay-11)/5)
        call    uDelay-((delay-11)%5)
        endm

;******************************************************************
;
;  init hardware and program variables
;
        org     0x000
Init
        bsf     STATUS,RP0      ; bank 1
        movlw   07h     ;turn comparators off and enable
    movwf   cmcon       ;    pins for I/O functions
        movlw   b'00001100'     ;
        movwf   TRISA           ; RA3-RA2 inputs, others outputs
        clrf    TRISB           ; portb all outputs
        bcf     STATUS,RP0      ; bank 0
        clrf    PORTB           ; clear portb output latches
        movlw   b'00000001'     ; digit select bits (RA1-RA0)
        movwf   PORTA           ; select the 'ones' display
        clrf    swlatch         ; clear switch state latch
        clrf    ones            ; clear 'ones'
        clrf    tens            ; clear 'tens'
        clrf    number          ; number = 00
;
;  isochronous 8 msec main program loop (62.5Hz refresh rate)
;
Main    clrf    PORTB           ; blank the display
        movf    PORTA,W         ;
        xorlw   b'00000011'     ; flip digit select bits
        movwf   PORTA           ;
        movf    tens,W          ; WREG = tens, 0..9
        btfss   PORTA,1         ; display tens? yes, skip, else
        movf    ones,W          ; WREG = ones, 0..9
        call    segtbl          ; get segment data
        movwf   PORTB           ; display new digit
TstSw   comf    PORTA,W         ; sample active low switches
        andlw   b'00001100'     ; on RA3 and RA2 pins
        xorwf   swlatch,W       ; changes (press or release)
        xorwf   swlatch,F       ; update switch state latch
        andwf   swlatch,W       ; filter out "new release" bits
        bnz     Bump            ; branch on a "new press", else
        DelayCy(8*msecs-23)     ; precise 8 msec loop timing
        goto    Main            ;
;
;  bump 'number' up or down with limit checking
;
Bump    andlw   1<<DnSw         ; the "Dn" switch?
        skpz                    ; no, skip (WREG=0), else
        movlw   -2              ; WREG = -2 (dn)
        addlw   1               ; WREG = 1 (up) or -1 (dn)
        addwf   number,F        ; number++ or number--
        movf    number,W        ; WREG = number = -1..100
        xorlw   100             ; test upper limit
        skpnz                   ; upper limit? no, skip, else
        decf    number,F        ; reset to 99
        btfsc   number,7        ; lower limit? no, skip, else
        incf    number,F        ; reset to 00
        movf    number,W        ; WREG = number = 00..99
;
;  setup 'tens' and 'ones' for next loop
;
        clrf    tens            ; isochronous bin2bcd routine
        addlw   -80             ; W = W - 80
        rlf     tens,F          ; shift in 2^3*10 bit
        btfss   tens,0          ; borrow? no, skip, else
        addlw   80              ; W = W + 80
        addlw   -40             ; W = W - 40
        rlf     tens,F          ; shift in 2^2*10 bit
        btfss   tens,0          ; borrow? no, skip, else
        addlw   40              ; W = W + 40
        addlw   -20             ; W = W - 20
        rlf     tens,F          ; shift in 2^1*10 bit
        btfss   tens,0          ; borrow? no, skip, else
        addlw   20              ; W = W + 20
        addlw   -10             ; W = W - 10, now W = "ones"
        rlf     tens,F          ; shift in 2^0*10 bit
        btfss   tens,0          ; borrow? no, skip, else
        addlw   10              ; W = W + 10, now W = "ones"
        movwf   ones            ; save "ones"
        DelayCy(8*msecs-54)     ; precise 8 msec loop timing
        goto    Main            ;
;
;  segment data table (caveat, non-boundary tolerant)
;
segtbl
        addwf      PCL,F
        retlw      b'00111111'     ; "0"   -|F|E|D|C|B|A
        retlw      b'00000110'     ; "1"   -|-|-|-|C|B|-
        retlw      b'01011011'     ; "2"   G|-|E|D|-|B|A
        retlw      b'01001111'     ; "3"   G|-|-|D|C|B|A
        retlw      b'01100110'     ; "4"   G|F|-|-|C|B|-
        retlw      b'01101101'     ; "5"   G|F|-|D|C|-|A
        retlw      b'01111101'     ; "6"   G|F|E|D|C|-|A
        retlw      b'00000111'     ; "7"   -|-|-|-|C|B|A
        retlw      b'01111111'     ; "8"   G|F|E|D|C|B|A
        retlw      b'01101111'     ; "9"   G|F|-|D|C|B|A
;
; DelayCy() subsystem 16-bit timing subroutine
;
        nop                     ; entry for (delay-11)%5 == 4
        nop                     ; entry for (delay-11)%5 == 3
        nop                     ; entry for (delay-11)%5 == 2
        nop                     ; entry for (delay-11)%5 == 1
uDelay  addlw   -1              ; subtract "loop" cycle time
        skpc                    ; borrow? no, skip, else
        decfsz  DelayHi,F       ; done?  yes, skip, else
        goto    uDelay          ; do another loop
        retlw   00              ;

        end

The following instructions test the buttons and only go to the increment/decrement routine (Bump) when a change in the value of the switches has been detected.
On the first pass of the program, the micro complements the value of the input port, (PortA) and loads it in to w. The instruction could have been movwf PORTA,w and the following instruction would need to be andlw b’11110011’ to produce the same result. The instruction comf changes all “0’s” to “1’s” and all “1’s” to “0’s.” This project uses “negative logic.” This means the input line is “active” when LOW.
If no buttons are pressed, the reading on RA2 and RA3 will be “1” and “1.” The result of comf PORTA will be: xxxx00xx where “x” is not assigned as an input bit.
The instruction: andlw b’00001100’ is called a “masking operation.” All the bits except bit 2 and 3 are masked or “removed” from the result.
When xxxx00xx is AND’ed with 00001100 the result is: aaaa00aa in the w register, where “a” is not taken into account as we have only allowed bits 2 and 3 to “enter the equation.”
If the UP button is pressed, bit 2 will be detected as “0” and the result of comf PORTA will be: xxxx01xx and the result of andlw b’00001100’ will be xxxx01xx in the w register.
swlatch is a flag file for the switches. It is initially cleared and the result of xorwf swlatch,w will be: xxxx00xx in the “w” file.
The instruction: xorwf swlatch,F does the Boolean XOR operation on xxxx00xx in the “w” file and 00000000 in the swlatch file and places the result: 00000000 in the swlatch file.
The instruction: andwf swlatch,W performs the Boolean AND operation on: 00000000 in the w file AND 00000000 in the swlatch file and places the result: 00000000 in w. The micro looks at the zero bit in the STATUS file and it the result is zero, the bit will be SET. The instruction bnz causes the micro to go the Bump if the zero bit is not set. The zero bit is not set and thus the micro does not go to Bump.

TstSw   comf    PORTA,W         ; sample active low switches
        andlw   b'00001100'     ; on RA3 and RA2 pins
        xorwf   swlatch,W       ; changes (press or release)
        xorwf   swlatch,F       ; update switch state latch
        andwf   swlatch,W       ; filter out "new release" bits
        bnz     Bump            ; branch on a "new press", else

The clever part of these 6 instructions is this: The micro will only branch on the first detection of a button being pressed.
We will now look at how the first detection is created, but it will take a lot of investigation to see how the Boolean operations perform the task:

If the UP button is pressed (RA2 - bit 2), the input data will be xxxx10xx
The result of comf PORTA will be: xxxx01xx and this will be stored in w.
andlw b’00001100’ will AND xxxx01xx with 00001100 to get 00000100 in w.
xorwf swlatch,W will XOR 00000100 in w with 00000000 in swlatch to get 00000100 in w.
xorwf swlatch,F will XOR 00000100 in w with 00000000 in swlatch to get 00000100 in swlatch file.
andwf swlatch,W will AND 00000100 in w with 00000100 in swlatch to get 00000100 in w.
bnz Bump will look at the zero flag in the STATUS file. This flag will not be set and thus the micro will jump to the sub-routine Bump.

If the button is still pressed when the micro executes: TstSw the second time, we will see what happens:
The result of comf PORTA will be: xxxx01xx and this will be stored in w.
andlw b’00001100’ will AND xxxx01xx with 00001100 to get 00000100 in w.
xorwf swlatch,W will XOR 00000100 in w with 00000100 in swlatch to get 00000000 in w.
xorwf swlatch,F will XOR 00000000 in w with 00000100 in swlatch to get 00000100 in swlatch file.
andwf swlatch,W will AND 00000000 in w with 00000100 in swlatch to get 00000000 in w.
bnz Bump will look at the zero flag in the STATUS file. This flag will be set and thus the micro will not jump to the sub-routine Bump.

If the button is released when the micro executes: TstSw we will see how the swlatch file is changed to: 00000000.
If no buttons are pressed, the reading on port A will be: xxxx11xx
The result of comf PORTA will be: xxxx00xx and this will be stored in w.
andlw b’00001100’ will AND xxxx00xx with 00001100 to get 00000000 in w.
xorwf swlatch,W will XOR 00000000 in w with 00000100 in swlatch to get 00000100 in w.
xorwf swlatch,F will XOR 00000100 in w with 00000100 in swlatch to get 00000000 in swlatch file.
andwf swlatch,W will AND 00000000 in w with 00000000 in swlatch to get 00000000 in w.
bnz Bump will look at the zero flag in the STATUS file. This flag will be set and thus the micro will not jump to the sub-routine Bump. And the swlatch file will be changed to 00000000.

The Bump sub-routine

The Bump sub-routine detects
If Up button is pressed, “w” will enter Bump sub-routine with: 00000100.
If Down button is pressed, “w” will enter Bump sub-routine with: 00001000

Bump    andlw   1<<DnSw         ; the "Dn" switch?
        skpz                    ; no, skip (WREG=0), else
        movlw   -2              ; WREG = -2 (dn)
        addlw   1               ; WREG = 1 (up) or -1 (dn)
        addwf   number,F        ; number++ or number--
        movf    number,W        ; WREG = number = -1..100
        xorlw   100             ; test upper limit
        skpnz                   ; upper limit? no, skip, else
        decf    number,F        ; reset to 99
        btfsc   number,7        ; lower limit? no, skip, else
        incf    number,F        ; reset to 00
        movf    number,W        ; WREG = number = 00..99

If the UP button is pressed, the result of the first instruction will produce w = 00000000. The zero flag will be SET and the micro will execute the instruction: addlw 1 - “One” will be added to “w”
addwf number,F will increment the number file.
movf number,W the value in the number file will be copied to “w”
xorlw 100 The value in w will be XOR’ed with 100. The XOR operation detects a match. Since each binary digit will be the same (i.e. either a 0 or 1) the result will be 0000 0000. The result will set the zero flag in the status (03) file and by testing bit 2 (the Z flag) you can skip when SET.
decf number,F The number file will be detected as 100. Decrement it to 99.
btfsc number,7 If the number file is decremented below zero, it rolls-over to 0FFh (255) and bit 7 is tested to see if it is SET.
incf number,F The number file is incremented (rolled over) from 256 to 000.
movf number,W The number file is copied to w.

The Binary to Binary Coded Decimal Routine

This routine is so complex that I am not going to explain it.

    ; isochronous bin2bcd routine

    clrf    tens
    addlw   -80             ; W = W - 80
    rlf     tens,F          ; shift in 2^3*10 bit
    btfss   tens,0          ; borrow? no, skip, else
    addlw   80              ; W = W + 80
    addlw   -40             ; W = W - 40
    rlf     tens,F          ; shift in 2^2*10 bit
    btfss   tens,0          ; borrow? no, skip, else
    addlw   40              ; W = W + 40
    addlw   -20             ; W = W - 20
    rlf     tens,F          ; shift in 2^1*10 bit
    btfss   tens,0          ; borrow? no, skip, else
    addlw   20              ; W = W + 20
    addlw   -10             ; W = W - 10, now W = "ones"
    rlf     tens,F          ; shift in 2^0*10 bit
    btfss   tens,0          ; borrow? no, skip, else
    addlw   10              ; W = W + 10, now W = "ones"
    movwf   ones            ; save "ones"
    DelayCy(8*msecs-54)     ; precise 8 msec loop timing
    goto    Main            ;

Here is a shorter Binary to Binary Coded Decimal Routine:

; Convert a binary number into two packed BCD digits
; ON ENTRY:
; w register has binary value in range 0 t o 9 9
; ON EXIT:
; output variables bcdLow and bcdHigh contain two
; BCD digits
; w contains two packed BCD digits
; Routine logic:
; The value 10 is subtracted from the source operand
; until the reminder is < 0 (carry cleared). The number
; of subtractions is the high-order BCD digit. 10 is
; then added back to the subtrahend to compensate
; for the last subtraction. The final remainder is the
; low-order BCD digit
; Variables:
; inNum storage for source operand
; bcdHigh storage for high-order nibble
; bcdLow storage for low-order nibble
; thisDig Digit counter

bin2bcd:
    movwf inNum     ; Save copy of source value
    clrf bcdHigh    ; Clear storage
    clrf bcdLow
    clrf thisDig
min10:
    movlw .10
    subwf inNum,f   ; Subtract 10
    btfsc STATUS,C  ; Did subtract overflow?
    goto sum10  ; No. Count subtraction
    goto fin10
sum10:
    incf thisDig,f  ;increment digit counter
    goto min10
            ; Store 10th digit
fin10:
    movlw .10
    addwf inNum,f   ; Adjust for last subtract
    movf thisDig,w  ; get digit counter
    movwf bcdHigh   ; Store it

            ; Calculate and store low-order BCD digit
    movf inNum,w    ; Store units value
    movwf bcdLow    ; Store digit
            ; Combine both digits
    swapf bcdHigh,w ; High nibble to HOBs
    iorwf bcdLow,w  ; ORin low nibble
    retlw 00

Here is a simpler Binary to Binary Coded Decimal Routine:

;  Binary-to-Decimal, 00..99
;
         radix   dec
btod99
         clrf    temp            ; W = 0x00..0x63, 0..99 input
         decf    temp,F          ; preset temp to -1
sub10    incf    temp,F          ; unconditionally
         addlw   -10             ; subtract 10. borrow?
         btfsc   status,0        ; no, test carry bit
     goto    sub10
         addlw   10              ; fix 'ones' in Wreg
         swapf   temp,F          ; put 'tens' in left nibble
         iorwf   temp,W          ; W = packed bcd 0x00..0x99
         retlw   00

Delay Routine

The delay sub-routine is quite complex however the following instructions form part and these will be discussed:
addlw -1 This instruction subtracts “1” from “w” (the PIC chip does not have a subtract literal from w, just a subtract w from literal.) W must have a value before entering this delay.
skpc This instruction skips the next instruction if the Carry (in the status file) (03,0) is SET. The Carry is SET when the result of an operation is more than 0ffh or less than zero. The equivalent instruction is: btfss 03,0 or btfss status,0
If w enters the delay with 0ffh, it will produce 256 loops by jumping over decfsz DelayHi,F and carrying out the instruction: goto uDelay.
When the carry is SET, the micro goes to the instruction: decfsz DelayHi,F and decrements the DelayHi file. It then performs another 256 loops.
When DelayHi file reaches zero, the micro returns. If w does not have a pre-value, only the first loop will be shorter. DelayHi file must have a value as this file determines the length of Delay routine.

uDelay  addlw   -1              ; subtract "loop" cycle time
        skpc                    ; borrow? no, skip, else
        decfsz  DelayHi,F       ; done?  yes, skip, else
        goto    uDelay          ; do another loop
        retlw   00

Conclusion

From the above coverage of the two programs you can see the enormous difference in understanding required to follow some of the Boolean operations. Since both programs occupy the same number of instructions, I do not see the need to introduce complex routines.
Things are wonderful when they are working but if something fails to produce a result as expected, you will have two levels of complexity to sort-through if you use Boolean instructions.
In addition to this, you need to comment each line, not only with details of what the instruction is doing, but WHY the instruction has been chosen.
This is very important so that you can come back months later and understand how and why a routine works and how it can be altered and adjusted.

Adding ALARM

The program can be modified to produce an alarm when the count reaches “00.”
The files for this are:

An active piezo buzzer is added to portA,6 (pin 15) as shown in the following diagram:


Alarm buzzer sounds when 00 on Displays

Going Further

This program/project can be modified to carry out all sorts of tasks. One constructor wanted a Quiz Score with 3 single displays, three contestant switches and a reset button. The adjudicator could increase the individual score or decrease the score according to the answer. This project could be adapted to this application.
Another reader wanted to count from 35 to 80 and cycle around these two values. Again, this can be done by simply changing a few lines of code.
You can add a relay, buzzer, set of LEDs, globes and actuators to the project to create a valuable piece of industrial equipment.

2Digit withBuzzIso
2 digit up/down counter with buzzer

Fast Increment

Another feature that can be added to the counter is fast-forward. Although it is not needed for this project, it is a sub-routine that can be added to other designs.
The way it works is this.
Each time the micro executes the FastUp sub-routine, a file is incremented. When the file reaches 100, the display is incremented at a rate of 5Hz.
The delay for the multiplexing routine is 10mS per display. This makes a total of 20mS. The counter detects 100 so the time before fast-increment occurs = 20mS x 100 = 2,000mS = 2 seconds.
FastUp sub-routine also needs a flag to detect when the fast increment is occurring. This is bit 7 of Sw_Flag. After the 2 second timing has occurred, the program increments the FastCount file for 5 loops to produce the fast increment function.
Here are the files:

If you use any of the following .asm files and find they fail to produce a .hex file, make sure you have the .inc file in the same folder as the .asm.

To add RESET to the up/down counter, add the following instructions:

SetUp   bsf status,rp0
    movlw   b'00011100' ;Make RA0,1 out   RA2,3,4 in
        Put reset on RA4   pin3
put 22k to positive and the switch between pin3 and 0v.
When sw is pressed the input will go low.

in main, the least few lines will be

 bcf    Sw_Flag,2   ;button not pressed. Clear Up flag
    bcf Sw_Flag,7   ;Clear Up repeat flag
    clrf    FastCount
    btfss   portA,4     ;test reset
    goto    SetUp
    goto    Main

    END
;****************************************************************
;*     Started 18/6/2009
;2 Digit UP / Down Counter with FastCount after 2secs
;Port B drives 7 segment display
;Up sw on RA2   Down on RA3
;Units drive on RA0   Tens drive on RA1             *
;*                              *
;****************************************************************

    list P = 16F628 ;microcontroller
    include ;registers for F628


    __Config    _cp_off & _lvp_off & _pwrte_on
        & _wdt_off & _intRC_osc_noclkout & _mclre_off

;code protection - off
;low-voltage programming - off
;power-up timer -  on
;watchdog timer - off
;use internal RC for 4MHz - all pins for in-out


;****************************************************************
; variables - names and files
;****************************************************************


        ;Files for F628 start at 20h

temp1       equ 20h ;for delay
temp2       equ 21h ;for delay
SwUp        equ 22h ;
SwDwn       equ 23h ;
units       equ 24h ;
tens        equ 25h ;
Sw_Flag     equ 26h ;
FastCount   equ 27h ;

;****************************************************************
;Equates
;****************************************************************
status      equ 0x03
cmcon       equ 0x1F
rp1     equ 0x06
rp0     equ 0x05
portA       equ 0x05
portB       equ 0x06

;****************************************************************
;Beginning of program
;****************************************************************
reset   org 00  ;reset vector address
    goto    SetUp   ;goto SetUp


table   addwf   PCL,F           ;02h,1  add W to program counter
        retlw   b'00111111'     ; "0"   -|F|E|D|C|B|A
        retlw   b'00000110'     ; "1"   -|-|-|-|C|B|-
        retlw   b'01011011'     ; "2"   G|-|E|D|-|B|A
        retlw   b'01001111'     ; "3"   G|-|-|D|C|B|A
        retlw   b'01100110'     ; "4"   G|F|-|-|C|B|-
        retlw   b'01101101'     ; "5"   G|F|-|D|C|-|A
        retlw   b'01111101'     ; "6"   G|F|E|D|C|-|A
        retlw   b'00000111'     ; "7"   -|-|-|-|C|B|A
        retlw   b'01111111'     ; "8"   G|F|E|D|C|B|A
        retlw   b'01101111'     ; "9"   G|F|-|D|C|B|A


;****************************************************************
;* port A and B initialisation                  *
;****************************************************************

SetUp   bsf     status,rp0
        movlw   b'00001100'   ;Make RA0,1 out   RA2,3 in
        movwf   05h           ;trisA
        clrf    06h           ;trisB Make all RB output
        bcf     status,rp0    ;select programming area - bank0
        movlw   b'10000000'   ;Turn off T0CKI, prescale for TMR0=1
        movwf   option_reg
        clrf    portB         ;Clear Port B of junk
        clrf    units         ;zero the units file
        clrf    tens          ;zero the tens file
        clrf    Sw_Flag
        movlw   07h           ;turn comparators off and enable
        movwf   cmcon         ;    pins for I/O functions
        goto    Main


;****************************************************************
;* Delay 10mS       10 x 1,000uS            *
;****************************************************************

D_10mS  movlw   0Ah
        movwf   temp2
D_a     nop
        decfsz  temp1,f
        goto    D_a
        decfsz  temp2,f
        goto    D_a
        retlw   00


FastUp  btfss   Sw_Flag,2   ;First time through loop?
        goto    FU_2        ;yes
        btfsc   Sw_Flag,7   ;Has 5Hz bit been set?
        goto    FU_3
FU_1    incfsz  FastCount,f ;Increment FastCount
        movlw   d'100'
        xorwf   FastCount,w
        btfss   status,2    ;reached 100 loops?
        retlw   00
        clrf    FastCount
        bsf     Sw_Flag,7   ;set bit for 5Hz incrementing

FU_2    bsf     Sw_Flag,2  ;Up button has been pressed
        incf    units,f
        movlw   0Ah    ;put 10 into w
        xorwf   units,w   ;compare units file with 10
        btfss   status3,2 ;zero flag in status file. Set if units is 10
        retlw   00
        clrf    units
        incf    tens,f
        movlw   0Ah ;put 10 into w
        xorwf   tens,w  ;compare units file with 10
        btfsc   status,2 ;zero flag in status file. Set if tens is 10
        clrf    tens
        retlw   00  ;display passes 99 but not below 0


FU_3    incfsz  FastCount,f ;Increment FastCount
        movlw   d'5'
        xorwf   FastCount,w
        btfss   status,2    ;reached 5 loops?
        retlw   00
        clrf    FastCount
        goto    FU_2

Dwn     btfsc   Sw_Flag,3
        retlw   00
        bsf Sw_Flag,3
        decf    units,f
        movlw   0FFh    ;put FFh into w
        xorwf   units,w ;compare units file with FFh
        btfss   status,2 ;zero flag in status file. Set if units is 10
        retlw   00
        movlw   09
        movwf   units   ;put 9 into units file
        decf    tens,f
        movlw   0FFh    ;put 0FFh into w
        xorwf   tens,w  ;compare tens file with 0FFh
        btfsc   status,2 ;zero flag in status file. Set if tens is 0FFh
        goto    $+2 ;tens file is 0FFh
        retlw   00
        clrf    tens
        clrf    units
        retlw   00  ;display  not below 0

;****************************************************************
;* Main                             *
;****************************************************************

Main    btfss   portA,2     ;test switch-press for UP
        call    FastUp      ;UP switch pressed
        btfss   portA,3     ;test switch-press for Down
        call    Dwn         ;Down switch pressed
        movlw   b'00000001' ;Make RA0 HIGH for units drive
        movwf   portA
        movf    units,w     ;copy unit value into w
        call    table       ;unit display value will return in w
        movwf   portB       ;output units value
        call    D_10mS      ;call delay
        clrf    portB       ;clear display
        movlw   b'00000010' ;Make RA1 HIGH for tens drive
        movwf   portA
        movf    tens,w      ;copy tens value into w
        call    table       ;tens display value will return in w
        movwf   portB       ;output tens value
        call    D_10mS      ;call delay
        clrf    portB       ;clear display
        btfsc   portA,3     ;bit will be zero when sw is pressed
        bcf     Sw_Flag,3   ;button not pressed. Clear down flag
        btfss   portA,2     ;bit will be zero when sw is pressed
        goto    Main
        bcf     Sw_Flag,2   ;button not pressed. Clear Up flag
        bcf     Sw_Flag,7   ;Clear Up repeat flag
        clrf    FastCount
        goto    Main

        END

Secret Functions

This project can be extended to have “Secret Functions” that are accessed when the project is turned on.
By pressing the “Up” button and turning the project on, the project will turn into a “DICE PROJECT” and show a random number from 1 to 6 when the Up button is pressed.
When the “Down” button is pressed and the project is turned on, it will randomly show a number from 00 to 99 when the Up button is pressed.

Firstly we will describe the instructions that look at the buttons when the project is turned on.
The following instructions are added to the SetUp routine:

SetUp   . . . . . .
        . . . . . .
        goto    AAA     ;Button A detected for DICE (detects LOW)
        btfsc   portA,3
        goto    Main
        clrf    Count
        goto    Random  ;Button B detected

These programs are covered on page 2:


Colin Mitchell

Colin Mitchell

Expertise

electronics
writing
PIC-Chips

Social Media

instagramtwitterwebsite

Related Posts

TODO
Transistor Test
© 2021, All Rights Reserved.

Quick Links

Advertise with usAbout UsContact Us

Social Media