Timer1 interrupt conflicts with I2C

Seems I'm stuck again. For various system design reasons, I'm working directly with the twi.c library. I studied it and wiring.cpp quite thoroughly, and came up with a peer-to-peer (i.e. multi-master) linkage that works using only the twi.c library. The code for a simple ping/pong demo is below. That sketch works fine.

My problem is that another component of the design, one of the peers, is driving a ping sensor sitting on top of a servo. The servo sweeps back and forth in a speed-controlled fashion. This is achieved with Timer1 and some code wrapped with ISR( TIMER1_COMPA_vect ). That design works fine too.

But when I began to study the integration of the two, I ran into a conflict I can't explain.

Specifically, inclusion of TIMSK1 |= (1 << OCIE1A) disables I2C communications between the two peer Unos. If I comment it out, then I see "ABC" and "123" on my logic analyzer.

Thoughts, anyone?

extern "C"
{
  #include <twi.h>
}

// Reverse for the other device
#define THIS_ADDRESS 0x8
#define OTHER_ADDRESS 0x9
uint8_t ping_string[] = "ABC"; // use "123" for the other

volatile uint8_t ping = 0;     // must be volatile because of the ISR

// I2C stuff
#define BUFFER_LENGTH 32
uint8_t rxBuffer[BUFFER_LENGTH];
uint8_t rxBufferLength = 0;

int main( void )
{

// ping & servo stuff for study
  DDRD   |= (1 << 3 );                   // Enable pin 3 for OUTPUT
  DDRC   |=  (1 << 0);                   // Enable pin 14 for OUTPUT
  DDRC   |=  (1 << 1);                   // Enable pin 15 for OUTPUT
  TCCR1A  = 0;                           // normal counting mode.
  TCCR1B |= (1 << WGM12 );               // Put Timer1 into CTC mode
  TCCR1B |= (1 << CS11);                 // Prescale to 8
//  TIMSK1 |= (1 << OCIE1A);             // Enable Timer1 CTC interrupt

  
// I2C Setup
  twi_setAddress( THIS_ADDRESS );
  twi_attachSlaveRxEvent( onReceive );
  twi_init();
  sei();
  
  while( !ping )
  {  
    pong();              // to get things started
    cheap_delay( 1000 ); // wait a bit, then try again
  }
  
  while( 1 )
  {
    if( ping )
    {
      ping = 0;
      pong();
    }
  }
}

void pong()
{
  twi_writeTo(  OTHER_ADDRESS,
                ping_string,
                sizeof( ping_string ),
                1,  // blocking wait
                1); // sendStop
}

void cheap_delay( volatile unsigned long count )
{
  while( count > 0 ) count--;
}

void onReceive( uint8_t *twi_rxbuffer, int twi_count )
{
  memcpy( rxBuffer, twi_rxbuffer, twi_count );
  rxBufferLength = twi_count;
  ping = 1;
}

ISR( TIMER1_COMPA_vect )
{
}

Well, it seems I found the problem. If I change the Timer1 prescale from 8 to 256, i.e. from TCCR1B |= (1 << CS11) to TCCR1B |= (1 << CS12), the I2C ISR has enough time to do its job. Otherwise, the Timer1 ISR, with its higher priority, is blocking the I2C ISR because of the higher interrupt frequency.

Clearly that's not the timer1 ISR you are really using as its empty.

What are you doing in the ISR and how long does it take to run - ideally
as little time as possible (a few us). Then it shouldn't be too intrusive even with
timer1 running fast.

How often do you expect the ISR to run, BTW?

You're absolutely correct in your intuition. That very fact dawned me as well. The Timer1 ISR in the main project is driving a servo using software PWM. The minimum interrupt interval is 600us for the narrowest pulse. So I just plugged in the ping/pong code--without any Timer1 prescale changes--and I get both servo action and my I2C test strings.