Problem with HCSR04 ultrasonic sensor

Go To Last Post
20 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi! This is my first post here. Im doing a project and im using a atmega324p and therefore a hcsr04. The problem is: this doesn't work(every port is well connected, and the atmega and hcsr04 have a 5v supply). I don't why. Can you help me? Thanks in advance.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define F_CPU 16000000L

volatile int pulse = 0;
volatile int dist = 0;

ISR(INT0_vect)
{
	if (0<<PIND2)
	{ // se int0 tiver LOW, começa o timer.
		TCCR1B|=(0<<CS10)|(1<<CS11)|(0<<CS12); //presclaer 8, 16000000/8=2MHz =5us //// o mais proximo de 10 us
	}
	if (1<<PIND2)
	{
		pulse=TCNT1;
		TCCR1B|=(0<<CS10)|(0<<CS11)|(0<<CS12); //STOP timer
		TCNT1=0;
	}
}

void init()
{
	DDRB |= (1<<PB0); // led -> PB0 OUTPUT
	DDRD |= (0<<PD2)|(1<<PD0); // echo -> PD2 intput // trig -> PD0 OUTPUT

	EIMSK|=(1<<INT0);
	EICRA|=(1<<ISC01)|(0<<ISC00);

	TCCR1A |= 0;

	sei();
}

int main(void)
{

	init();

	while(1)
	{
		PORTD|=(0<<PD0);
		PORTD|=(1<<PD0);
		_delay_us(10);
		PORTD|=(0<<PD0);

		dist = pulse/58;
		
		if (dist<=10) {
			PORTB|=(1<<PB0);
		} 

	}
}

 

 

nguterresn

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I believe that your if statements are wrong in the INT0 interrupt. 

 

Try:  if ( (PIND & (1<<PIND2)) == FALSE) {  do this if pin D2 is 0 }  else { do this if Pin D2 is 1 }

 

With the HCSR04 proximity sensor, you send the 10uS trigger pulse.  Then wait until the Echo line goes active (high, if I remember correctly).  Then time how long that it stays high.  When it goes low, apply the distance formula according to the length of the Echo pulse.


 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:
I believe that your if statements are wrong in the INT0 interrupt. 

Indeed they are!

 

NGUTERRESN wrote:

	if (0<<PIND2)

and

	if (1<<PIND2)

 

PIND2 is a constant - it just tells you the position of the bit within the PIND register.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

NGUTERRESN wrote:

#define F_CPU 16000000L

 

How have you proved that your chip is running at 16MHz?

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

NGUTERRESN wrote:

		PORTD|=(0<<PD0);
		PORTD|=(1<<PD0);
		_delay_us(10);
		PORTD|=(0<<PD0);

 

That code doesn't do what you think it does. ORing a value with 0 will leave ti set to 1 if it was already was set. It does not clear it to 0.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

Thanks everyone for the quick answer.

First: 

Brian Fairchild wrote:

NGUTERRESN wrote:

#define F_CPU 16000000L

 this is proved, I have a crystal and the fuses are set.

Brian Fairchild wrote:

NGUTERRESN wrote:

		PORTD|=(0<<PD0);
		PORTD|=(1<<PD0);
		_delay_us(10);
		PORTD|=(0<<PD0);

 

That code doesn't do what you think it does. ORing a value with 0 will leave ti set to 1 if it was already was set. It does not clear it to 0.

This is the code that is meant to send the trig pulse of 40khz. I have seen it in the oscilloscope and it seems fine.

 

 

 

 

 

nguterresn

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And I made some changes on what you said its wrong: 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define F_CPU 16000000L

volatile int pulse = 0;
volatile int dist = 0;

ISR(INT0_vect)
{
	if ((PIND & (1<<PIND2)) == 0)
	{ // se int0 tiver LOW, começa o timer.
		TCCR1B|=(0<<CS10)|(1<<CS11)|(0<<CS12); //presclaer 8, 16000000/8=2MHz =5us //// o mais proximo de 10 us
	}
	if ((PIND & (1<<PIND2)) == 1)
	{
		pulse=TCNT1;
		TCCR1B|=(0<<CS10)|(0<<CS11)|(0<<CS12); //STOP timer
		TCNT1=0;
	}
}

void init()
{
	DDRB |= (1<<PB0); // led -> PB0 OUTPUT
	DDRD |= (0<<PD2)|(1<<PD0); // echo -> PD2 intput // trig -> PD0 OUTPUT

	EIMSK|=(1<<INT0);
	EICRA|=(1<<ISC01)|(0<<ISC00);

	TCCR1A |= 0;

	sei();
}

int main(void)
{

	init();

	while(1)
	{
		PORTD &= ~(1<<PD0);
		PORTD |= (1<<PD0);
		_delay_us(10);
		PORTD &= ~(1<<PD0);

		dist = pulse/58;

		if (dist<=10) {
			PORTB|=(0<<PB0);
		}

	}
}

 

nguterresn

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But still.. This doesn't work.

nguterresn

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looking at last code line of message #8, If you get a signal out, then something else is wrong, because 

PORTD|=(0<<PD0);

DOES NOT clear pin D0! If you OR 0 with anything, there is exactly no change. But, worse, (0<<PD0) does nothing itseslf. It takes a byte that  starts out as 0b00000000 and sort of makes the least bit 0. So, what do you have? 0b00000000! No change. Then you OR PORTD with 0b00000000 and, guess what? No change!

 

The only statement in all that, that does anything, is:

 

PORTD|=(1<<PD0);

If you are getting a signal out, it may just run through your program, reset, and start over. I recommend that you rethink your code snippet, above.

 

Jim

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Sat. Dec 9, 2017 - 07:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

ka7ehk wrote:

Looking at last code line of message #8, If you get a signal out, then something else is wrong, because 

PORTD|=(0<<PD0);

DOES NOT clear pin D0! If you OR 0 with anything, there is exactly no change. But, worse, (0<<PD0) does nothing itseslf. It takes a byte that  starts out as 0b00000000 and sort of makes the least bit 0. So, what do you have? 0b00000000! No change. Then you OR PORTD with 0b00000000 and, guess what? No change!

 

The only statement in all that, that does anything, is:

 

PORTD|=(1<<PD0);

If you are getting a signal out, it may just run through your program, reset, and start over. I recommend that you rethink your code snippet, above.

 

Jim

 

You are correct, but in message #8 I do not define 

PORTD|=(0<<PD0);

Already changed that to: 
 

PORTD &= ~(1<<PD0);

 

nguterresn

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Pardon, wrong port, but the argument is the same. Your last lines have:

 

if (dist<=10) {
       PORTB|=(0<<PB0);
       }

That does exactly nothing.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Sat. Dec 9, 2017 - 08:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you changed this one as well?

if (dist<=10) {
	PORTB|=(0<<PB0);
}

 

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile int pulse = 0;
volatile int dist = 0;

#define F_CPU 16000000UL

ISR(INT0_vect)
{												

	pulse = TCNT1;							
	TCCR1B |= (0<<CS10)|(0<<CS11)|(0<<CS12);	//STOP timer

	dist = pulse/58;

	if (dist<=10)
	{
		PORTB |= (1<<PB0);
		TCNT1 = 0;
		dist=0;					//Liga o led se for <= 10cm
	}
	else
	{
		PORTB &=~(1<<PB0);
		TCNT1 = 0;
		dist=0;						//Desliga o LED se não for
	}

}

void init()
{
	DDRB |= (1<<PB0); // led -> PB0 OUTPUT
	DDRD |= (0<<PD2)|(1<<PD0); // echo -> PD2 intput // trig -> PD0 OUTPUT

	EIMSK |= (1<<INT0);									//Define que se vai utilizar a interrupção INT0
	EICRA |= (0<<ISC01)|(1<<ISC00);						//Falling edge gera uma interrupção

	TCCR1A |= 0;										//Modo normal

	sei();
}

int main(void)
{

	init();

	while(1)
	{

		PORTD |= (1<<PD0);							
		TCCR1B |= (0<<CS10)|(1<<CS11)|(0<<CS12);		
		_delay_us(10);
		PORTD &= ~(1<<PD0);

	}
}

Now the Led on the PB0 answers to the sensor values. BUT, it blinks too much. I don't know if is noise or the code is just wrong.

nguterresn

Last Edited: Sat. Dec 9, 2017 - 10:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So change your program a bit.

 

In the Main Loop:

Turn LED on for 1/10 Sec

Turn LED off

Send a Ping

Do the distance calculation

Turn the LED on, if the distance is < X cm

Delay 1 sec to see the LED

Turn off the led

delay 1 sec, all leds are off

Loop

 

