HC - SR04 interrupt driven MEGA code

I wasn't happy with the code examples which are blocking and use pulseIn for what are hopefully obvious reasons. Because of the issues with blocking I have written an interrupt based sketch for the MEGA. Consulting the datasheet should allow this to be easily converted to work on any of the 8 bit arduinos.

Trigger is pin 11

Echo is pin 12

I hope this helps someone.

#define FREQ_TRIG 15
#define PRESCALE_TRIG 64
#define PERIOD_TRIG ((F_CPU/PRESCALE_TRIG/FREQ_TRIG) - 1)

volatile uint32_t start,width;
float pingDistCentimeters,pingDistMeters;
volatile boolean newPing;

void setup(){
  Serial.begin(115200);
  SonarInit();//must go above detectRC or the program breaks!
}

void loop(){
  if (newPing == true){
    newPing = false;
    pingDistCentimeters = width  * 0.01724137;
    pingDistMeters = pingDistCentimeters * 0.01;
    Serial.println(pingDistCentimeters);
  }  
}

void SonarInit(){

  DDRB |= (1<<PB5);
  TCCR1A = (1<<WGM11)|(1<<COM1A1);
  TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11)|(1<<CS10);
  ICR1 = PERIOD_TRIG;  
  OCR1A = 2; 

  DDRB &= ~(1<<PB6);
  PORTK |= (1<<PB6);
  PCMSK0 |= 1<<PCINT6;
  PCICR |= 1<<0;

}


ISR(PCINT0_vect){
  if (newPing == true){
    return;
  }
  if (((PINB & 1<<PB6)>>PB6) == 1){
    start = micros();

  }
  else{
    width = (micros() - start);
    if (width <= 17400){
      newPing = true;
    }

  }

}

The above code has been corrected as per robtillaart's suggestion.

thanks for sharing!

refactored your code a bit,

  • newping should be volatile too
  • removed volatile from two others
  • use the newping flag to shortcut the ISR
    (prevents width to be changed by the ISR during the multiplication in loop, is not atomic)
volatile uint32_t start, width;
volatile boolean newPing;

uint32_t pingDistCentimeters;
float pingDistMeters;


void setup()
{
  Serial.begin(115200);
  SonarInit(); //must go above detectRC or the program breaks!
}

void loop()
{
  if (newPing == true)
  {
    pingDistCentimeters = width  * 0.01724137931034482758620689655172;
    newPing = false;       
    pingDistMeters = pingDistCentimeters * 0.01;
    Serial.println(pingDistCentimeters);
  }  
}

void SonarInit()
{
  DDRB |= (1<<PB5);
  TCCR1A = (1<<WGM11)|(1<<COM1A1);
  TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11)|(1<<CS10);
  ICR1 = PERIOD_TRIG;  
  OCR1A = 2; 

  DDRB &= ~(1<<PB6);
  PORTK |= (1<<PB6);
  PCMSK0 |= 1<<PCINT6;
  PCICR |= 1<<0;
}

ISR(PCINT0_vect)
{
  if (newPing == true) 
    return;
  if (PINB & (1 << PB6))  // all values != 0 are true  // HIGH
  {
    start = micros();
  }
  else  // LOW
  {
    width = (micros() - start);
    if (width <= 17400)
    {
      newPing = true;
    }
  }
}

Nice catch there. Thanks. Though I suppose that both the distance in meters and centimeters should be floats.