Programming ATmega328P Input Capture with Interrupt

AVR microcontroller such as ATmega328P can be used to capture external events using the input capture feature. That is the time stamp of occurrence of such events can be captured. Example of such events includes capturing the time period of an incoming signal, duty cycle and frequency of the incoming signal. The input capture feature of ATmega328P can be operated in polling method and in interrupt method. The operation of polling method was explained in details with code example in the previous tutorial Programming ATmega328p Input Capture. This is continuation of that tutorial which explains how to use and program ATmega328P with input capture using interrupt. 

For input capture demonstration purpose we connect a switch to the input capture pin 14(PB0) of ATmega328P. A LED is connected to some pin like pin 15(PB1 pin) here. When the switch is pressed, the input capture hardware detects this and an interrupt is generated. Due to this interrupt the LED is turned on for one second.

The following circuit diagram shows how this input capture application works.

The following is the C program code for this input capture with interrupt example.

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

#define LED (1<<PB1)

int main(){ 
	DDRB |= LED;	//configure LED pin as output
	DDRB &= ~(1<<PB0);	//configure input capture pin as input
        PORTB |= (1<<PB0);	//activate input capture pin internal pullup 

	//Timer 1 config: no noise canceller, rising edge, normal mode, no prescalar
        TCCR1A = 0x00;		//COM1A1 COM1A0 COM1B1 COM1B0 – – WGM11 WGM10
	TCCR1B = 0b01000001;	//ICNC1 ICES1 – WGM13 WGM12 CS12 CS11 CS10
	TCCR1C = 0x00;	//FOC1A FOC1B – – – – – –
	TIMSK1 |= (1<<ICIE1);	//enable input capture interrupt
	sei();				//enable global interrupt


   return 0;

        TCNT1 = 0;		//clear TCNT1 register
	PORTB |= LED; 	//turn on LED on event
	_delay_ms(1000);	//LED on for 1 sec
	PORTB &= ~LED;	//turn off LED	after 1sec
In the code above, we have to include interrupt.h header file because we are using the interrupt subroutine which is defined in that header file. The delay.h header file is used because we use the delay function _delay_ms() function to create delay of one second down later in the program. If you want to create your own delay function using either timer 0, timer 1 or timer 2 then see the tutorials Time delay using timer 0 without inbuild functions in Arduino, Time Delay with Arduino Timer 1 or Time Delay with Arduino Timer 2

Next we have the define statement where we give alias to the LED connected port pin. In the main function we have setup the LED pin as output and the input capture pin PB0 as input(which is optional). Then we also enable the internal pullup of the input capture pin PB0. 

The next statements are used to configure the Timer 1 input capture mode as well as pre-scalar. The input capture mode works on using the Normal mode. To configure the timer 1 in normal modes, all the wave generation bits(WGM13,WGM12,WGM11,WGM10) are set to LOW. The WGM13 and WGM12 bits are located in TCCR1B register while the WGM11 and WGM10 are located in the TCCR1A register.

TCCR1 registers

 The pre-scalar bits CS12, CS11 are set to LOW and CS10 set to HIGH for no pre-scalar. These bits are located in TCCR1A are all set to LOW.

We also need to set whether to use noise canceller feature and whether to detect the incoming signal on rising edge or falling edge. Here the noise canceller feature is disabled by setting the ICNC1 bit to LOW and use the rising edge detection by setting the ICES1 bit HIGH. Both of these bits are located in TCCR1B register.

 Then the input capture interrupt is enabled by setting the ICIE1 bit located in TIMSK1 register high. 


The global interrupt needs also to be enabled which is done by the inbuilt function sei().

Then we enter in the continuous while loop and wait for interrupt to occur. Once incoming pulse is detected and transition between logic is detected, the input capture hardware fires interrupt. The ICF1 bit located in TIMSK1 is set HIGH and the program enters the interrupt sub routine(ISR) function. In the ISR(TIMER1_CAPT_vect) function, we clear the TCNT1 register and turn on the LED for 1 second.

Here we have used the input capture feature of ATmega328p to simply detect push button. The input capture method can be used to measure frequency, duty cycle, period of a signal as well. The frequency that can be measure using the input capture is not that high however. Another method to measure the frequency of a signal is using the timer counter. This is illustrated in the tutorial High Frequency Counter with Arduino.

The next step is to compile and upload the code into the microcontroller. If you don't know which ATmega328p programming software to use then see the atmega328p isp programming tutorial. And if you have difficulty with checking this input capture interrupt program is working or not, you can try the atmega328p led blink program.

Post a Comment

Previous Post Next Post