NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » how do we use timer1 input capture? (ICP1) on PB0? help?

January 22, 2011
by huzbum
huzbum's Avatar

I've searched and no one on the forum has illustrated a successful attempt at using ICP1. I know it's function can be emulated with a pin change interrupt, but not as efficiently as actually using ICP1. So here are my questions.

1) Can ICP1 be used with the bootloader?

2) If not, can it be used if programmed without a bootloader? Are there any special settings required?

I am hoping the staff will have some input here as I know that more than just I wish to use it.

A code example would be most helpful, as I'm convinced I'm just doing something small and specific wrong. it wouldn't need to even do anything useful, just an example of the correct way to setup and access the ICP1 function.

Thanks for any help. I have little to no experience with MC's and I can't figure out what I'm doing wrong.

January 23, 2011
by Ralphxyz
Ralphxyz's Avatar

Hi huzbum, I sure wish one of the "experts" here on the forum had answered your question.

It appears "we" are left with my findings posted on your other thread.

What I found out was that you cannot use ICP1 (PB0) unless you remove the bootloader (foodloader).

This really seems stupid to me (but what do I know).

I mean the Input Capture Function appears to be a valuable function:

15.2.1 Registers
The Input Capture Register can capture the Timer/Counter value at a given external (edge triggered)
event on either the Input Capture pin (ICP1) or on the Analog Comparator pins (See
”Analog Comparator” on page 244) The Input Capture unit includes a digital filtering unit (Noise
Canceler) for reducing the chance of capturing noise spikes.

Plus there are other functions one can get from using PB0.

Apparently the foodloader.c code would have to be changed (and flow logic rethought).

I have very quickly looked at foodloader.c and do not see any reference to PB0 or ICP1 so I haven't the foggiest idea where to start with re-writing the foodloader.

If you want to use PB0 you have to flash your program directly to the mcu using a programmer

utilizing ISP (Inline Serial Programming) or the like.

Well like I said I wish others had chimed in with their wisdom.


January 24, 2011
by Rick_S
Rick_S's Avatar

Ralph, are you saying to use the ICP1 function, the bootloader gets in the way, or to use PB0 the bootloader gets in the way?

If so, I wonder why that is? I can say I haven't messed around much with it but I thought once you were into your own code, you could re-configure any port as needed to use any way. Did you ever get a reason as to why it couldn't be done?


January 24, 2011
by Ralphxyz
Ralphxyz's Avatar

If the bootloader is on the mcu you cannot use the ICP1 (Input capture).

With the bootloader loaded PB0 is at Vcc (high). But at very low amperage a LED will just barely glow.

So I do not believe you can use PB0 or the ICP1 function with the bootloader loader.

Also with the bootloader loaded a jumper between PB4 and PB0 forces the mcu into Programming mode at startup the same as closing the programing switch.

I wanted to use ICP1 (input Capture) for the built in filtering, it seems using the hardware native functions to debounce switch closings would be much more efficient than doing it in code. Besides it saves code space.

I have quickly looked at the foodloader.c code but do not see the connection to PB0 for the programming switch.


January 24, 2011
by huzbum
huzbum's Avatar

Sounds like the pullup resistor is on... maybe turning it off will do it? (page 74 of the atmega168 datasheet)

DDRB&=~(1<<ICP);    //ICR1 as input
PORTB|=~(1<<ICP);   //pullup disabled
January 24, 2011
by huzbum
huzbum's Avatar

oops... should be an & instead of |

PORTB&=~(1<<ICP);   //pullup disabled
DDRB&=~(1<<ICP);    //ICR1 as input
January 24, 2011
by mrobbins
(NerdKits Staff)

mrobbins's Avatar

Hi all,

You are correct that once the bootloader passes control to your own code, PB0 is a normal port that might be used for other purposes. You are also correct that the bootloader leaves PB0 in input mode, with pull-up resistor enabled.

The only thing to be concerned with is whether your external timing source might pull the pin low during startup, which would make your bootloader stay in its programming mode.

Also, take a look at the datasheet -- the Analog Comparator unit can be used as the source for the Timer1 input capture event. That might be an easier way to go!


January 24, 2011
by Ralphxyz
Ralphxyz's Avatar

Mike, if I load the program that uses ICP1 (PB0) for counter input. I get the two black bars across LCD lines 1 and 3 with the switch in run mode after the program is loaded. If I load the same program using my programmer without the bootloader the program runs fine (doesn't work but runs fine).

This is without any connection on PB0 at startup.


January 25, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Ralph,

That is indeed quite strange. Assuming nothing else is connected at startup (and you have not modified the bootloader) the chip should boot into run mode. Then in the program code you should be free to turn off the pull up resistor and use the pin as a normal I/O pin.


January 27, 2011
by huzbum
huzbum's Avatar

ok, I have a working solution posted in my tach thread:

but for the sake of making future searches easier, here is the code:

// tach.c
// for NerdKits with ATmega168

#define F_CPU 14745600
#define ICP PINB0

// ICP1 - (pin 14) Ignition pulse input capture
// PC4  - (pin 27) 2Hz test pulse

#include <stdio.h>
#include <math.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"

volatile uint16_t revTick;      // Ticks per revolution
volatile uint16_t revCtr;           // Total elapsed revolutions

void setupTimer() 
{           // setup timer1
   TCCR1A = 0;      // normal mode
   TCCR1B = 68;     // (01000100) Rising edge trigger, Timer = CPU Clock/256 -or- 14745600/256 -or- 57.6KHz 
   TCCR1C = 0;      // normal mode
   TIMSK1 = 33;     // (00100001) Input capture and overflow interupts enabled

   TCNT1 = 0;       // start from 0

ISR(TIMER1_CAPT_vect)  // PULSE DETECTED!  (interrupt automatically triggered, not called by main program)
   revTick = ICR1;      // save duration of last revolution
   TCNT1 = 0;       // restart timer for next revolution
   revCtr++;                // add to revolution count

ISR(TIMER1_OVF_vect)    // counter overflow/timeout
   { revTick = 0; }     // RPM = 0

int main (void)
   sei();       // enable global interrupts.
   setupTimer();    // set timer perameters
   FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);

   PORTB|=(1<<ICP);     //pullup enabled
   DDRB&=~(1<<ICP);     //ICR1 as input
   DDRC |= (1<<PC4);    // PC4 as output

   uint16_t RPM;    // Revolutions per minute

   while (1)
   {  // do calculations and talk to lcd while we're not doing anything...

      if (revTick > 0) // You're not Chuck Norris, DO NOT DIVIDE BY ZERO.
         {RPM = 3456000 / revTick;}     // 3456000 ticks/minute  
         {RPM = 0;}

      lcd_write_string(PSTR("     RPM: "));     
      lcd_write_string(PSTR("Ticks/REV: "));    
      lcd_write_string(PSTR("revolutions: "));      

      PORTC |= (1<<PC4);    // test signal  120RPM
      PORTC &= ~(1<<PC4);

      delay_ms(425);    // wait 1/2 a second to update... I can't read that fast...

   return 0;

Works without a hitch. Jumper wire directly from PC4 to PB0 (ICP1) to show 120RPM. Doesn't interfere with boot-loader or programming mode.

Post a Reply

Please log in to post a reply.

Did you know that an electroluminescent backlight for an LCD panel requires hundreds of volts AC to run? Learn more...