Here is an example of how to use the input capture with interrupt with ATmega32/ATmega32A microcontroller. Input capture is a special feature of AVR microcontroller which can be used to measure period or frequency of a signal, can also be used to count external events, record time stamps etc. Here we will turn on a LED for a second when the external signal is detected on the input capture pin. The input capture pin on ATmega32A is port D pin number 6(PD6) also labelled ICP1. There is only one input capture pin on ATmega32A and it is associated with Timer 1.
There are two methods in which we can monitor the external signal and take action once signal is detected- using poll method and using hardware interrupt. Here we are showing the input capture with hardware interrupt method. For polling method see the previous tutorial ATmega32A Input Capture Example.
For this tutorial the simple hardware setup is shown below.
Here push button is connected to the input capture pin(ICP1) port D pin 6(PD6). The ATmega32 microcontroller is configured to detect external signal at rising edge with interrupt enabled. So when the push button is pressed and released, the microcontroller input capture detects it and interrupt is turned on. When the interrupt happens an ISR(Interrupt Service Routine) is invoked and the LED which is connected to Port C pin 5 is turned on.Input Capture Interrupt Program Code
The following is the program which detects external signal from the push button and launches ISR(Interrupt Service Routine) where the LED is turned on.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define ICP (1<<PD6)
#define LED (1<<PC5)
int main(){
DDRC |= LED; //configure LED pin as output
DDRD &= ~ICP; //configure input capture pin as input
PORTD |= ICP; //activate input capture pin internal pullup
//Timer 1 config: no noise canceller, rising edge, normal mode, no prescalar
TCCR1A = 0b00000000; //COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
TCCR1B = 0b01000001; //ICNC1 ICES1 – WGM13 WGM12 CS12 CS11 CS10
TIMSK |= (1<<TICIE1); //enable input capture interrupt
sei(); //enable global interrupt
while (1);
return 0;
}
ISR(TIMER1_CAPT_vect){
TCNT1 = 0; //clear TCNT1 register
PORTC |= LED; //turn on LED on event
_delay_ms(1000); //LED on for 1 sec
PORTC &= ~LED; //turn off LED after 1sec
}
In the above code, first we have to use the interrupt header file or interrupt library. Then in order to use the delay_ms() function for creating time delay we have included the delay.h header file. This is optional and one can build own time delay functions as was illustrated in the tutorial . The following code fragment is for including required libraries.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
We have made two alias names for the LED pin and the input capture pin using the statements below.
#define ICP (1<<PD6)
#define LED (1<<PC5)
In the main function we have setup the port pin direction for the LED pin as output and made the input capture pin as an input and enabled the internal pull-up so that we don't have to put extra resistor. These are done using the following program statements.
DDRC |= LED; //configure LED pin as output
DDRD &= ~ICP; //configure input capture pin as input
PORTD |= ICP; //activate input capture pin internal pullup
After this comes the part to configure the Timer 1 input capture. First we configure the Timer 1 in normal mode, set the pre-scalar value to 1. The normal mode is configured using the WGM bits which are set to all 0. The pre-scalar is set using the CS bits which are set to 001. The COM bits are not required since we are not using the compare output units and hence they all are set to 0. Then the FOC(Force Output Compare) bits are also not required in this example and hence are set to 0. The noise cancelling feature is not use so the ICNC1 bit is set to 0. The input capture edge select(ICES1) bit is used to set the edge selection of signal transition to either falling or rising. Here we have set the ICES1 to 1 in order to set the edge transition detection to rising. Then we have to enable the input capture interrupt by setting the TICIE1 bit located in the TIMSK register to 1. Also to use the interrupt we have to enable the global interrupt which is done by using the sei() function. This configuration of Timer1 is done using the program codes below.
TCCR1A = 0b00000000; //COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
TCCR1B = 0b01000001; //ICNC1 ICES1 – WGM13 WGM12 CS12 CS11 CS10
TIMSK |= (1<<TICIE1); //enable input capture interrupt
sei(); //enable global interrupt
Once the program is started, the external signal coming into the input capture pin is monitored by the input capture hardware. Once it detects any signal on rising edge, interrupt is generated and the ISR routine is launched. In the ISR we clear the TCNT1 register, and turn on the LED for 1 second. This is done using the following program code.
ISR(TIMER1_CAPT_vect){
TCNT1 = 0; //clear TCNT1 register
PORTC |= LED; //turn on LED on event
_delay_ms(1000); //LED on for 1 sec
PORTC &= ~LED; //turn off LED after 1sec
}
So in this way we can use the ATmega32 microcontroller input capture with interrupt. Although the program application is simple, it teaches how to use the input capture with interrupt on AVR microcontroller. Instead of turning on the LED, we can for example create alarm system such that if external event is detected by the hardware alarm sound is produced. Similarly we can utilize this input capture to build frequency counter, period measurement, take temperature measurement by turning on temperature sensor, turn on DC motor controller etc.
The following are related tutorials.