Timer1 Input Capture

I am trying to time stamp rising edges using Timer1. I believe I have enabled interrupts on detection of the rising edges, and on overflow of the 16 bit counter. Overflow interrupt seems to be fine, but I don't seem to get any capture interrupts. When I waggle port B, least significant bit, marked as digital pin 8 on my Arduino Leonardo, I'm expecting the capture interrupt servicing routine to trigger, but diagnostic says that's not happening. Is there another I/O register bit that I have to write? It is OK to hit the pin with 0V & +5V, isn't it? I was driving it from a standard logic gate carrying a square wave, but have since resorted to touching the power rails. I was hoping to read PORTB as a straight digital input port to confirm arduino is seeing different input levels, but it doesn't seem to. I haven't blown the input buffers, have I? Here's my code:-


const float cfOverflowSecs = 0.032768; const unsigned int cuPrintMax = 30;

volatile float fRunTime; volatile unsigned int uPrintCount; volatile float fLoopCount; void setup() { fRunTime = 0.0; uPrintCount = 0; Serial.begin(9600); TCCR1A = 0; // Normal counting mode TCCR1B = B010; // Setup Prescale to 0.5usec per tick bitSet(TCCR1B, 6); // Enable Input Capture bitSet(TIMSK1, 0); // Enable Interrupt for Timer1 overflow bitSet(TIMSK1, 5); // Enable Interrupt for Timer1 capture ??? }

void loop() { if(uPrintCount >= cuPrintMax) { uPrintCount -= cuPrintMax; OneSecond(); fLoopCount = 0; } else { fLoopCount++; } }

ISR(TIMER1_OVF_vect) { uPrintCount++; fRunTime += cfOverflowSecs; }

ISR(TIMER1_CAPT_vect) { Serial.print(fRunTime); Serial.print(" "); Serial.println("Capture Event"); }

void OneSecond() { Serial.print(fRunTime); Serial.print(" "); Serial.print(fLoopCount); Serial.print(" "); Serial.print(DDRB); Serial.print(" "); Serial.print(PINB); Serial.print(" "); Serial.println("Hello World"); }

I have come to the conclusion that Serial (probably) makes use of external interrupt int1. Timer1 capture event cues timer 1 servicing routine, but it also sets int1 flag. It looks like whatever else has made use of int1 gets to their servicing routine first; and execution of the int1 servicing routine clears both int1 flag and timer1 capture flags. So, when interrupts are re-enabled after execution of int1 servicing routine, timer1 capture interrupt is no longer pending.

I'm using Leonardo. Can anyone tell me if my interpretation is correct, & if so is there another flavour of Arduino that doesn't suffer from this problem? External interrupt int1 seems a pretty precious resource to be out of bounds for my application.

I have come to the conclusion that Serial (probably) makes use of external interrupt int1.

It does not.

It is OK to hit the pin with 0V & +5V, isn't it?

Yes.

I haven't blown the input buffers, have I?

No.

I have come to the conclusion that Serial (probably) makes use of external interrupt int1.

No. Why would it?

Timer1 capture event cues timer 1 servicing routine, but it also sets int1 flag.

Huh?

ISR(TIMER1_CAPT_vect)
{
    Serial.print(fRunTime);
    Serial.print(" ");
    Serial.println("Capture Event");
}

Don't do serial prints inside an ISR. No ifs or buts.

http://www.gammon.com.au/interrupts

I have a page about timers including the input capture unit: http://www.gammon.com.au/forum/?id=11504

Thanks for your helpful reply & links, Nick. I'm afraid that, having done my best to grasp what's relevant from this information I haven't cracked it.

Looking at your forum post 31/8/2014 04:33 UTC, you seem to either interrupt processing or serial output. When you do serial output you disable capture interrupts. When you are allowing capture interrupts you're not doing anything significant in the main loop. Can't you do concurrent interrupt handling & serial output?

I am trying to get the arduino to staticise ICR1 from TCNT1 from an external signal event, & get the capture interrupt to read ICR1. I thought I had to feed my signal to port B pin 0, which is marked as digital 8 on the arduino board, and matches pinMode(8, ...). You seem to say the input should be to digital 5 = port D pin 5, consistent with pinMode(5, ...). I have tried both, & neither seem to trigger TIMER1_CAPT interrupt, although the overflow interrupt is executing quite happily, and correctly. I can read either input correctly from code within loop.

/***********************************************/

const float cfOverflowSecs = 0.032768; const unsigned int cuPrintMax = 30;

volatile float fRunTime; volatile unsigned int uPrintCount; volatile float fLoopCount; volatile unsigned int uCapture;

void setup() { Serial.begin(9600);

fRunTime = 0.0; uPrintCount = 0; uCapture = 0; pinMode(5, INPUT_PULLUP); TCCR1A = 0; // Normal counting mode TCCR1B = 0x02; // Setup Prescale to 0.5usec per tick bitSet(TIMSK1, 0); // Enable Interrupt for Timer1 overflow bitSet(TIMSK1, 5); // Enable Interrupt for Timer1 capture }

void loop() { if(uPrintCount >= cuPrintMax) { uPrintCount -= cuPrintMax; OneSecond(); fLoopCount = 0; } else { fLoopCount++; } }

ISR(TIMER1_OVF_vect) { uPrintCount++; fRunTime += cfOverflowSecs; }

ISR(TIMER1_CAPT_vect) { uCapture++; }

void OneSecond() { Serial.print(fRunTime); Serial.print(" "); Serial.print(fLoopCount); Serial.print(" "); Serial.print(DDRD); Serial.print(" "); Serial.print(PIND); Serial.print(" "); Serial.println("Hello World");

Serial.print(uCapture); Serial.print(" "); Serial.println("Capture Event"); }

I think you had it right. ICP1 pin is the same as digital 8.

I loaded your code. It said no events. Then I connected pin 8 to the serial output of an actively transmitting GPS chip and it captured hundreds of events each time loop() printed.

It seems to be doing what it's supposed to do.

@rtdgreg: Please edit your post, select the code, and put it between [code][/code] tags.

You can do that by hitting the “Code” button above the posting area (It looks like a scroll with < > inside it).

How to use this forum

I am trying to get the arduino to staticise ICR1 from TCNT1 from an external signal event, & get the capture interrupt to read ICR1.

I don't understand that.

Apart from whatever "staticise" means, the purpose of the input capture unit is for the hardware to remember the exact value of a timer when an interrupt occurs. The whole point of using it is to remove the jitter introduced by the time it takes for an interrupt to fire when the external interrupt occurs. Thus we can found out what the time was exactly, rather than 2 µS or so later when the interrupt routine gets control.

ISR(TIMER1_CAPT_vect)
{
    uCapture++;
}

Your code isn't reading the input capture register, so I am guessing you are not understanding that part.

... neither seem to trigger TIMER1_CAPT interrupt ...

Because you haven't set the bit to tell it to do that. (bit ICIE1 in TIMSK1).

[quote author=Nick Gammon date=1415412129 link=msg=1952852]I don't understand that.[/quote]

Me neither. A typo? English the second language?

[quote author=Nick Gammon date=1415412129 link=msg=1952852]Your code isn't reading the input capture register, so I am guessing you are not understanding that part.

Because you haven't set the bit to tell it to do that. (bit ICIE1 in TIMSK1).[/quote]

He did set the ICIE1 bit:

bitSet(TIMSK1, 5); // Enable Interrupt for Timer1 capture

That's why I got those interrupts when I tried it.

I was assuming he wasn't bothering to read the capture register yet because he didn't think he was getting any interrupts in the first place. I was giving him the benefit of the doubt since he is new.

(As an aside, I find the code tag produces too big a box when just a tiny amount of code is written, like the one line I reproduced above.)

(As an aside, I find the code tag produces too big a box when just a tiny amount of code is written, like the one line I reproduced above.)

Yes, there has been lengthy discussion about the code box. However not a satisfactory reason to not use it.

He did set the ICIE1 bit:

bitSet(TIMSK1, 5); // Enable Interrupt for Timer1 capture

OK, I searched for “ICIE1”. There are so many obscure ways you can set a bit, I can’t be expected to find all of them.

    bitSet(TIMSK1, ICIE1);            //  Enable Interrupt for Timer1 capture

or

    TIMSK1 |= bit (ICIE1);

Are the acceptable ways in my book.

Or even:

TIMSK1 |= 1 << ICIE1;

Anyway, I don't see the point in using the input capture unit if you aren't retrieving the capture time, so everything else is academic.

Basically I expect this:

volatile   unsigned int timer1CounterValue;

ISR (TIMER1_CAPT_vect)
  {
  timer1CounterValue = ICR1;  
  // possibly other stuff
  }

You are correct. I wasn't bothering to read ICR1 because I wasn't getting the interrupt servicing routine to execute; & I'm still not. It was comforting to get the feedback from jboyton, having plugged in a gps, because, I think, my code should have been running the service routine & with my trivial increment, I could see that it wasn't.

Nick, I appreciate there may well have been a lengthy discussion about the code box but, as you've probably spotted, I am new to this arena. I will try to do better in future. Thanks for making the request.

I chose to put in the numeric values rather than named values to expose what I'm doing at the lowest level I could manage. Being new to this, I didn't want to cloud the issue by intending to do a left shift of the value one by five places, & find out my code didn't do that. Basically, I'm trawling through the Atmel datasheet, and using names tends to confuse my understanding.

The nub of it appears to be that my timer 1 capture interrupt isn't being invoked, but, from the code, it should be, and when jboydon runs it on his target, it is.

In answer to your question about staticise, Nick, it's a synonym for capture. Sorry - just did a search of the internet for it & couldn't spot it. Obviously jargon I've been using for years that's simply wrong. And no ... English is my first language. I'm just not very good at it.

I wonder if it's anything to do with the fact that I'm using a Leonardo. Leonardo uses Atmel 32U4, not 328.

Is anybody arguing that I have to read ICR1 to let another interrupt happen? I don't think so. If I do have to read ICR1 to prime for another interrupt, I would be concerned about a fragile architecture - losing one event could result in interrupts being lost forever, putting great reliance on providing decent watchdog recovery. I haven't spotted a directive to do that in the datasheets.

I do appreciate this discussion, but, I think the conclusion is if I feed the waveform into digital 8 = port B, least significant bit, then I should get a stream of interrupts. I am able to read that bit by polling, so I do know it's waggling up and down, and it looks just fine on my oscilloscope. By polling I can confirm the program can see the changes. I'm afraid I still don't have a fix. Maybe I should just get a Uno & try that. Does anyone agree? Any other suggestions?

Ha! What do you know. It's a computer science term, you just misspelled it.

staticize - 1. To convert (a) serial data or time-dependent parallel data to (b) a static form, such as to convert (i) data in delay line storage to (ii) data on a printed page without loss of the information represented by the data.

There's even a library at github called grunt-staticize. I'm not sure what that does, but to use it you're supposed to add it to your "gruntfile".

I often do the same sorts of sloppy things with code when I'm just puzzling something out, like leaving out otherwise important parts and using explicit constants. Then when it makes sense I go back and clean it up. But when asking for help in a forum it is nice to make things as clear as possible. I'll bet Nick isn't being paid to answer questions.

If you look at the data sheet for the 32U4 you'll see ICP1 does not share the same pin as D8. It's on D4. The Arduino website even has a nice table for the pin mapping on the Leonardo:

http://arduino.cc/en/Hacking/PinMapping32u4

Two countries separated by a common language eh? The English tend to end words "ise", whereas our friends across the ocean use "ize".

Since last writing I have just gone down to my local Arduino shop &, at enormous expense, purchased a Uno. What do you know? I plugged it in & it all seems to be working now! A bit of work to do, but, at least, the interrupts are happening.

I must admit, I thought I was making it more clear by disclosing the actual numbers that I was using, in order to remove all doubt. It's particularly relevant when you're trying to map programming code to physical connectors.

Unfortunately, I was using the 328 datasheet for quite some time before a friend pointed out that a Leonardo doesn't use it.

Great link jb. Many thanks.

I thought I had to feed my signal to port B pin 0, which is marked as digital 8 on the arduino board, and matches pinMode(8, ...). You seem to say the input should be to digital 5 = port D pin 5, consistent with pinMode(5, ...). I have tried both, & neither seem to trigger TIMER1_CAPT interrupt, although the overflow interrupt is executing quite happily, and correctly. I can read either input correctly from code within loop.

Yes, well you have to study both the datasheet and the schematic for the Leonardo quite carefully when you switch boards.

ICP1 on the chip is PD4 (pin 25). Now looking at the Leonardo schematic that is connected to D4 on the board (not D5 or D8). Hence your problems.

I must admit, I thought I was making it more clear by disclosing the actual numbers that I was using, in order to remove all doubt.

Perhaps, but using constants in code is pretty standard. I mean you may as well look at what number register TIMSK1 is and use its memory address. Tedious, though, eh? The code becomes un-maintanable, as someone working on it has to look up the meaning of every last "magic number".