ATMega328P with HC-SR04 C Code Debugging

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

Hello All,

 

I am having trouble with code that I have written to interface an HC-SR04 with an ATMega328P. I do not have a FTDI cable yet, so although I have included the code that should work for serial output of the distance, I am trying to get the code working with an LED indicator first. Currently I am getting what appears to be a permanent high pulse from the sensor to the board. Any help would be greatly appreciated.

 

/*
 * HC-SR04.c
 *
 * Created: 12/17/2015 11:47:50 PM
 * Author : 
 * Utilized Pins
 *		PD0 : Serial RX
 *		PD1 : Serial TX
 *		PD2 : HC-SR04 Echo
 *		PD3 : HC-SR04 Trigger
 *		PD4	: LED - HC-SR04 Test 1-ft
 */ 

#define F_CPU 1000000UL
#define BAUD_CALC ((F_CPU/(16UL*19200UL))-1)
#define TCNT1_START_HCSR04 42336

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

uint8_t pingState = 0;
int echoInches = 0;

void serialOut(char* Message){
	int i = 0;
	for(i=0; i < strlen(Message); i++){
		while ( !( UCSR0A & (1<<UDRE0)) );
		UDR0 = Message[i];
	}
}

int main(void)
{
	SPH = (RAMEND & 0xFF00) >> 8;
	SPL = (RAMEND & 0x00FF);
	
	TCCR1B = (1<<CS10);
	TIMSK1 = (1<<TOIE1);
	
	PCICR = (1<<PCIE2);
	PCMSK2 = (1<<PCINT18);
	
	UBRR0 = BAUD_CALC;
	UCSR0B = (1<<RXEN0)|(1<<TXEN0);
	UCSR0C = (1<<UCSZ01)|(3<<UCSZ00);
	
	DDRD = (1<<PIND3) | (1<<PIND1) | (1<<PIND4);
	DDRB = 0xFF;
	TCNT1 = 0;

	sei();
	while (1)
	{
		switch (pingState){
			case 0:
				
				PORTD &= ~(1<<PIND3);
				_delay_us(10);
				PORTD |= (1<<PIND3);
				_delay_us(10);
				PORTD &= ~(1<<PIND3);
				pingState++;
				break;
			case 1:
			
				break;
			case 2:
				PORTB = echoInches;
				if(echoInches == -1){
					serialOut("OUT OF RANGE");
				}else{
					char temp[7];
					itoa(echoInches, temp, 10);
					serialOut(temp);
					serialOut("in\r");
					if(echoInches > 1200){
						PORTD |= (1<<PIND4);
					}else{
						PORTD &= ~(1<<PIND4);
					}
				}
				pingState++;
				break;
			case 3:
				_delay_us(30);
				pingState = 0;
				break;
		}
	}
}

ISR(PCINT2_vect){
	unsigned char sreg;
	
	sreg = SREG;
	cli();
	if(PIND & (1<<PIND2)){
		
		TCNT1 = TCNT1_START_HCSR04;
		TCCR1B |= (1<<CS10);
	}else{
		PORTD |= (1<<PIND4);
		TCCR1B &= ~(1<<CS10);
		echoInches = (TCNT1 - TCNT1_START_HCSR04)/148;
		pingState = 2;
	}
	
	SREG = sreg;
	sei();
}
ISR(TIMER1_OVF_vect){
	unsigned char sreg;
	
	sreg = SREG;
	cli();
	TCCR1B &= ~(1<<CS10);
	echoInches = -1;
	pingState = 2;

	SREG = sreg;
	sei();
}

 

Last Edited: Mon. Jan 11, 2016 - 06:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A popular model. http://www.micropik.com/PDF/HCSR... Did you try to find "proven" AVR code on the Web?

 

Is your AVR really running at 1MHz?  Have you proven that?

 

Are you sure that your LED is connected and operational?  Have you done a test "heartbeat" program to indicate that?

 

Do you have a 'scope or logic analyzer to look at the trigger pins and return pulse to see what is going on at the AVR pins?

 

Aha!

inkspell4 wrote:
uint8_t pingState = 0; int echoInches = 0;

http://www.nongnu.org/avr-libc/u...

 

I think there is a Tutorials forum article on "volatile" as well.

http://www.avrfreaks.net/forum/t...

 

[sheesh--impossible to use the "new and improved" forum search to find it...]

 

 

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Wed. Jan 6, 2016 - 08:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A couple of things jump out at me,

first, any variable shared between an ISR and main should be declared "volatile" , google it if you don't understand why.

second, to enable interrupts within ISR is generally discouraged, get rid of the sreg save/restore, and the cli/sei and see what happens.

 

