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

Discussion in 'Embedded Systems and Microcontrollers' started by kris_maher, Sep 21, 2009.

  1. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    90
    0
    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.
     
  2. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    Are you using an ultrasonic transmitter/receiver as the basis for your range finder?

    hgmjr
     
  3. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    90
    0
    Yes I have a tx/rx ultrasonic transducer pair.
     
  4. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    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
     
  5. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    90
    0
    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: Sep 21, 2009
  6. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    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
     
  7. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    90
    0
    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.
     
  8. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    a) Yes.
    b) Should be fine.

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

    hgmjr
     
  9. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    90
    0
    thanks mate I appreciate it a lot. I'll post it once i goto the uni labs later on...
     
  10. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    90
    0
    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...
     
  11. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    90
    0
    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..

    Code ( (Unknown Language)):
    1.  
    2. #include <avr/io.h>
    3. #include <util/delay.h>
    4. #include <avr/interrupt.h>
    5. #include <math.h>
    6. #include "lcd.h"
    7.  
    8. #define F_CPU 16000000UL
    9.  
    10. // Define Global Variables here
    11. unsigned int DISTANCE = 0;
    12. float ROUND_TRIP_TIME = 0; // Total time it takes for pulse to go and come
    13. unsigned char i;
    14.  
    15. // Define Variables relating to timing the pulse
    16. unsigned int currentTIME; // Time of the most recent return pulse
    17. unsigned int currentDISTANCE;
    18.  
    19. // Define Function Prototypes here
    20. void welcome();
    21. void distanceLCD(int DISTANCE);
    22. void startTIMER(); // Work in progress
    23. void stopTIMER();
    24.  
    25. // GLOBAL VARIABLES FOR STATE MACHINE
    26. unsigned char state = 0; // We're starting with state 0 first to allow devices to settle
    27. int wait = 0;
    28. volatile unsigned char ucPulseCount = 8;
    29. unsigned int sentpulseFLAG = 0; // This flag is set every time the interrupt transmits pulses
    30.  
    31.  
    32. int main(void)
    33. {
    34.  
    35. // Initialize the LCD
    36. InitLCD(LS_BLINK|LS_ULINE);
    37.  
    38. //Clear the screen    
    39. LCDClear();
    40.  
    41. welcome(); // Welcome the user to the device
    42.  
    43. // Setup the 8-bit Timer 0 system
    44. TIMSK |= (1 << OCIE0) | (1 << TOIE1) | (1 << TICIE1); // enables Timer 0 COMPA interrupt, Timer1 overflow interrupt enable, Timer1 input capture enable
    45. TCCR0 |= (1 << WGM01) | (1 << CS01); // Enable CTC interrupts, prescaler = 8
    46. OCR0 = 25; // 40 KHz square wave at 50% duty cycle
    47. DDRB |= (1 << PB3); // Set PB3 as output
    48.  
    49. /*
    50. // Setup the external INT0 interrupt system
    51. GICR = 0x40; // Enable INT0 interrupt
    52. MCUCR = 0x02; // Trigger on an falling edge
    53. TIFR |= TIFR; // Clear any pending interrupt flags
    54. DDRD |= (0 << PD2); // Set PD2 (INT0) as an input
    55. PORTD |= (1 << PD2); // Enable pullup resistor */
    56. PORTD |= (1 << PD6); // Enable pullup resistor for ICP pin
    57.  
    58. sei(); // Enable Global Interrupts
    59.  
    60.  
    61. for(;;)
    62. {
    63. //distanceLCD(DISTANCE);
    64. }
    65. return 0;
    66. }
    67.  
    68. void startTIMER()
    69. {
    70. TCNT1 = 0; // Reset timer 1 for measuring
    71. TCCR1B |= (1 << CS10) | (0 << ICES1); // Start timer one with prescaler of 1, trigger on falling edge
    72. }
    73.  
    74. void stopTIMER()
    75. {
    76. TCCR1B = 0x00; // Stop the timer
    77. currentTIME = ICR1; // Recorded elapsed time into the variable
    78. }
    79.  
    80. ISR(TIMER1_CAPT_vect)
    81. {
    82. stopTIMER(); // Stop recording as soon as interrupt occurs
    83.  
    84. //DISTANCE; // Finish this line off. The value in currentTIME is now used how?
    85.  
    86. distanceLCD(DISTANCE);
    87. }
    88.  
    89. ISR(TIMER1_OVF_vect)
    90. {
    91. TCCR1B = 0x00; // Stop the timer if it takes longer than 16.384ms to return
    92. /* 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*/
    93. }
    94.  
    95. /*
    96. ISR(INT0_vect)
    97. {
    98. // Receiver Interrupt Routine
    99. // To make this ISR more efficient, place distanceLCD elsewhere!!
    100.  
    101. DISTANCE++;
    102.  
    103. distanceLCD(DISTANCE);
    104. }*/
    105.  
    106.  
    107. ISR(TIMER0_COMP_vect)
    108. {
    109. // Compare Match Interrupt Routine
    110. // "State Machine" Driven 40 KHz transmiter
    111.  
    112. switch (state)
    113. {
    114.     case 0:    
    115.       wait++;
    116.         if(wait == 80) // Wait 2ms
    117.         {
    118.             state = 1;
    119.             sentpulseFLAG = 0; // Reset this flag to indicate no pulses have been sent
    120.         }
    121.       break;
    122.  
    123.     case 1:
    124.         PORTB ^= 0b00001000; // Toggle PB3
    125.         ucPulseCount--;
    126.         if(ucPulseCount == 0)
    127.         {
    128.             sentpulseFLAG = 1; // This flag indicates the interrupt has sent all the pulses
    129.             DDRB |= (0 << PB3); // Set Port D bits as input
    130.             state = 0;
    131.             wait = 0; // Reset this delay value
    132.             ucPulseCount = 8;
    133.         }
    134.         break;    
    135. }
    136.  
    137.  
    138. //PORTB ^= 0b00001000; // Toggle PB3
    139. startTIMER(); // Start the timer for recording the pulse
    140.  
    141.  
    142. // MUST HAVE A TIMEOUT THAT RESETS ICR1 IN CASE NOTHING IS RECEIVED!!
    143. }
    144.  
    145.  
    146.  
    147. void welcome()
    148. {
    149.     // Simple string printing
    150.     LCDWriteString("Welcome to ");
    151.    
    152.     // A string on line 2
    153.     LCDWriteStringXY(0,1,"Rangefinder! ");
    154.  
    155.     // Delay to show text
    156.     for(i=0;i<125;i++) _delay_loop_2(0);
    157.  
    158.     // Clear the screen
    159.     LCDClear();
    160.  
    161.     // Write some text
    162.     LCDWriteString("By Kris ");
    163.     LCDWriteStringXY(0,1,"Maher"); // Print on 2nd line
    164.  
    165.     // Delay to show text
    166.     for(i=0;i<125;i++) _delay_loop_2(0);
    167.  
    168.     // Clear the screen
    169.     LCDClear();
    170. }
    171.  
    172.  
    173. void distanceLCD(int DISTANCE)
    174. {
    175.     // Show the calculated distance in cm
    176.     LCDWriteStringXY(0,0,"Distance:");
    177.  
    178.     // Print the calculated distance with a field length of 3
    179.     LCDWriteIntXY(10,0,DISTANCE,3);
    180.     LCDWriteStringXY(13,0,"cm");
    181.  
    182.     // Print the calculated Round-Trip Time in ms with a field length of 4
    183.     // Example: RTT = 1.07ms
    184.     LCDWriteStringXY(0,1,"RTT: "); // Print on 2nd line
    185.     LCDWriteIntXY(5,1,currentTIME,4); // Displaying ICR1 value here
    186.     LCDWriteStringXY(9,1,"ms");
    187.  
    188.     // Provide delay to show the calculated distance for a while before refreshing
    189.     for(i=0;i<30;i++) _delay_loop_2(0);
    190.  
    191.     // LCDClear();
    192. }
    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: Sep 22, 2009
  12. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    90
    0
    I solved the problem no worries. thanks to all.
     
  13. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    That's good news. What accuracy and range are you able to obtain?

    hgmjr
     
  14. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    90
    0
    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.
     
Loading...