Parallax Ultrasonic Sensor and Orangutan Jr

I wish to use the Parallax Ultrasonic Sensor with the Orangutan Jr. Has anyone already done this and have some sample code they can share?

Joemur,

I am interested in using this device. Were you able to get a good working distance type code to work for the Orangutan?

-Tony

Hi,

I am working on it. I got a response to my query about getting the RC signal from a radio and a link to a piece of code that should work for this as well. I finished making my test platform last night and I’m ready to start playing with the code.

The concept is actually relatively simple. In block form, change he ddr to output, send a high for about 5 uS, change to an input and read the clock timer, wait for the interrupt and read the clock timer again. Check to see that the second timer is larger than the first to make sure it hasn’t wrapped. Subtract first from second and you have a time duration. Determine by measuring time duration against actual inches and you should get a linear result you can calculate.

Now to go and test the theory…

Also, since I am new to this micro, I still haven’t figured out yet how to send debug information to a terminal using the printf or whatever method is available in AVR Studio. Any help there?

Joemur,

I am very new to this stuff as well and I am afraid I can’t help you. I started with the EZ1 and had little success of getting usable distance measurements. However, Check this stream ez1 code for Orangutan LV-168 for what I tryed with the EZ1. Maybe it can help you. Let me know.

-Tony

Thanks for the sugggestion but it appears the sensor you were asking about uses an analog input and therefore works completely different. Also, I am using the Or. Jr. which does not have an LCD so displaying debug data to the LCD also does not apply…but thanks for suggesting a possible solution.

I will probably not have any useful data until early next week because work is sending me out of town but I will post my results once I have some.

Hey again Joemur (Joe?), unfortunately sending bytes back to the computer from a Baby Orangutan isn’t quite that simple, but it’s not too hard. It sounds like you’ve used another microcontroller system before that was based on a serial bootloader (i.e. Basic Stamp, Handy Board, OOPic, Arduino, Butterfly…am I close?). With one of those boards, you program the device right from your computer serial port, and you can talk to and get information from the device over that same serial port, sometimes directly from the compiler.

Your AVR programmer is using a different interface to program the ATMega168 chip on your Baby Orangutan, and in general these programmers don’t support real-time two way communication (there is a weird on-chip emulator that lets a special $$$ programmer control the flow of the program and read the chip state at any time, but its super-complicated, and I’m guessing you have one of the standard programmers).

Anyway, the ATMega168 does have a USART serial port, and I can show you some code to configure it to send bytes back to a terminal program on your computer, but the real problem is how you’re going to connect the two. The chip uses 0-5V, or logic level (or TTL) serial, while a computer serial port uses +/- 12V, so a direct connection could really damage your Orangutan and ruin your day.

Other than the voltage levels, the protocols are compatible, so you have a couple of options of how to hook them up. If you have an RS-232 or USB to TTL-level serial converter (like the Pololu deluxe serial adapter, USB-to-serial adapter, or the one built into the Pololu USB Orangutan programmer) you’re set! You could also rig up a circuit yourself with something like a max232 logic level shifter. There’s also a trick for one way communication from a 5V logic device to an RS-232 serial port, but you have to build a circuit to invert the logic.

So, do any of these sounds good to you?

-Adam

P.S. While AVRStudio can’t communicate in real time with your board, it does have a built-in code simulator which is useful for some debugging. You can’t hook it up to other virtual devices, but you can twiddle bits at will to see how it responds to fake inputs. Okay, actually there is a virtual LCD extension, but I’ve never really gotten that to work!

P.P.S. Some of those serial bootloader boards are based on AVR microcontrollers, so if you really, really wanted to, you could actually convert your Baby Orangutan. In particular there is a nice open-source compiler/bootloader system called Arduino that uses a simplified C language with built-in functions for accessing the chip hardware. It’s a little easier to get started, but I find you aren’t able to do as much in the end. You would still need a way to make a bi-directional serial connection with your Baby Orangutan, but after downloading the bootloader once, you’d be set (and yes, there is a Serial.println() function).

Yes, the name is Joe last name is Murawski which I then truncate and combine into JoeMur.

