rise-interrupt

amperka/pc-interrupt/rise-interrupt

Attaches the interrupt to the specified port in the FALLING mode. It triggers whenever the pin goes from high to low. The change increments a counter and stores the time in microseconds of the latest interrupt. The node emits these values and a pulse as soon as possible, but not right at the moment when the interrupt occurred.
rise-interrupt
@/rise-interrupt
Attaches the interrupt to the specified port in the FALLING mode. It triggers whenever the pin goes from high to low. The change increments a counter and stores the time in microseconds of the latest interrupt. The node emits these values and a pulse as soon as possible, but not right at the moment when the interrupt occurred.
PORTport
Board port to attach interrupt to. Board port to attach interrupt to. Note that not all pins support hardware interrupts. Refer to your board specs to learn which are compatible.
ACTboolean
When ACT is true the node is watching for interrupts.
rise-interrupt
OUT
PORT
ACT
T
NUM
OUTpulse
Fires a pulse on the next transaction after the interrupt occurred.
NUMnumber
The number of interrupts that occurred before the node fired a pulse.
Txod/core/micros
Time in microseconds of the latest interrupt.
To use the node in your project you should have the amperka/pc-interrupt library installed. Use the “File → Add Library” menu item in XOD IDE if you don’t have it yet. See Using libraries for more info.

C++ implementation

#pragma XOD require "https://github.com/GreyGnome/EnableInterrupt"

#include <EnableInterrupt.h>

nodespace {
    template<uint8_t PORT>
    struct Interrupt {
        volatile static uint32_t lastMicros;
        volatile static uint8_t counter;
        static bool attached;

        static void handler() {
            ++counter;
            lastMicros = micros();
        }
    };

    template<uint8_t PORT>
    volatile uint32_t Interrupt<PORT>::lastMicros = 0;

    template<uint8_t PORT>
    volatile uint8_t Interrupt<PORT>::counter = 0;

    template<uint8_t PORT>
    bool Interrupt<PORT>::attached = false;
}

node {
    uint32_t lastVal = 0;

    void evaluate(Context ctx) {
        bool act = getValue<input_ACT>(ctx);

        if (act && !Interrupt<constant_input_PORT>::attached) {
            // Attach the interrupt if it is not attached yet
            ::pinMode(constant_input_PORT, INPUT);
            enableInterrupt(constant_input_PORT, Interrupt<constant_input_PORT>::handler, RISING);
            Interrupt<constant_input_PORT>::attached = true;
        } else if (!act && Interrupt<constant_input_PORT>::attached) {
            // Detach the interrupt if it was attached and ACT is false now
            disableInterrupt(constant_input_PORT);
            Interrupt<constant_input_PORT>::attached = false;
        }

        // Check is the interrupt occurred and emit values and pulse
        uint32_t value = Interrupt<constant_input_PORT>::lastMicros;
        if (lastVal != value) {
            emitValue<output_OUT>(ctx, 1);
            emitValue<output_T>(ctx, value);
            emitValue<output_NUM>(ctx, Interrupt<constant_input_PORT>::counter);
            lastVal = value;
            Interrupt<constant_input_PORT>::counter = 0;
        }

        // Make it dirty on each transaction as long as ACT is true
        if (act) {
            setImmediate();
        }
    }
}