attachInterruptVector() with Teensy 4.0

Status
Not open for further replies.

hitz_s

New member
Hi

I need to use the attachInterruptVector() to be able to capture a digital signal with a max. freq of 10 MHz with the superfast Teensy 4.0. The normally used (and easy to use) attachInterrupt has a too big delay between the signal change and the start of the interrupt routine.
After reading and searching in this forum, I realized why the attachInterrupt() is slower and that I need to use the attachInterruptVector(). I also once had to make that with an Arduino / ATmega32u4, but the Teensy 4.0 / IMXRT1060RM is at a completely different level...

I've found a post with a similar topic and tried to adapt it to my project (see last post): https://forum.pjrc.com/threads/57717-port-and-toggle-question-T4

That is my code, but sadly it doesn't work:
Code:
void change() {
 //do something extremely fast
  GPIO9_ISR = 0x08; // clear the IRQ
  asm("dsb");
}

void setup() {
  pinMode(2, INPUT); // connected to EMC_04
  pinMode(5, INPUT); // connected to EMC_08
  attachInterruptVector(IRQ_GPIO6789, &change);
  NVIC_ENABLE_IRQ(IRQ_GPIO6789);
  GPIO9_GDIR &= ~0x08;
  GPIO9_EDGE_SEL = 0x08;
  GPIO9_ISR = 0xffff;
  //attachInterrupt(digitalPinToInterrupt(2), change, CHANGE); // works, but "slow"
  //attachInterrupt(digitalPinToInterrupt(5), change, CHANGE); // works, but "slow"
}

void loop() {
  // do something else
}

It compiles perfectly, but no interrupt occurs. Tested at 600 MHz/Fast settings. For sure I did something wrong...

Can you please help me?

Thanks,
Stephan
 
I don't see anywhere where you are enabling the interrupt? That is the IMR register and probably maybe need to setup ICR1 and/or ICR2 depending on which pin...
 
Hi KurteE

Thank you for your answer. I'm in general confused about those thousands of registers available, so any help is appreciated!
I'll try to find the mentioned registers in the manual of the chip and will write back if I was successful or not. I think I'll need some time for it...
 
Double check these 2 lines?

Code:
  GPIO9_GDIR &= ~0x08;
  GPIO9_EDGE_SEL = 0x08;

You're using either pin 2 (bit 4 in GPIO9) or pin 5 (bit 8 in GPIO9), but this manipulates bit 3.

Maybe you meant this?

Code:
  GPIO9_GDIR &= ~(1<<8);
  GPIO9_EDGE_SEL = (1<<8);

Likewise with writing to GPIO9_ISR within your interrupt.
 
Thank you very much for your help, Paul and jonr!

I've finally found a working solution, this is my code right now:
Code:
uint8_t encA = 0;
uint8_t encB = 0;
uint8_t oldOutA = 0;
uint8_t oldOutB = 0;
uint8_t oldEncA = 0;
uint8_t oldEncB = 0;

FASTRUN void pin_isr() {
  uint8_t encA = digitalReadFast(2);
  uint8_t encB = digitalReadFast(5);
  if ((encA ^ oldEncA) | (encB ^ oldEncB)) { // step happened
    uint8_t dir = (encA ^ oldEncB); // set direction
    uint8_t outA = !(dir ^ oldOutB); //set out A
    oldOutB = dir ^ oldOutA; // set out B
    oldOutA = outA;
    digitalWriteFast(14, oldOutA); // write output signal A
    digitalWriteFast(22, oldOutB); // write output signal B
    oldEncA = encA;
    oldEncB = encB;
  }
  GPIO9_ISR = ((1<<4) | (1<<8)); // clear the IRQ
  asm("dsb");
}

void setup() {
  
  attachInterruptVector(IRQ_GPIO6789, &pin_isr);
  NVIC_ENABLE_IRQ(IRQ_GPIO6789);
  GPIO9_GDIR &= ~((1<<4) | (1<<8)); // Set as Input (0 = input)
  GPIO9_EDGE_SEL = ((1<<4) | (1<<8)); // Edge  select register, overrides ICR register, p.1034
  //GPIO9_ICR1 = 0x00; // not needed because of EDGE_SEL register
  GPIO9_IMR = ((1<<4) | (1<<8)); // Interrupt mask register, p.1032
  GPIO9_ISR = ((1<<4) | (1<<8)); // Interrupt status register, p. 1033

  pinMode(14, OUTPUT); // connected to AD_B1_02
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_02 = 0b11011111; // Max speed, fast slew rate, drive strength R0/3, p. 662ff
  
  pinMode(22, OUTPUT); // connected to AD_B1_08
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_08 = 0b11011111; // p. 674ff

  while (1) {}
  
}

void loop() {
}

Inside the ISR, I calculate something small and then, if needed, toggle an output. I now measured the delay between the change of the input signal and the change in the output signal:


  • Constantly polling two inputs (see code below): Delay between 65ns and 100ns (All interrupts disabled with cli(), 1.008 GHz clock freq., fastest option)
  • Interrupt methode: Delay usually 74ns, but if another interrupt occurs up to 134ns (1.08 GHz clock freq., fastest option). With another interrupt I mean an interrupt, which I didn't configure, but still happens (USB connection?). If someone knows which interrupt(s) it could be and how I can disable it, that would already be helpful.

So polling can be a little bit faster (65ns instead of 74ns), but varies continuously. The interrupt methode is very stable at 74ns, as long as no other (unwanted) interrupt occurs.
To conclude, if I can not "accelerate" one of the methods, I won't be able to catch an external signal at 10 MHz. But I still learned something and I'm still impressed about the speed of the Teensy 4.0!

This is the polling version:
Code:
#define PIN_A_IN 2
#define PIN_B_IN 5
#define PIN_A_OUT 22 // 22 = AD_B1_08
#define PIN_B_OUT 14 // 14 = AD_B1_02

void setup() {
  pinMode(PIN_A_OUT, OUTPUT);
  pinMode(PIN_B_OUT, OUTPUT);
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_02 = 0b11011111;
  IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_08 = 0b11011111;
  prog();
}

void loop() { // too slow
}

FASTRUN void prog() {
  uint8_t oldOutA = 0;
  uint8_t oldOutB = 0;
  uint8_t oldEncA = 0;
  uint8_t oldEncB = 0;
  cli();
  while (1) {
    uint8_t encA = digitalReadFast(PIN_A_IN);
    uint8_t encB = digitalReadFast(PIN_B_IN);
    if ((encA ^ oldEncA) | (encB ^ oldEncB)) { // step happened
      uint8_t dir = (encA ^ oldEncB); // set direction
      uint8_t outA = !(dir ^ oldOutB); //set out A
      oldOutB = dir ^ oldOutA; // set out B
      digitalWriteFast(PIN_A_OUT, outA); // write output signal A
      digitalWriteFast(PIN_B_OUT, oldOutB); // write output signal B
      oldOutA = outA;
      oldEncA = encA;
      oldEncB = encB;
    }
  }
}
 
You could tighten up that polling code to be a little faster. I think a reliable < 50 ns is possible.

> if I can not "accelerate" one of the methods, I won't be able to catch an external signal at 10 MHz

The teensy 4.0 has hardware for input capture with time stamp. This works for some applications.
 
Last edited:
Status
Not open for further replies.
Back
Top