As you surmised, I have used several different IDE’s over the years. Funny the only one you mentioned is the Basic Stamp which I haven’t used for years because I moved on to using MBasic programming PIC’s. In addition, I have used a JTAG to program TI’s DSP, and a different compiler to generate code for an 8051. I have also programmed Rabbit boards which has the in circuit debugger and recently I have been programming the Netburner boards with NBEclipse which is supposed to have an in circuit debugger but I haven’t used that. Instead I am used to putting in printf statements that generate output to stdio and see it on a terminal screen. I know it is a little old school but I’m used to it and it works for me.

With that said, I was thinking I need to add the max232 chip to my setup (maybe on a separate board that I can remove later) so that I can send serial messages telling me what is going on. That would work pretty much the same as sending the data to the lcd.

I guess I was looking for confirmation that there wasn’t an easier way to do it.

Of which, I am using the USB programmer from Pololu which as you mentioned does work as a usb to serial converter (or at least I think it is supposed to since I program on Com2) which lead me to believe I could use that for some feedback. I assumed (probably wrongly) that the AVRStudio would have a terminal screen built into it to work with the device so I wouldn’t have to switch a bunch of stuff to accomplish this.

That’s quite a microcontroller CV you’ve got there. Now I feel silly for going into such detail before.

Anyway, some AVRs have JTAG support, but the ATMega168 has Atmel’s own flavor of in-circuit debugger called debugWIRE, which you need their special programmer to access. Personally I’ve never used it.

You could hook in a MAX232 board, or you could use your Pololu USB programmer itself to handle the serial level shifting. The programmer has two chips, an ATMega48 with code to emulate a serial AVRISP programmer, and a CP2102 USB to TTL level serial adapter. The jumper on the programmer connects the adapter’s RX pin either to the ATMega48’s TX pin, or to that set of user-accessable TTL level TX, RX, and ground pads.

So basically you could connect the programmer’s (really the adapter’s) TX and RX pins to your Baby Orangutan’s RX and TX pins, and communicate serially over the same com port you use to program, just not at the same time. The programming header should even take care of the ground connection (although real AVRISP’s have a nasty tendency of resetting your (my) board at the worst possible time when you leave them connected).

The down side to this is that 1) you have to close the programmer window (but not AVRStudio itself) and connect your terminal program each time you want to talk to a program you just downloaded, and 2) you need to move the jumper, or make some kind of physical switch to switch the RX line from program to talk.

By the way, whatever you decide to do, here’s some code to setup and test your Baby O’s USART (make sure it’s set to run off its 20MHz external oscillator, or change F_CPU to 8000000 in the code if it’s running of of its internal 8MHz clock):

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

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

volatile unsigned char newSerCmd=0;
volatile unsigned char serCmd;

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;
}

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

int main(){
	USART_Init(MYUBRR);

	USART_Trans('H');
	USART_Trans('i');
	USART_Trans(0X0D);//Carriage Return

	sei();//enable global interrupts

	while(1){
		if(newSerCmd){
			USART_Trans('T');
			USART_Trans('e');
			USART_Trans('s');
			USART_Trans('t');
			USART_Trans(0X0D);//Carriage Return
			newSerCmd=0;
		}
	}

	return 0;
}

-Adam

P.S. Oh man, if the USB to Serial programmer broke out all of the ATMega48 pins I would so get one just to use the MCU itself with built in serial<->USB. Oh well, maybe on the Baby X2…

OK, my first attempt to try and read the ultrasonics failed.

I cannot get the pin change interrupt to work properly. As a test, I tried setting up PCIMSK from 0 to 23 to see if maybe I was missing something and it seems i can generate a pin interrupt when I generate a servo command but it isn’t the right one and doesn’t seem to pertain to the correct pin. In the following code most of the stuff is commented out because of testing different things but you can probably see what I have tried.

Basically, I am trying to turn on pins C3, C4 and C5 as outputs set high then change them to inputs for a return ping from the ultrasonic. I according to the datasheet, it should be PCINT11, PCINT12, and PCINT13 but I do not get any edge triggering on those.

Any help will be appreciated.

// F_CPU tells util/delay.h our clock frequency
//#define F_CPU 8000000UL	// Orangutan frequency (8MHz)
#define F_CPU 20000000UL	// Baby Orangutan frequency (20MHz)
#include <avr/io.h>
#include <util/delay.h>
#include <servo.h>
#include <avr/interrupt.h>

// Include information about the device we're building for
#include <device.h>

