Parallax Ultrasonic Sensor and Orangutan Jr

Hello Ben, Adam, everyone,

With respect to Adam’s question about the WDTON fuse:
You bring up a good point; sorry for the confusion. I have left the WDTON box UNCHECKED when I program the fuses, so in this instance, I should be turning off that fuse. Additionally, as you point out, the WDT_off() routine is doing is its job, so cool: WDT should not be a problem. Thank you for reviewing the code!

Ben: I set TIMSK1 = 0x24 because I blindly copied some code I found on the internet describing the ICP mode of the AVR processor. I should have looked at it a bit more closely. Thank you for catching this! I will code it up and see what happens.

Thank you all for the help! I will post with the results soon.

Cheers!
Dave

Hello again,
I tried Ben’s suggestion of setting TIMSK1 = 0x20 (instead of 0x24) and everything worked as it should! Thank you Ben (and Adam)!!
I have some followup observations that I include just for completeness:

  1. thinking about what Ben said about the Stack Pointer, I set SP=0x100; right after my WDT_off() routine. When I did this, the ATMega168 would reset after coming out of the TIMER1_Capture_vect ISR. When I don’t specify the SP (i.e., don’t give it a particular value) I don’t get this odd reset effect. So, clearly, as Ben mentioned, if you mess with the Stack, odd things happen.

  2. My fuse settings (as read from the programmer interface) are: 0xE2 0xDF 0xF9

  3. I’m using 8.1% of my program memory and 0% of my data memory.

So, now that that’s been solved, I can return to my original quest, which is to integrate the PING))) to the ATMega168.
My plan:
turn off WDT
setup the TIMER1 as before, TIMSK1 = 0x20, etc.
set PORTB to be an output.
send a high pulse on PIN0 of PortB (to be connected to the PING))) )
change PORTB to input
enable the global interrupt (SREG = 0x80)
use the counter in the ISR routine to count the width of the pulse from the PING)))
send that count to the USART port for testing purposes.

I think I’ll send a pulse out of PORTB PIN0 inside a while(1) loop, with a delay of every second or so.

Oh yeah - and put a LOT of capacitors around the ATMega and the PING))). Also, I like Ben’s recommendation of using a voltage regulator; that couldn’t hurt, either.

Thanks again for all the help!!! If there’s interest, I’ll post my progress.

Cheers!!
Dave

Your fuse settings look good, so you can skip disabling the watchdog timer.

Instead of triggering the PING sensor from a delay-driven while loop, you may find it easier to trigger it using interrupts from Timer1, which you use for the input capture anyway. That way if you set the timer prescaler properly you can insure that Timer1 doesn’t overflow during the pulse you’re trying to measure, which will make things a little simpler.

I just came across some old code I wrote to use the timer capture function on an old Baby Orangutan (Mega168) to measure the pulses generated by the Maxbotix EZ ultrasonic sensors, which are very similar to the Parallax Ping (you trigger the sonar with a high logic pulse, then measure the time between the rising and falling edges of the return pulse).

The code is attached below, you may find it useful to look at. There’s also some other stuff going on in there (I think I was experimenting with sending a modulated IR signal at the same time), but I didn’t want to go cutting parts out without testing it in case I accidentally messed up something important.

/*Input Capture Test
Adam Borrell
Mobile Robotics Lab
University of Michigan
2/14/08

Timers:
Timer0 - LED PWM
Timer1 - sonar triggering/measuring
Timer2 - PPM transmission
*/

//Definitions
#define F_CPU 20000000//CPU clock
#define BAUD 19200//baud rate for UART
#define MYUBRR (F_CPU/16/BAUD-1)//baud rate variable for UART hardware

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

//Global Constants
const int d1A=15625;//duration of Timer1
//const unsigned char PPMIN[4]={127,127,0,127};//PPM signal parameters

//Global Variables
unsigned char ovfc=0;//initialize Timer1 CompareA overflow counter
volatile unsigned char checkAccel=0;//check accelerometer
volatile unsigned char newSonar=0;//new sonar reading ready
volatile unsigned char newSerCmd=0;
volatile unsigned char serCmd;
volatile unsigned int range;//sonar reading (in 1.742 cm)
volatile unsigned char tog=0;//toggle counter

//Function Prototypes
void USART_Init(unsigned int ubrr);
void USART_Trans(unsigned char data);
void USART_Process();

//Interrupt Handlers
ISR(TIMER1_COMPA_vect){//Sonar trigger pulse on
	PORTD|=(1<<PD4);//sonar pulse on
	PORTC^=(1<<PC3);//toggle blue indicator LED
	TIFR1=(1<<OCF1B);//Clear Timer1 CompareB interrupt flag
	TIMSK1|=(1<<OCIE1B);//enable Timer1 CompareB interrupt
	newSonar++;
	TIMSK0|=(1<<OCIE0A);//enable Timer0 CompareA interrupt
	TCNT0=OCR0A;//set Timer0
}