Jim

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

I am new to the AVR chip as well as C programming of microprocessors and wanted to learn on my own how to program the board. I am limited in what I have to use to debug, but I can verify that the sensor works with an arduino, and the the LED is connected and does work. Is there a specific issue that you see with the line that was pulled out?

 

I will have access to a scope next week that I can use to test.

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

inkspell4 wrote:
Is there a specific issue that you see with the line that was pulled out?

??? I gave you two links, and a hint.  And Jim said the same thing.

 

On to Jim's:

 

ki0bk wrote:
to enable interrupts within ISR is generally discouraged, get rid of the sreg save/restore, and the cli/sei...

Most here would say "never" vs. "discouraged".  Re-read the datasheet section...

 

When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts are disabled. The
user software can write logic one to the I-bit to enable nested interrupts. All enabled interrupts can then
interrupt the current interrupt routine. The I-bit is automatically set when a Return from Interrupt
instruction – RETI – is executed.

So the first negates the need for cli, and the second the sei.

 

In addition, your compiler will take care of saving and restoring SREG.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Awesome, I will test it later tonight. I hadn't seen Jim's post yet, but once I saw it it made sense.

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

And my post with links and hints didn't make sense?  Doesn't the FAQ link take you right to the same information?  Doesn't the Tutorial article get into more discussion?

 

Maybe my links were broken on your Android or whatever?  I guess I'm out.  Then I won't have to get into why TCNT is started at 47K.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

I actually did have some problems with your links, I started TCNT at 47K so that it would max out and overflow when the sensor reached its maximum distance. Thank you for your help.

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

OK, so I ran the code with some edits, that lead me to believe that it may be a timing issue now as the pulse from the sensor is clearly audible and ranges in tone as objects move closer and further to it. My fuses are set up as follows:

 

High : 0xD9

Low : 0x07

Extended : 0x62

 

Also, the following is the modified code (I commented out the serial lines, as I have no way to verify the operation of the code):

/*
 * HC-SR04.c
 *
 * Created: 12/17/2015 11:47:50 PM
 * Author :
 * Utilized Pins
 *        PD0 : Serial RX
 *        PD1 : Serial TX
 *        PD2 : HC-SR04 Echo
 *        PD3 : HC-SR04 Trigger
 *        PD4    : LED - HC-SR04 Test 1-ft
 */

#define F_CPU 1000000UL
#define BAUD_CALC ((F_CPU/(16UL*19200UL))-1)
#define TCNT1_START_HCSR04 42336

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

volatile uint8_t pingState = 0;
volatile int echoInches = 0;

void serialOut(char* Message){
    int i = 0;
    for(i=0; i < strlen(Message); i++){
        while ( !( UCSR0A & (1<<UDRE0)) );
        UDR0 = Message[i];
    }
}

int main(void)
{
    SPH = (RAMEND & 0xFF00) >> 8;
    SPL = (RAMEND & 0x00FF);
    
    TCCR1B = (1<<CS10);
    TIMSK1 = (1<<TOIE1);
    
    PCICR = (1<<PCIE2);
    PCMSK2 = (1<<PCINT18);
    
    UBRR0 = BAUD_CALC;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    UCSR0C = (1<<UCSZ01)|(3<<UCSZ00);
    
    DDRD = (1<<PIND3) | (1<<PIND1) | (1<<PIND4);
    DDRB = 0xFF;
    TCNT1 = 0;

    sei();
    while (1)
    {
        switch (pingState){
            case 0:
                
                PORTD &= ~(1<<PIND3);
                _delay_us(10);
                PORTD |= (1<<PIND3);
                _delay_us(10);
                PORTD &= ~(1<<PIND3);
                pingState++;
                break;
            case 1:
            
                break;
            case 2:
                if(echoInches == -1){
                    //serialOut("OUT OF RANGE");
                }else{
                    //char temp[7];
                    //itoa(echoInches, temp, 10);
                    //serialOut(temp);
                    //serialOut("in\r");
                    if(echoInches < 1200){
                        PORTD |= (1<<PIND4);
                    }else{
                        PORTD &= ~(1<<PIND4);
                    }
                }
                pingState++;
                break;
            case 3:
                _delay_us(300);
                pingState = 0;
                break;
        }
    }
}

ISR(PCINT2_vect){
    if(PIND & (1<<PIND2)){
        TCNT1 = TCNT1_START_HCSR04;
        TCCR1B |= (1<<CS10);
    }else{
        TCCR1B &= ~(1<<CS10);
        echoInches = (TCNT1 - TCNT1_START_HCSR04)/148;
        pingState = 2;
    }
}
ISR(TIMER1_OVF_vect){
    TCCR1B &= ~(1<<CS10);
    echoInches = -1;
    pingState = 2;
}