void init (void);
void PCINT0_vect(void);
void PCINT1_vect(void);
void PCINT2_vect(void);
void PCINT3_vect(void);
void PCINT4_vect(void);
void PCINT5_vect(void);
void PCINT6_vect(void);
void PCINT7_vect(void);
void PCINT8_vect(void);
void PCINT9_vect(void);
void PCINT10_vect(void);
void PCINT11_vect(void);
void PCINT12_vect(void);
void PCINT13_vect(void);
void PCINT14_vect(void);
void PCINT15_vect(void);
void PCINT16_vect(void);
void PCINT17_vect(void);
void PCINT18_vect(void);
void PCINT19_vect(void);
void PCINT20_vect(void);
void PCINT21_vect(void);
void PCINT22_vect(void);
void PCINT23_vect(void);
void send_pulse(int j);


void delayms( uint16_t millis ) {
	while ( millis ) {
		_delay_ms( 1 );
		millis--;
	}
}

// PC0 = Side
// PC1 = Fwd/Rvs
// PC2 = Weapon
// PC3 = Ultra1
// PC4 = Ultra2
// PC5 = Ultra3

unsigned int ultra1_st, ultra2_st, ultra3_st, ultra1_dn, ultra2_dn, ultra3_dn, ultra1, ultra2, ultra3 = 0;
unsigned int distance1, distance2, distance3 = 0;

int main( void ) {
	init();

	// Three servos
	unsigned char side, fwd, weapon;
	unsigned char side2, fwd2, weapon2;


	// Initialize the servo system
	servo_init();

	// Define our servos to use PC0 and PC1, respectively
	side  = servo_define(_SFR_IO_ADDR(DDRC), _SFR_IO_ADDR(PORTC), 0);
	fwd = servo_define(_SFR_IO_ADDR(DDRC), _SFR_IO_ADDR(PORTC), 1);
	weapon = servo_define(_SFR_IO_ADDR(DDRC), _SFR_IO_ADDR(PORTC), 2);

	side2  = servo_define(_SFR_IO_ADDR(DDRC), _SFR_IO_ADDR(PORTC), 3);
	fwd2 = servo_define(_SFR_IO_ADDR(DDRC), _SFR_IO_ADDR(PORTC), 4);
	weapon2 = servo_define(_SFR_IO_ADDR(DDRC), _SFR_IO_ADDR(PORTC), 5);




	// Turn them both on
	servo_active(side);
	servo_active(fwd);
	servo_active(weapon);

	servo_active(side2);
	servo_active(fwd2);
	servo_active(weapon2);



	PORTD &= ~( 1 << PD1 );	// LED off
	delayms( 900 );			// delay 900 ms
	DDRD |= 1 << PD1;			// set LED pin PD1 to output
	PORTD &= ~( 1 << PD1 );	// LED off
	delayms( 900 );			// delay 900 ms

	for (;;)
	{
//		int j = 0;	

//		for (j = 3; j < 6; j++)
//		{
//			send_pulse(j);
//		}

		// check for return

//		if (ultra1){
//			ultra1 = 0; 
//			distance1 = ultra1_dn - ultra1_st;
//			if (distance1 > 0)
//			{
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 100 );			// delay 100 ms
//			}
//		}
//		if (ultra2){ultra2 = 0; distance2 = ultra2_dn - ultra2_st;}
//		if (ultra3){ultra3 = 0; distance3 = ultra3_dn - ultra3_st;}

		// Add servo control to three different pins
		// Run from 1000us to 2000us in 10us increments
		//servo_set(weapon, 1510);
		//servo_set(side, 1510);
		//servo_set(fwd, 1510);

		servo_set(weapon2, 1510);
		//servo_set(side2, 1510);
		//servo_set(fwd2, 1510);
		delayms(900);

	}
	return 0;
}



void init()
{

	DDRC = 0x07; // Set PC0 - PC2 to outputs, PC3 - PC5 to inputs
	PCMSK0 = 0xFF;
	PCMSK1 = 0xFF;
	PCMSK2 = 0xFF;
	PCICR = 0x07;
	//PCMSK1 |= (1 << PCINT11) | (1 << PCINT12) | (1 << PCINT13);  // Set pin-change interrupt mask for desired pins
	//PCICR |= 1 << PCIE2; // Enable pin change interrupt for masked pins of port C.
	sei(); // set global interrupt enable
}

