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 tens, named LZ1-LZ3 and 9 LEDs for the units, named LU1-LU9, for a maximum count of 39.
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
TIMSK |= (1<<OCIE1A); //enable interrupt for ISR
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, tens = 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
tens = 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(tens & (1<<0)){
PORTD |= (1<<LZ1); //only LZ1 on for 1 tenth
}
if(tens & (1<<2)){
PORTD |= (1<<LZ1) | (1<<LZ2); //LZ1 and LZ2 for 2 tenths
}
if(tens > 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
}