(Solved...I think) Strange interrupt behavior on Teensy 4.1

I'm using a Teensy 4.1 with Win 10 and 1.8.19. I have multiple files in the project. The ISR tied to attachInterrupt() is in a file separate from the ino file. The program is fairly large (almost 200K), but I've commented out everything in loop() but the post-interrupt code. When the interrupt fires (pins 14/15), the ISR sets a global variable to 0 (nothing happened), 1 (CW move of an encoder), -1 (CCW move of the encoder). Debug prints show the interrupt fires and does set the global int variable to one of those values. However, testing that variable back in loop() shows that it remains at 0 even though the debug statements in the ISR show its changed. (I did remove the debug statements in the ISR; no change.) This is probably a forest-for-the-trees problem, but I'm not seeing it. (What happened to the "add code" icon??)

Is the variable that is set in the ISR declared volatile?

Read the forum guidelines to see how to post code.
Use the IDE autoformat tool (ctrl-t or Tools, Auto format) before posting code in code tags.

Thanks, groundFungus...it is now, but still no joy.

The ISR:

void EncoderFilter()
{
  char result;

  result = filterEncoder.process();   // Read the encoder
  if (result == 0) {                  // Nothing read
    filterEncoderMove = 0;
    return;
  }
  if (result == DIR_CW) {
    filterEncoderMove = 1;            // Turned it clockwise, result = 16
  } else {
    filterEncoderMove = -1;
  }
//Serial.print("filterEncoderMove = ");
//Serial.println((int)filterEncoderMove); 
}

In loop():


  if (filterEncoderMove != 0) {
    Serial.print("In loop: Filter encoder moved: ");
    if (filterEncoderMove == 1) {
      Serial.println("right");
    } else {
      Serial.println("left");
    }
    filterEncoderMove = 0;
  }

Post your complete code. Snippets are useless.

Where is the code for that function?

I think that there is too much code in the ISR. I would suggest that the ISR only contain a flag and the code from the ISR go into a function in loop() so that it is called on condition of that flag and the flag reset. The ISR should have the bare minimum of code.

The code is for a SDT (software defined transceiver) seen at the top of this page and has a little over 12,000 lines of code spread over 22 files. I think some people here might get a little miffed if I posted all of it.

That method comes from the Rotary library and distributed with the Arduino IDE. It's based on Ben Buxton's encoder library. I'll cut down the code and report back.

I tried this code:


void EncoderFilter()
{
   char result;

   result = filterEncoder.process();   // Read the encoder
   if (result == 0) {                  // Nothing read
    filterEncoderMove = 0;
   } else {
     filterEncoderMove = (int) result;
   }
 }

No change.

Sorry, you're correct. Can you make a MRE that demonstrates the problem?

I've tried, using the interrupt example from the library as a base, but it works perfectly. That's good in a way, as it confirms the encoder and wiring are fine. The debug prints in the ISR also confirm that the correct values are return from the process() method and correctly assigned into filterEncoderMove and the encoder is working. The only thing left in loop() is a function call that scans the switches seen on the right in the picture. (That only uses one analog pin, reading values through a resistance ladder. That works fine and is polled, not interrupt-driven.)

All: Thanks for trying and let me know if anything flashes through your mind as I'm not seeing anything obvious.

Hi econjack,
this is not the same thing ?

:wink:

void EncoderFilter()
{
   filterEncoderMove = filterEncoder.process();   // Read the encoder
}

Are you sure that "void EncoderFilter()" is an ISR? Normally, they are handled in the libraries, it seems to me that you don't see them in the main code?

In the above, result is a single 'char'.
Is there somewhere that DIR_CW is defined as a char?

From the Rotary header file:

// No complete step yet.
#define DIR_NONE 0x0
// Clockwise step.
#define DIR_CW 0x10
// Counter-clockwise step.
#define DIR_CCW 0x20

Since it's a symbolic constant, it's typeless.

