Vertical Counters with a PIC

A vertical counter is a counter whose bits reside in separate bytes. For example, a 3-bit vertical counter's most significant bit resides in one byte of RAM its middle bit in another byte and its least significant bit in a third byte of RAM. Boolean logic is used to increment or decrement the counter. The obvious question is why in the world would anyone want to do this when the PIC already can increment or decrement a byte? Well, suppose you needed to maintain eight separate 3-bit counters. This is a total of 3*8 = 24 bits = 3 bytes. How would you arrange the bits so that they could be easily incremented and decremented? You certainly could waste one bit per counter and place each in a nibble. But, not only do you waste a byte of RAM with this approach, the software to increment (or decrement) and check for carries is inefficient. With the vertical counter approach, the bits are efficiently stored in memory AND all eight counters are operated on in parallel.

A simple example of a vertical counter can be found in the pic_debounce routine. Here eight 2-bit counters are used to count the number of samples that have occurred since the last switch transition was detected. It's a decrementing counter that terminates when zero is reached. The counting sequence is 3,2,1,0,0,0,....

The way to implement this counter is to imagine it is a state machine with four states. It is initialized to state 3. The transition to the next state is accomplished with a CALL (or if you wish, turn the routine into a macro) to the routine that decrements the clock. The state diagram is:

PRESENT STATE     |    NEXT STATE
      A B         |       A+ B+
------------------+-----------------
      1 1         |       1  0
      1 0         |       0  1
      0 1         |       0  0
      0 0         |       0  0

The transition equations can be read off of the state diagram:

A+ = A & B

B+ = A & ~B

Aside 1: The notation I use here is: A+ means the "next state" of A, "&" means logical and, "|" means logical or, "~" means logical negation, "^" means exclusive or. Those of you familiar with C will recognize this usage for the boolean operators. Those of you who have taken logic design most probably are used to something different.

Aside 2: It's really beyond the scope of this document to discuss all of the details of logic design, but I feel compelled to briefly mention how one obtains the next state equations A+ and B+. For example, to determine the A+ equation look at the column of 1's and 0's for A+; you'll see 1,0,0,0. In other words, the first row is a 1 and the rest are zeroes. This row corresponds to the state when A and B are both 1. Thus A+ is true for the logical condition A & B but false otherwise. Similarly, B+ is true for the condition when A is true and B is false. It is imperative to note that the next states A+ and B+ are not the same thing as A and B until AFTER the state machine has made the complete transition. If you were to implement this in C for example, you would write:

temp_A = A & B;
B = A & ~B;         //O.k. to assign to B
A = temp_A;

Here are several examples of vertical counters along with some software illustrating their use. In each case, the counters start at zero. The cyclic counters pass through the "all-ones" state and then roll over to zero again. The terminating counters stay in the all ones state until the counter is reinitialized. Many of the counters skip states. For example, the cyclic counter with 5 states counts: 0 1 2 4 7 0 1 2 ... Thus states 3,5, and 6 are skipped.

Cyclic counters:

counter_4c

counter_5c

counter_6c

counter_7c

counter_8c

Terminating Counters:

counter_4t

counter_5t

counter_6t

counter_7t

        list    p=16C64,t=ON,c=132,n=80,st=off
        radix   dec


        include "P16C64.INC"

  cblock  0x20
        SA,SB,SC

        temp
  endc



        ORG     0               ;Reset Vector

        GOTO    Main

        ORG     4               ;Interrupt Vector


Main

        BCF     STATUS,RP0      ;Point to BANK 0



        CLRF    SA
        CLRF    SB
        CLRF    SC
        MOVLW   8
        MOVWF   temp
t1      NOP
        CALL    counter_8c
        NOP
        DECFSZ  temp,F
          goto  t1


        CLRF    SA
        CLRF    SB
        CLRF    SC
        MOVLW   8
        MOVWF   temp
t2      NOP
        CALL    counter_5t
        NOP
        DECFSZ  temp,F
          goto  t2

stay    goto    stay

;------------------------------------------------------------
;counter_4c   2-bit cyclic vertical counter - 4 counts long
;
; Counting sequence: 0,1,2,3,0,1,...
;
;State Table:     Karnaugh Maps:
;pres  next      B
; SS  SS         0   1  
; AB  AB       +---+---+    +---+---+
;--------   A 0|   | 1 |    | 1 |   |
; 00  01       +---+---+    +---+---+
; 01  10      1| 1 |   |    | 1 |   |
; 10  11       +---+---+    +---+---+
; 11  00      A+ = A ^ B     B+ = ~B
;
;Program memory: 4 words
;           RAM: 2 Bytes
;Execution time: 5 cycles

