Go Down

Topic: arduino ultrasound sensor example (Read 1 time) previous topic - next topic

ridgel

Hi All,

I just received my Arduino Dieimila last week, and I wanted to say thanks to the community for something that was so easy to set up with my Mac and get running.

As a first project, I worked through the Ultrasound example using the Parallax ultrasonic range finder. I modified the code to detect the returning pulse inside an external interrupt, rather than polling for it. I thought I'd post my code in case it proves useful for anyone else going in this direction. I haven't calibrated the sensor yet. The distances look approximately correct, with 0" starting a few inches from the sensor. That may be a timing issue on my part. Any comments or suggestions are welcome.

Thanks,

-Rob

/* Ultrasound Sensor
*------------------
*
* Reads values (00014-01199) from an ultrasound sensor (3m sensor)
* and writes the values to the serialport.
*
* http://www.xlab.se | http://www.0j0.org
* copyleft 2005 Mackie for XLAB | DojoDave for DojoCorp
*
* 2007-09: Modified by RJL (robertlucier@yahoo.com)
* to detect the ultrasound return pulse via INT1
*
*/

#define TIMER_US_PER_TICK 4 // 16MHz / 64 cycles per tick
#define TIMER_OVERFLOW_US TIMER_US_PER_TICK * 65536 // timer1 is 16bit
#define INCH_PER_SEC 13526 // speed of sound


// use pin 3 because it is connected to external interrupt 1 (INT1)
int ultraSoundSignal = 3; // Ultrasound signal pinint val = 0;

int ledPin = 13; // LED connected to digital pin 13
int count = 0;

// these are volatile because they are changed in interrupt handlers
// otherwise the compiler will assume they are unchanged and replace with constants.
volatile long ofStart = 0;
volatile long timerStart = 0;
volatile long ultrasoundValue = 0;
volatile int timer1_overflow = 0;

ISR(SIG_OVERFLOW1) {
 // keep track of timer1 overflows.
 // these should happen every TIMER_OVERFLOW_US microseconds (approx 4 per second at 16MHz clockspeed)
 timer1_overflow += 1;
};

void ultrasound_response() {
/* Listening for echo pulse
* -------------------------------------------------------------------
* response should come withing ~.5ms (6") and 10ms(20")
* handles falling pin interrupt signal
*/
if (ofStart != -1) {
  // check ofStart and return if a new ping has started.
  ultrasoundValue = diffTime();
}
}

void markTime() {
// !!! even though interrupts are disabled, this calculation is still error prone
// if the TCNT1 rolls over, but timer1_overflow isn't incremented.
cli(); // disable global interrupts
timerStart = TCNT1;
ofStart = timer1_overflow;
sei(); // enable global interrupts
}

long diffTime() {
 // return diff in microsec between now and the last mark
 long us;
 us = (timer1_overflow - ofStart) * 65536;
 us += (TCNT1 - timerStart);
 us *= TIMER_US_PER_TICK;
 return us;
}

long microsecs() {
 // for debugging
 long us;
 us = timer1_overflow * 65536;
 us += TCNT1;
 us *= TIMER_US_PER_TICK;
 return us;
}

void setup() {
 beginSerial(9600);                  // Sets the baud rate to 9600
 pinMode(ledPin, OUTPUT);            // Sets the digital pin as output
 attachInterrupt(1, ultrasound_response, FALLING);
 TCNT1 = 0; // reset timer 1
 TCCR1A = 0x00;          // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,
                         // PWM10=0 => PWM Operation disabled                                                                      
 TCCR1B = 0x03; // clock/64 = increments every 3.2us  - overflows in 209.7mS
 TIMSK1 |= _BV(0);   // timer1 overflow interrupt enable TOIE1=1. atmel_doc2545 p. 135      
}

void triggerPulse() {
 /* Send low-high-low pulse to activate the trigger pulse of the sensor
  * -------------------------------------------------------------------
  */
 int val;
 ultrasoundValue = -1; // reset any previous value
 ofStart = -1; // indicator to INT1 interrupt to ignore this LOW
 pinMode(ultraSoundSignal, OUTPUT); // Switch signalpin to output
 digitalWrite(ultraSoundSignal, LOW); // Send low pulse
 delayMicroseconds(2); // Wait for 2 microseconds
 digitalWrite(ultraSoundSignal, HIGH); // Send high pulse
 delayMicroseconds(5); // Wait for 5 microseconds
 digitalWrite(ultraSoundSignal, LOW); // Holdoff  
 Serial.print("!");

 // this could also be handled in an interrupt
 pinMode(ultraSoundSignal, INPUT); // Switch signalpin to output
 // poll until ultrasound sensor goes low
 val = digitalRead(ultraSoundSignal); // Append signal value to val
   while(val == LOW) { // Loop until pin reads a high value
   val = digitalRead(ultraSoundSignal);
 }
 markTime();
 Serial.print("_");
}

void printValue() {
 long in;
 long ft;
 // divide by 1000000 here in three steps of 100
 in = ((ultrasoundValue/100) * (INCH_PER_SEC/100))/100;
 in /= 2; // total elapsed time is there and back - divide by two for one way
 ft = in / 12;
 in = in % 12;
 Serial.print("\n");
 Serial.print('A'); // Example identifier for the sensor
 Serial.print(ultrasoundValue);
 Serial.print("\t");
 Serial.print(ft);
 Serial.print("'\t");
 Serial.print(in);
 Serial.print("\"");
 Serial.print("\n");
 serialFlush();
}

void loop() {
 printValue();
 triggerPulse();
 Serial.print(".");
 delay(100);
}

Daniel

hey

that is terrific... if you can, it's good to also make a page in the Playgorund, as the search function for the forum is terrible. At the bottom right corner of the Playground there is a link called "request contributor access". It issues you a password automatically.

ridgel

Thanks for the tip. I created a page called PingInterruptCode, and linked it from the Interrupts Example page. That page also links to the cool DCF77 receiver clock, which provided some inspiration to me on timer code.

Go Up