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

Thread Starter

kris_maher

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

1st:
http://www.dailymotion.com/video/xahwzr_1
2nd:
http://www.dailymotion.com/video/xahx5i_3


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:

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

#define F_CPU 16000000UL

// Define Global Variables here
int DISTANCE = 0;
unsigned char i;

// Define Function Prototypes here
void welcome();
void distanceLCD(int DISTANCE);

// 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 Timer 0 system
TIMSK |= (1 << OCIE0); // enables Timer 0 COMPA interrupt
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

sei(); // Enable Global Interrupts

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

return 0;
}


ISR(INT0_vect)
{
// Receiver Interrupt Routine
DISTANCE++;
distanceLCD(DISTANCE);

}


ISR(TIMER0_COMP_vect)
{
// Compare Match Interrupt Routine
// "State Machine" Driven 40 KHz transmiter
switch (state)
{
    case 0:
    for(wait; wait < 2700;wait++) //2700
    {
        if(wait == 2699) //2699
        {
            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
}

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 Krishna ");
    LCDWriteStringXY(0,1,"Nadoor s3134817"); // 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
    LCDWriteString("Distance:");

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

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

    // LCDClear();
}
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.
 

Attachments

Last edited:

Thread Starter

kris_maher

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

hgmjr

Joined Jan 28, 2005
9,027
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.

hgmjr
 

Thread Starter

kris_maher

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

hgmjr

Joined Jan 28, 2005
9,027
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.

hgmjr
 

Thread Starter

kris_maher

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

hgmjr

Joined Jan 28, 2005
9,027
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.

hgmjr
 

John Luciani

Joined Apr 3, 2007
475
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 *)
 

Thread Starter

kris_maher

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

Rich (BB code):
ISR(TIMER0_COMP_vect)
{
// Compare Match Interrupt Routine
// "State Machine" Driven 40 KHz transmiter
// Interrupt operating at 40KHz, so it occurs every 25us.
switch (state)
{
    case 0:
    for(wait; wait < 2700;wait++) // This is the 2ms delay loop
    {
        if(wait == 2699) //2699
        {
            state = 1;
            sentpulseFLAG = 0; // Reset this flag to indicate no pulses have been sent
            break; 
        }
    }
    case 1:
        PORTB ^= 0b00001000; // Toggle PB3
        ucPulseCount--;        // This is set to 8 at the top. When this == 0 four cycles have passed 
        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; // Cause state 0 (for the 2ms delay) to begin on the next interrupt and don't transmit any pulse
            wait = 0; // Reset this delay value
            ucPulseCount = 8;
        }
        break;    
}


//PORTB ^= 0b00001000; // Toggle PB3
}
 
Last edited:

rjenkins

Joined Nov 6, 2005
1,013
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.
 

Thread Starter

kris_maher

Joined Apr 24, 2009
90
Hi, you mean like this:

Rich (BB code):
case 0:
        wait++;
    
        if(wait == 2699)
        {
            state = 1;
            sentpulseFLAG = 0; // Reset this flag to indicate no pulses have been sent
            break; 
        }
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:

do
{
wait++;
}while(wait != 2700)

Same as the for.. Oh and I don't suppose a NOP would be all that useful?
 
Last edited:

rjenkins

Joined Nov 6, 2005
1,013
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.
 

hgmjr

Joined Jan 28, 2005
9,027
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.

hgmjr
 
Top