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

Thread: Teensy 4.0/4.1 Interrupts Issue

  1. #1

    Teensy 4.0/4.1 Interrupts Issue

    I am writing some code to count the U/V/W encoder signals from a BLDC eScooter motor. The encoder signals from my motor seem to be rather noisy, which I can see on my digital analyzer. However, my code works as expected on the Teensy 3.5 chip, seeing the encoder fire signals in the expected order with no transient signals. However, when I changed to the Teensy 4.1 chip, I found that I get transient signals that are duplicating a previously fired signal or a signal out of order. My motor encoders are at 5v, so I use a level shifter to bring them down to 3.3 to connect to the Teensy 4.1 pins. Is there something about the Teensy 4.1 that would cause it to be more susceptible to noise?

    Here are the major excerpts of my code:

    Code:
    //// pin assignments
    // Teensy 3.5/4.0/4.1 - These assignments work across all versions
    const uint8_t PWM_SPEED_PIN(2);
    const uint8_t BRAKE_PIN(3);
    const uint8_t MOTOR_DIR_PIN(4);
    const uint8_t LED_BUILTIN_PIN(13);
    const uint8_t U_ENCODER_SIGNAL_PIN(14); // yellow - Ha - U
    const uint8_t V_ENCODER_SIGNAL_PIN(15); // blue   - Hb - V
    const uint8_t W_ENCODER_SIGNAL_PIN(16); // green  - Hc - W
    const uint8_t BUTTON_PIN(23);
    
    volatile int32_t tickCount;
    volatile int32_t uCount;
    volatile int lastUVal;
    volatile int32_t vCount;
    volatile int lastVVal;
    volatile int32_t wCount;
    volatile int lastWVal;
    volatile char lastEncoder;
    
    ...
    
      pinMode(U_ENCODER_SIGNAL_PIN, INPUT);
      lastUVal = digitalRead(U_ENCODER_SIGNAL_PIN);
      attachInterrupt(U_ENCODER_SIGNAL_PIN, countUTick, CHANGE);
      
      pinMode(V_ENCODER_SIGNAL_PIN, INPUT);
      lastVVal = digitalRead(V_ENCODER_SIGNAL_PIN);
      attachInterrupt(V_ENCODER_SIGNAL_PIN, countVTick, CHANGE);
      
      pinMode(W_ENCODER_SIGNAL_PIN, INPUT);
      lastWVal = digitalRead(W_ENCODER_SIGNAL_PIN);
      attachInterrupt(W_ENCODER_SIGNAL_PIN, countWTick, CHANGE);
    
    /**
      Interrupt handler for the U encoder signal.
    **/
    void countUTick() {
      // Read the current signal value
      int val = digitalRead(U_ENCODER_SIGNAL_PIN);
    
      // Check for an encoder fault  
      bool encoderFault = (lastEncoder == 'U') || // Last encoder was this one
                          (lastEncoder == 'W' && motorContext.motorDirection) || // Last encoder was W, but going forward
                          (lastEncoder == 'V' && !motorContext.motorDirection);  // Last encoder was V, but going reverse
    
      //if (encoderFault) {
        DebugMsgs.notification().print("U ").print(lastUVal).print(' ').print(val).print(' ')
          .print(lastEncoder).println(encoderFault ? " *" : "");
      //}
    
      // If there is a fault, don't record a tick.
      if (encoderFault) { return; }
      
      int increment = (lastEncoder == 'W') ? 1 : -1;
      uCount += increment;
      tickCount += increment;
      lastEncoder = 'U';
      lastUVal = val;
    }
    The interrupt handlers for the V and W encoders look exactly the same, only the variable names are changed to protect the innocent. If you want to see the full source, you can find it here.

    I took the step of running the encoder signals through a schmitt trigger, and that helped by cleaning up the signal somewhat. But on the Teensy 4.1 I still get some number of transients.

    Any advice or insight appreciated.

    -Mark
    Last edited by KurtE; 01-07-2023 at 12:02 PM. Reason: code tags

  2. #2
    Sorry, I should have said that my current hardware is using the Teensy 4.0, though I was seeing the same thing with the Teensy 4.1 before.

  3. #3
    Senior Member BriComp's Avatar
    Join Date
    Apr 2014
    Location
    Cheltenham, UK
    Posts
    1,135
    Code:
    //// pin assignments
    // Teensy 3.5/4.0/4.1 - These assignments work across all versions
    const uint8_t PWM_SPEED_PIN(2);
    const uint8_t BRAKE_PIN(3);
    const uint8_t MOTOR_DIR_PIN(4);
    const uint8_t LED_BUILTIN_PIN(13);
    const uint8_t U_ENCODER_SIGNAL_PIN(14); // yellow - Ha - U
    const uint8_t V_ENCODER_SIGNAL_PIN(15); // blue - Hb - V
    const uint8_t W_ENCODER_SIGNAL_PIN(16); // green - Hc - W
    const uint8_t BUTTON_PIN(23);
    
    volatile int32_t tickCount;
    volatile int32_t uCount;
    volatile int lastUVal;
    volatile int32_t vCount;
    volatile int lastVVal;
    volatile int32_t wCount;
    volatile int lastWVal;
    volatile char lastEncoder;
    
    ...
    
    pinMode(U_ENCODER_SIGNAL_PIN, INPUT);
    lastUVal = digitalRead(U_ENCODER_SIGNAL_PIN);
    attachInterrupt(U_ENCODER_SIGNAL_PIN, countUTick, CHANGE);
    
    pinMode(V_ENCODER_SIGNAL_PIN, INPUT);
    lastVVal = digitalRead(V_ENCODER_SIGNAL_PIN);
    attachInterrupt(V_ENCODER_SIGNAL_PIN, countVTick, CHANGE);
    
    pinMode(W_ENCODER_SIGNAL_PIN, INPUT);
    lastWVal = digitalRead(W_ENCODER_SIGNAL_PIN);
    attachInterrupt(W_ENCODER_SIGNAL_PIN, countWTick, CHANGE);
    
    /**
    Interrupt handler for the U encoder signal.
    **/
    void countUTick() {
    	// Read the current signal value
    	int val = digitalRead(U_ENCODER_SIGNAL_PIN);
    
    	// Check for an encoder fault
    	bool encoderFault = (lastEncoder == 'U') || // Last encoder was this one
    		(lastEncoder == 'W' && motorContext.motorDirection) || // Last encoder was W, but going forward
    		(lastEncoder == 'V' && !motorContext.motorDirection); // Last encoder was V, but going reverse
    
    	//if (encoderFault) {
    	DebugMsgs.notification().print("U ").print(lastUVal).print(' ').print(val).print(' ')
    		.print(lastEncoder).println(encoderFault ? " *" : "");
    	//}
    
    	// If there is a fault, don't record a tick.
    	if (encoderFault) { return; }
    
    	int increment = (lastEncoder == 'W') ? 1 : -1;
    	uCount += increment;
    	tickCount += increment;
    	lastEncoder = 'U';
    	lastUVal = val;
    }
    When you paste code can you put it between CODE tags using the # button above (when editing post).
    It makes your code so much more easy to read, understand and therefore be able to help you.

  4. #4
    Junior Member
    Join Date
    Apr 2020
    Posts
    2
    have you considered using the onboard hardware decoders? seems like the perfect job.

    https://forum.pjrc.com/threads/58478...ncoder-Library

  5. #5
    Quote Originally Posted by digibluh View Post
    have you considered using the onboard hardware decoders? seems like the perfect job.

    https://forum.pjrc.com/threads/58478...ncoder-Library
    Also, the QuadTimer peripheral used by the QuadEncoder library also has programmable input filters that may help with the OP's noise issue. I've never used the filters on QuadTimer, but I've had very good success in solving noise issues with similar filter features on timers in other processors.

  6. #6
    Thank you for the replies. I will look into those options. But I wanted to also update some exploration I did with the interrupts on the 3.5 and 4.0.

    I created a simple circuit that has a potentiometer with output connected to both an analog pin (so I could read the level) and an interrupt pin (so it could trigger). I created the same circuit for the 3.5 and 4.0 chips. And I wrote a simple program to trigger an interrupt on a RISING edge and report the level.

    Code:
    #include <Arduino.h>
    #include <inttypes.h>
    
    //// pin assignments
    const uint8_t INTERRUPT_PIN(2);
    const uint8_t SIGNAL_LEVEL_PIN(0);
    
    void handleInterrupt() {
        int signalLevelAtInterrupt = analogRead(SIGNAL_LEVEL_PIN);
        Serial.print("Interrupt: "); // I know this can't be here in real code
        Serial.println(signalLevelAtInterrupt);
    }
    
    void setup() {
      Serial.begin(9600);
      // give serial some time to catch up
      delay(1000);
    
      // Pin setup
      pinMode(SIGNAL_LEVEL_PIN, INPUT);
      pinMode(INTERRUPT_PIN, INPUT);
    
      attachInterrupt(INTERRUPT_PIN, handleInterrupt, RISING);
    
      Serial.println("Starting...");
    }
    
    void loop() {
      // Loop for an interrupt...
    }
    I will say upfront that I am doing this all by hand, turning the potentiometer very slowly to find the trigger point. I wish I had a digital potentiometer that I could use to drive this programmatically. As it is, my potentiometer value can be somewhat noisy, the value can switch between a value +/-2, even when I am not touching it. At least that I saw when I had a lot more output to serial, which made the program very unwieldly to debug with.

    3.5: It triggered consistently around 1.83v with everything at 5v. There was only a single interrupt, and I can change the value quite a bit (making it less), before it will triggers with another RISING interrupt when i increase the value.

    4.0: It triggered consistently around 1.58v with everything at 3.3v. But interestingly, there were two interrupts, one right after the other. The values reported are different by about 8 (510 vs 518). And there is a much narrower window that I can change the value before it triggers again. But it appears that my potentiometer is noisy enough to cause dual triggers? I also got into a state where I was not moving the potentiometer, but the noise it was creating was just triggering over and over. But I was only able to do that once, so I can't speak to it as well.

    But it does appear that 3.5 and 4.0 have different behavior here. The 4.0 seems to trigger multiple times for some reason. Maybe it is more sensitive when values drop after a RISING trigger that it triggers again when there is just a small increase?

    Without a digital potentiometer, I can't do much more to simulate noise in a controlled manner. And the signals I am getting from the encoders does not hover around the trigger point. It goes to 0 and 3.3v, not 1.58v. But there seems to be a difference in behavior here.

    -Mark

  7. #7
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,970
    And there is a much narrower window that I can change the value before it triggers again
    You can try to change
    Code:
      pinMode(INTERRUPT_PIN, INPUT);
    to
    Code:
      pinMode(INTERRUPT_PIN, INPUT_PULLDOWN);
    This is supposed to enable some hysteresis on the pin which should also remove multiple interrupts caused by to noisy input.

Posting Permissions

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