void PCINT0_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT1_vect()
{
				PORTD |= 1 << PD1; 		// LED on
				delayms( 900 );			// delay 100 ms
				PORTD &= ~( 1 << PD1 );	// LED off
				delayms( 900 );			// delay 900 ms
}

void PCINT2_vect()
{
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
}

void PCINT3_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT4_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT5_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT6_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT7_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT8_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT9_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT10_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT14_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT15_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT16_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT17_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT18_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT19_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT20_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT21_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT22_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT23_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
}

void PCINT11_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//	if (PC3 & 0)
//	{
//		ultra1_st = TCNT0;   // Get value from timer (replace zero)
//	}
//	else
//	{
//		ultra1_dn = TCNT0;	 // Get value from timer (replace zero)
//		if (ultra1_st < ultra1_dn)
//		{
//			ultra1 = 1;
//		}
//	}
}

void PCINT12_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//	if (PC4 & 0)
//	{
//		ultra2_st = TCNT0;   // Get value from timer (replace zero)
//	}
//	else
//	{
//		ultra2_dn = TCNT0;	 // Get value from timer (replace zero)
//		if (ultra2_st < ultra2_dn)
//		{
//			ultra2 = 1;
//		}
//	}
}

void PCINT13_vect()
{
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
//				PORTD &= ~( 1 << PD1 );	// LED off
//				delayms( 900 );			// delay 900 ms
//				PORTD |= 1 << PD1; 		// LED on
//				delayms( 900 );			// delay 100 ms
//	if (PC5 & 0)
//	{
//		ultra3_st = TCNT0;   // Get value from timer (replace zero)
//	}
//	else
//	{
//		ultra3_dn = TCNT0;	 // Get value from timer (replace zero)
//		if (ultra3_st < ultra3_dn)
//		{
//			ultra3 = 1;
//		}
//	}
}


void send_pulse(int j)
{
	// Set DDRC to all outputs
	DDRC = 0x3F; // Set PC0 - PC2 to outputs, PC3 - PC5 to outputs

//		PORTC &= ~( 3 << PC3 );	// Ultrasonic 1 Off
//		PORTC &= ~( 4 << PC4 );	// Ultrasonic 1 Off
//		PORTC &= ~( 5 << PC5 );	// Ultrasonic 1 Off
//		delayms( 900 );			// delay 900 ms
//		PORTC |= 3 << PC3; 		// ultrasonic 1 On
//		PORTC |= 4 << PC4; 		// ultrasonic 1 On
//		PORTC |= 5 << PC5; 		// ultrasonic 1 On
//		delayms( 900 );			// delay 100 ms


	// set output high
	switch (j)
	{
		case 3:
			//PORTC |= (1 << PC3); 		// ultrasonic 1 On
			break;
		case 4:
			//PORTC |= (1 << PC4); 		// ultrasonic 2 On
			break;
		case 5:
			//PORTC |= (1 << PC5); 		// ultrasonic 3 On
			break;
	}
	delayms( 50 );			// delay 100 ms
	
	//PORTC &= ~( 1 << PC3 );	// Ultrasonic 1 Off
	//PORTC &= ~( 1 << PC4 );	// Ultrasonic 2 Off
	//PORTC &= ~( 1 << PC5 );	// Ultrasonic 3 Off

	// Set ultra to input
	DDRC = 0x07; // Set PC0 - PC2 to outputs, PC3 - PC5 to inputs
}

I haven’t looked at your code in great detail, but I can tell one reason your interrupts aren’t working. Making functions with the names of the interrupt vectors doesn’t really associate those functions with the interrupts (it has some effect on their vectors, but not the one you really want). An enabled interrupt event causes a jump to that interrupt’s vector in your code, and there has to be another vector put there pointing to the function you want to execute at interrupt. At least I think that’s how it works; it’s all very assembly-esque, putting special memory addresses at other special memory addresses, but there’s an easy way to do it in C too.

You just need to write your interrupt functions as ISRs (interrupt service requests):

ISR(PCINT0_vect){
	//what you want to happen
}

ISR functions don’t need prototypes, and you can have as many of them as you have interrupt vectors, for example:

ISR(PCINT0_vect){
	//what you want to happen in response to a pin change on PortB
}

