Tuesday, March 29, 2011

Binary Counters

Binary counters can be used for a variety of things from time keeping to generating/measuring frequencies. Today, we will talk about the concepts behind binary counters using an atmega328 for practice.

Logic!
Let's start with a simple 4-bit counter based on JK flip-flops. A JK flip-flop has the following truth table:

J K Qt+1 State
0 0 Qt No change
0 1 0 Clear
1 0 1 Set
1 1 Qt` Complement

Where Qt is the current output Qt+1 is the output after the next clock edge.

The idea behind it is quite simple, the output of each flip-flop is feed to one input of an AND gate (the other input is the enable signal) so at the next raising-edge of the clock JKn is complemented only if all the least significant JKs are set/high and this is basically how you count in binary.

The following is a snapshot of the counter, I paused the simulation at the count of 3, at the next rising-edge of the clock the first three flip-flops are complemented output of the counter becomes 100b.
The last AND gate is the carry bit it can be used to extend the counter or even as an interrupt, note that the JK flip-flop inputs are connected  this is practically equivalent to a T flip-flop, so it's also possible to use a T flip-flop for the same counter.

The next counter is slightly more complex. It's a 4-bit counter with parallel load and synchronous clear:

It's basically the same counter with the addition of a couple of subcircuits, we could see how this counter works using boolean algebra to evaluate J and K and comparing the results with the JK truth table:

J  = ICL + CLE
K = C + ICL + CLE

Enable Load Clear
Input Output
0 0 0  J=0, K=0 No change
1 0 0  J=1, K=1 Complement
1 0 1  J=0, K=1 Clear
1 1 0 J=I,  K=I Load


AVR Timer/Counter
The atmega328 has 2 8-bit timers and 1 16-bit timer.  We will use Timer/Counter2 to generate a 1Hz square wave. That is, the event of the line going from low to high and then low again occurs once per second, so we need to switch the line state every 500ms.

First, set the clock source (prescalar) in  TCCR2. Setting the prescalar to 0 disables the timer, 1 means no prescaling, any other value will divide the system clock into smaller frequencies, e.g. assuming a clock frequency of 16Mhz and a 1:1024 prescalar the clock frequency is
f  = 16Mhz / 1024 = 15.6Khz
And the period is
T = 1/15.6Khz = 64us
Next we need to set the Output Compare Register (OCR2). The value contained in this register is continuously compared with the counter value, when the counter matches this value it will trigger an interrupt. If we load OCR2 with 250 the counter will will match this value every 16ms
250 * 64us = 16.0ms

Finally, we need to set the compare match interrupt enable flag in TIMSK. Given the clock frequency, 31 interrupts, roughly speaking, are needed for 500ms to pass. The following example demonstrates using TIMER2:
ISR(TIMER2_COMPA_vect) 
{
    if (ticks++ == 31) { 
      ticks = 0;
      toggle_pin(OUT_PIN);      
    }  
}

void main()
{   
    /**
      * f = 16Mhz / 1024 = 15.6Khz
      * p = 1/f
      * p = 1/15.6Khz = 64us
      * interrupt every = 250 * 64us = 16.0ms
      * round(500 / 16) = 31 interrupts
      **/
    TCCR2B = 0;            /*choose timer clock source, 0 = timer disabled*/
    TCCR2A = ((1<<WGM21)|~(1<<WGM20));/*CTC mode WGM22:0 = 2*/
    TCNT2  = 0;            /*clear timer*/
    OCR2A  = 250;          /*load output compare register with 255*/
    TIFR2  |= (1<<OCF2A);  /*Clear compare match flag*/    
    TIMSK2 |= (1<<OCIE2A); /*enable compare match interrupt*/
    TCCR2B = ((1<<CS22)|(1<<CS21)|(1<<CS20));/*1:1024 prescalar*/
    while(1);
}
Using a logic analyzer to sample the signal, we should see something like the following snapshot:



References
atmega328 datasheet 
Computer System Architecture-Morris Mano 

No comments:

Post a Comment