Teensy 3.5 Hardware Pin Interrupt

Status
Not open for further replies.

binaryfrost

New member
I've been reading the Interrupts documentation here (https://www.pjrc.com/teensy/interrupts.html) as I have some Arduino Mega code that I need to port to a Teensy 3.5.

The Teensy/PJRC documentation states that I need to include the right headers, then use pre-defined ISR names to define the functions that execute upon a hardware interrupt occuring.

Some of the other documentation (especially code examples) shows using the Adruino-esque attachInterrupt(INTERRUPT_PIN, myISR, RISING) function instead.

Which should it be?

If I'm meant to be using the predefined ISR macros (as they sort out any machine state issues) rather than attachInterrupt, it's not clear exactly which ISR vectors I should be using for which pin.

The documentation states that "All digital pins have interrupt capability" - but I'm struggling to find a decent example.

Your help is much apreciated! I'm using teensyduino.
 
Last edited:
I would start off using the attachInterrupt(pin, function, RISING) like interrupts and they should work fine for the majority case.

The only time you might resort to going directly to the hardware Interrupt handler is if you find you need the absolute speed. That is the attachInterrupt code assigns it's own interrupt handler to the system interrupt vector and when the event happens, it has to figure out which event happened on what pin and call the users appropriate handler. This is because the hardware handler is not a 1 pin to 1 interrupt, but instead one interrupt for a set of pins...

But you always have the options to write your own port interrupt handler, by figuring out which port your pin belongs to and then find the interrupt number and then use the attachInterruptVector(vector_num, my_isr) and when it gets called you either have to figure out which pin caused the interrupt and state or try to assume it, if your code only has one interrupt pin on that port...

But Again I would start off simple, unless you find a strong need to go deeper.
 
Which should it be?

Try the easy Arduino way first. Odds are it will "just work" and be more than adequate.

This advice applies to much more than just interrupts....

The documentation states that "All digital pins have interrupt capability" - but I'm struggling to find a decent example.

Yes, this applies to the Arduino attachInterrupt() function.

Relatively little info exists about other ways because attachInterrupt() pretty much always works out well.

However, one thing that almost never works well is using interrupts for pushbuttons or switches. You didn't give any info or context about what you're doing with the interrupts, but this comes up so often that I'll mention it anyway, just in case you have switches in mind. For mechanical contacts, use the Bounce library. The mechanical chatter in switches can be frustrating on any processor, but it's especially difficult with much faster chips like Teensy 3.5. The Bounce library works extremely well.
 
Thanks.

The interrupt is triggered by a clean digital signal, which occurs 100 times per second when AC zero-crosses.

So speed is key, as I have a few calculations and operations to perform before the next trigger.

Perhaps an update to the online documentation will make it clearer for those of us moving from Arduino to Teensy because of the advantages Teensy brings?
 
How fast signals can the interrupts detect. I would need to detect a signal that goes down for 150 ns. (This happens 24 to 60 times in a second.)
 
There is no reason why it would not be detected. AFAIK, there is no filtering in the GPIO engine.

But simplest answer: Just try it out...
 
Thanks for the fast reply.

It is just that need to build some HW to test, SMD components... so asking.

But so if I understand correctly there is a trigger that detects the raising or falling edge, i.e. no reguirement that it should remain low or high for given time.

Then it also guestion what kind of bandwith the inputs have. 150 ns is pretty short pulse. Where could I find the input carasteristics of the Teensy pins.
 
The duration of the pulse should not matter. And you might do tests without external hardware just by generating some 150ns / 9999850ns PWM in software on one pin and bridge it to the interrupt test pin.

Technical details can be found here (electrical data sheet) and here (full reference manual).
 
Thanks Theremingenieur and Paul, It looks like as it should be fine, especially as I noted the actual signal we will read will remain 350 ns down.
 
Paul, I have a somewhat tangential question.
You say that
one thing that almost never works well is using interrupts for pushbuttons or switches. ... For mechanical contacts, use the Bounce library.
.

Is there a good way to set up the "touch" pins so that I can reliably activate an interrupt when touching (or releasing) a capacitive pad attached to one of them?
 
