The interrupt routine just calls 'Wire.onService()', so if I disable the interrupt then I can just invoke 'onService' by myself in a loop (assuming this won't cause any timing issues).
So here's the C++ code for 'Wire::onService' taken from 'Wire.cpp':
void TwoWire::onService(void)
{
if ( sercom->isSlaveWIRE() )
{
if(sercom->isStopDetectedWIRE() ||
(sercom->isAddressMatch() && sercom->isRestartDetectedWIRE() && !sercom->isMasterReadOperationWIRE())) //Stop or Restart detected
{
sercom->prepareAckBitWIRE();
sercom->prepareCommandBitsWire(0x03);
//Calling onReceiveCallback, if exists
if(onReceiveCallback)
{
onReceiveCallback(available());
}
rxBuffer.clear();
}
else if(sercom->isAddressMatch()) //Address Match
{
sercom->prepareAckBitWIRE();
sercom->prepareCommandBitsWire(0x03);
if(sercom->isMasterReadOperationWIRE()) //Is a request ?
{
txBuffer.clear();
transmissionBegun = true;
//Calling onRequestCallback, if exists
if(onRequestCallback)
{
onRequestCallback();
}
}
}
else if(sercom->isDataReadyWIRE())
{
if (sercom->isMasterReadOperationWIRE())
{
uint8_t c = 0xff;
if( txBuffer.available() ) {
c = txBuffer.read_char();
}
transmissionBegun = sercom->sendDataSlaveWIRE(c);
} else { //Received data
if (rxBuffer.isFull()) {
sercom->prepareNackBitWIRE();
} else {
//Store data
rxBuffer.store_char(sercom->readDataWIRE());
sercom->prepareAckBitWIRE();
}
sercom->prepareCommandBitsWire(0x03);
}
}
}
}
If I replace all the invocations of 'sercom' methods with inline code (copy-pasted from SERCOM.cpp), then it looks like this:
struct WireDerived : decltype(Wire) { void onService(void); };
void WireDerived::onService(void)
{
decltype(this->sercom->sercom) &srcm = this->sercom->sercom;
if ( I2C_SLAVE_OPERATION != srcm->I2CS.CTRLA.bit.MODE ) return;
if ( (srcm->I2CS.INTFLAG.bit.PREC) || ((srcm->I2CS.STATUS.bit.SR) && !(srcm->I2CS.STATUS.bit.DIR))) //Stop or Restart detected
{
if ( I2C_MASTER_OPERATION == srcm->I2CS.CTRLA.bit.MODE ) srcm->I2CM.CTRLB.bit.ACKACT = 0;
else srcm->I2CS.CTRLB.bit.ACKACT = 0;
if( I2C_MASTER_OPERATION == srcm->I2CS.CTRLA.bit.MODE )
{
srcm->I2CM.CTRLB.bit.CMD = 0x03;
while( srcm->I2CM.SYNCBUSY.bit.SYSOP ); // Waiting for synchronization
}
else
{
srcm->I2CS.CTRLB.bit.CMD = 0x03;
}
//Calling onReceiveCallback, if exists
if ( onReceiveCallback )
{
onReceiveCallback(available());
}
rxBuffer.clear();
}
else if ( srcm->I2CS.INTFLAG.bit.AMATCH ) //Address Match
{
if ( I2C_MASTER_OPERATION == srcm->I2CS.CTRLA.bit.MODE ) srcm->I2CM.CTRLB.bit.ACKACT = 0;
else srcm->I2CS.CTRLB.bit.ACKACT = 0;
if ( I2C_MASTER_OPERATION == srcm->I2CS.CTRLA.bit.MODE )
{
srcm->I2CM.CTRLB.bit.CMD = 0x03;
while ( srcm->I2CM.SYNCBUSY.bit.SYSOP ); // Waiting for synchronization
}
else
{
srcm->I2CS.CTRLB.bit.CMD = 0x03;
}
if ( srcm->I2CS.STATUS.bit.DIR ) //Is a request ?
{
txBuffer.clear();
transmissionBegun = true;
//Calling onRequestCallback, if exists
if(onRequestCallback)
{
onRequestCallback();
}
}
}
else if( srcm->I2CS.INTFLAG.bit.DRDY ) // Data is ready
{
if ( I2C_MASTER_OPERATION == srcm->I2CS.CTRLA.bit.MODE )
{
uint8_t c = 0xff;
if( txBuffer.available() ) c = txBuffer.read_char();
//Send data
srcm->I2CS.DATA.bit.DATA = c;
//Problems on line? nack received?
if( !srcm->I2CS.INTFLAG.bit.DRDY || srcm->I2CS.STATUS.bit.RXNACK )
transmissionBegun = false;
else
transmissionBegun = true;
}
else
{
//Received data
if ( rxBuffer.isFull() )
{
if( I2C_MASTER_OPERATION == srcm->I2CS.CTRLA.bit.MODE ) srcm->I2CM.CTRLB.bit.ACKACT = 1;
else srcm->I2CS.CTRLB.bit.ACKACT = 1;
}
else
{
//Store data
char retval;
if( I2C_MASTER_OPERATION == srcm->I2CS.CTRLA.bit.MODE )
{
while( srcm->I2CM.INTFLAG.bit.SB == 0 && srcm->I2CM.INTFLAG.bit.MB == 0 ); // Waiting complete receive
retval = srcm->I2CM.DATA.bit.DATA;
}
else
{
retval = srcm->I2CS.DATA.reg;
}
rxBuffer.store_char(retval);
if( I2C_MASTER_OPERATION == srcm->I2CS.CTRLA.bit.MODE ) srcm->I2CM.CTRLB.bit.ACKACT = 0;
else srcm->I2CS.CTRLB.bit.ACKACT = 0;
}
if ( I2C_MASTER_OPERATION == srcm->I2CS.CTRLA.bit.MODE )
{
srcm->I2CM.CTRLB.bit.CMD = 0x03;
while(srcm->I2CM.SYNCBUSY.bit.SYSOP); // Waiting for synchronization
}
else
{
srcm->I2CS.CTRLB.bit.CMD = 0x03;
}
}
}
}
If I compile this, I get the following assembler:
0000237c <WireDerived::onService()>:
237c: b570 push {r4, r5, r6, lr}
237e: 6902 ldr r2, [r0, #16]
2380: 0004 movs r4, r0
2382: 6813 ldr r3, [r2, #0]
2384: 6819 ldr r1, [r3, #0]
2386: 06c9 lsls r1, r1, #27
2388: 0f49 lsrs r1, r1, #29
238a: 2904 cmp r1, #4
238c: d15c bne.n 2448 <WireDerived::onService()+0xcc>
238e: 7e19 ldrb r1, [r3, #24]
2390: 07c9 lsls r1, r1, #31
2392: d436 bmi.n 2402 <WireDerived::onService()+0x86>
2394: 8b59 ldrh r1, [r3, #26]
2396: 06c9 lsls r1, r1, #27
2398: d502 bpl.n 23a0 <WireDerived::onService()+0x24>
239a: 8b59 ldrh r1, [r3, #26]
239c: 0709 lsls r1, r1, #28
239e: d530 bpl.n 2402 <WireDerived::onService()+0x86>
23a0: 7e19 ldrb r1, [r3, #24]
23a2: 0789 lsls r1, r1, #30
23a4: d455 bmi.n 2452 <WireDerived::onService()+0xd6>
23a6: 7e19 ldrb r1, [r3, #24]
23a8: 0749 lsls r1, r1, #29
23aa: d54d bpl.n 2448 <WireDerived::onService()+0xcc>
23ac: 6819 ldr r1, [r3, #0]
23ae: 06c9 lsls r1, r1, #27
23b0: 0f49 lsrs r1, r1, #29
23b2: 2905 cmp r1, #5
23b4: d000 beq.n 23b8 <WireDerived::onService()+0x3c>
23b6: e07d b.n 24b4 <WireDerived::onService()+0x138>
23b8: 2388 movs r3, #136 ; 0x88
23ba: 009b lsls r3, r3, #2
23bc: 18e3 adds r3, r4, r3
23be: 68d9 ldr r1, [r3, #12]
23c0: 25ff movs r5, #255 ; 0xff
23c2: 2900 cmp r1, #0
23c4: d010 beq.n 23e8 <WireDerived::onService()+0x6c>
23c6: 68d9 ldr r1, [r3, #12]
23c8: 2900 cmp r1, #0
23ca: d100 bne.n 23ce <WireDerived::onService()+0x52>
23cc: e06d b.n 24aa <WireDerived::onService()+0x12e>
23ce: 2092 movs r0, #146 ; 0x92
23d0: 6899 ldr r1, [r3, #8]
23d2: 0040 lsls r0, r0, #1
23d4: 1861 adds r1, r4, r1
23d6: 5c08 ldrb r0, [r1, r0]
23d8: 6899 ldr r1, [r3, #8]
23da: 3101 adds r1, #1
23dc: 4029 ands r1, r5
23de: 6099 str r1, [r3, #8]
23e0: 68d9 ldr r1, [r3, #12]
23e2: 3901 subs r1, #1
23e4: 60d9 str r1, [r3, #12]
23e6: b2c5 uxtb r5, r0
23e8: 6813 ldr r3, [r2, #0]
23ea: 3328 adds r3, #40 ; 0x28
23ec: 701d strb r5, [r3, #0]
23ee: 6813 ldr r3, [r2, #0]
23f0: 7e1a ldrb r2, [r3, #24]
23f2: 0752 lsls r2, r2, #29
23f4: d55c bpl.n 24b0 <WireDerived::onService()+0x134>
23f6: 8b5b ldrh r3, [r3, #26]
23f8: 075b lsls r3, r3, #29
23fa: d459 bmi.n 24b0 <WireDerived::onService()+0x134>
23fc: 2301 movs r3, #1
23fe: 75a3 strb r3, [r4, #22]
2400: e022 b.n 2448 <WireDerived::onService()+0xcc>
2402: 6819 ldr r1, [r3, #0]
2404: 484d ldr r0, [pc, #308] ; (253c <WireDerived::onService()+0x1c0>)
2406: 6859 ldr r1, [r3, #4]
2408: 4001 ands r1, r0
240a: 6059 str r1, [r3, #4]
240c: 23c0 movs r3, #192 ; 0xc0
240e: 6811 ldr r1, [r2, #0]
2410: 029b lsls r3, r3, #10
2412: 6808 ldr r0, [r1, #0]
2414: 06c0 lsls r0, r0, #27
2416: 0f40 lsrs r0, r0, #29
2418: 2805 cmp r0, #5
241a: d116 bne.n 244a <WireDerived::onService()+0xce>
241c: 6848 ldr r0, [r1, #4]
241e: 4303 orrs r3, r0
2420: 604b str r3, [r1, #4]
2422: 6812 ldr r2, [r2, #0]
2424: 69d3 ldr r3, [r2, #28]
2426: 075b lsls r3, r3, #29
2428: d4fc bmi.n 2424 <WireDerived::onService()+0xa8>
242a: 238e movs r3, #142 ; 0x8e
242c: 009b lsls r3, r3, #2
242e: 58e5 ldr r5, [r4, r3]
2430: 2d00 cmp r5, #0
2432: d004 beq.n 243e <WireDerived::onService()+0xc2>
2434: 6823 ldr r3, [r4, #0]
2436: 0020 movs r0, r4
2438: 691b ldr r3, [r3, #16]
243a: 4798 blx r3
243c: 47a8 blx r5
243e: 2300 movs r3, #0
2440: 34fc adds r4, #252 ; 0xfc
2442: 61e3 str r3, [r4, #28]
2444: 6223 str r3, [r4, #32]
2446: 6263 str r3, [r4, #36] ; 0x24
2448: bd70 pop {r4, r5, r6, pc}
244a: 684a ldr r2, [r1, #4]
244c: 4313 orrs r3, r2
244e: 604b str r3, [r1, #4]
2450: e7eb b.n 242a <WireDerived::onService()+0xae>
2452: 6819 ldr r1, [r3, #0]
2454: 4839 ldr r0, [pc, #228] ; (253c <WireDerived::onService()+0x1c0>)
2456: 6859 ldr r1, [r3, #4]
2458: 4001 ands r1, r0
245a: 6059 str r1, [r3, #4]
245c: 23c0 movs r3, #192 ; 0xc0
245e: 6811 ldr r1, [r2, #0]
2460: 029b lsls r3, r3, #10
2462: 6808 ldr r0, [r1, #0]
2464: 06c0 lsls r0, r0, #27
2466: 0f40 lsrs r0, r0, #29
2468: 2805 cmp r0, #5
246a: d11a bne.n 24a2 <WireDerived::onService()+0x126>
246c: 6848 ldr r0, [r1, #4]
246e: 4303 orrs r3, r0
2470: 604b str r3, [r1, #4]
2472: 6811 ldr r1, [r2, #0]
2474: 69cb ldr r3, [r1, #28]
2476: 075b lsls r3, r3, #29
2478: d4fc bmi.n 2474 <WireDerived::onService()+0xf8>
247a: 6813 ldr r3, [r2, #0]
247c: 8b5b ldrh r3, [r3, #26]
247e: 071b lsls r3, r3, #28
2480: d5e2 bpl.n 2448 <WireDerived::onService()+0xcc>
2482: 2388 movs r3, #136 ; 0x88
2484: 2200 movs r2, #0
2486: 009b lsls r3, r3, #2
2488: 18e3 adds r3, r4, r3
248a: 605a str r2, [r3, #4]
248c: 609a str r2, [r3, #8]
248e: 60da str r2, [r3, #12]
2490: 2301 movs r3, #1
2492: 75a3 strb r3, [r4, #22]
2494: 238d movs r3, #141 ; 0x8d
2496: 009b lsls r3, r3, #2
2498: 58e3 ldr r3, [r4, r3]
249a: 4293 cmp r3, r2
249c: d0d4 beq.n 2448 <WireDerived::onService()+0xcc>
249e: 4798 blx r3
24a0: e7d2 b.n 2448 <WireDerived::onService()+0xcc>
24a2: 6848 ldr r0, [r1, #4]
24a4: 4303 orrs r3, r0
24a6: 604b str r3, [r1, #4]
24a8: e7e7 b.n 247a <WireDerived::onService()+0xfe>
24aa: 2001 movs r0, #1
24ac: 4240 negs r0, r0
24ae: e79a b.n 23e6 <WireDerived::onService()+0x6a>
24b0: 2300 movs r3, #0
24b2: e7a4 b.n 23fe <WireDerived::onService()+0x82>
24b4: 0021 movs r1, r4
24b6: 2080 movs r0, #128 ; 0x80
24b8: 31fc adds r1, #252 ; 0xfc
24ba: 6a4d ldr r5, [r1, #36] ; 0x24
24bc: 0040 lsls r0, r0, #1
24be: 4285 cmp r5, r0
24c0: d115 bne.n 24ee <WireDerived::onService()+0x172>
24c2: 2180 movs r1, #128 ; 0x80
24c4: 6818 ldr r0, [r3, #0]
24c6: 6858 ldr r0, [r3, #4]
24c8: 02c9 lsls r1, r1, #11
24ca: 4301 orrs r1, r0
24cc: 6059 str r1, [r3, #4]
24ce: 23c0 movs r3, #192 ; 0xc0
24d0: 6811 ldr r1, [r2, #0]
24d2: 029b lsls r3, r3, #10
24d4: 6808 ldr r0, [r1, #0]
24d6: 06c0 lsls r0, r0, #27
24d8: 0f40 lsrs r0, r0, #29
24da: 2805 cmp r0, #5
24dc: d12a bne.n 2534 <WireDerived::onService()+0x1b8>
24de: 6848 ldr r0, [r1, #4]
24e0: 4303 orrs r3, r0
24e2: 604b str r3, [r1, #4]
24e4: 6812 ldr r2, [r2, #0]
24e6: 69d3 ldr r3, [r2, #28]
24e8: 075b lsls r3, r3, #29
24ea: d4fc bmi.n 24e6 <WireDerived::onService()+0x16a>
24ec: e7ac b.n 2448 <WireDerived::onService()+0xcc>
24ee: 6818 ldr r0, [r3, #0]
24f0: 06c0 lsls r0, r0, #27
24f2: 0f40 lsrs r0, r0, #29
24f4: 2805 cmp r0, #5
24f6: d105 bne.n 2504 <WireDerived::onService()+0x188>
24f8: 7e18 ldrb r0, [r3, #24]
24fa: 0780 lsls r0, r0, #30
24fc: d402 bmi.n 2504 <WireDerived::onService()+0x188>
24fe: 7e18 ldrb r0, [r3, #24]
2500: 07c0 lsls r0, r0, #31
2502: d5f9 bpl.n 24f8 <WireDerived::onService()+0x17c>
2504: 2080 movs r0, #128 ; 0x80
2506: 3328 adds r3, #40 ; 0x28
2508: 781b ldrb r3, [r3, #0]
250a: 6a4d ldr r5, [r1, #36] ; 0x24
250c: b2db uxtb r3, r3
250e: 0040 lsls r0, r0, #1
2510: 4285 cmp r5, r0
2512: d009 beq.n 2528 <WireDerived::onService()+0x1ac>
2514: 69c8 ldr r0, [r1, #28]
2516: 1824 adds r4, r4, r0
2518: 7623 strb r3, [r4, #24]
251a: 69cb ldr r3, [r1, #28]
251c: 3301 adds r3, #1
251e: b2db uxtb r3, r3
2520: 61cb str r3, [r1, #28]
2522: 6a4b ldr r3, [r1, #36] ; 0x24
2524: 3301 adds r3, #1
2526: 624b str r3, [r1, #36] ; 0x24
2528: 6813 ldr r3, [r2, #0]
252a: 4804 ldr r0, [pc, #16] ; (253c <WireDerived::onService()+0x1c0>)
252c: 6819 ldr r1, [r3, #0]
252e: 6859 ldr r1, [r3, #4]
2530: 4001 ands r1, r0
2532: e7cb b.n 24cc <WireDerived::onService()+0x150>
2534: 684a ldr r2, [r1, #4]
2536: 4313 orrs r3, r2
2538: 604b str r3, [r1, #4]
253a: e785 b.n 2448 <WireDerived::onService()+0xcc>
253c: fffbffff ; <UNDEFINED> instruction: 0xfffbffff
It looks like I've converted 'TwoWire::onService' into position-independent code, so now in my own sketch, after I've disabled the interrupts, I just call 'onService' as follows inside a loop:
static_cast<WireDerived&>(Wire).onService();
This will work fine because the 'onService' method is not virtual -- otherwise I would have to manually edit the v-table for the 'Wire' variable.
I'm getting closer to getting this working.