Need some pointers with measuring 2 different time intervals (atmega32)

Thread Starter

kris_maher

Joined Apr 24, 2009
90
Hi,

I'm building a rangefinder project that transmits a signal on each Timer 0 interrupt on compare match (via PD3. It is my transmitter pin). And the MCU receives the amplified square wave from the receiver on PD5 (which is the INT0 pin) for processing of time and hence distance etc.

My question is where does timer 1's Input capture fit into all this?

I'm planning on having 2 different functions that are called:

startTIMER(); // This will be placed in the transmitter code function
// Enables Timer 1's input capture, starts counting

stopTIMER(); // This will be placed in the receiver code function
// Stops Timer1, records elapsed time and converts into ms


Since I'm sending a burst on each interrupt it will start the timer 1. And then on a receive where it performs an interrupt (INT0) it will stop the timer via the function stopTIMER() that will be placed within the ISR at the end.

2 issues:
For input capture to work from my understanding is that the receive signal must come to the ICP pin. However my signal is coming to the PD5 pin (INT0).

And lastly once I stop the timer via TCCR1B = 0x00;, the value of ICR1 (sorry im saying this from the top of my head its late night) will give me the time in ms?

I've read the material from various places and people say and do different things so I'm kinda confused sorry.

What I have done here is for the initialization of Timer 1, this was done in main:

TCCR1B = 0x01; // Enable Capture on falling edge, with prescaler of 1
TIMSK = 0x20; // Enable Input Capture Interrupt Enable

I made it capture on a falling edge since my receiver interrupt (INT0) is set to trigger on an falling edge. Also I'm using a 16MHz crystal.

From here on I'm not sure how to start it off would like some advice thanks.
 

hgmjr

Joined Jan 28, 2005
9,027
The power of Input Capture is that it frees you from having to stop and start a timer. You should be able to read the input capture counter value at the start of your pulse. The return signal will cause the interrupt and the input capture interrupt will automatically snapshot the timer value.


hgmjr
 

Thread Starter

kris_maher

Joined Apr 24, 2009
90
Okay so this means that I can't have my receiver code (which finds the delay and hence the distance) as PD5 since that is for INT0, I'll have to make my receiver interrupt as TIMER1_CAPT with the receive signal going into the ICP pin?

This is something that just came to my head:

uint16_t CurrentTime;
uint16_t DelayedTime;

CurrentTime = ICR1; // This holds the initial empty value of ICR1 in the variable and placed in startTimer();

DelayedTime = ICR1; // Get the recorded value of the Input Capture register one a receive interrupt placed in stopTimer(); This is the time that has passed since a pulse was sent.

In terms of using Input Capture that's what I'm familiar with besides of course setting up the timer, prescaler etc. Am I correct in my understanding?
 
Last edited:

hgmjr

Joined Jan 28, 2005
9,027
The initial value of the Input Capture counter need not be zero. You just will need to set up your code to cope with a wrap in the counter.

hgmjr
 

Thread Starter

kris_maher

Joined Apr 24, 2009
90
Ok so is my idea correct? I'll post my code once i have a chance to test it out. Also want to confirm:

a) I need the received signals from the receiver to goto ICP for it to work.
b) I'm looking at ICR1 to know the delay in us.
 

hgmjr

Joined Jan 28, 2005
9,027
Ok so is my idea correct? I'll post my code once i have a chance to test it out. Also want to confirm:

a) I need the received signals from the receiver to goto ICP for it to work.
b) I'm looking at ICR1 to know the delay in us.
a) Yes.
b) Should be fine.

I will be happy to take a look at the code once you post it.

hgmjr
 

Thread Starter

kris_maher

Joined Apr 24, 2009
90
Hi,

In the data sheet it's talking about ICR1H and ICR1L (low). Should I just address it as:

Current_Time = ICR1;

Or do I need to worry about the high and low bits? In several examples they used the high or low, but one example I saw didn't use high or low but simply addressed it as ICR1. Is this alright?

Also once I have the value of ICR1, the input capture register, how will I know how long time has passed? Okay sorry silly question but what I mean is that each step must take some time.

But I want to keep the code as simple as possible but yet does the job.

I'll post in what I've done so far in a moment...
 

Thread Starter

kris_maher

Joined Apr 24, 2009
90
ok here it is:
Right now when I put an object over the sensors the ICR1 value (which is being printed as currentTIME on the LCD) shows values of like: 10 --> 70 --> 120 --> 15 etc..

Rich (BB code):
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <math.h>
#include "lcd.h"

#define F_CPU 16000000UL

// Define Global Variables here
unsigned int DISTANCE = 0;
float ROUND_TRIP_TIME = 0; // Total time it takes for pulse to go and come
unsigned char i;

// Define Variables relating to timing the pulse
unsigned int currentTIME; // Time of the most recent return pulse
unsigned int currentDISTANCE;

// Define Function Prototypes here
void welcome();
void distanceLCD(int DISTANCE);
void startTIMER(); // Work in progress
void stopTIMER();

// GLOBAL VARIABLES FOR STATE MACHINE
unsigned char state = 0; // We're starting with state 0 first to allow devices to settle
int wait = 0;
volatile unsigned char ucPulseCount = 8;
unsigned int sentpulseFLAG = 0; // This flag is set every time the interrupt transmits pulses