ISR(PCINT1_vect){
	//what you want to happen in response to a pin change on PortC
}

ISR(PCINT2_vect){
	//what you want to happen in response to a pin change on PortD
}

It looks wrong in C, but you can do this because you’re not really defining a function called ISR, you’re instructing the compiler to create your interrupt function off somewhere in the HEX code, and to put it’s start address at the memory vector associated with that interrupt. Neat!

Which brings up another point, although there are 24 pin change interrupt pins on the ATMega168, there are only three actual pin change interrupts (i.e. interrupt vectors). As indicated in the code comments above, PCINT0_vect gets called in response to a pin change of any (enabled) pin of PortB, aka PCINT0-PCINT7, PCINT1_vect in response to a pin change of any (enabled) pin of PortC, and PCINT2_vect in response to a pin change of any (enabled) pin of PortD.

Also, in your specific application, you’re trying to send a high pulse out a particular pin, then time how long it takes for an input level change to occur in response on that same pin, right? Keep in mind that any state change of a pin with its associated pin change interrupt enabled will cause an interrupt, independent of its input/output setting. So, if you set PC3 to an output, then set it high, that change will cause the PCINT1_vect interrupt (if it is enabled). Basically you’ll want to disable that interrupt in some way (globally, or in PCICR) before setting the pin to an output, then enable it again after setting the pin back to an input.

You seem to have figured out the rest, you’re including interrupt.h, the PCMSK registers control which pins will trigger their respective port interrupts, the lower three bits of PCICR enable the three pin change interrupt vectors respectively, and the sei() function enables interrupts globally. Just use the ISR function and you should be set!

-Adam

P.S. Are you talking about the PING))) sensor? I haven’t used that exact one, but I thought it was funny that they made a product with a logo that lent itself to be reproduced in ASCII.

Thanks for the advice. I will give it a try and it sounds like it should work. One thing you have me a little confused on and is how you mask just one pin. It sounds like you can only get to the port level then have to check the value of each of the pins to see what transitioned.

Yes, I’m talking about the PING))). I understand the need to turn off the interrupts or it will see a transition. In truth, I need to see the negative transition when changing from high to low and then the transition from low to high. I will figure those details out once I get the isr working the way I need to.

I will post the results once I get everything working.

In general you’re right. If you have multiple pins in a port enabled as pin-change interrupts (their corresponding bits set in PCMSKn), you’ll only know that an interrupt has happened, not automatically which of the pins caused it. It’s useful for some things, like quadrature encoder counting, or waiting for one of a large number of inputs to change. Also, you can set it on just one pin of a port, then it’s like a regular external interrupt, but on almost any pin you want.

-Adam

The code I linked you to in that other thread has one problem with it as Nexisnet has already pointed out: I forgot to use the ISR tag on the interrupt function. Additionally, that code was for the mega644, which links PCINT0 to port A. The Baby Orangutan has a mega48/168, which links PCINT0 to port B. Fixing these problems gives an example of how you can figure out which pin of a given port has changed. pinB is a global unsigned char that is set equal to PINB just before I enable the pin-change interrupts.

ISR(PCINT0_vect)
{
    unsigned char newPinB = PINB;    // get new state of port B pins
    unsigned char changedBits = pinB ^ newPinB;    // if pin has changed, the corresponding changedBits bit will be set
    pinB = newPinB;    // save the new state of the pins

    unsigned char i, mask = 1;
    for (i = 0; i < 8; i++)
    {
        if (changedBits & mask)
        {
            if (pinB & mask)
            {
                // handle pin PBi change from low to high
            }
            else
            {
                // handle pin PBi change from high to low
            }
        }
       mask <<= 1;
    }
}

- Ben

Hello all,
I also have been attempting to use a PING))) Sensor with an ATMega168 (not on an Orangutan, but just
the microcontroller all by itself). I’ve been getting some quirky results, and I hope someone can help me out on this:

I was planning to use the ICP mode of the ATMega168, and have attempted to write some code that
uses an interrupt service routine for the TIMER1 Capture vector.

And so my troubles began: my first attempt failed to control this sensor. I determined
this failure by connecting the ATMega168 to a PC serial port and reading out the counter values
I was getting from the TIMER/Counter on the ATMega (which should have been counting the
width of the pulses from the PING sensor). I noticed the values displayed had no relation to the distance
between my PING sensor and a wall. I sensed something was fishy with my code.

