UNO R4 WIFI and interrupts stops after a few seconds

hello every one !
so i 'm working on a ltc reader, and got it working fine on a R3 and leonardo.
when i switched to a uno R4 Wifi, when i input LTC, it acts erratically for a few seconds and then stops.
The LTC signal is an output from a souncard plugged into the port2 of the arduino R4
i made this simple sketch just to monitor the interrupts :

volatile unsigned long int increment;
unsigned long int interval = 0;
volatile unsigned long int timeincrement=0;

#include "RTC.h"


void setup() {
pinMode(2,INPUT_PULLUP);  // Setup interrupt pin
  Serial.begin(115200);

  RTC.begin();

    RTCTime startTime(30, Month::JUNE, 2023, 13, 37, 00, DayOfWeek::WEDNESDAY, SaveLight::SAVING_TIME_ACTIVE);

  RTC.setTime(startTime);
  if (!RTC.setPeriodicCallback(periodic_cbk, Period::ONCE_EVERY_1_SEC)) {
    Serial.println("ERROR: periodic callback not set");
  }

  
    attachInterrupt(digitalPinToInterrupt(2), int0ISR, FALLING);
  // put your setup code here, to run once:

}

void loop() {
 interrupts();
  if (millis()-interval>= 1000)
    {
      Serial.print(increment);
      Serial.print("  time inc :");
      Serial.print(timeincrement);
Serial.print("    ");
Serial.println(millis()/1000);

    
      interval=millis();
      }
  // put your main code here, to run repeatedly:

}

void periodic_cbk() {
timeincrement++;
}


void int0ISR(){
increment++;

}

the result on a uno r3 : (4k triggers per seconds) :

16:33:28.137 -> 8672  time inc :0
16:33:29.132 -> 12905  time inc :0
16:33:30.125 -> 17264  time inc :0
16:33:31.152 -> 21920  time inc :0
16:33:32.144 -> 26789  time inc :0
16:33:33.137 -> 31845  time inc :0
16:33:34.134 -> 36995  time inc :0
16:33:35.128 -> 42224  time inc :0
16:33:36.156 -> 47563  time inc :0
16:33:37.150 -> 52973  time inc :0
16:33:38.144 -> 57856  time inc :0
16:33:39.137 -> 61874  time inc :0
16:33:40.162 -> 65796  time inc :0
16:33:41.159 -> 70691  time inc :0
16:33:42.153 -> 76959  time inc :0
16:33:43.147 -> 79483  time inc :0

and on th R4 Wifi , we can see that it s way slower, (like triggers only around 1k times per seconds) and stops triggering after a few seconds.i added the RTC interrupt to see if all interrupts were stopped at some point, but the RTC interuption routines continues to trigger fine.

16:29:09.238 -> 0  time inc :1    1
16:29:10.229 -> 1  time inc :2    2
16:29:11.223 -> 912  time inc :3    3
16:29:12.250 -> 2173  time inc :4    4
16:29:13.243 -> 3438  time inc :5    5
16:29:14.238 -> 4706  time inc :6    6
16:29:15.264 -> 5984  time inc :7    7
16:29:16.257 -> 7247  time inc :8    8
16:29:17.251 -> 8512  time inc :9    9
16:29:18.276 -> 9775  time inc :10    10
16:29:19.272 -> 10993  time inc :11    11
16:29:20.297 -> 11212  time inc :12    12
16:29:21.292 -> 11222  time inc :13    13
16:29:22.287 -> 11230  time inc :14    14
16:29:23.282 -> 11230  time inc :15    15
16:29:24.308 -> 11231  time inc :16    16
16:29:25.300 -> 11231  time inc :17    17
16:29:26.327 -> 11231  time inc :18    18

is that a normal behaviour of the R4 ? do you have any fix for that?

Well, can you provide something which enables us to see what you originally are putting in on port 2 from the sound card? When you say port 2, which input is it? I think first and foremost it would be great if you start using the mnemonics, ie. D0, D1, D2, etc., as that is a more safe way of doing it.

