Skip to content

Instantly share code, notes, and snippets.

@SimenZhor
Created October 1, 2020 09:56
Show Gist options
  • Select an option

  • Save SimenZhor/04b0f4aa3f3fd5e52bfec7202dcac961 to your computer and use it in GitHub Desktop.

Select an option

Save SimenZhor/04b0f4aa3f3fd5e52bfec7202dcac961 to your computer and use it in GitHub Desktop.

Revisions

  1. SimenZhor created this gist Oct 1, 2020.
    94 changes: 94 additions & 0 deletions Arduino_TC_PulseWidth_Measurement.ino
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,94 @@
    // Pulse Width measurement using input capture unit
    // Author: Simen E. Sørensen
    //(overflow calculations based on code by Nick Gammon 31 August 2013:
    // https://www.gammon.com.au/forum/?id=11504)
    // Date: 01 October 2020

    // Input: Pin D8 (Arduino Uno (and I think Arduino Nano as well))

    volatile boolean risingEdge;
    volatile boolean fallingEdgeFlag;
    volatile unsigned long overflowCount;
    volatile unsigned long pulseStartTime;
    volatile unsigned long pulseFinishTime;
    float pulseWidth; //us

    ISR(TIMER1_OVF_vect){
    // timer overflows (every 65536 counts)
    overflowCount++;
    }

    ISR(TIMER1_CAPT_vect){
    noInterrupts();
    unsigned int timer1CounterValue = ICR1;
    unsigned long overflowCopy = overflowCount;
    // Check if we have just missed an overflow
    if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 0x7FFF)
    overflowCopy++;

    if(risingEdge){
    //Store counting value, reconfigure to trigger on falling edge
    pulseStartTime = (overflowCopy << 16) + timer1CounterValue;
    risingEdge = false;
    // Change to trigger on falling edge
    // ICES1 = 0 means Falling Edge Trigger
    TCCR1B &= ~bit(ICES1); // Set ICES1 low, keep all other bits in TCCR1B
    interrupts(); // Re-enable interrupts, so we can catch the falling edge
    return;
    }else{
    //Read counting value, set flag that values can be calculated, reconfigure to trigger on rising edge
    pulseFinishTime = (overflowCopy << 16) + timer1CounterValue;
    fallingEdgeFlag = true;
    TIMSK1 = 0; // pause interrupts until pulse width has been calculated
    }
    }

    void initTC(){
    noInterrupts (); // protected code
    fallingEdgeFlag = false; // re-arming for new pulse
    risingEdge = true; // This function sets rising edge as the event trigger
    // reset Timer 1 configuration registers
    TCCR1A = 0;
    TCCR1B = 0;

    TIFR1 = bit (ICF1) | bit (TOV1); // clear flags so we don't get a bogus interrupt
    TCNT1 = 0; // Reset counter
    overflowCount = 0; // Reset overflow counter

    // Configure Timer 1
    // TOIE1 = Overflow IRQ, ICIE1 = Event IRQ
    TIMSK1 = bit (TOIE1) | bit (ICIE1); // interrupt on Timer 1 overflow and input capture
    // Select clock, select event trigger and apply filter (filter causes 4 clock cycles on delay but does not alter the pulsewidth measurement)
    // CS10 = no prescaler (TC_CLK = F_CLK), ICES1 = Rising Edge Trigger, ICNC1 = Input Capture Noise Canceller
    TCCR1B = bit (CS10) | bit (ICES1) | bit(ICNC1); // Set CS10, ICES1 and ICNC1 - clear all other bits in TCCR1B
    interrupts ();
    }

    void calculatePulseWidth(){
    unsigned long numCounts = pulseFinishTime - pulseStartTime;
    // Period time = 1/F_CPU (= 62.5 ns at 16 MHz)
    pulseWidth = float(numCounts) / (F_CPU*0.000001); // Pulse width = Period time * Num counts
    }

    void reArmTC(){
    //Function only added for clarity in naming
    initTC(); //re-arm for next pulse
    }

    void setup(){
    Serial.begin(9600);
    Serial.println("Pulse Width Counter");
    pulseWidth = 0; //us
    // set up for interrupts
    initTC();
    }

    void loop(){
    if(fallingEdgeFlag){
    calculatePulseWidth();
    Serial.print ("PulseWidth: ");
    Serial.print (pulseWidth);
    Serial.println (" µs. ");
    reArmTC();
    }
    }