So, I started debugging and quickly found out that the ATMeg168 was inexplicably resetting.
I immediately suspected the watchdog timer, but then my attempts to turn off the WDT didn’t
seem to work, either. I then suspected that the serial port connection to the ATMega168
was causing some sort of ground bounce and thus resetting the ATMega. To test this, I disconnected
the serial port, and to keep track of what parts of the code were running, I arranged to
light LEDs at different points in the code.

After many iterations, I programmed the code below. The problem is, I’m still reseting
unexpectedly and repeatedly (so, the serial port is not apparently the problem). I copied the
WDT_off function code from the Atmel Datasheet for the ATMega168 (I did comment out the __watchdog_reset()
function statement, which was not defined anywhere in the datasheet).

My code:

#define F_CPU 8000000UL

#define FOSC 8000000  //usart clk spd
#define BAUD 9600
#define MYUBRR FOSC/16/BAUD-1 // sets the baud rate for the serial port

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


void delayus( uint16_t micros ) {
	while ( micros ) {
		_delay_us( 1 );
		micros--;
	}
}


void WDT_off(void)
{
 
  cli();
// __watchdog_reset();  // where is this defined??
  /* clear WDRF in MCUSR */
 MCUSR &= ~(1<<WDRF);
 /* Write logical 1 to WDCE and WDE */
 /* keep old prescalar setting to prevent unintentional time out */
 WDTCSR |= (1<<WDCE) | (1<<WDE);
 WDTCSR = 0b00100001;

 /* turn off the WDT */
 WDTCSR = 0x00;

}


////////////////////////////
// Define Iterrupt Service Routines
////////////////////////////


//Timer1 capture interrupt service subroutine

ISR (TIMER1_CAPT_vect)
{
      
	  TCCR1B = TCCR1B&0xBF;
	  TIFR1 = 32;
	  PORTD = 16; // turn on PORTD Pin 4 to see if I get here
	  delayus(50000);
	  delayus(50000);
	  
}
ISR (WDT_vect)
{
 PORTD = 0;
}

// main program

int main(void) {

// turn off the Watchdog timer 
   WDT_off();
    SREG = 0x00; //globally disable interrupts
    USART_Init(MYUBRR); // I initialize the USART, but currently don't use it
    DDRD = 0xFF;
	/*Noise canceller, without prescaler, rising edge*/
     TCCR1B=0xC1;
	TIFR1=0x20;
    TIMSK1=0x24; // includes setting ICIE1 to 1; enable this interrupt
    PORTD = 8;   // turn on PORTD Pin3  to see if I get to this place
	delayus(50);
    delayus(50);
	delayus(50);
    DDRB = 0x00; 

    SREG = 0x80;

   while(1)
   {

     PORTD = 32; // turn on  PORTD Pin 5 to see if I get here 

   }

}

The Fuse settings on the ATMega168 are WDTON set to 0; Brownout detect is disabled.

The results of my experiment are:
The LED on PORTD Pin3 is dimly lit (but steady); this tells me I’m still resetting;
the LED on PORTD Pin5 is brightly lit indicating that I get to the while loop, and stay in there for some time

Whenever I take PIN0 of PORTB high, the LED on PORTD Pin4 lights for a time, then goes out. At this point
the two other LEDs on PORTD are lit as described above. This tells me the ISR functions.

So, I’m stuck in that the ATMega is resetting when I don’t want it to. Now, my ultimate goal is to write some
code to use the Parallax PING sensor properly, so if anyone can help me directly with that, I would be most
obliged. If, however, someone could also point out what I’m doing wrong here, I would it appreciate that as well!

BTW: I’m using AVRStudio4 (revision 4.16, build 638) with the gcc compiler for code entry and compile/link/assemble.

Thanks in advance for any help!!

Dave

I’m guessing your problem is most likely due to your electrical connections. Sonar sensors draw large bursts of current when they emit their pings, which can cause voltage dips on your supply voltage and interfere with digital electronics in the circuit. How are you powering the components of your circuit? Do you have your sonar sensor’s power sufficiently isolated/decoupled from your AVR’s power?

- Ben