There can be many issues - but one of them is - if you have an interrupt triggering at the same time while you are in the interrupt routine. I recall from many microprocessors that one need to switch off interrupts as the first thing inside of an interrupt service routine. Then you switch on just before you do the Return from Interrupt. MAYBE the Arduino R4 does not need that. I cannot tell.

thanks for the answers !
i solve part of the problem , the interrupts triggers be still behave wierdly (the signal was bad )

description of the setup :
ltc signal is outputed from a Decklink card on a XLR output.

arduino (both leonardo and R4 ) receive the LTC Signal on D3 : pinMode(3,INPUT_PULLUP);
"optionnal an op amp is used for ltc input, leonardo works fine without it"
(code at the end of the post)
the result :

  • on the leonardo or uno r3 , things runs fine.
  • on the R4 WIFI , it looks like interrupts are too slow and the lcd displays erratic values ).

video here : VID_20230807_114617.mp4 - Google Drive

i tried disabling and re-enabling the interrupts routine and it doesn t fix it.

the original creator of the code gave me a direction to look into :
on ESP32 , the interrupt should be declared like this

void IRAM_ATTR ISR() {
    Statements;
}

but IRAM_ATTR is not recognized on compile :

C:\Users\julien\Documents\Arduino\just_tc\just_tc.ino:127:16: error: expected initializer before 'ISR'
 void IRAM_ATTR ISR() {

but if i switch to a esp32 board, the IDE recognize it

any hints for me ?
Thanks in advance.

the code ::


volatile bool test = false;
#include <LiquidCrystal.h>

char lastframenum,lastdecnum;   
byte maxframe=0;


int timer1_compare_match;

 volatile int uMax0 = 625; // Any time greater than this is to big
 volatile int uMax1 = 375 ;// Midpoint timing between a 1 and 0 bit length
 volatile int uMin1 = 125; // Any time less than this is to short



LiquidCrystal lcd(8, 9, 4, 5, 6, 7);


unsigned long int testmillis =0;
bool TCVALID =false;



byte blinkx = 12;
 int sequence[6] = {0,0,0,4,1,5};

volatile uint8_t tc[10] = {0};                            // ISR Buffer to store incoming bits
volatile uint8_t xtc[8] = {0};                            // Buffer to store valid TC data - sync bytes
volatile uint8_t tcFlags = 0;                             // Various flags used by ISR and main code
volatile uint32_t uSeconds;  





volatile bool recording = false;
volatile bool ismenu = false;
int pressedbutton = 100;


const word sync = 0xBFFC; // Sync word to expect when running tape forward

enum flagBits {
  tcValid,        // TC copied to xtc is valid (Used by main loop to determing if a timecode has arrived)
  tcFrameError,   // ISR edge out of timing bounds (only gets reset after next valid TC read)
  tcOverrun,      // TC was not read from xtc by main loop before next value was ready (so main loop can tell if timecodes have been lost)
  tcForceUpdate,  // Valid TC will always be copied to buffer even if last value not read (if set by main code then TC will always be copied)
  tcHalfOne       // ISR is reading a 1 bit so ignore next edge (Internal to ISR)
};



class cHeartBeat
{
  private:
  
  unsigned long m_count;
  int m_pinLED;
  int m_pin_state, m_pin_state_old;
  unsigned long m_mask_result, m_mask_result_old;
  const unsigned long m_mask = B01111110;
  
  public:
  cHeartBeat(const int pinLED)            // define heartbeat LED pin
  {
    pinMode(pinLED, OUTPUT);
    digitalWrite(pinLED, LOW);
    m_pinLED = pinLED;
    m_count = 0;
    m_pin_state = 1;
    m_pin_state_old = !m_pin_state;
    m_mask_result_old = 0;
  }

  void run()
  {
    // Serial.println(m_mask);
    m_count++;
    m_mask_result = m_count & (m_mask << 11);
    m_pin_state = m_mask_result && 1;
    if (m_pin_state != m_pin_state_old)
    {
      digitalWrite(m_pinLED, m_pin_state);
      m_pin_state_old = m_pin_state;
    }
  }

 
};

struct FLAGS
{
  uint8_t drop_frame, color_frame;
  char drop[2], color[2];
};



                             // ISR store of last edge change time

char timeCode[13];                                        // For example code another buffer to write decoded timecode
char userBits[12];                                        // For example code another buffer to write decoded user bits
char tc_send[9];                                          // For 4 bit decimal communication (HHMMSSff + FINISH CODE)

bool DCBconversion[][4] =  {{0,0,0,0},     // Number 0
                            {0,0,0,1},
                            {0,0,1,0},
                            {0,0,1,1},
                            {0,1,0,0},
                            {0,1,0,1},
                            {0,1,1,0},
                            {0,1,1,1},
                            {1,0,0,0},
                            {1,0,0,1},   // Number 9
                            {1,0,1,0}};  // Number 10 (Finish signal)


//led_fback debug_ledA(12);             // LED for debugging
//led_fback debug_ledB(11);
cHeartBeat Heartbeat(13);                   // start heartbeat with LED pin number


FLAGS signal_flag;





void setup(){
 
  lcd.begin(16,2);


  Serial.begin(115200);

    lcd.setCursor(0,0);
    lcd.print("LOADING         ");
    lcd.setCursor(0,1);
    lcd.print("         LOADING");


  /////Pinhere
  pinMode(3,INPUT_PULLUP);  // Setup interrupt pin
  pinMode(10,OUTPUT);
  digitalWrite(10, HIGH);   // LCD Backlight on



  pinMode(A1, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(A3, OUTPUT);
  pinMode(A4, OUTPUT);
  pinMode(A5, OUTPUT);

  digitalWrite(A1, LOW);
  digitalWrite(A2, LOW);
  digitalWrite(A3, LOW);
  digitalWrite(A4, LOW);
  digitalWrite(A5, LOW);
  

  Serial.println(F("Waiting For TC"));


  
  lcd.setCursor(0,0);
  lcd.print("                ");

  lcd.setCursor(0,1);
  lcd.print("                ");
  Serial.flush();

  signal_flag.drop[0] = ' ';
  signal_flag.drop[1] = 'd';
  signal_flag.color[0] = ' ';
  signal_flag.color[1] = 'c';



  attachInterrupt(digitalPinToInterrupt(3), int0ISR, CHANGE);
 interrupts();
}

bool CheckTC(){

  lcd.setCursor(11,0);

  lcd.print("   ");
  lcd.setCursor(14,0);
  lcd.print(maxframe);
  if (lastframenum == timeCode[10] && lastdecnum ==timeCode[7])
  {
    //TCVALID=false;
    return false;
  }
  else
  {
  TCVALID=true;
  //return true;

  }
 lastframenum = timeCode[10];
 lastdecnum = timeCode[7];
 return TCVALID;
  



}
void loop(){
 
CheckTC();
  lcd.setCursor(3,1);
  lcd.print(timeCode); 

  if (millis()>= testmillis+ 1000)
  {
    testmillis=millis();
    lcd.setCursor (2,0);
    lcd.print(millis()/1000);
   


  }
/*
  while(!bitRead(tcFlags, tcValid)){ 
      Heartbeat.run();
      };                // Wait for valid timecode

*/
  timeCode[0] = (xtc[0] & 0x03) + '0';                    // 10's of hours
  timeCode[1] = (xtc[1] & 0x0F) + '0';                    // hours
  timeCode[2] = ':';                
  timeCode[3] = (xtc[2] & 0x07) + '0';                    // 10's of minutes
  timeCode[4] = (xtc[3] & 0x0F) + '0';                    // minutes
  timeCode[5] =  ':';               
  timeCode[6] = (xtc[4] & 0x07) + '0';                    // 10's of seconds
  timeCode[7] = (xtc[5] & 0x0F) + '0';                    // seconds
  timeCode[8] =  '.';               
  timeCode[9] = (xtc[6] & 0x03) + '0';                    // 10's of frames
  timeCode[10] = (xtc[7] & 0x0F) + '0';                   // frames




//char fr[2]={timeCode[9],timeCode[10]};




  bitClear(tcFlags, tcValid);                             // Finished with TC so signal to ISR it can overwrite it with next TC
  
}




void int0ISR(){
  
 
  uint32_t edgeTimeDiff = micros() - uSeconds;            // Get time difference between this and last edge
  uSeconds = micros();                                    // Store time of this edge

  if ((edgeTimeDiff < uMin1) or (edgeTimeDiff > uMax0)) { // Drop out now if edge time not withing bounds
    bitSet(tcFlags, tcFrameError);
    test=true;

   

    return;
  }
  test=false;
  
  if (edgeTimeDiff > uMax1)                               // A zero bit arrived
  {

    if (bitRead(tcFlags, tcHalfOne) == 1){                // But we are expecting a 1 edge
      bitClear(tcFlags, tcHalfOne);
      clearBuffer(tc, sizeof(tc));
       interrupts();
      return;
    }
    // 0 bit
    shiftRight(tc, sizeof(tc)-1);                           // Rotate buffer right
//    Shift replaces top bit with zero so nothing else to do
    bitClear(tc[0], 7);                                   // Reset the 1 bit in the buffer
  }
  else                                                    // Not zero so must be a 1 bit
  { // 1 bit
    if (bitRead(tcFlags, tcHalfOne) == 0){                // First edge of a 1 bit
      bitSet(tcFlags, tcHalfOne);                         // Flag we have the first half
       interrupts();
      return;
    }
    // Second edge of a 1 bit
    bitClear(tcFlags, tcHalfOne);                         // Clear half 1 flag
    shiftRight(tc, sizeof(tc)-1);                           // Rotate buffer right
    bitSet(tc[0], 7);                                     // Set the 1 bit in the buffer

  }
  
  // Congratulations, we have managed to read a valid 0 or 1 bit into buffer
  if (word(tc[0], tc[1]) == sync){                        // Last 2 bytes read = sync?
    //debug_ledB.toggle();                                  // Feedback LED for every sync
    bitClear(tcFlags, tcFrameError);                      // Clear framing error
    bitClear(tcFlags, tcOverrun);                         // Clear overrun error
    if (bitRead(tcFlags, tcForceUpdate) == 1){
      bitClear(tcFlags, tcValid);                         // Signal last TC read
    }
    if (bitRead(tcFlags, tcValid) == 1){                  // Last TC not read
      bitSet(tcFlags, tcOverrun); 
      Serial.println("notc");                        // Flag overrun error
       interrupts();
      return;                                             // Do nothing else
    }


    for (uint8_t x = 0; x < sizeof(xtc); x++){            // Copy buffer without sync word
      xtc[x] = tc[x + 2];
    }
    bitSet(tcFlags, tcValid);
                             // Signal valid TC
  }
 interrupts();
  return;
}


void clearBuffer(volatile uint8_t theArray[], uint8_t theArraySize){
  
  for (uint8_t x = 0; x < theArraySize - 1; x++){
    theArray[x] = 0;
  }
  
}

void shiftRight(volatile uint8_t theArray[], uint8_t theArraySize){
  
  uint8_t x;
  for (x = theArraySize; x > 0; x--){
    uint8_t xBit = bitRead(theArray[x - 1], 0);
    theArray[x] = theArray[x] >> 1;
    theArray[x] = theArray[x] | (xBit << 7);
  }
  theArray[x] = theArray[x] >> 1;
  
  
}