Now there is a brief flash once every 2 seconds, when you send a ping

And the LED turns on for a second, if the distance is < 10 cm

 

And there is a delay with everything off before the cycle repeats.

 

JC

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As you said, I made some changes in the interrupt. But, this doesn't work. Led only blinks. 
Could it be that the TCNT1 doesn't have enough time to be correct?

the code: 


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define F_CPU 16000000UL

volatile int pulse = 0;
volatile int dist = 0;


ISR(INT0_vect)
{	
	
	TCCR1B |= (0<<CS10)|(0<<CS11)|(0<<CS12);	//STOP timer
	pulse = TCNT1/2; 
	dist = pulse/58;
	
	PORTB &= ~(0<<PB0);
	
	if (dist<=10)
	{
		PORTB |= (1<<PB0);
		_delay_ms(10);
		TCNT1 = 0;
		dist=0;	
						//Liga o led se for <= 10cm
	}
	else
	{
		PORTB &=~(1<<PB0);
		_delay_ms(100);
		TCNT1 = 0;
		dist=0;		
					//Desliga o LED se não for
	}

}

void init()
{
	DDRB |= (1<<PB0); // led -> PB0 OUTPUT
	DDRD |= (0<<PD2)|(1<<PD0); // echo -> PD2 intput // trig -> PD0 OUTPUT

	EIMSK |= (1<<INT0);									//Define que se vai utilizar a interrupção INT0
	EICRA |= (0<<ISC01)|(1<<ISC00);						//Falling edge gera uma interrupção

	TCCR1A |= 0;										//Modo normal

	sei();
}

int main(void)
{

	init();

	while(1)
	{

		PORTD |= (1<<PD0);
		TCCR1B |= (0<<CS10)|(1<<CS11)|(0<<CS12);
		_delay_us(10);
		PORTD &= ~(1<<PD0);

	}
}

 

 

nguterresn

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

this is proved, I have a crystal and the fuses are set.

You still need to write a simple test program to blink the LED at one flash per second, to prove (verify) that the clock is running at the expected speed.

 

If the clock is wrong, then all of the timing calculations will be wrong.

 

Pseudocode:

Configure I/O pin for output

Do:

  Turn on led

  wait 500 msec

  Turn off led

  wait 500 msec

Loop

 

This verifies that the correct clock source is being used, (the osc using the external crystal), AND that any divide by 8 or other such setting is not altering the clock frequency.

 

Do this and report back.

 

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Put this statement first . . .

#define F_CPU 16000000UL

 

In consideration of others, please RTFM!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define F_CPU 16000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile int pulse = 0;
volatile float dist = 0;

void update_led();

ISR(INT0_vect)
{	
	pulse = TCNT1;
	TCNT1 = 0;
}

void init()
{
	DDRB |= (1<<PB0); // led -> PB0 OUTPUT
	DDRD |= (0<<PD2)|(1<<PD0); // echo -> PD2 intput // trig -> PD0 OUTPUT

	EIMSK |= (1<<INT0);									//Define que se vai utilizar a interrupção INT0
	EICRA |= (1<<ISC01)|(0<<ISC00);					

	TCCR1A |= 0;										//Modo normal

	sei();
}

int main()
{
	init();
	TCCR1B |= (1<<CS10)|(0<<CS11)|(0<<CS12);
	while(1)
	{
		PORTD |= (1<<PD0);	
		_delay_us(10);
		PORTD &= (0<<PD0);
		update_led();
	}
}

void update_led(){
	
	dist = (pulse/58)/2;
	
	if (dist<=10)
	{
			PORTB |=(1<<PB0);	
	}
	else
	{		PORTB &= (0<<PB0);		
						
	}
	
}

I've made these changes...

Now the led is ON when dist <= X. And it's X, because it's not really 10 cm as it says in the code(maybe 20cm). The problem now is: even if I change the X value (10cm in the code), it doesn't matter. The led still turns on when dist <= 20 cm.

 

nguterresn

Last Edited: Tue. Dec 12, 2017 - 01:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It appears you zero the count when you receive the echo and there is no synchronization between sending the pulse and receiving the pulse...

Back in Post #15 DocJC gave a good outline of how to accomplish what you are doing.

 

The only thing I would add is where DocJC said "Send a Ping" I would change to "Start a Ping, Zero the Ping Timer, delay and Stop the Ping"

 

David (aka frog_jr)