Programming ATmega328p Input Capture

 ATmega328p microcontroller and most of the AVR microcontroller have a feature called input capture mode. This input capture mode allows user to measure period of an input signal and hence also frequency, measure the pulse width, duty cycle and recording events in time. Recording time events means you can create log of events. This is useful in many application. 

Here AVR timer input capture programming in C is presented with ATmega328P as the example microcontroller. First it is explained how the input capture feature works and then it is illustrated how to write program for ATmega328p that uses input capture feature. This example code can be easily be used for programming other AVR microcontrollers and Arduino board.

ATmega328p Timer 1 Input Capture

The ATmega328P microcontroller input capture functionality works only on Timer 1. To understand how the input capture works it is good to know the following block diagram.

input capture block diagram

The input capture pin in ATmega328p is pin 14(Port B pin 0 or PB0) labelled ICP1(n=1) in the above diagram. The signal source is connected to this pin, the input capture routine is started, the timer 1 starts counting and the microcontroller starts to sample the signal. Once it detects the transition in logic level, high to low or low to high depending upon the setting, the content of TCNT1(Timer Counter 1) register is transferred to ICR1(Input Capture Register 1). Both ICR1 and TCNT1 of timer 1 are 16 bit wide. So to read the time stamp we have to read the ICR1 content and not TCNT1. At the same time of transfer, the Input Capture Flag (ICF1) is set High. We can either monitor this flag to take action in application program. This flag must be cleared by the user. Alternatively, instead of monitoring the ICF1 flag we can enable the input capture interrupt by setting the ICIE1 bit High and perform action in the interrupt subroutine. This bit is automatically cleared by the CPU.

Steps to configure input capture

Before writing the program we have to know other aspects of input capture so that we can correctly configure the microcontroller. These are as follows.

1. Select Input Capture pin as source

 The internal input capture hardware is shared by two sources of signal (a) the input from the input capture pin 14 and (b) the output from internal analog comparator. The following diagram illustrates this.

 Which of the input to use is configured using the ACIC(Analog Comparator Input Capture) enable bit located in the ACSR(Analog Comparator Control and Status Register) register shown below.
The input from input capture pin is selected by setting ACIC bit to Zero and input from analog comparator is seleted by setting the ACIC bit to One. So in default case, the input is from the input capture pin.

2. Configure Noise Canceller and Edge Select bits

Another decision one has to make before using input capture is whether to use noise canceller feature and whether to use rising edge or falling edge detection. This is configured using the ICNC1 and ICES1 bits located in the TCCR1B register as shown below.

When ICNC1 bit is set High then the noise canceller is enabled. Setting ICES1 to High means capturing in rising edge and when ICES1 is Low it means capturing on falling edge.

3. Select Normal Mode

For using input capture we configure the Timer 1 is normal mode(see ATmega328p Timer Programming Examples) which is mode 0 where all the WGM bits are 0. The following is the table of wave generation mode for timer 1.

Note that  the WGM11 and WGM10 birs are located in the TCCR1A register.

 4. About Clock Select bits

The clock select bits(CS12, CS11, CS10) which are located in TCCR1B(provided above) register configures the pre-scalar value. This can be chosen according to user need. It effects the size of the byte of the measured result.

5. Flags

The last thing we need to know about while using the input capture is the flag associated with the input capture feature. Once logic level transition is detected, the ICF1 flag bit located in the Timer/Counter1 Interrupt Flag Register(TIFR1) is set. This flag can be monitored to know that capture event has occurred and take necessary action. The TIFR1 is shown below.

 The other way to know that event capture is to enable the input capture event interrupt. This is done by enabling the ICIE1 bit in the TIMSK1 register(Timer Interrupt Mask Register 1) which is shown below.

The input capture interrupt routine is ISR(TIMER1_CAPT_vect).

ATmega328p Input Capture Programs

Here we illustrate how to program ATmega328p in Input Capture mode using the polling and the interrupt method. In the illustrated program, a LED is turned on whenever a switch press is detected and captured. The switch is connected to the ICP1 pin (pin 14). The pressing of switch is the event we want to capture.

1. Polling method of Input Capture

The following shows the circuit diagram for testing the input capture functionality of ATmega328p. A push button is connected to the input capture pin ICP1 which is pin 14 on the microcontroller. When the push button is pressed, the input signal goes low. The input capture edge detector is configured to detect event on rising edge. So when the push button pressing event is detected, the ICF1 flag bit in the TIFR1 is set to high. So we continuously monitor this bit and when it set we turn on the LED connected to the pin PB1 for 1 second and then turn it off. 

Input Capture using ATmega328P circuit animation

The following is the C program code example of using input capture polling method to detect button press and then turn on and off a LED.

#include <avr/io.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 – – – – – –

   while (1){
		while((TIFR1 & (1<<ICF1)) == 0);	//poll the ICF1 bit
		TIFR1 |= (1<<ICF1);	//clear the ICF1
		PORTB |= LED; 	//turn on LED on event
		_delay_ms(1000);	//LED on for 1 sec
		PORTB &= ~LED;	//turn off LED	after 1sec
   return 0;

In the code, the LED pin is configured as an output. Similarly the input capture pin(PB0) is configured as input pin and the internal pullup is activated. Note that it is optional to configure the input capture pin as an input.

Then we have configure the Timer 1 for input capture functionality. For this we have to set up the TCCR1 registers(TCCR1A,TCCR1B,TCCR1C). The configuration of input capture is largely done via the TCCR1B register. Via the TCCR1B register, the noise canceller was disabled(ICNC1 set to 0), rising edge detect was selected(ICES1 set to 1), normal mode of operation was selected(all WGM sets set to 0), and the pre-scalar value was set to 1(CS12=0,CS11=0 and CS10=1). 

Within the while loop we monitor the ICF1 bit using the while function. When this is set then we exit the while loop and continue to next statements. We then clear the ICF1 bit and turn on the LED for 1 second to indicate the switch pressed capture event. The _delay_ms() function was used to create the 1 second delay. To use this function the delay.h header file was used with include at the top. We can also use timer0 or timer1 or timer 2 to create such delay instead of using _delay_ms() function. For this see Time delay using timer 0 without inbuild functions in Arduino, Time Delay with Arduino Timer 1 or Time Delay with Arduino Timer 2.

So in this AVR input capture example we showed how to turn on LED or do something when an event is detected using polling method. This code can also be used for Arduino input capture. The input capture pin on Arduino Uno is digital pin 8. No other pins has that special capability. So if you somehow broke that particular pin you will have no replacement. Also since input capture pin is so rare, you might not be able to use the timer 1 for other application. 

In the next AVR capture tutorial Programming ATmega328P Input Capture with Interrupt we show how to use the interrupt feature of the input capture mode.

Post a Comment

Previous Post Next Post