Thank you for all of your help so far.

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

Is your AVR really running at 1MHz?  Have you proven that?        The Ush yesterday.

lead me to believe that it may be a timing issue

 

A comfortable and traditional way to test wether F-CPU is what one hoped (delay_us and delay depend on it is to have a LED blinking. If your USART sent characters at the right baud rate (I do not know : you commented out) this test is redundant.

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

dbrion0606 wrote:
If your USART sent characters at the right baud rate (I do not know : you commented out) this test is redundant.

OP said that the USART link isn't yet set up.

 

But when it is, trying to get 19200 at 1MHz is a fool's errand--won't work; baud rate error is too high.

 

So short answer is to use 8MHz.  But note that the internal oscillator is some per cent off nominal unless tunes/calibrated somehow...

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Then OP should test with LED blinking ... and, if he wants to use UART -this is very useful to show results/gather them on a PC/RPi)  ... add a crystal (would Annie Way make US distance measurements even better.)

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

Can I hard wire an Atmega to the RPi or an Arduino for Serial Coms?

 

I have the code cleaned up a little including adding comments and removing serial information. Can anyone that has utilized the HC-SR04 before verify the calculations?

 

#define F_CPU 1000000UL

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

#define LEDPin PIND4
#define TrigPin PIND3
#define EchoPin PIND2

volatile uint8_t pingState = 0;
volatile int echoInches = 0;

int main(void)
{
	SPH = (RAMEND & 0xFF00) >> 8;
	SPL = (RAMEND & 0x00FF);
	
	TCCR1B = (1<<CS10);
	TIMSK1 = (1<<TOIE1);
	
	PCICR = (1<<PCIE2);
	PCMSK2 = (1<<PCINT18);
	
	DDRD = (1<<TrigPin) | (1<<LEDPin);		// Set Pins As Output
	TCNT1 = 0;

	sei();
	while (1)
	{
		switch (pingState){
			case 0:
				
				PORTD &= ~(1<<TrigPin);			// Set Trigger Pin To 0
				_delay_us(10);					// Wait
				PORTD |= (1<<TrigPin);			// Set Trigger Pin To 1
				_delay_us(10);					// Wait
				PORTD &= ~(1<<TrigPin);			// Set Trigger Pin To 0
				pingState++;					// Increment State
				break;
			case 1:
												// While In This Case, Do Nothing
				break;
			case 2:
				if(echoInches == -1){			// If Overflow Occured

				}else{							// Otherwise
					if(echoInches > 12){		// If Distance Is Less Than 1ft
						PORTD |= (1<<LEDPin);	// Set LED High
					}else{						// Otherwise
						PORTD &= ~(1<<LEDPin);	// Set LED Low
					}
				}
				pingState++;					// Increment State
				break;
			case 3:
				_delay_us(300);					// Wait Before Going Back To State 0
				pingState = 0;					// Go To State 0
				break;
		}
	}
}

ISR(PCINT2_vect){
	if(PIND & (1<<EchoPin)){					// If EchoPin Is High
		TCNT1 = 0;								// Reset Timer
		TCCR1B |= (1<<CS10);					// Enable Timer
	}else{										// Othewise
		TCCR1B &= ~(1<<CS10);					// Disable Timer
		echoInches = TCNT1/148;					// Count echoInches
		pingState = 2;							// Increment State
	}
}
ISR(TIMER1_OVF_vect){
	TCCR1B &= ~(1<<CS10);						// Enable Timer
	echoInches = -1;							// Set -1 As echoInches For Overflow
	pingState = 2;								// Increment State
}

 

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

Looks like it should be close enough for government work!  q:-)

Unless your trying to dock a supply ship with the ISS.....   but then ultrasonics may not work too well in space!

 

 

Jim

 

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

Can I hard wire an Atmega to the RPi or an Arduino for Serial Coms?

 

