Delay in timer interrupt

OK. as principle you say "yes".

I tried to find information how the different constants for interrupt-enable-bits
like

OCIE1A); // enable timer compare interrupt
OCIE1B); // enable timer compare interrupt
ICIE1); 

etc. are related to the parameter used in the ISR like

TIMER1_OVF_vect
TIMER1_CAPT_vect
TIMER1_ICR1_vect

But I had no succes so far.

I looked up the table in the datasheet from atmel

But this did not help

I googled for tutorials that would explain them all but only found tutorials for
compare match A / compare match B

Maybe my basic code is just not useful for creating the DCC-bitstream as asked from the thread opener

This basic code uses compare match A register for the duty-cycle simply set to half the period
and uses the ICR1-register to define the period / the frequency of the fast-pwm-signal
This code changes between 2 * 58 = 116 µsec = 8.6 kHz and 2 * 100 = 200 µsec = 5 kHz frequency

As long a changing the values for

  ICR1   = PERIOD_bitIsOne - 1;  // period
  OCR1A  = PERIOD_bitIsOne / 2;  // duty cycle 1/2 = 50%

is done in loop() this works

code fast-pwm mode 14 TOP = ICR1

unsigned long MyTestTimer = 0; // Timer-variables MUST be of type unsigned long
const byte OnBoard_LED = 13; // onboard-LED uno, mega

constexpr float PWM_bitIsOne = 8620.69;
constexpr float PWM_bitIsZero = 5000.0;

constexpr uint16_t PERIOD_bitIsOne = round(F_CPU / PWM_bitIsOne);
constexpr uint16_t PERIOD_bitIsZero = round(F_CPU / PWM_bitIsZero);

boolean bitIs1 = true;

void setupTimer1() {
// Configure Timer 1 for X kHz PWM on pin 9 = PB1 = OC1A.
DDRB |= _BV(PB1); // set pin as output
TCCR1B = 0; // stop timer
TCCR1A = _BV(COM1A1) // non-inverting PWM on OC1A
| _BV(WGM11); // mode 14: fast PWM, TOP = ICR1
TCCR1B = _BV(WGM12) // ditto
| _BV(WGM13) // ditto
| _BV(CS10); // clock @ F_CPU
ICR1 = PERIOD_bitIsOne - 1; // period
OCR1A = PERIOD_bitIsOne / 2; // duty cycle 1/2 = 50%
}

void setup() {
Serial.begin(115200);
Serial.println("Setup-Start");
PrintFileNameDateTime();

setupTimer1();
}

void loop() {
BlinkHeartBeatLED(OnBoard_LED, 250);

if ( TimePeriodIsOver(MyTestTimer, 5000) ) {

if (bitIs1) {
  bitIs1 = false;
  cli();
  ICR1   = PERIOD_bitIsZero - 1;  // period
  OCR1A  = PERIOD_bitIsZero / 2;  // duty cycle 1/2 = 50%
  sei();
  Serial.print(PWM_bitIsZero);
  Serial.println(" kHz");
}
else {
  bitIs1 = true;
  cli();
  ICR1   = PERIOD_bitIsOne - 1;  // period
  OCR1A  = PERIOD_bitIsOne / 2;  // duty cycle 1/2 = 50%
  sei();
  Serial.print(PWM_bitIsOne);
  Serial.println(" kHz");
}

}
}

// helper-functions
void PrintFileNameDateTime() {
Serial.println( F("Code running comes from file ") );
Serial.println( F(FILE) );
Serial.print( F(" compiled ") );
Serial.print( F(DATE) );
Serial.print( F(" ") );
Serial.println( F(TIME) );
}

// easy to use helper-function for non-blocking timing
// explanation see here
// Example-code for timing based on millis() easier to understand through the use of example-numbers / avoiding delay()
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}

void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
static unsigned long MyBlinkTimer;
pinMode(IO_Pin, OUTPUT);

if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
}
}

All my attempts to change these values at the end of each PWM-cycle failed so far

Is it possible to invoke an interrupt on ICR1-match?
if yes where can I look up the constant name for the parameter used in the ISR
I mean the analogic constant name like
Timer1 COMP1A-register COMP1A-match-vector constant-name "TIMER1_COMPA_vect"

or if this does not work at all with TOP = ICR1

is there a different mode for fast PWM that defines a different register to set the PWM-frequency and can invoke an interrupt?

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName,timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;   // onboard-LED uno, mega


constexpr float PWM_bitIsOne  = 8620.69;//17241.379; // 15e3;  // 15 kHz
constexpr float PWM_bitIsZero = 5000.0; // 15e3;  // 15 kHz

constexpr uint16_t PERIOD_bitIsOne  = round(F_CPU / PWM_bitIsOne);
constexpr uint16_t PERIOD_bitIsZero = round(F_CPU / PWM_bitIsZero);

constexpr uint16_t PERIOD_Diff = PERIOD_bitIsZero - PERIOD_bitIsOne;

volatile boolean bitIs1 = true;
volatile byte ISRCnt = 0;

void setupTimer1() {
  // Configure Timer 1 for X kHz PWM on pin 9 = PB1 = OC1A.
  DDRB  |= _BV(PB1);    // set pin as output
  TCCR1B = 0;           // stop timer
  TCCR1A = _BV(COM1A1)  // non-inverting PWN on OC1A
           | _BV(WGM11);  // mode 14: fast PWM, TOP = ICR1
  TCCR1B = _BV(WGM12)     // ditto
           | _BV(WGM13)   // ditto
           | _BV(CS10);   // clock @ F_CPU
  ICR1   = PERIOD_bitIsOne - 1;  // period
  OCR1A  = PERIOD_bitIsOne / 2;  // duty cycle 1/2 = 50%

  TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt

  dbg("TS", PERIOD_bitIsOne);
  dbg("TS", PERIOD_bitIsZero);
}


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();

  setupTimer1();
}


ISR(TIMER1_COMPA_vect) {
  ISRCnt++;

  if (ISRCnt == 2) {
    ISRCnt = 0;

    if (bitIs1) {
      bitIs1 = false;
      ICR1   = PERIOD_bitIsZero - 1;  // period
      OCR1A  = PERIOD_bitIsZero / 2;  // duty cycle 1/2 = 50%
    }
    else {
      bitIs1 = true;
      ICR1   = PERIOD_bitIsOne - 1;  // period
      OCR1A  = PERIOD_bitIsOne / 2;  // duty cycle 1/2 = 50%
    }
  }
}



void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);

}




// helper-functions
void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
// explanation see here
// https://forum.arduino.cc/t/example-code-for-timing-based-on-millis-easier-to-understand-through-the-use-of-example-numbers-avoiding-delay/974017
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}



void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}