int main(void)
{

// Initialize the LCD
InitLCD(LS_BLINK|LS_ULINE);

//Clear the screen    
LCDClear();

welcome(); // Welcome the user to the device

// Setup the 8-bit Timer 0 system
TIMSK |= (1 << OCIE0) | (1 << TOIE1) | (1 << TICIE1); // enables Timer 0 COMPA interrupt, Timer1 overflow interrupt enable, Timer1 input capture enable
TCCR0 |= (1 << WGM01) | (1 << CS01); // Enable CTC interrupts, prescaler = 8
OCR0 = 25; // 40 KHz square wave at 50% duty cycle
DDRB |= (1 << PB3); // Set PB3 as output

/*
// Setup the external INT0 interrupt system
GICR = 0x40; // Enable INT0 interrupt
MCUCR = 0x02; // Trigger on an falling edge
TIFR |= TIFR; // Clear any pending interrupt flags
DDRD |= (0 << PD2); // Set PD2 (INT0) as an input
PORTD |= (1 << PD2); // Enable pullup resistor */
PORTD |= (1 << PD6); // Enable pullup resistor for ICP pin

sei(); // Enable Global Interrupts


for(;;)
{
//distanceLCD(DISTANCE);
}
return 0;
}

void startTIMER()
{
TCNT1 = 0; // Reset timer 1 for measuring
TCCR1B |= (1 << CS10) | (0 << ICES1); // Start timer one with prescaler of 1, trigger on falling edge
}

void stopTIMER()
{
TCCR1B = 0x00; // Stop the timer
currentTIME = ICR1; // Recorded elapsed time into the variable
}

ISR(TIMER1_CAPT_vect)
{
stopTIMER(); // Stop recording as soon as interrupt occurs

//DISTANCE; // Finish this line off. The value in currentTIME is now used how?

distanceLCD(DISTANCE);
}

ISR(TIMER1_OVF_vect)
{
TCCR1B = 0x00; // Stop the timer if it takes longer than 16.384ms to return
/* I found that without the overflow interrupt, due to Timer 1 capture the system would just restart itself and show the welcome to LCD every time and not transmit any pulse*/
}

/*
ISR(INT0_vect)
{
// Receiver Interrupt Routine
// To make this ISR more efficient, place distanceLCD elsewhere!!

DISTANCE++;

distanceLCD(DISTANCE);
}*/


ISR(TIMER0_COMP_vect)
{
// Compare Match Interrupt Routine
// "State Machine" Driven 40 KHz transmiter

switch (state)
{
    case 0:    
      wait++;
        if(wait == 80) // Wait 2ms
        {
            state = 1;
            sentpulseFLAG = 0; // Reset this flag to indicate no pulses have been sent
        }
      break;

    case 1:
        PORTB ^= 0b00001000; // Toggle PB3
        ucPulseCount--;
        if(ucPulseCount == 0)
        {
            sentpulseFLAG = 1; // This flag indicates the interrupt has sent all the pulses
            DDRB |= (0 << PB3); // Set Port D bits as input
            state = 0;
            wait = 0; // Reset this delay value
            ucPulseCount = 8;
        }
        break;    
} 


//PORTB ^= 0b00001000; // Toggle PB3
startTIMER(); // Start the timer for recording the pulse


// MUST HAVE A TIMEOUT THAT RESETS ICR1 IN CASE NOTHING IS RECEIVED!!
}



void welcome()
{
    // Simple string printing
    LCDWriteString("Welcome to ");
    
    // A string on line 2
    LCDWriteStringXY(0,1,"Rangefinder! ");

    // Delay to show text
    for(i=0;i<125;i++) _delay_loop_2(0);

    // Clear the screen
    LCDClear();

    // Write some text
    LCDWriteString("By Kris ");
    LCDWriteStringXY(0,1,"Maher"); // Print on 2nd line

    // Delay to show text
    for(i=0;i<125;i++) _delay_loop_2(0);

    // Clear the screen
    LCDClear();
}


void distanceLCD(int DISTANCE)
{
    // Show the calculated distance in cm
    LCDWriteStringXY(0,0,"Distance:");

    // Print the calculated distance with a field length of 3
    LCDWriteIntXY(10,0,DISTANCE,3);
    LCDWriteStringXY(13,0,"cm");

    // Print the calculated Round-Trip Time in ms with a field length of 4
    // Example: RTT = 1.07ms
    LCDWriteStringXY(0,1,"RTT: "); // Print on 2nd line
    LCDWriteIntXY(5,1,currentTIME,4); // Displaying ICR1 value here
    LCDWriteStringXY(9,1,"ms");

    // Provide delay to show the calculated distance for a while before refreshing
    for(i=0;i<30;i++) _delay_loop_2(0);

    // LCDClear();
}
The added functions/routines are:
void startTIMER();
void stopTIMER();
ISR(TIMER1_CAPT_vect)
ISR(TIMER1_OVR_vect)

I'm having the function 'startTIMER()' placed in the transmitter code that's in the Timer0 interrupt routine. And I'm having the function 'stopTIMER()' in the Input Capture ISR. I don't quite know what I should do with the ICR1 value now, or if there is anything funny with what I've done.

Any suggestions please anyone? Thanks

PS: I found that adding the overflow timer1 interrupt it prevented the system from 'restarting' itself (ie. shows the welcome screen and my name over and over again without transmitting a pulse). Also yes my reset is connected properly.
 
Last edited:

Thread Starter

kris_maher

Joined Apr 24, 2009
90
Accuracy is within +-1 cm.

The range is not so great, at 35cm. I'll need to build myself a better transmitter circuit that amplifies at higher levels than just the 8-9V I'm pumping to the transducer now.
 
Top