Counting the number of button presses in ATMEGA16A

by CyberFox   Last Updated September 11, 2019 18:25 PM

I am looking forward to counting the number of presses of a button in a 2 seconds interval in ATMEGA16A. I have prepared some code below. Is there another way around? My first solution was to perform polling inside the while() loop, but I think that would be imprecise.

Problem description: Two buttons, START and PV are given and two internal states: IDLE and COUNTING. Once START is pressed the state changes to COUNTING and the timer starts to count up. During this time, the number of PV presses is recorded by incrementing a variable 'na'. One the timer reaches 2 seconds, no further presses are recorded. The output is represented by 12-LEDs, 3 of them for the tenths, named LZ1-LZ3 and 9 LEDs for the units, named LU1-LU9.

My second solution used interrupts for the 2 buttons and the timer overflow. Initially, the clock for the timer was set to 0, so the timer won't start. I would only enable an interrupt for the START(INT0) button. It would enable the interrupt for the PV (INT1). When a low signal is detected on START (button pressed), it would change the state to COUNTING and it would enable interrupts for PV. When PV is pressed, na is incremented. When the timer overflows (i.e. it reaches 2 seconds), the interrupts for PV would be disabled, the timer would be stopped and reset and the program would go into state IDLE to output the last 'na'.

I used timer 1 in CTC mode with N = 46875 and a clock of 256.

Now, for some code:

#include <avr/io.h>
#include <avr/interrupt.h>

#define N 46875                         //TOP value for the timer 1

#define IDLE 0                          //in this state we print the last number of presses
#define COUNTING 1                      //we do nothing here, only used as intermediary state

#define LZ1 PD4
#define LZ2 PD5
#define LZ3 PD6
#define LU9 PD7

volatile unsigned char na = 0;          //number of presses shared between main and ISRs
volatile unsigned char state = IDLE;    //initial state shared between main and ISRs


/************************************************************************/
/*
PA0-PA9: LEDs which display units 0-8
PD0-1: unused
PD1-2: START button(INT0) and PV button(INT1)
PD4-6: tenths LEDs
PD7: LED for unit 9
ex: 35 presses of PV
LU - LED for units
LZ - LED for tenths
PD4 PD5 PD6             PA 0 1 2 3 4 5 6 7  PD7
LZ1 LZ2 LZ3 (PD4-6)     LU 1 2 3 4 5 6 7 8   9
1   1   1                  0 0 0 0 1 1 1 1   1
*/
/************************************************************************/
int main(void)
{
    //setup ports
    DDRA = 0xff;            //whole PORTA as output
    DDRD = 0b111100--;      //buttons PS and PV on PD2 and PD3

    //setup timer 1
    TCCR1B = 0b---10000;    //CTC mode 4 and CLOCk = '0' -> timer 1 not counting yet
    TCCR1A = 0b------00;    //CTC mode 4
    OCR1A = N - 1;          //setup TOP for CTC mode

    //setup interrupts
    GIFR |= (1<<INT1);      //enable interrupts from INT1 (PV)
    MCUCR = 0b----0000;     //INT 0 and INT1 are active on low
    sei();                  //enable global interrupts

    //declarations
    unsigned char lut_units[] = {
        0b00000000, // 0
        0b00000001, // 1
        0b00000011, // 2
        0b00000111, // 3
        0b00001111, // 4
        0b00011111, // 5
        0b00111111, // 6
        0b01111111, // 7
        0b1111111   // 8
    };//lookup table used to store the LED configurations for units 0-9

    unsigned char units = 0, tenths = 0;    //used as temporary variables

    while (1)
    {
        switch(state):{
            //used to print out the number of presses
            case IDLE:{
                units = na % 10;            //extract the units from the number of presses ex. 23 -> 3
                tenths = na / 10;           //extract the tenths from the number of presses ex. 23 -> 2

                //write the units to the LEDS
                PORTA = lut_units[units];   //write the appropriate lookup table value to PORTD
                if(units < 9){
                    PORTD &= ~(1<<LU9);     //the LEd for 9 units is placed on PD7, turn it OFF
                    }else{
                    PORTD |= (1<<LU9);      //the LEd for 9 units is placed on PD7, turn it ON
                }

                //write the tenths to the 3 LEDs on PORTD
                if(tenths & (1<<0)){
                    PORTD |= (1<<LZ1);                              //only LZ1 on for 1 tenth
                }

                if(tenths & (1<<2)){
                    PORTD |= (1<<LZ1) | (1<<LZ2);                   //LZ1 and LZ2 for 2 tenths
                }

                if(tenths > 2){
                    PORTD |= (1<<LZ1) | (1<<LZ2) | (1<<LZ3);        //LZ1, LZ2 and LZ3 for more tenths
                }
            }

            case COUNTING:{
                //do nothing, the interrupts will handle everything
            }
        }
    }
}

/************************************************************************/
/*
ISR for the START button on PD2 (INT0). Once this is pressed the
program switches its state to counting
*/
/************************************************************************/
ISR(INT0_vect)
{
    state = COUNTING;                               //switch state to counting
    GICR = (1<<INT1);                               //allow interrupts from PV
    TCCR1B |= (1<<CS12);                            //set the clock to 256
    na = 0;
}

/************************************************************************/
/*
ISR for the PV button which is pressed and counted.
*/
/************************************************************************/
ISR(INT1_vect)
{
    na++;   //simply increment the number of presses
}


/************************************************************************/
/*
ISR for the compare match with OCR1A. When this ISR is triggered it
means 2 seconds have passed and that the program should no longer
count any presses from PV. The timer should be reset and put in
stand-by.
*/
/************************************************************************/
ISR(TIMER1_COMPA_vect)
{
    GICR &= ~(1<<INT1);     //disable interrupts coming from PV
    TCCR1B &= 0b11111000;   //set the clock to '0'
    TCNT1 = 0;              //reset the timer 1 state to BOTTOM
    state = IDLE;           //switch the state to IDLE
}
Tags : atmega


Related Questions


Updated April 17, 2015 20:10 PM

Updated June 08, 2015 02:10 AM

Updated July 09, 2016 08:10 AM

Updated April 25, 2015 20:10 PM

Updated September 02, 2016 08:10 AM