Dear People at Bulletin,

I need this menu routine for my project but is seem the the encoder is not working with Interrupts?

Is there anyone who can help to get this running?

Code:
#include <avr/io.h>
#include <avr/interrupt.h>

#include <SPI.h>


#define SECS_PER_MIN  (60UL)
#define SECS_PER_HOUR (3600UL)
#define SECS_PER_DAY  (SECS_PER_HOUR * 24L)

/* Useful Macros for getting elapsed time */
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)  
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) 
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY)  

#define maxItemSize 10

const int itemsPerScreen = 7;
const int fontSize = 8;


static int pinA = A12; // Our first hardware interrupt pin is digital pin 2
static int pinB = A13; // Our second hardware interrupt pin is digital pin 3
static int enSW = 30;

volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile uint16_t encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile uint16_t
oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent

#include <Adafruit_SSD1325.h>
#define OLED_CLK    A16       // 7 
#define OLED_MOSI   A17       // 8
#define OLED_CS     A18       // 15
#define OLED_RESET  A19       // 16
#define OLED_DC     A20       // 4
//                            // 2 VCC +3.3 Volt
//                            // 1 GND
Adafruit_SSD1325 lcd (OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
//Declare the Menus you need. 
char menu[][maxItemSize] ={"Date","Time","Alarm","Format","Zone","Daylight","BACK"};
// Only 2 sub-menus are shown. You can add as many as you wish.
char subMenu0[][maxItemSize] = {"Date", "Month", "Year", "BACK"};
char subMenu1[][maxItemSize] = {"Hours", "Min", "Secs", "BACK"};


int cnt = 0; 
int itemSelected, subMenuSelected;
int itemsToDisplay=0;
unsigned long startmillis, milliSecs,mins,secs,hour; 
void setup() {

  lcd.begin();// Init the LCD
  lcd.setRotation(0);
  lcd.clearDisplay();
  lcd.display();
  lcd.setTextColor(WHITE,BLACK);
 
  
  pinMode(enSW, INPUT_PULLUP); 
  pinMode(pinA, INPUT_PULLUP); 
  pinMode(pinB, INPUT_PULLUP);
  attachInterrupt(0,PinA,RISING);
  attachInterrupt(1,PinB,RISING); 
  Serial.begin(115200); 
 
  lcd.display();
  delay(2000);
  lcd.clearDisplay();  
  //display.setTextSize(fontSize/8);
  

  startmillis = millis();

}


  

void loop() {

      //Show time on the default Screen.
  Serial.println(pinB);
  lcd.clearDisplay();
  lcd.setCursor(20,16);
  time(millis() / 1000);
  lcd.display();
  //lcd.setTextSize(fontSize/8);
  
 // Enter the settings menu if select Switch is pressed
 if(digitalRead(enSW)==0){
    while(digitalRead(enSW)==0);//wait till switch is released.
    
    itemSelected = displayMenu(menu, sizeof(menu)/maxItemSize); 
    switch(itemSelected){
        
         case 0: 
                Serial.print("calling submenu");
                subMenuSelected = displayMenu(subMenu0, sizeof(subMenu0)/maxItemSize); 
                break;
  
         case 1: 
                subMenuSelected = displayMenu(subMenu1, sizeof(subMenu1)/maxItemSize); 
                break;

         //you may include other cases as required!        
  
         default: break;       
              
     }


   //if the selected option is BACK, we will go straight to the main menu.  
   if(itemSelected!= 6){
     switch(subMenuSelected){
          // Only case 0 is shown. Also the user input is not saved anywhere, which might be required in real use-case.
          case 0: lcd.clearDisplay();
                  lcd.setCursor(0,0);
                  lcd.println("Date");
                  lcd.setCursor(28,16);
                 lcd.println(encoderPos);
                  lcd.display();
                  
                  while(digitalRead(enSW)){
                        lcd.setCursor(0,0);
                        lcd.clearDisplay();
                        lcd.println("Date");
                  
                        lcd.setCursor(16,16);
                        lcd.println(encoderPos);
                        lcd.display(); 
                  }  

                  while(digitalRead(enSW)==0);
                  break;
          
          default:break;       
         
     }

   }   
    
 }

}






// This function accepts the a 2D character array and its length and display it on the screen.
// The function montiers the encoder position and moves the menu up and down.
// It returns the selected menu option to the calling functions
     
int displayMenu(char menuInput[][maxItemSize], int menuLength){
    int curPos,startPos, endPos;
 do{ 
      
            startPos = encoderPos%menuLength;    
            Serial.println("startPos:");
            Serial.println(startPos);
            lcd.clearDisplay();
      
            endPos = itemsPerScreen;
            
            if(menuLength < itemsPerScreen)
            {
                 endPos = menuLength -startPos;  
            }
      
            if((menuLength-startPos)<itemsPerScreen)
            {
                endPos = menuLength -startPos;
            }

            Serial.print("endPos:");
            Serial.println(endPos);
      
            for(cnt = 0; cnt<=(endPos-1); cnt++){
                if(cnt == 0)
                {
                  lcd.setCursor(0,0);
                  lcd.print("->");
                }
                
                lcd.setCursor(16, cnt*fontSize);
                lcd.println(menuInput[cnt+startPos]);
                Serial.println(menuInput[cnt+startPos]);   
            }
            
            lcd.display();
            cnt = 0;

          if(oldEncPos != encoderPos) {
            oldEncPos = encoderPos;   
          }  
 }while(digitalRead(enSW));
 while(digitalRead(enSW)==0); //wait till switch is reseleased 
 return startPos;
}

/*******************************Utility Functions *******************************/
void time(long val){  
int days = elapsedDays(val);
int hours = numberOfHours(val);
int minutes = numberOfMinutes(val);
int seconds = numberOfSeconds(val);

 // digital clock display of current time
// display.print(days,DEC);  
// printDigits(hours);  
 lcd.print(minutes);
 printDigits(seconds);
 lcd.println();  
 
}

void printDigits(byte digits){
 // utility function for digital clock display: prints colon and leading 0
 lcd.print(":");
 if(digits < 10)
   lcd.print('0');
 lcd.print(digits,DEC);  
}

void PinA(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
  if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    encoderPos --; //decrement the encoder's position count
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  sei(); //restart interrupts
}


void PinB(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    encoderPos ++; //increment the encoder's position count
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

Edited with Code tags for better readability
 
Last edited by a moderator:
The code which you try to use for the interrupts does not work on a Teensy 3.5 which is a 32bit ARM processor while your code is written for 8bit AVR processors.
 
You'd have to modify the void PinA() and void PinB() functions, especially the reading = PIND & 0xC; line since there is nothing like PIND in ARM processors.

Why don't you just use one of the ready to use encoder libraries for Teensy 3.x which come with the Teensyduino installation?
 
Yes but to make this menu structure is a lot of work and knowledge, perhaps add a simple encoder part at the top and use the rest of the program?
 
First, fix this:

Code:
static int pinA = A12; // Our first hardware interrupt pin is digital pin 2
static int pinB = A13; // Our second hardware interrupt pin is digital pin 3

Pins A12 & A13 are analog only pins on Teensy. You need to choose pins which can be used at digital and interrupt.


Change this:

Code:
  attachInterrupt(0,PinA,RISING);
  attachInterrupt(1,PinB,RISING);

to this:


Code:
  attachInterrupt(digitalPinToInterrupt(pinA),PinA,RISING);
  attachInterrupt(digitalPinToInterrupt(pinB),PinB,RISING);


This code needs to be replaced too.

Code:
  reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values

There are many possible ways. One might look like:

Code:
  reading = 0;
  if (digitalRead(pinA)) reading |= B00000100;
  if (digitalRead(pinB)) reading |= B00001000;


The original code was not designed to run on any other boards, even other AVR boards.

Hopefully these edits are easy to understand? Understanding *why* is important, so you will be able to resolve problems. There's only so much we can do for you over the internet, so please take a moment to learn why these edits are needed so you will have better odds at resolving problems.
 
Status
Not open for further replies.
Back
Top