Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 3 of 3

Thread: Teensy 3.2 difference vs Uno: pinmode() detaches attachInterrupt()

  1. #1

    Teensy 3.2 difference vs Uno: pinmode() detaches attachInterrupt()

    I copied some tachometer code from which had an attachInterrupt() before setting the pinMode() and discovered that on the Teensy 3.2, different from the Uno, setting the pinMode after the attachInterrupt seems to detach the interrupt.

    After spending too long debugging my way to the difference and into some much improved different tachometer code, I found some discussion at and tried this code:

    // Interrupt comparison between Uno & Teensy 3.2
    // Based on code
    // Expected output (obtained on Uno)
    // Pin high, count=0
    // Pin low, count=1
    // Pin high, count=1
    // Pin low, count=2
    // BUT...
    // On due (Teensy 3.2), count remains 0
    int pin = 3;    // INT 1 on UNO
    volatile int i = 0;
    void test() { i++; }
    void setup() {            
      attachInterrupt(digitalPinToInterrupt(pin), test, FALLING);    // Uno: Counts up, Teensy: does not count
      pinMode(pin, OUTPUT);
      //attachInterrupt(digitalPinToInterrupt(pin), test, FALLING);    // Both Uno and Teensy count
    void loop() {
      digitalWrite(pin, HIGH);
      Serial.print("Pin high, count="); Serial.println(i);
      digitalWrite(pin, LOW);
      Serial.print("Pin low, count="); Serial.println(i);

    The github thread indicated a use-case for keeping interrupts while changing pinmode, but that wasn't what I needed. I would expect the Teensy behavior to be the same as the Uno, and to not have to pay attention to the order of calls on different architectures.

    My kluged working tachometer code is below. It worked well enough to test the 5-650RPM range of speeds on a DIY wood lathe with a 12" plywood pulley driven by a treadmill motor.

    // Tachometer from
    // But its interrupts were odd.
    // This measures dT between pulses, debounced
    // and reports when RPM changes
    const byte millis_not_micros = 0;
    const byte debug = 1; // should optimize out stuff
    const int debounce = (2 * millis_not_micros?1:1000); // ms for settling. 
    const int sensorPin = 14; // reed sensor
    const int sensor_ground = 15 ; // Ground for pulling sensor low  
    volatile int countsD = 0;
    const float alpha = 0.5; // EWMA smoother
    float rpm; 
    unsigned long tnow = 0UL;
    unsigned long prev = 0UL;
    volatile unsigned long dt = 1;
    void count_function()
    { /*The ISR function
        Called on Interrupt */
        tnow=millis_not_micros? millis() : micros();
        if ((tnow - prev) >= debounce){ 
           dt = tnow - prev;
           prev = tnow;
    void setup() 
      pinMode(sensorPin, INPUT_PULLUP); //Sets sensor as input
      pinMode(sensor_ground, OUTPUT); //Sets sensor as input
      attachInterrupt(digitalPinToInterrupt(sensorPin), count_function, FALLING); //Interrupts are called on Rise of Input
      rpm = 0;  
      count_function(); // initialize
    void loop()
      static int lastcount;
      if (countsD != lastcount ) {
        lastcount = countsD;
        rpm = (1-alpha)* rpm+ alpha * 60.0 * (millis_not_micros?1000.0:1e6) /(dt);
        Serial.print(rpm); //Calculated values are displayed
          Serial.print(" Sensor: ");Serial.print(digitalRead(sensorPin));
          Serial.print(" tnow: ");Serial.print(tnow);
          Serial.print(" prev: ");Serial.print(prev);
        Serial.print(" countsD: ");Serial.print(countsD);
        Serial.print(" dt: ");Serial.print(dt);;

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Perhaps this special case code for compatibility is causing trouble:

    	if ((*config & 0x00000700) == 0) {
    		// for compatibility with programs which depend
    		// on AVR hardware default to input mode.
    		pinMode(pin, INPUT);
    Not looked through/beyond that code - but assume the pin not yet set that defaults to a DISABLED state may trigger making the pin INPUT while attaching the interrupt - for AVR compatibility

    Then changing the pin to OUTPUT and attaching the interrupt does a wholesale change to "portConfigRegister" losing the interrupt indication. Though in general it looked like that code was working around bits to some degree.

    In any case it clearly indicates PJRC's great effort to suffer through 'compatibility' that either cannot always be done with the architectural differences - or just a case where it is better to do it as 'discovered' rather than adding yet another 'special case' for that AVR stuff that could end up just making yet another set of problems living forever like the above code where working around one issue causes another in some atypical case ... i.e. doing an attachInterrupt() on an OUTPUT ... which I've done before but just for an odd test case.

  3. #3
    I saw that bit of code, but I think the difference is in the pinMode() code -- AVR's code twiddles 1-2 bits but ARM has more options and more bits to handle, and PJRC's code re-writes the register:

    void pinMode(uint8_t pin, uint8_t mode)
            uint8_t bit = digitalPinToBitMask(pin);
            uint8_t port = digitalPinToPort(pin);
            volatile uint8_t *reg, *out;
            if (port == NOT_A_PIN) return;
            // JWS: can I let the optimizer do this?
            reg = portModeRegister(port);
            out = portOutputRegister(port);
            if (mode == INPUT) { 
                    uint8_t oldSREG = SREG;
                    *reg &= ~bit;
                    *out &= ~bit;
                    SREG = oldSREG;
            } else if (mode == INPUT_PULLUP) {
                    uint8_t oldSREG = SREG;
                    *reg &= ~bit;
                    *out |= bit;
                    SREG = oldSREG;
            } else {
                    uint8_t oldSREG = SREG;
                    *reg |= bit;
                    SREG = oldSREG;

    void pinMode(uint8_t pin, uint8_t mode)
            volatile uint32_t *config;
            if (pin >= CORE_NUM_DIGITAL) return;
            config = portConfigRegister(pin);
            if (mode == OUTPUT || mode == OUTPUT_OPENDRAIN) {
    #ifdef KINETISK
                    *portModeRegister(pin) = 1;
                    *portModeRegister(pin) |= digitalPinToBitMask(pin); // TODO: atomic
                    *config = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
                    if (mode == OUTPUT_OPENDRAIN) {
                        *config |= PORT_PCR_ODE;
                    } else {
                        *config &= ~PORT_PCR_ODE;
            } else {
    #ifdef KINETISK
                    *portModeRegister(pin) = 0;
                    *portModeRegister(pin) &= ~digitalPinToBitMask(pin);
                    if (mode == INPUT) {
                            *config = PORT_PCR_MUX(1);
                    } else if (mode == INPUT_PULLUP) {
                            *config = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS;
                    } else if (mode == INPUT_PULLDOWN) {
                            *config = PORT_PCR_MUX(1) | PORT_PCR_PE;
                    } else { // INPUT_DISABLE
                            *config = 0;

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts