this is what I'm looking for..thank you. Where is that macro defined? Was it in the code @johnwasser provided and I missed it? That is the reference documentation I"m inquiring about ---where do I find all these instructions /macros available to the ARduino.?
@jreminton,
I think you misunderstand me. I don't know what you mean by "Input Capture Mode does not depend on interrupts". I'm using an signal to generate an interrupt which I will use to stop the counter and get its current value. The counter is not generating the interrupt (except perhaps the overflow one which I will have to handle).
I'm also not measuring any pulse width, I"m measuring the the time for an event to occur after I enable it and wait for it to occur.
I'm using an signal to generate an interrupt which I will use to stop the counter and get its current value.
To avoid interrupt latency, stop doing that and use Input Capture Mode.
Good luck with your project.
If you look at the block diagram of timer1 you will see it has dedicated inputs so the trigger and count go directly to the timer without any "high level" commands to cause a delay.
The Timer1 will create interrupts for when things happen with the timer, like overflow etc.
@LarryD - so , is the avr-lib automatically loaded and accessible to the Arduino IDE? The compiler understands this C lib? I didn't realize this.
There is no need for the user to include the library, done for us.
If remembering correctly bit(n) might be better as larger shifts can be accommodated.
example: bit(24)
All of the #include files needed are already included by Arduino.h.
The _BV(x) is a macro in one of those files, roughly equivalent to (1 << x) but may be limited to 8 bits.
You can generally use the C-language examples in the datasheet. You can usually read and write 8-bit and 16-bit registers as if they were global variables. Pin names (PB0-7, PD0-7) are just bit numbers (0-7). That's why _BV(x) or (1 << x) is used to make them a bitmask. Some registers have weird behaviors. For example, in most Interrupt Flag registers you CLEAR the flag by writing a 1 bit in the bit location. The locations written with 0 bits do nothing.
Print out a copy of the Arduino board pinout so you can translate between Arduino pin numbers and AVR ports and bits. For example, on the UNO and Nano (ATmega328P) pins 0 through 7 are PD0 through PD7 and pins 8 through 13 are PB0 through PB5. That will be different on other processors like the MEGA and Leonardo.
I think you get a lot of benefit from loading @johnwasser 's code and playing with it. For me things become clearer when I'm interacting with the code.
@JohnRob -Agreed...in the process of doing so.
Btw, reviewing NIck Gammon's examples (reply #12 on Nick's webpage), it seems he's using instruction bit() set individual bits in various registers. However, from the Arduino reference documentation, the bit() instruction is used to read the register since, as I understand it, it returns the bit's value. I noticed that @johnwasser's code uses the _BV macro instead. Has the functionality of bit() changed since Nick created his examples? or am I totally misunderstanding the code?
Thanks!
Good discussion of _BV and bit() here
https://forum.arduino.cc/t/bit-versus-_bv/267975
Nick Gammon explains his preference for bit() in reply #4.
Well, my first attempt at implementing a solution is giving me problems, and I can't figure out what may be going on. I've added various Serial.println() to track where the code is going wrong, and for some reason, the monitor shows that I go through function "setup_comparator" , but gets messed up (prints messed up characters to the serial monitor) as it starts on function "reset_capacitor" and somehow, I enter function "setup_comparator" again. I don't know how this can happen without going through a reset -- so this is where I'm focusing to find my bug(s).
Anyway, I'm attaching the code to see if I can get some help. I decided to use the analog comparator with the bandgap reference. My intent is to
-
set Timer 1 to input capture mode.
-
then I set up the analog comparator- using the bandgap ref on AIN0 ) and generating an interrupt to the timer1 on the falling edge.
-
then drive an I/0 pin low holding the node between a resistor and a capacitor low to reset the external capacitor. I initially wanted to use the same AIN1 pin as a digital output first to hold the capacitor in reset, and then switch it to the comparator input after I started the timer, but I thought this might be part of the behavior I was seeing, so I decided to have a separate I/O reset the capacitor. This is not what I prefer, since now I'm adding a second I/O's capacitance to the external capacitor (the input capacitances of AIN1 and PD5) which I'm measuring, but at least I can get it working this way first.
-
I then start the timer, enable interrupts, and switch the Digital I/O (PD5) to "release" the external cap and allow it to begin charging.
I set up the comparator and reset the cap first in setup(), and then in loop, I start the timer and wait for the interrupt to occur.
When the interrupt occurs, I calculate the time, and call the reset_cap() after some delay to prepare the capacitor to be measured again.
From what I can tell, I don't seem to come out of setup() correctly.
Here's the code:
// Arduino Input: Digital Pin 8 (ICP1), AVR pin PB0
// If using analog comparator input, use AIN1 (PD7, inverting input), AIN0(PD6,non-inverting)
volatile unsigned long dlyTime;
volatile boolean triggered;
// These constants won't change:
const int COMPIN = 7; // PD7 used as input to comparator
const int RST_B = 5; // PD7 used for reseting capacitor
ISR (TIMER1_OVF_vect) // timer overflows (every 65536 counts)
{
Serial.println ("TimerCounter OVRFLW ");
} // end of TIMER1_OVF_vect
ISR (TIMER1_CAPT_vect)
{
// grab counter value before it changes any more
dlyTime = ICR1; // see datasheet, page 117 (accessing 16-bit registers)
TCCR1B = 0;// Timer 1 control register 8-bit -- Normal Mode/Clock source stopped
triggered = true;
TIMSK1 = 0; // no more interrupts for now
} // end of TIMER1_CAPT_vect
void setup_comp ()
{
Serial.println("in setup_comp");
//Set up Comparator
//reference voltage AIN1, sense on AIN0 (using bandgap ref on AIN0, interrupt on falling edge of COMPOUT)
ADCSRB = 0; // disable mux--AIN1 connected to neg input of comparator
ACSR =(0<<ACD)|(1<<ACBG)|(1<<ACIC)|(1<<ACIS1);//enable comparator, bandgap selected,
DIDR1 = (0<<AIN1D)|(1<<AIN0D);
Serial.println("leaving setup_comp");
return;
} // end of setup_comp
void reset_cap ()
{
noInterrupts (); //protected code
Serial.println("in reset_cap");
pinMode(RST_B,OUTPUT);
digitalWrite(RST_B,0); //drive output low to reset sense capacitor
TCNT1 = 0; //reset counter
TIFR1 = (1<<ICF1)|(1<<TOV1); // clear flags so we don't get a bogus interrupt
ACSR |= (1<<ACI); //clear any comparator interrupt
triggered = false;
return;
} // end reset_capacitor
void start_timer ()
{
TIMSK1=(1<<ICIE1)|(1<<TOIE1); //interrupt on Timer 1 input capture and overflow
TCCR1B = (1<<CS10); //timer started
ACSR |=(1<<ACIE);//enable comparator interrupt\
interrupts ();
pinMode(RST_B,INPUT); // switch port to comparator input
return;
}
void setup (){
Serial.begin(9600);
Serial.println("Tau");
delay(1000);
TCCR1A = 0; // Timer 1 control register 8-bit -- Normal Mode
TCCR1B = (0<<ICES1);// Timer 1 control register 8-bit -- Normal Mode/Clock source stopped; falling edge
setup_comp();
delay(1000);
reset_cap ();
delay(1000);
} // end of setup
void loop ()
{
Serial.println("Timer Started...");
Serial.println("*");
start_timer ();
Serial.println(triggered);
//wait until interrupt
while (!triggered)
{
Serial.println("Waiting");
}
// Calculate time from counts
float elapsedTime = F_CPU * float (dlyTime); // each tick is 62.5 ns at 16 MHz
Serial.print ("Time =: ");
Serial.println (elapsedTime);
delay(10000);
reset_cap ();
} // end of loop
I'm am a HW guy trying to write code my first Arduino program, so please bear that in mind. I appreciate any help I can get.
Thanks!
Why are you multiplying 'dlyTime' in clock ticks by 16 million? Shouldn't you divide by 16 million to get time in seconds? Or divide by 16 to get time in microseconds?
yes, of course -- it was late last night .. ---but that is the least of my problems!
You can not put Serial.print() functions inside your ISR. Interrupts are off when the routine executes and Serial uses interrupts to clock out the data. It will work partially, but no guarantees.
@blh64, that would explain the weird behavior...I'll fix that and try again later today...
thank you.
You can, but you probably shouldn't. Everything will be fine until the output buffer fills up.
When the buffer is full and interrupts are disabled (as they are in an ISR), the serial output goes from interrupt-driven to polling. It will wait ages for the hardware buffer to be available and then move one character out of the software buffer. That will make room for another character of your output. It repeats the waiting until the last of your characters reaches the buffer.
The last function call in setup is to reset_cap() where you call noInterrupts().
I don't see where they are renabled.
I enable the interrupts in start_timer (). I copied the code to this post in the "wrong" state when I was debugging it.
With it enabled, I still get the same kind of behavior. It is as if the Arduino were resetting at some point during the execution of the "reset_cap" function, since it seems like the setup () code is executing again. I repeatedly see "in setup_comp" and "leaving setup_comp", followed by "in reset_cap", but I don't see "Timer Started...." nor "Waiting". I will be checking the signals with an oscilloscope tomorrow when I have some time. In the meantime, if anyone sees something blatantly wrong with the code, please let me know.
Thanks!
@johnwasser, @jremington, @johnRob, and anyone else, I need help. I can't figure out what is going on. The Arduino seems to be going through reset at random points. However, when I scope out the RESET pin, it never moves, but I do see the execution of code that should only have happened once. Perhaps memory is getting corrupted somehow?
So, for debug purposes, I've put all the code (except for the timer/capture ISR) in the setup() function:
volatile unsigned long dlyTime;
volatile boolean triggered;
// These constants won't change:
const int COMPIN = 7; // PD7 used as input to comparator
const int RST_B = 5; // PD5 used for reseting capacitor
const int comp_int=4; // PD4
ISR (TIMER1_CAPT_vect)
{
// grab counter value before it changes any more
dlyTime = ICR1; // see datasheet, page 117 (accessing 16-bit registers)
TCCR1B = 0;// Timer 1 control register 8-bit -- Normal Mode/Clock source stopped
triggered = true;
digitalWrite(comp_int,0);
//TIMSK1 = 0; // no more interrupts for now
return;
} // end of TIMER1_CAPT_vect
void setup() {
pinMode(comp_int,OUTPUT);
digitalWrite(comp_int,0); // debug interrupt pin
Serial.begin(9600);
Serial.println("Tau");
TCCR1A = 0; // Timer 1 control register 8-bit -- Normal Mode
TCCR1B = (1<<ICNC1)|(1<<ICES1);// Timer 1 control register 8-bit -- Normal Mode/Clock source stopped; falling edge
//Setup Comparator
Serial.println("in setup_comp");
//Set up Comparator
//reference voltage AIN1, sense on AIN0 (using bandgap ref on AIN0, interrupt on falling edge of COMPOUT)
ADCSRB = 0; // disable mux--AIN1 connected to neg input of comparator
ACSR |=(1<<ACBG)|(1<<ACIC)|(1<<ACIS1); // bandgap selected
delay(100);
ACSR &=~(1<<ACD); //enable comparator by clearing ACD bit
Serial.print("ACSR = 0x");
Serial.println(ACSR,HEX);
DIDR1 = (1<<AIN1D)|(1<<AIN0D);
Serial.println("leaving setup_comp");
//Reset the capacitor
Serial.println("in reset_cap");
pinMode(RST_B,OUTPUT);
digitalWrite(RST_B,1);
delay(200);
digitalWrite(RST_B,0); //drive output low to reset sense capacitor
TCCR1B = 0;
TCNT1 = 0; //reset counter
TIFR1 = (1<<ICF1)|(1<<TOV1); // clear flags so we don't get a bogus interrupt
ACSR |= (1<<ACI); //clear any comparator interrupt
triggered = false;
//Start the timer
Serial.println("Timer Started...");
TIMSK1=(1<<ICIE1)|(1<<TOIE1); //interrupt on Timer 1 input capture and overflow
//TCCR1B = (1<<CS10); //timer started
ACSR |=(1<<ACIE);//enable comparator interrupt
TCNT1=0;
Serial.print("TimerCount = 0x");
Serial.println(TCNT1,HEX);
TCCR1B |= (1<<CS10); //timer started
pinMode(RST_B,INPUT); // switch port to comparator input
//wait until interrupt
while(!triggered)
{
Serial.print("ACSR = ");
Serial.println(ACSR,HEX);
}
Serial.println("interrupt received, timercnts = 0x");
Serial.println(ICR1,HEX);
}
void loop() {
// put your main code here, to run repeatedly:
}
Here's a capture of the Comms console:
All I have connected to the Ardunion UNO is an RC network from 5V to GND, with the common node connected to pins PD7 and PD5 on the Arduino UNO. I'm powered off of USB.
I would appreciate any suggestions!
Thank you.