Hello Ben,
Thanks for the reply!
You bring up a good point. However, I’ve noticed the ATMega resetting even with the PING sensor removed from the circuit. Having said that,I think electrical connections is a suspect. I am powering the ATMega with pack of 4 AA batteries. I’ve measured the voltage at the ATMega when it’s operating, and I see 4.81V. But that’s on a DMM, not an o-scope, so there could be some spikes. Also, my ATMega and the LEDs are in one of those solderless prototype boards - the white boards with rows of contacts in which you plug the component, and then jumper wires to make connections. That’s possibly not the most stable platform. And, I don’t have any de-coupling capacitors.

While we’re on the subject: should I be tying my unused inputs (especially those related to things like external interrupts) to ground? I haven’t enabled those interrupts explicitly, but do they default to enabled when I power up?

Thanks again for any help!!
Dave

Hello Ben,
I tried replying, but it didn’t seem to update. If there are two replies, I apologize.

Thank you for your reply! I have removed the PING Sensor from the circuit entirely, and still experience the resetting phenomenon. However, the circuit is assembled on one of those white prototype boards with solderless connections: the type with rows of holes in which you insert components and jumper wires to make a circuit. These don’t strike me as a “clean” circuit, but they are handy.
I am powering the circuit with 4AA batteries, which develop a voltage of 4.81V at the ATMega. However, I measured that with a DMM, and no an o-scope, so there could well be dips in the power that I’m not seeing. Also, I am not using a decoupling capacitor; maybe I should.
Lastly, I have not tied any of the external interrupt pins to ground or Vcc since I have not explicitly enabled those interrupts in my code. Should I set these pins to ground or power anyway?

Thanks again for all the help!
Dave

The best thing for your circuit would probably be a regulator, but I’d at least suggest putting a capacitor or two across power and ground near your AVR, and another one across your sensor’s power and ground somewhere near the sensor. There’s no reason to connect unused pins to power or ground; if you’re paranoid about the voltages on those pins you can always enable their internal pull-ups to weakly connect them to 5V, but this won’t do anything for you if you’re not enabling their external/pin-change interrupts (and also setting the global interrupt flag). Interrupts default to disabled and must be explicitly enabled.

Do you have brownout detection enabled for your AVR (via the fuses)? There’s a register you can check on startup to see what caused the last reset; maybe that could give you a hint about what’s going on.

Some other things to consider:

  1. Using interrupts incorrectly can have results that make it look like your AVR is resetting. If you enable an interrupt but don’t create an ISR for it, the interrupt event will jump your code to the appropriate line of the interrupt look-up table, but the instruction there will not jump you to an ISR, so program instruction does not return where it left off when the interrupt occurred. Is it possible you have enabled an interrupt without defining the correct ISR?

  2. Stack problems can lead to bizarre program behavior. On the AVR, RAM is used to store the stack on one side and your globals on the other. If the two ever overlap, you can overwrite your stack data by setting your global variables and you can overwrite your global variables with the stack. Are you using a lot of your RAM? For example, are you using recursion? Do you have any huge global or local variable arrays?

- Ben

Hi Dave,

When you say you have “WDTON set to 0”, do you mean that you have the box unchecked in the fuse settings in AVR Studio, or do you mean that the WDTON fuse bit is set to zero? Setting the fuse bit to zero actually enables the watchdog timer, which will repeatedly reset your AVR as you describe.

Can you post the hex values of the extended, high, and low fuse bytes? You can read these off of the bottom of the fuses tab in the AVR Studio programming menu.

In any case this probably isn’t your problem, since the watchdog timer disabling function at the beginning of your code looks okay. You’re setting the watchdog timer prescaler bits twice, but that shouldn’t be an issue.

-Adam

Dave,

Paul just reminded me that you have already posted your code and fuse settings, which renders much of my previous post moot. Looking at your code, I see that you’re enabling two Timer1 interrupts:

TIMSK1=0x24; // includes setting ICIE1 to 1; enable this interrupt

This enables the input-capture interrupt and the output-compare-B-match interrupt, but you have only written an input-capture ISR. Why are you enabling the OCIE1B interrupt? I suspect that this interrupt is being generated every time the timer1 counter TCNT1 equals 0 (which is the default value of OCR1B), which jumps to the interrupt vector table only to find no appropriate redirect at that location, thereby simulating a reset. Try using the line:

TIMSK1 = 0x20;

Or, conversely, try defining an empty ISR for the compare-match interrupt.

- Ben