counter_4c
        MOVF    SB,W    ;W = B
        XORWF   SA,F    ;A+ = A ^ B
        COMF    SB,F    ;B+ = ~B

        RETURN
;------------------------------------------------------------
;counter_4t   2-bit terminating vertical counter - 4 counts long
;
; Counting sequence: 0,1,2,3,3,3,...
;
;State Table:     Karnaugh Maps:
;pres  next      B
; SS  SS         0   1  
; AB  AB       +---+---+    +---+---+
;--------   A 0|   | 1 |    | 1 |   |
; 00  01       +---+---+    +---+---+
; 01  10      1| 1 | 1 |    | 1 | 1 |
; 10  11       +---+---+    +---+---+
; 11  11      A+ = A | B   B+ = A | ~B
;
;Program memory: 6 words
;           RAM: 2 Bytes
;Execution time: 7 cycles

counter_4t
        MOVF    SA,W    ;W = A
        COMF    SB,F    ;B = ~B
        IORWF   SB,F    ;B+ = A | ~B
        COMF    SB,W    ;W = ~B+ = ~A & B
        XORWF   SA,F    ;A+ = A ^ (~A&B) = A&(A|~B) | ~A&(~A&B)
                        ;   = A & (1 | ~B) | ~A&B = A | ~A&B = A | B.
                        ;(Note, IORWF SA,F would also work for the
                        ; last instruction.)
        RETURN