@PatMax: Looking at the class declaration in the Rotary header file, there is only the constructor, begin() and process() as public methods.
ADDED: Assigning directly into filterEncoderMove doesn't change the outcome. The "wordy" form I used is partly because it's Open Source with C experience of widely differing levels. I wouldn't be too surprised if the compiler didn't optimize the if block away!

@gfvalvo: The project is Open Source, so you can look at the complete source code (and schematics, BOM's, and Gerbers) in the Files section of my site.

I am unable to replicate your problem.
Since I don't have an encoder connected at this time, I just put in 0, and then 0x20 for result, and I get the correct prints in loop().

However, I am unable to compile the code with the #define statements for DIR_CCW, etc. Both PlatformIO and Wokwi choke on it. Not sure why

The logic of your code is good, even if it could be made shorter.

I don't see anything that prevents the code from working; do you call the "EncodeFilter()" function before testing "filterEncoderMove", in the loop?


void EncoderFilter() {
  filterEncoderMove = filterEncoder.process();   // Read the encoder
}



in loop : 
  
  EncodeFilter();    
  if (filterEncoderMove == 0x10) Serial.println("In loop: Filter encoder moved: right");
  if (filterEncoderMove == 0x20) Serial.println("In loop: Filter encoder moved: left");
  filterEncoderMove = 0;
  

Those symbolic constants are defined in the Rotary.h header file from the library supplied with the Arduino IDE.

Wait a minute...

WHAT THE HECK!!

I just moved the Encoder function from the Encoder.cpp file into the INO file and it works fine!! Why for cuz it work now???

Econjack

1 Like

@econjack
rule 0:
the bug is always in that part of the code you haven't posted. Except you posted the
complete sketch.

you should never do serial output inside an ISR

You can post code by using this method that adds the code-tags
There is an automatic function for doing this in the Arduino-IDE
just three steps

  1. press Ctrl-T for autoformatting your code
  2. do a rightclick with the mouse and choose "copy for forum"
  3. paste clipboard into write-window of a posting

best regards Stefan

Yes, I'm aware of the dangers of debug statements in an ISR, which is why I removed them after I observed what their values were. And I do know how to post code now; it's just different that it was when I posted a long time ago. You can see I've posted code since I asked that question.

I really didn't post the entire sketch, because it's over 12,000 lines of C code. Those who are interested can see it at the link I posted above. The complete source code is in the Files section of the web site. The more important thing now is for someone to 'splain to me why moving the ISR from the Encoder.cpp file to the project's INO file causes it to work.

Can't, after as much struggling to do that I have time for.

Can you provide a direct link to the code please?

In the meantime my guess is you are inadvertently talking about two distinct entities with the same name. When you put everything in one file, there is only one entity with that name.

a7

I think I figured it out...at least it works now. If you're familiar with the Teensy 4.1 architecture, the 1Mb of RAM is broken into two banks with the clever names RAM1 and RAM2. RAM1 is tightly coupled for speed while RAM2 is not. (I think RAM2 runs at 1/4 the CPU speed.) Because I need loop() to execute as fast as possible, I used the Teensy keyword FASTRUN before loop() which places it in RAM1. Evidently my EncoderFilter() function got placed in RAM2 and it further appears the two don't play together nicely. As soon as I placed the FASTRUN directive immediately before the EncoderFilter() function, the interrupt functioned as it should.

The reason I'm not positive this explains things is because the FASTRUN documentation says that functions are stored in RAM1 by default. As such, I thought that would mean they share RAM1. Also, I don't know if interrupts cannot cross RAM1/RAM2 boundaries. While the code is working, I would feel a lot better if I fully understood why.

1 Like

Alto777:

Go to the Files folder and click on it. Then click the Update header on the extreme right, which sorts by date; most recent on top. The V010 Files.7z is a compressed file that holds the Source code, Gerber files, BOMs, and schematics for the project. If you expand the Source Code files, Encoders.cpp has the encoder source code.

Econjack