(1) For RPi , You can,  through a USB-Serial adapter like a PC (if you use the RPi GPIO port, you will have two or three issues :(a) kernel messages are sent ont the RPi's port: I doubt avr will understand them; filtering needs extra configuration; (b) RPi like 3.3v; avr prefer 5v and adapters are as expensive as USB-Serial .... (c) a strange level is sent at RPis start....) . just like a PC (and pyserial works, just like a  PC; event matplotlib can be used to draw nice curves).

 

(2) For Arduini (I did not test : only use {Arduino|avr} <-> RPi) you have to write the software; maybe process and display results  (but PCs / RPis have better screens/computing skills).

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

I am pretty sure that I have it working now, thank you all for your help.

 

Also, I was able to get the Arduino to relay uart through the use of both hardware and software serial.

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

I am pretty sure that I have it working now

Time for some testing.

 

I've run the little ultrasound modules off of several different micros.

The photo below shows a Tiny driving one of them.

The PCB was re-purposed to test the US module, that is the nice thing about making several of any PCB one takes the time to make.

Only a few minutes worth of work to assemble the board.

 

Obviously an older project, as I still used a 10 Pin programming Header, which I only used on my first several AVR boards.

 

The graph was a plot of my test results, using several readings averaged in software.

 

JC

 

 

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

Inkspell4,

 

would you care to post your final source code so that others can benefit in the future?

 

Thanks,

 

Ross

 

Ross McKenzie ValuSoft Melbourne Australia

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

I am still working on some bugs, I am currently having an issue with the echo maxing out at 255ms although it work anywhere below. Has anyone else had this issue?

 

After I have it fully working I plan on posting the source code.

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

Not sure what your referring to when you say it max's out at 255ms?  The data sheet for the device says max echo return pulse is 25ms, usable range is 150us - 25ms, extending to 38ms when out of range.

 

Jim

 

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

Have you thought of working with a copy of echoinches in your main (if I read the last version you gave, in case 2: echoinches can be in an inconsistent state if an interrupt occurs : you should copy it, in a inbreakable way (cli()... sei() ?) , into another variable which would be used only by main, and interrupt routines cannot modify...

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

I have that changed in my most recent version, but by the datasheet and the implementation, you cannot have interrupts occur during state 2.

 

Also, I meant 255us.

 

Newest Code:

 

I am utilizing an external oscillator, as well as a custom made serial library

 

#define F_CPU 8000000UL
#define BAUD_RATE 9600

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

volatile uint8_t pingState = 0;
volatile uint8_t echoState = 0;
volatile uint8_t echo = 0;

int main(void)
{
	SPH = (RAMEND & 0xFF00) >> 8;
	SPL = (RAMEND & 0x00FF);
	
	TIMSK1 = (1<<TOIE1);
	TCNT1 = 0;
	
	EICRA = (1<<ISC00);
	EIMSK = (1<<INT0);
	
	serialInit8N1();
	
	DDRD |= (1<<PIND3) | (1<<PIND4);			// Set Pins As Output
	
	sei();
	while (1)
	{
		switch (pingState){
			case 0:
				PORTD &= ~(1<<PIND3);			// Set Trigger Pin To 0
				_delay_us(2);					// Wait
				PORTD |= (1<<PIND3);			// Set Trigger Pin To 1
				_delay_us(10);					// Wait
				PORTD &= ~(1<<PIND3);			// Set Trigger Pin To 0
				pingState++;					// Increment State
				break;
			case 1:
				// While In This Case, Do Nothing
				break;
			case 2:
				uint8_t echoInches = echo/148;
				char temp[7];
				itoa(echo, temp, 10);
				serialOutString(temp);
				serialOutLine("us");
				pingState++;					// Increment State
				break;
			case 3:
				_delay_us(60000);				// Wait Before Going Back To State 0 , 60ms
				pingState = 0;					// Go To State 0
				break;	
		}
	}
}

ISR(INT0_vect){
	if(echoState == 0 && ((PIND & (1<<PIND2)) == 1)){						// If PIND2 Is High
		TCCR1B |= (1<<CS11);					// Enable Timer
		TCNT1 = 0;								// Reset Timer
		PORTD &= ~(1<<PIND4);
		echoState = 1;
	}else if(echoState == 1 && ((PIND & (1<<PIND2)) == 0)){										// Otherwise
		TCCR1B &= ~(1<<CS11);					// Disable Timer
		echo = TCNT1;							// Count echoInches
		pingState = 2;							// Increment State
		echoState = 0;
	}
}
ISR(TIMER1_OVF_vect){
	PORTD |= (1<<PIND4);						// Set LED High
	TCCR1B &= ~(1<<CS11);						// Disable Timer
	pingState = 2;								// Increment State
	echoState = 0;
}

 

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

inkspell4 wrote:

I am utilizing ... a custom made serial library

 

For completeness sake, you should also show the custom serial library code... or attach it as a zip file.

Ross McKenzie ValuSoft Melbourne Australia

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

Makes Sense, See Attached
 

Attachment(s): 

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

Update


 

 

I still have not been able to solve the issue with regards to the maximum ping length of 255us. Have tried making minute changes to the code to no avail, reverted back to what is above.