Need another pair of eyes! What is wrong this code?

Hi and thanks for looking at this thread. I've been hacking around with this for the past few evenings so it is probably a bit of a mess but I cannot get the code below to work as expected and I cannot see what is wrong with it.

The intent is that the Arduino receives an interrupt on interrupt 0 (pin 2) and then sends a short burst of 40kHz on pin 3. The interrupt is a little complex because it comes in over a radio receiver as a pulse train but that bit is working, in fact it all works except for sending the 40kHz burst. The timer code for this is correct, and it always sends one burst at start up but then does not send anymore, however the "transmitPing" method gets called because I see output on the serial console.

I assume something is disabling the timer or the pin but cannot see what.

const int TOL = 10; //frequency tolerence in uS
const int freqBounds[] = {200-TOL, 200+TOL};
const int LED = 13;
const byte DRIVE = 3;  // Timer 2 "B" output: OC2B pin 9 on the mega
const long frequency = 40000L;  // Hz

volatile unsigned long prevTime;
volatile unsigned long curTime;
volatile boolean sendPing; 
volatile int pulseCount;
volatile boolean trigPending;

//debug
volatile unsigned long irpStart;
volatile unsigned long irpEnd;
volatile unsigned long timings[12];
volatile int tct = 0;

void setup() {
    attachInterrupt(0, trigger, RISING); //interupt 0 - pin 2 on the nano
    Serial.begin(9600);      // open the serial port at 9600 bps:   
   
   trigPending = false; 
   setupUltrasonic();
}


void setupUltrasonic() 
{
  pinMode (DRIVE, OUTPUT);
  digitalWrite(DRIVE, LOW);

  TCCR2A = _BV (WGM20) | _BV (WGM21) | _BV (COM2B1); // fast PWM, clear OC2B on compare
  OCR2A =  ((F_CPU / 8) / frequency) - 1;    // zero relative  
  OCR2B = ((OCR2A + 1) / 2) - 1;             // 50% duty cycle
}  // setupUltrasonic


void loop() {

    if(sendPing) {
        transmitPing();
        pulseCount = 0;
        sendPing = false;
        digitalWrite(LED, HIGH);
        delay(100);
        digitalWrite(LED, LOW);
        
        //dumpTimes();
    }
    else {
        digitalWrite(LED, LOW);
        
        //debug
        if(pulseCount > 9) Serial.println(">>>>> Counter overflow <<<<<");        
    }
    
    //debugging, only dump if not in the middle of a pluse train
    //if(pulseCount == 0)  dumpTimes();
    if(pulseCount == 0) {
      //tct = 0;
      //dumpTimes();
    }

}


void dumpTimes() {

    String msg = String();
    
    //int irpDur = irpEnd - irpStart;
    //msg = "Interrupt duration: " + String(irpDur) + 
    //       ", " + String(irpStart) + ", " + String(irpEnd);
    int d = curTime - prevTime;
    msg = "d, prev, cur " + String(d) +  ", " + String(prevTime) +  ", " + String(curTime);
    Serial.println(msg);
    
    Serial.print("Timings:\t");    
    for(int i=0; i<12; i++) {
        msg = String(timings[i]) + "\t";
        timings[i] = 0;
        Serial.print(msg);
    }
    Serial.println();
}


void trigger() {
    //Rising edge interrupt
    prevTime = curTime;
    curTime = micros();
    irpStart = curTime;    
    
    //allow a variation around 100us
    int d = curTime-prevTime;
    int d2 = d/2;
    int d15 = d/1.5; //the period from short rising edge to long rising edge
                     //is d/1.5 not d/2
    if (d > freqBounds[0] && d < freqBounds[1]) {
      //in-tolerance signal
      trigPending = true; 
    }
    else if(trigPending && 
    (d2 > freqBounds[0] && d2 < freqBounds[1])
    || (d15 > freqBounds[0] && d15 < freqBounds[1])){
      pulseCount++;        
      sendPing = pulseCount == 2;
    }
    else {
      //not a valid pulse width so restart
      pulseCount = 0;
      trigPending = false;
      
      timings[0] = d;
    }
    
    //debug
    if(tct > 11) tct = 0;           
    timings[tct++] = d;
    
    irpEnd = micros();
}


void transmitPing() {
   //setupUltrasonic();
   //start the ping
  TCCR2B = _BV (WGM22) | _BV (CS21);         // fast PWM, prescaler of 8
   
  delay(100);
  TCCR2B = _BV (WGM22) ;         // fast PWM, timer off  
  
  digitalWrite(DRIVE, LOW);
  
  Serial.println(">>>>> PING SENT <<<<<"); 
}

Serial.begin(9600); // why not use 115200 is far faster

clear OC2B on compare

doesn't that mean you need to reload that value after a first burst? (just echoing your own comments :wink:

I believe that that just clears the flag so that it will run again. I have used exactly the same code, just running in the loop() function with delay() and it works as expected.

The problem seems to be that transmitPing() only works once. In that case, the other things going on should be irrelevant and you could strip all that code out and just call transmitPing() at intervals. I assume you have some external method of knowing whether it is actually transmitting.

If this still shows the same problem then you know you just need to look at what's going on within transmitPing().

More likely IMO you'll find that transmitPing() works OK on its own and it's something the rest of the code is doing which is breaking it. In that case you can reinstate the remaining code bit by bit until you find it breaks again.

By the way, what's the purpose of digitalWrite(DRIVE, LOW) in transmitPing()? As far as I can see you never set the DRIVE pin HIGH so this line should have no effect - so I wonder whether there may be something else going on that I'm missing.

Thanks for the reply. I have run all the bits of code separately and the seem to work. I have run it without the digitalWrite(LOW) - which is there to ensure the pin is low unless a ping is being sent because that pin drives a transistor which would be conducting if the pin was left high.

My hunch is that having the timer and the interrupt running together is causing the problem but I'm not sure why?

sirch:
which is there to ensure the pin is low unless a ping is being sent because that pin drives a transistor which would be conducting if the pin was left high.

You never set it HIGH, though, do you?