A small hitch in my ultrasonic receiver. I don't know if its a H/W or S/W issue.

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

  1. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    Hi all,

    I'm working on an Ultrasonic Rangefinder project using a Atmega32 micro-controller. I have a LCD connected as well which shows the distance in cm. The transmitter generates an amplified 9V p-p 40KHz Square wave pulse, and the receiver receives the weakened echos of a few mv's and amplifies it into 5V p-p Square Waves (and there is no noise, it's a clean signal)

    I've already fully built the hardware 2 months back for the receiver and the transmitter and am in the interim stages of C programming using AVR Studio.

    I'm using 2 interrupts, one is an internal interrupt which is done on 'compare match' using Timer 0 which generates the 40KHz square wave for the transmitter, the other is an external interrupt INT0 that responds to a received amplified 40KHz square wave signal 5V p-p on PD2 on a falling edge.

    My transmitter code is "State Machine" driven. So when state is 1 it sends a 4-cycle burst, and when state is 0 it waits 2ms before sending the next burst and this works quite well.

    I've included 2 videos which I've taken myself to show my lecturer and others for assistance as it's the next best thing for them of actually being in the lab room. They're pretty short, only 1.5 minutes long but they explain and show my problem.


    The videos go in order.
    Video Notes: The LCD is connected to PORTC on the transmitter board, the board that is closest to the scope and not connected to the LCD is the receiver. I made a small program that tests if the receiver interrupt was working properly, by simulating a 40KHz signal square wave into the PD5 interrupt pin a number should increment by 1. However I originally had a problem of the interrupt interrupting by itself (INT0 interrupt for the receiver routine) and incrementing even when there was no signal - however I fixed this with a pull up resistor so this is no longer an issue with it picking up static from the atmosphere.

    Also noting that I'm not sure if its the hardware or the software that's causing the 'hang'. The top signal on the scope is the receiver, and the bottom signal is the pulsed square wave from the transmitter.

    The problem is when you put an object over the sensor ideally when you remove the object the echo response on the scope should also disappear. However in my case it doesn't disappear and stays there but covers complete horizontally - at this stage it does not respond to any object moving across the sensors at all and I'll have to restart the power after pulling the battery.

    After watching the videos, here are things which I've tried to isolate the problem but still can't get to the bottom of it. :(

    1) I do not believe me enabling the pull-up resistor caused the problem as it fixed the problem I had earlier of the interrupts 'interrupting' by itself. I've tried commenting out all the INT0 code and the problem still occurs. In relation to this I wrote a small program in the receiver routine that to simply test if the interrupt works on a falling edge when a square wave signal response (an echo) is received will increment a number I have on the LCD (its labeled distance and is initialized in the code to 0). However the issue I had before was that it would increment by itself even when there was no square wave signal - it was picking up static producing false interrupts. I fixed this problem.

    2) I tried creating a 'common ground' in an attempt to fix the problem by connecting a wire to both the grounds on the boards but to no avail. I have 2 seperate breadboards, one for transmit and the other is for the receiver. The receiver circuit takes up a fair amount of space on the board so that's why I'm using two.

    3) Instead of sending the signal powered by a state machine, I also tried sending it as a continuous wave (non-stop) but this still gives the same problem. I prefer the state machine design.

    4) I've tried increasing the delay between bursts to 3ms from 2ms, but still the same problem. if I remove the battery it goes away, but once I reconnect you can see the pulses being transmitted on the bottom but if you put an object over the receiver transducer you see the problem that's occurring. It won't respond to any other objects, nor will it allow the transmitter to transmit pulses.

    5) A few weeks back before I built the state machine or even attempted to put in a receiver interrupt I could have the transmitter transmit the signal, and the receiver (exactly the same hardware) receiving the echos (also connected to the oscilloscope) and the problem not occurring at all - totally perfect. I could see the amplified signal on the scope when the object was in range of the sensors, and the amplified echo signals (the echos were in bursts and not as a continuous wave as shown in the videos) would disappear from the scope as it should when the object is not there and not hang and just stay there.

    So any ideas?

    Also I'm sorry if it's a little big, but I took the time to take the videos in the past few days to show my lecturer for help and also here as well - I've tried whatever I knew but I haven't made much head way yet.

    I've also included my circuit schematics for the transmitter and receiver as attachments. And here is my latest source code as well:

    Code ( (Unknown Language)):
    1. #include <avr/io.h>
    2. #include <util/delay.h>
    3. #include <avr/interrupt.h>
    4. #include "lcd.h"
    6. #define F_CPU 16000000UL
    8. // Define Global Variables here
    9. int DISTANCE = 0;
    10. unsigned char i;
    12. // Define Function Prototypes here
    13. void welcome();
    14. void distanceLCD(int DISTANCE);
    17. unsigned char state = 0; // We're starting with state 0 first to allow devices to settle
    18. int wait = 0;
    19. volatile unsigned char ucPulseCount = 8;
    20. unsigned int sentpulseFLAG = 0; // This flag is set every time the interrupt transmits pulses
    23. int main(void)
    24. {
    26. // Initialize the LCD
    29. //Clear the screen    
    30. LCDClear();
    32. welcome(); // Welcome the user to the device
    34. // Setup the Timer 0 system
    35. TIMSK |= (1 << OCIE0); // enables Timer 0 COMPA interrupt
    36. TCCR0 |= (1 << WGM01) | (1 << CS01); // Enable CTC interrupts, prescaler = 8
    37. OCR0 = 25; // 40 KHz square wave at 50% duty cycle
    38. DDRB |= (1 << PB3); // Set PB3 as output
    41. // Setup the external INT0 interrupt system
    42. GICR = 0x40; // Enable INT0 interrupt
    43. MCUCR = 0x02; // Trigger on an falling edge
    44. TIFR |= TIFR; // Clear any pending interrupt flags
    45. DDRD |= (0 << PD2); // Set PD2 (INT0) as an input
    46. PORTD |= (1 << PD2); // Enable pullup resistor
    48. sei(); // Enable Global Interrupts
    50. for(;;)
    51. {
    52. //distanceLCD(DISTANCE);
    53. }
    55. return 0;
    56. }
    59. ISR(INT0_vect)
    60. {
    61. // Receiver Interrupt Routine
    62. DISTANCE++;
    63. distanceLCD(DISTANCE);
    65. }
    68. ISR(TIMER0_COMP_vect)
    69. {
    70. // Compare Match Interrupt Routine
    71. // "State Machine" Driven 40 KHz transmiter
    72. switch (state)
    73. {
    74.     case 0:
    75.     for(wait; wait < 2700;wait++) //2700
    76.     {
    77.         if(wait == 2699) //2699
    78.         {
    79.             state = 1;
    80.             sentpulseFLAG = 0; // Reset this flag to indicate no pulses have been sent
    81.             break;
    82.         }
    83.     }
    84.     case 1:
    85.         PORTB ^= 0b00001000; // Toggle PB3
    86.         ucPulseCount--;
    87.         if(ucPulseCount == 0)
    88.         {
    89.             sentpulseFLAG = 1; // This flag indicates the interrupt has sent all the pulses
    90.             DDRB |= (0 << PB3); // Set Port D bits as input
    91.             state = 0;
    92.             wait = 0; // Reset this delay value
    93.             ucPulseCount = 8;
    94.         }
    95.         break;    
    96. }
    99. //PORTB ^= 0b00001000; // Toggle PB3
    100. }
    102. void welcome()
    103. {
    104.     // Simple string printing
    105.     LCDWriteString("Welcome to ");
    107.     // A string on line 2
    108.     LCDWriteStringXY(0,1,"Rangefinder! ");
    110.     // Delay to show text
    111.     for(i=0;i<125;i++) _delay_loop_2(0);
    113.     // Clear the screen
    114.     LCDClear();
    116.     // Write some text
    117.     LCDWriteString("By Krishna ");
    118.     LCDWriteStringXY(0,1,"Nadoor s3134817"); // Print on 2nd line
    120.     // Delay to show text
    121.     for(i=0;i<125;i++) _delay_loop_2(0);
    123.     // Clear the screen
    124.     LCDClear();
    125. }
    128. void distanceLCD(int DISTANCE)
    129. {
    130.     // Show the calculated distance in cm
    131.     LCDWriteString("Distance:");
    133.     // Print the calculated distance with a field length of 3
    134.     LCDWriteIntXY(10,0,DISTANCE,3);
    135.     LCDWriteStringXY(13,0,"cm");
    137.     // Provide delay to show the calculated distance for a while before refreshing
    138.     for(i=0;i<30;i++) _delay_loop_2(0);
    140.     // LCDClear();
    141. }
    thanks again

    BTW you may have seen another post that has similar code to mine, just want to say that the poster is me, forgot the details for this account so made another - but I remembered this one fortunately.
    Last edited: Sep 15, 2009
  2. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    And also I realise that I have the lcd function in the interrupt, but even if I didn't have it there and in the for(;; ) loop instead it would still give the same problem.
  3. hgmjr


    Jan 28, 2005
    You appear to be missing a pullup resistor on the reset input to your AVR. This needs to be corrected. A 10Kohm resistor is the most commonly recommended value.

  4. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    You think that could be a reason?

    Also BTW the reason I didnt connect the reset to ground is because it would trigger a reset all the time and not start any program

    So would I connect a 10k resistor to the reset and then put it to ground and not force it to reset as was the case in the past?

    thanks btw
  5. hgmjr


    Jan 28, 2005

    The pullup resistor should go from the reset pin to the +5V power bus.

  6. hgmjr


    Jan 28, 2005
    I really can't stress too strongly how important it is to avoid spending a lot of time inside an interrupt service routine.

    Consider how complicated things can get if you are inside one interrupt service routine and another interrupt takes place either on the interrupt you are already servicing or another interrupt that you are using occurs. The chances of the microprocessor going off the rails is very certain.

  7. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    Yes you have an excellent point and my lecturer said the same thing yesterday (I forgot to ask him this question which I posted on this topic)

    From my understanding on the AVR when an interrupt occurs it switches of all other interrupts until it's finished and then continues normal program execution (well thats what we were taught in class) thus allowing other interrupts to take place.

    I noticed when it was actually working properly in the past when an interrupt for the receive occurred the transmitter would stop sending (from the oscilloscope I could see) and when it would stop receiving the transmitter would re-enable itself to transmit the pulses again.

    Also when I put the displayLCD(distance); inside the for loop it would print out random stuff such as: 2 --> 60 --> 244 --> ?%# --> #() etc..but when placed in the isr it would not do such a thing.

    but you think the reset could be the reason for this problem (the continuous receiver wave hanging)? On the 2nd video which isn't showing due to some encode problem when I give the receiver breadboard a slight nudge the continuous receive wave would disappear and the transmitter wave would reappear. If I put an object on again the 'hanging' would occur, and if I give the receiver board a slight nudge against the transmitter board it would disappear again.
  8. hgmjr


    Jan 28, 2005
    You definitely need to put the 10K pullup to 5 Volts on the reset pin. The reset line is otherwise a floating input and will be subject to noise and trigger at anytime without warning.

  9. John Luciani

    Active Member

    Apr 3, 2007
    As was mentioned you need a resistor on the reset. 10K is the typical value although
    you can higher but not should not go much lower. On other Atmel uC ('328) a protection
    diode is recommended (anode on reset and cathode on Vcc). The reset pin does
    not have a protection diode to Vcc.

    Another could idea would be to filter your AVcc and Vcc lines.

    I have a schematic in the ZB1 datasheet at http://tinyurl.com/5lnhtj which
    shows the recommended filtering and reset circuitry for the '168 and '328.
    I believe that the Atmega32 would have similar requirements.

    (* jcl *)
  10. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    hi, thanks to all.

    I found that the reset issue fixed a lot of the problem.

    It still occured however I've isolated the problem as an software issue, ever since I added the reset the CW (continuous wave transmitter code) works great as is expected. That is, it is shown on the scope when an object is there and not shown when there is no object. So it doesn't get stuck at all anymore.

    However my state machine transmitter (I have the code on my 1st post. It uses timer 0) I've concluded that it is the problematic function and this is something which I have to look into.

    Do you guys see anything 'funny' or 'clumsy' with my code for the state machine transmitter? It works but there must be a bug in it somewhere or something for it to cause the issue I had earlier.

    Code ( (Unknown Language)):
    1. ISR(TIMER0_COMP_vect)
    2. {
    3. // Compare Match Interrupt Routine
    4. // "State Machine" Driven 40 KHz transmiter
    5. // Interrupt operating at 40KHz, so it occurs every 25us.
    6. switch (state)
    7. {
    8.     case 0:
    9.     for(wait; wait < 2700;wait++) // This is the 2ms delay loop
    10.     {
    11.         if(wait == 2699) //2699
    12.         {
    13.             state = 1;
    14.             sentpulseFLAG = 0; // Reset this flag to indicate no pulses have been sent
    15.             break;
    16.         }
    17.     }
    18.     case 1:
    19.         PORTB ^= 0b00001000; // Toggle PB3
    20.         ucPulseCount--;        // This is set to 8 at the top. When this == 0 four cycles have passed
    21.         if(ucPulseCount == 0)
    22.         {
    23.             sentpulseFLAG = 1; // This flag indicates the interrupt has sent all the pulses
    24.             DDRB |= (0 << PB3); // Set Port D bits as input
    25.             state = 0; // Cause state 0 (for the 2ms delay) to begin on the next interrupt and don't transmit any pulse
    26.             wait = 0; // Reset this delay value
    27.             ucPulseCount = 8;
    28.         }
    29.         break;    
    30. }
    33. //PORTB ^= 0b00001000; // Toggle PB3
    34. }
    Last edited: Sep 16, 2009
  11. rjenkins

    AAC Fanatic!

    Nov 6, 2005
    Shouldn't 'state 0' increment the counter and fall through until it reaches the 2mS count, eg. just wait++; rather than the for() loop?
    (And terminate at 80 for 2mS).

    Each time you hit state 0, you lock the whole program for the duration of that for loop, so nothing else can happen.
  12. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    Hi, you mean like this:

    Code ( (Unknown Language)):
    1. case 0:
    2.         wait++;
    4.         if(wait == 2699)
    5.         {
    6.             state = 1;
    7.             sentpulseFLAG = 0; // Reset this flag to indicate no pulses have been sent
    8.             break;
    9.         }
    I don't think 80 will do it, in my testing I found that 2700 gives the 2ms delay. However this would only increment wait by one and not as 2ms.

    Also doing:

    }while(wait != 2700)

    Same as the for.. Oh and I don't suppose a NOP would be all that useful?
    Last edited: Sep 16, 2009
  13. rjenkins

    AAC Fanatic!

    Nov 6, 2005
    Remember you are in an interrupt handler, being called once every 25uS.

    2mS is 80 x 25uS so you do nothing but increment the counter until you get to that count, at which point you set up for State 1. On the next interrupt, it goes to the state 1 code instead of state 0.

    You should never have delay loops inside interrupt routines, always count the interrupts instead until you get to the required time.

    Most compilers block further interrupts while an existing handler is active.

    If you have any form of loop at that point, the whole program is locked while in state 0.
    You cannot re-enable interrupts as then this same routine will get re-started every 25uS and never complete.

    The only way is to do the minimal processing in the interrupt and exit it promptly, so the next interrupt can be processed and the counter incremented up to the proper time for the next transmit sequence.
  14. hgmjr


    Jan 28, 2005
    Its like rjenkins is saying. You have an interrupt that you expect to get executed every 25 microseconds and inside that interrupt service routine you have a 2 milliseconds delay. What is happening is that the interrupt happens and then the delay loop executes and keeps the interrupt disabled for 2 milliseconds during which it is ignoring the incoming 25 microseconds. I don't think this is consistent with what you what to happen.

  15. kris_maher

    Thread Starter Active Member

    Apr 24, 2009
    thanks to both, I'll try it out and see what happens.