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