ISR(TIMER1_COMPB_vect){//Sonar trigger pulse off
	PORTD&=~(1<<PD4);//sonar pulse off
	TIMSK1&=~(1<<OCIE1B);//disable Timer1 CompareB interrupt
}

ISR(USART_RX_vect){//USART Byte reieved
	serCmd=UDR0;
	newSerCmd=1;
}


ISR(TIMER1_CAPT_vect){
	if(TCCR1B&(1<<ICES1)){//rising edge
		range=ICR1;
		TCCR1B&=~(1<<ICES1);//input capture on falling edge
	}else{//falling edge
		range=(ICR1-range);
		newSerCmd=1;
		TCCR1B|=(1<<ICES1);//input capture on rising edge
	}
}

ISR(TIMER0_COMPA_vect){
	tog++;
	if(tog<13){
		TCCR0A&=~(1<<COM0A1);//Toggle OC0A on CompareA Match
	}else{
		TCCR0A|=(1<<COM0A1);//Set OC0A on CompareA Match
		tog=0;
		TIMSK0&=~(1<<OCIE0A);//disable Timer0 CompareA interrupt
	}
}

ISR(TIMER2_COMPA_vect){//Main control loop timer
	PORTD^=(1<<PD7);
}

int main(){
	DDRB|=(1<<PB1)|(1<<PB2);//Enable output pins
	DDRD|=(1<<PD5)|(1<<PD6)|(1<<PD7);//Enable output pins
	DDRD|=(1<<PD4);//Configure PortD IO for sonar trigger
	DDRC|=(1<<PC3);//Configure PortC IO for indicator LED

	USART_Init(MYUBRR);

	OCR1A=d1A;//set Timer1 CompareA value
	OCR1B=7;//set Timer1 CompareB to sonar pulse trigger width (25us, measured)
	TIMSK1|=(1<<OCIE1A);//enable Timer1 CompareA interrupt
	TCCR1B|=(1<<WGM12);//set Timer1 to CTC mode CompareA top
	TCCR1B|=(1<<CS11)|(1<<CS10);//set Timer1 to operate on system clock/64

	TCCR1B|=(1<<ICES1);//input capture on rising edge
	TIMSK1|=(1<<ICIE1);//input capture interrupt enable

	TCCR0A|=(1<<WGM01)|(1<<COM0A0);//Set Timer0 to CTC mode CompareA top, Toggle OC0A
	OCR0A=178;//Set OCRA for 112KHz with no prescale (2X modulation frequency, calibrated)
	TCCR0B|=(1<<CS00);//Set Timer0 to operate on system clock

	OCR2A=219;//half loop delay (experimentally determined)
	TCCR2A|=(1<<WGM21);//set Timer2 to CTC mode CompareA top
	TIMSK2|=(1<<OCIE2A);//enable Timer2 CompareA interrupt
	TCCR2B|=(1<<CS22)|(1<<CS21)|(1<<CS20);//Enable Timer2 on system clock/1024

	sei();//enable global interrupts

	while(1){
		if(newSerCmd){
			newSerCmd=0;
			USART_Trans(range);
		}
		if(newSonar){
			newSonar=0;
		}
	}

	return 0;
}

void USART_Init(unsigned int ubrr){//Initialize USART hardware & settings for Serial Radio
	UBRR0H=(unsigned char)(ubrr>>8);//set buad rate
	UBRR0L=(unsigned char) ubrr;
	UCSR0B=(1<<RXEN0)|(1<<TXEN0);//enable reciever & transmitter, recieve interrupt
	UCSR0B|=(1<<RXCIE0);
	UCSR0C=(3<<UCSZ00);//Set frame format for 8bit with 1 stop
}

void USART_Trans (unsigned char data){//Transmit a byte of data over USART
	while(!(UCSR0A&(1<<UDRE0)));//wait for transmition to complete
	UDR0=data;
}

I was hoping that by using a 16-bit timer to measure the sonar pulse widths directly I could get higher resolution readings from the EZ sonar than by reading the 8-bit serial or analog outputs of the sensor. To my disappointment the pulses did not vary continuously, but had 256 very distinct lengths. I would be curious to know how the Ping sensor compares.

-Adam

Thank you Adam,
I will give your code a try, and let you know how it all worked out with the PING)))

Thanks again!
Dave

Great post!!! looking forward on sharing my expertise and learning new ideas here at the same time…ill see you around guys…