;------------------------------------------------------------
;counter_5c   3-bit cyclic vertical counter - 5 counts long
;
;  Counting sequence: 0 1 2 4 7 0 ...
;
;State Table:     Karnaugh Maps: (The X's are the "don't care" states)
;pres  next       BC
; SSS  SSS        00  01  11  10
; ABC  ABC      +---+---+---+---+    +---+---+---+---+
;----------  A 0|   |   | X | 1 |    |   | 1 | X |   |
; 000  001      +---+---+---+---+    +---+---+---+---+
; 001  010     1| 1 | X |   | X |    | 1 | X |   | X |
; 010  011      +---+---+---+---+    +---+---+---+---+
; 100  111      A+ = A ^ B           B+ = A ^ C
; 111  000
;               +---+---+---+---+
;               | 1 |   | X |   |
;               +---+---+---+---+
;               | 1 | X |   | X |
;               +---+---+---+---+
;               C+ = ~(B | C)
;
;Program memory: 8 words
;           RAM: 3 Bytes
;Execution time: 9 cycles

counter_5c
        MOVF    SC,W
        XORWF   SA,W
        XORWF   SB,W
        XORWF   SB,F
        XORWF   SB,W
        XORWF   SA,F
        IORWF   SC,F
        COMF    SC,F
        RETURN
        
;------------------------------------------------------------
;counter_5t   3-bit terminating vertical counter - 5 counts long
;
;  Counting sequence: 0 1 2 4 7 7 7 7 ...
;
;State Table:     Karnaugh Maps: (The X's are the "don't care" states)
;pres  next       BC
; SSS  SSS        00  01  11  10
; ABC  ABC      +---+---+---+---+    +---+---+---+---+
;----------  A 0|   |   | X | 1 |    |   | 1 | X |   |
; 000  001      +---+---+---+---+    +---+---+---+---+
; 001  010     1| 1 | X | 1 | X |    | 1 | X | 1 | X |
; 010  011      +---+---+---+---+    +---+---+---+---+
; 100  111      A+ = A | B           B+ = A | C
; 111  000
;               +---+---+---+---+
;               | 1 |   | X |   |
;               +---+---+---+---+
;               | 1 | X | 1 | X |
;               +---+---+---+---+
;               C+ = ~B ^ C = B ^ ~C  (i.e. Exclusive NOR)
;
; There are a few tricks you can use to greatly simplify the
;equations. For example, if you exclusive or each of the final states
;with the other final states (i.e. find A+^B+, A+^C+ and B+^C+),
;you'll discover a few useful relations. For example, you'll
;find that A+ ^ B+ == B ^ C. This means that once you know A+
;then you can easily find B+ = A+ ^ (B ^ C) = (A | B) ^ B ^ C.
;If you don't believe this, then look at the comments in the
;program below where boolean algebra is used to show the
;same thing.
;
;Program memory: 8 words
;           RAM: 3 Bytes
;Execution time: 9 cycles

counter_5t
        MOVF    SB,W    ;W = B
        IORWF   SA,F    ;A+ = A | B
        XORWF   SC,W    ;W = B ^ C
        MOVWF   SC      ;C+ = B ^ C (temporarily)
        XORWF   SA,W    ;W = (A | B) ^ B ^ C
                        ;  = (A | B) & (~B^C)  |  (~A & ~B) & (B^C)
                        ;  = A & (~B^C) | B&C  | ~A&~B&C
                        ;  = A&~B&~C | A&B&C | B&~C | ~A&~B&C
                        ;  = A&~C | A&B | ~A&~B&C 
        MOVWF   SB      ;  = B+
        COMF    SC,F    ; C+ = ~(B^C) = ~B ^ C
        RETURN
;------------------------------------------------------------
;counter_6c   3-bit cyclic vertical counter - 6 counts long
;
;  Counting sequence: 0 1 2 3 4 7 0 ...
;
;State Table:     Karnaugh Maps: (The X's are the "don't care" states)
;pres  next       BC
; SSS  SSS        00  01  11  10
; ABC  ABC      +---+---+---+---+    +---+---+---+---+
;----------  A 0|   |   | 1 |   |    |   | 1 |   | 1 |
; 000  001      +---+---+---+---+    +---+---+---+---+
; 001  010     1| 1 | X |   | X |    | 1 | X |   | X |
; 010  011      +---+---+---+---+    +---+---+---+---+
; 011  100      A+ = A ^ (B&C)          B+ = (B^C) | A&~C
; 100  111
; 111  000
;               +---+---+---+---+
;               | 1 |   |   | 1 |
;               +---+---+---+---+
;               | 1 | X |   | X |
;               +---+---+---+---+
;               C+ = ~C
;
;Program memory: 10 words
;           RAM: 3 Bytes
;Execution time: 11 cycles

counter_6c
        MOVF    SC,W    ;W = C
        XORWF   SB,F    ;B = B^C
        XORWF   SB,W    ;W = (B^C) ^ C = B
        ANDWF   SC,W    ;W = B & C
        XORWF   SA,F    ;A+ = B&C ^ A
        COMF    SC,F    ;C+ = ~C
        MOVF    SC,W    ;W = ~C
        ANDWF   SA,W    ;W = ~C & (B&C ^ A) = A & ~C
        IORWF   SB,F    ;B+ = B^C + A&~C

        RETURN

;------------------------------------------------------------
;counter_6t   3-bit terminating vertical counter - 6 counts long
;
;  Counting sequence: 0 1 2 3 4 7 7 ...
;
;State Table:     Karnaugh Maps: (The X's are the "don't care" states)
;pres  next       BC
; SSS  SSS        00  01  11  10
; ABC  ABC      +---+---+---+---+    +---+---+---+---+
;----------  A 0|   |   | 1 |   |    |   | 1 |   | 1 |
; 000  001      +---+---+---+---+    +---+---+---+---+
; 001  010     1| 1 | X | 1 | X |    | 1 | X | 1 | X |
; 010  011      +---+---+---+---+    +---+---+---+---+
; 011  100      A+ = A | (B&C)          B+ = (B^C) | A
; 100  111
; 111  111
;               +---+---+---+---+
;               | 1 |   |   | 1 |
;               +---+---+---+---+
;               | 1 | X | 1 | X |
;               +---+---+---+---+
;               C+ = ~C | A
;
;Program memory: 11 words
;           RAM: 3 Bytes
;Execution time: 12 cycles

counter_6t
        MOVF    SC,W    ;W = C
        XORWF   SB,F    ;B = B^C
        MOVF    SA,W    ;W = A
        IORWF   SB,F    ;B+ = B^C | A
        COMF    SC,F    ;C = ~C
        IORWF   SC,F    ;C+ = ~C | A
        MOVF    SC,W    ;W = ~C | A
        IORWF   SB,W    ;W = ~C | A | B^C = ~(B&C)
        XORLW   0xff    ;W = B&C
        IORWF   SA,F    ;A+ = A | B&C

        RETURN

;------------------------------------------------------------
;counter_7c   3-bit cyclic vertical counter - 7 counts long
;
;  Counting sequence: 0 1 2 3 4 5 7 0 ...
;
;State Table:     Karnaugh Maps: (The X's are the "don't care" states)
;pres  next       BC
; SSS  SSS        00  01  11  10
; ABC  ABC      +---+---+---+---+    +---+---+---+---+
;----------  A 0|   |   | 1 |   |    |   | 1 |   | 1 |
; 000  001      +---+---+---+---+    +---+---+---+---+
; 001  010     1| 1 | 1 |   | X |    |   | 1 |   | X |
; 010  011      +---+---+---+---+    +---+---+---+---+
; 011  100      A+ = A ^ (B&C)          B+ = B^C
; 100  101
; 101  111
; 111  000      +---+---+---+---+
;               | 1 |   |   | 1 |
;               +---+---+---+---+
;               | 1 | 1 |   | X |
;               +---+---+---+---+
;               C+ = ~C | (A&~B)
;
;Program memory: 10 words
;           RAM: 3 Bytes
;Execution time: 11 cycles

counter_7c
        MOVF    SC,W    ;W = C
        XORWF   SB,F    ;B+ = B ^ C
        XORWF   SB,W    ;W = C ^ (B^C) = B
        ANDWF   SC,W    ;W = C & B
        XORWF   SA,F    ;A+ = A ^ (B&C)
        MOVF    SA,W    ;W = A+
        ANDWF   SB,W    ;W = A+ & B+ = A&(B^C)
        COMF    SC,F    ;C = ~C
        IORWF   SC,F    ;C+ = ~C | A&(B^C) = ~C | A&~B

        RETURN

;------------------------------------------------------------
;counter_7t   3-bit terminating vertical counter - 7 counts long
;
;  Counting sequence: 0 1 2 3 4 7 7 ...
;
;State Table:     Karnaugh Maps: (The X's are the "don't care" states)
;pres  next       BC
; SSS  SSS        00  01  11  10
; ABC  ABC      +---+---+---+---+    +---+---+---+---+
;----------  A 0|   |   | 1 |   |    |   | 1 |   | 1 |
; 000  001      +---+---+---+---+    +---+---+---+---+
; 001  010     1| 1 | 1 | 1 | X |    |   | 1 | 1 | X |
; 010  011      +---+---+---+---+    +---+---+---+---+
; 011  100      A+ = A | (B&C)          B+ = (B^C) | A&C
; 100  101
; 101  111
; 111  111      +---+---+---+---+
;               | 1 |   |   | 1 |
;               +---+---+---+---+
;               | 1 | 1 | 1 | X |
;               +---+---+---+---+
;               C+ = ~C | A
;
;Program memory: 12 words
;           RAM: 3 Bytes
;Execution time: 13 cycles

counter_7t
        MOVF    SC,W    ;W = C
        XORWF   SB,F    ;B = B^C
        ANDWF   SA,W    ;W = A&C
        IORWF   SB,F    ;B+ = A&C | B^C
        COMF    SC,F    ;C = ~C
        MOVF    SA,W    ;W = A
        IORWF   SC,F    ;C+ = ~C | A
        MOVF    SC,W    ;W = C+
        IORWF   SB,W    ;W = C+ | B+ = A | ~B | ~C
        XORLW   0xff    ;W = ~A&B&C
        IORWF   SA,F    ;A+ = A | ~A&B&C = A | B&C

        RETURN
;------------------------------------------------------------
;counter_8c   3-bit cyclic vertical counter
;
;  Counting sequence: 0 1 2 3 4 5 6 7 0 ...
;
;State Table:     Karnaugh Maps:
;pres  next
; SSS  SSS        00  01  11  10
; ABC  ABC      +---+---+---+---+    +---+---+---+---+
;----------    0|   |   | 1 |   |    |   | 1 |   | 1 |
; 000  001      +---+---+---+---+    +---+---+---+---+
; 001  010     1| 1 | 1 |   | 1 |    |   | 1 |   | 1 |
; 010  011      +---+---+---+---+    +---+---+---+---+
; 011  100      A+ = A ^ (B&C)       B+ = B^C
; 100  101
; 101  110      +---+---+---+---+
; 110  111      | 1 |   |   | 1 |
; 111  000      +---+---+---+---+
;               | 1 |   |   | 1 |
;               +---+---+---+---+
;               C+ = ~C
;
;Program memory: 7 words
;           RAM: 3 Bytes
;Execution time: 8 cycles

counter_8c
        MOVF    SB,W   	;W = B
        ANDWF   SC,W    ;W = B&C
        XORWF   SA,F    ;A+ = A ^ (B&C)
        MOVF    SC,W    ;W = C
        XORWF   SB,F    ;B+ = B ^ C
        COMF    SC,F    ;C+ = ~C
        RETURN

        


        END

And here's more software.

BACK HOME


This page is maintained by Scott Dattalo. You can reach me at: scott@dattalo.com.
Last modified on 13DEC99.