[ATmega328P] Can I use the Capture function w/o interrupts?

Go To Last Post
25 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can't seem to make the Capture function of Timer1 work w/o interrupts.

 

For a home project I wanted to make a HC-SR04 measurement using timer1.  I know this has been accomplished thousand's of times in various processors and codes.   However I got it in my mind that I shouldn't need interrupts to make this measurement since my program flow is pretty simple.

 

My code skills ..... in golfing terms I'd be a duffer.

I started with pure C statements then in frustration I degraded into Arduino.

 

In the code below I am using outputs C0 to C3 for debugging.

 

In this code I am simply trying to verify I can capture the rising edge of the ICP1 input (aka echo).  My findings were that I cannot :(

 

I clear C0 to C3

I clear TCNT1

Set TCCR1B for capture on rising edge

clear all bits in TIFR1

 

Generate the sensor trigger pulse

Test for ICF1 = 1  (signifying a rising edge was captured)   If so set C0 to 1

Looking at the logic traces, I see ICF1 is set to 1   4.6 µs after the trigger ends, with no change in ICP1 (echo)

 

So I'm at a loss as to why I can't do this.   I've never found the capture used without an interrupt so maybe it can't be performed.  Or maybe I'm blinded by an initial assumption that is incorrect.

 

Any suggestions would be appreciated.

 

Thanks

John

 

/*
V06a  only rising capture

 Pin Assignments:
   UART:   TXO PORTD1
           RXI PORTD0
  Trigger Pulse out:        Pin  9  PORTB1
  Echo Pulse In:      ICP1  Pin  8  PORTB0
  Internal LED              Pin 13  PORTB5
 */

//#include <Arduino.h>

#define trigger 9
#define echo 8
#define LED 13
#define C0 14
#define C1 15
#define C2 16
#define C3 17

// *** Main *******************************************************
// ****************************************************************

void setup(void)
{
// Variables
  pinMode(echo,INPUT);
  pinMode(trigger,OUTPUT); // PB1 Trigger
  pinMode(LED,OUTPUT); // LED
  PORTB = 0;

  pinMode(C0,OUTPUT); //C0
  pinMode(C1,OUTPUT);
  pinMode(C2,OUTPUT);
  pinMode(C3,OUTPUT); //C3
  digitalWrite(trigger,0);

  //sei();      /* Enable global interrupt */

 // TCCR1A = 0;   /* Set all bit to zero Normal operation */
  //TIMSK1 = 0b00100001;  // enable ICIE1 and TOIE1
}
// *** Start LOOP ****************************************
// ****************************************************************
 void loop () {

    digitalWrite(C0,0);
    digitalWrite(C1,0);
    digitalWrite(C2,0);
    digitalWrite(C3,0);
    delay(5);
    TCNT1 = 0;            // Clear Timer counter
    TCCR1B = 0b01000001;  // Capture on rising edge, prescale 1
    TIFR1 = 0;

   /* 10us trigger pulse*/
    digitalWrite(trigger,1);
    delayMicroseconds(8);
    digitalWrite(trigger,0);

/* ------------------------------------------------------------- */
/* ---- Capture rising edge ----------------------------------- */

    if ((TIFR1 & (1 << ICF1)) == 1);    digitalWrite(C0,1);  // C0

    while ((TIFR1 & (1 << ICF1)) == 0){   //  <<<<<<<<<<<<<<<<<<<<<<<<<<<< wait rising edge
      digitalWrite(C1, !digitalRead(C1)); // toggle C1
      TCNT1 = 0;              // Clear Timer counter
      }
   digitalWrite(C2,1);  // C2

   delay(2000);
  }

// --- eof --------------------------------------------------------------------

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think your T1 clock is too fast, it overflows before the echo pulse even begins!

note: once that is working, there is no need to clear T1 at the start of the echo pulse, just note (or capture) t1 value as start time, then capture the end echo pulse time and t2-t1 using uint16_t values will give the correct time diff.

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@ki0bl

 

Thanks for the input.   Unfortunately the issue seems to not be related to the counter clock.

 

I tried both prescale = 1 and prescale = 1024 with exactly the same result (on the logic analyzer).

 

Now I've assumed the processor prescale and timer1 prescale are independent.

 

My calculation for timer1 count/µs  is:

 1/16Mhz  * 1 * 1024 =  64µs/count

where 1 = the processor prescale

and

1024 = timer 1 prescale.

 

 

John

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Some tutorials suggest the echo pin should have a 10k pull down resistor to gnd to reduce noise, another thing to try is enable the IC noise canceler bit ICNC1 in the TCCR1B reg.

Try one then the other then both and see if that helps.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I appreciate the input but I don't believe there is a rising edge on the echo input.  Even a glitch.  My last test was to:

 

Ground the ICP1 input.

Remove the HC-SR04 from the circuit.

 

The logic analyzer shows the same (except no echo at all).

 

 

I'm at a loss...

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try clearing the flag register bit (write a 1 to the flag bit to clear) first.

JohnRob wrote:

TIFR1 = 0;

Writing zero's does not clear it!

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for all the suggestions.  Since your last post I researched resetting the TIFR1 flags and tried numerous variations.   My conclusion is the edge capture function will not work without interrupts.  My guess is the rising edge detection and interrupt creation are somehow tied together in the chip.

 

I'm going to break down and use interrupts sad

 

John

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interrupts are not required! Whatever gave you that idea? The functionality is exactly the same, except enable an interrupt tells you when it is ready.

 

 if ((TIFR1 & (1 << ICF1)) == 1)

That line looks suspect. Is ICF1 is the bit 0 position? If not, your test will never be true.

 

 if (TIFR1 & (1 << ICF1))

 

To clear the interrupt flag:

 TIFR1 = (1 << ICF1);

 

Also realise that the timer still keeps on ticking. The input capture is like a stop watch - it captures the time of the event. Your code doesn't seem to account for this.

 

Also be aware that the Arduino infrastructure might be using timer1. You might want to check this.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A few various options, try them all and see how measurements differ-

 

https://godbolt.org/z/PEGGGE

edit- changed (I had line 48 wrong)-

https://godbolt.org/z/T9PGTT

 

I would eventually add a way to timeout on the blocking while's, but that can happen later (one should assume you may not get a reply). I also made some quick calculations about the max count you should ever see (using 400cm as max distance, t1 clock 16MHz/8) and I think it is right so am using an overflow flag to indicate when the pulse is too long.

 

They are all simple, but a good baseline is to use the timer 'manually' while watching pins. It may turn out that is just as good as anything else. The timing errors by doing this may be insignificant or maybe when using arduino pin functions it may be more important. Using the capture will become a better option when you do not want to block, and even in this case when polling the use of capture will make creating a timeout easier/better since you will be able to add more code without affecting the timing (otherwise adding more code between watching the pins could lead to less accurate time- may or may not matter, but it is now more than just a few clock cycles).

 

 

 

 

Last Edited: Sat. Dec 12, 2020 - 05:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Kartman

 

Thank you for your response smiley

 

" Interrupts are not required! Whatever gave you that idea? "   Only that I could not get them to work.

 

The code you found as suspect is an error due to my frustration.  Its goal was to test ICF1 and I was using C0 as a debug output. (with logic analyzer).

 

The posted code is a stripped down version of where I started.  Its goal is to simply verify the rising edge was captured and the counter was transferred to the 16 bit input capture register (ICR1).   I have not been able to get this to work.

 

 

TIFR1 = (1 << ICF1);

Question:  here you said the above code would clear the interrupt flag.  I've seen this written before.  I'm hoping my thinking is wrong here, but I thought the  1<< ICF1 would shift the 1 to the left to be located at the ICF1 bit so it would set the interrupt.   and I would need 0 to clear that bit.

 

I realize the counter runs whenever the prescale register bits are not all  0.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohnRob wrote:
Question:  here you said the above code would clear the interrupt flag.  I've seen this written before.  I'm hoping my thinking is wrong here, but I thought the  1<< ICF1 would shift the 1 to the left to be located at the ICF1 bit so it would set the interrupt.   and I would need 0 to clear that bit.

What does the datasheet say?  Further, what does the datasheet say for THAT register, the flag register.  What are you thinking "set" the interrupt?  Enable it?  That is a different register.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The datasheet says Flag registers,  0 = clear the .   Interrupt Registers 1 to set (enable it).

 

However when your not getting the results you expect by a logical approach one starts to question everything.

 

Anyway another forum participant sent a link that looks like it will help me.   I'll have to check it compared with what I had that was not working and see where I went astray. 

 

I will report back if I find a solution.

 

Thanks for the reply

 

John

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohnRob wrote:
The datasheet says Flag registers,  0 = clear the .   Interrupt Registers 1 to set (enable it).

 

From the datasheet section 13.11.9

 

"ICF1 is automatically cleared when the Input Capture Interrupt Vector is executed. Alternatively, ICF1 can be cleared by writing a logic one to its bit location"

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks,

 

I'll look at that,  perhaps my only issue is I assumed ICF1 worked in normal manner (i.e. set = 1 and clear = 0) however that appears not to be the case.

 

Still the beginning of that same paragraph is     "This flag is set when a capture event occurs on the ICP1 pin. "

 

If this is the case I think they should have named it ICF1\ (not)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clearing the flag is an operation, not an assignment. How else would you do it without affecting the other flags?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Apparently I don't know.

 

I thought " TIFR = 1<<ICF1 " works.  I've been under the impression this command would

If ICF1 = 5 then   1<<ICF1 would generate  0b0010 0000 ( or course w/o the space) then write it to TIFR.

 

And to not effect the other bits one would have to read the register, change the bit, then rewrite it back to the register.

 

I think I need to step back and study the datasheet some more.

 

Thanks

John

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're assuming the register you read is the register you write - not so. Each interrupt flag is the status of a flip/flop. The interrupt source sets the flip/flop, A write with the appropriate bit set clears the flip/flop.

 

Similar to the USART UDR register, the register you read is not the register you write.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohnRob wrote:

And to not effect the other bits one would have to read the register, change the bit, then rewrite it back to the register.

 

The danger with that is what happens if another bit in that register changes between the time when you read the register and the time when you rewrite it?

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Apparently that is an incorrect assumption I made.  It wasn't done because I am a maverick coder but because I didn't understand. 

 

As I said, I will have to step back as study the data sheet somemore.

 

Thanks for all your help.

 

John

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohnRob wrote:
The datasheet says Flag registers,  0 = clear the .

Where does it say that?

JohnRob wrote:

I'll look at that,  perhaps my only issue is I assumed ICF1 worked in normal manner (i.e. set = 1 and clear = 0) however that appears not to be the case.

 

Then why did you state the first, without looking at the datasheet?  Register fraud, eh?

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0



Then why did you state the first, without looking at the datasheet?  Register fraud, eh?

Not nice :(

 

I've read the datasheet many times.  I've printed out the pages, I've copied the related register bit defns to excel and looked at them there.  I've googled and tried numerous posts/blogs/articles on Timer1.   Some time ago I wrote a PWM generator for the 328p and was able to use timer1 to generate a 2 phase pwm.

I've quoted parts of the datasheet the seem contradictory to me (admittedly it could be misinterpretation). 

 

Quoted from another poster:

TIFR1 = 0;

 

Writing zero's does not clear it!

 

How should I interpret this paragraph?  Do I test for "0" when it is set?   If I am not using interrupts ICF1 can be cleared by writing "1".   I always assumed "set" was 1 and "clear" was 0 is this incorrect?

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohnRob wrote:

Then why did you state the first, without looking at the datasheet?  Register fraud, eh?

Not nice :(

 

I've read the datasheet many times.  I've printed out the pages, I've copied the related register bit defns to excel and looked at them there.  I've googled and tried numerous posts/blogs/articles on Timer1.   Some time ago I wrote a PWM generator for the 328p and was able to use timer1 to generate a 2 phase pwm.

I've quoted parts of the datasheet the seem contradictory to me (admittedly it could be misinterpretation). 

 

Quoted from another poster:

TIFR1 = 0;

 

Writing zero's does not clear it!

 

How should I interpret this paragraph?  Do I test for "0" when it is set?   If I am not using interrupts ICF1 can be cleared by writing "1".   I always assumed "set" was 1 and "clear" was 0 is this incorrect?

 

 

 

 

 

You do what it says - write a '1' to the appropriate interrupt flag.

 

A write to the interrupt flag register is not a normal write - if it was it would corrupt other bits in the same register and potentially lose interrupts.

 

By doing it the way described you can clear that particular bit without disturbing others. If you do need to clear multiple flag bits at the same time you can write to multiple bits simultaneously but that is extremely unusual and I can't think of a reason to do so.

 

Kevin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Maybe it will help if you see the h/w behind the flag register:

It's just a simple RS flip flop, a 1 written to the Set pin sets the Q output to one (flag set), this is done when the condition for the flag is true.

writting a 1 to the Reset pin clears the Q output to zero (flag cleared), this done either by the ISR() entry, or by the user writting to the flag register.

 

 

Jim

PS: the /Q output is not used!

 

 

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

Last Edited: Sun. Dec 13, 2020 - 08:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you understand the problem the mcu designer has, then maybe it will make more sense. They need to be able to clear single bits in a flag register where the other flag bits could bet set by hardware in any clock cycle. In other words something needs to be done in some way that does not affect the other bits, and still allows these other bits to be set at any time. There is nothing that can be done in 1 clock cycle via the instruction set that could also preserve other bits, so you need to go to plan b.

 

The solution is for these flag bits to ignore a write of 0, and clear on a write of 1. The bit of interest gets cleared, and the others were not touched (where hardware is still free to set in any clock cycle). Which means you write a bitmask of the bit(s) you want cleared. This also means you cannot directly set a flag bit.

 

Not all flags are like this, but many/most are. In any case the datasheet will inform what is required, and all it takes is finding the register description which will specify how to clear the bit. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Take a look at this:

// poll
if(flagsonly & singlebitmask) {
    flagsonly=singlebitmask;  // clears a single bit
    whatever
}

I expect the datasheet has similar code.

 

Atmel could have picked zero to clear.,

In that case, singlebitmask and ~singlebitmask would both have been needed.

 

To make it work the way you seem to think it should,

the sense of the flags would have to be reversed.

Moderation in all things. -- ancient proverb