Hi! Here's a little problem that has had me stumped for the last couple of days, and I think it's time I throw in the towel and ask for some help ![]()
I have have a Peggy2 pegboard ("Peggy"), which is essentially an Arduino Duemilanove with a 328p and a LOT of LEDs. (Peggy 2). This talks to another board, "MegaMan", an Arduino MEGA2560, over I2C.
For reasons I'd rather not spend too long arguing (I do notice that the "use the Wire library for heaven's sake" answer is the most common here and for good reason I'd say
), I cannot use the Wire library on Peggy. On MegaMan, I do though. Peggy is a receiving slave and MegaMan a transmitting master. What MegaMan transmits to Peggy is either a header which indicates what animation Peggy is supposed to display (hardcoded into Peggy and referred to by a number after the header), or simply a full framebuffer, allowing Peggy to "stream" whatever MegaMan sends it, like for example a scrolling text. This all works quite well.
I do have one problem though, and that is that whenever I make Peggy run a built-in animation, which can take several seconds to complete, I want it to immediately interrupt that animation if new data is received on the I2C bus. I do for example have a "busy" animation (equivalent to the hour glass in Windows or Spinning Beach Ball of Death on Mac) that Peggy runs when waiting for data from MegaMan under certain conditions.
The I2C implementation on Peggy is not interrupt driven, as the screen update is run on a timer interrupt, and I spend most of my time on Peggy sitting in a while-loop waiting for a state change (it is after all the only purpose of Peggy, so there was no need to get very fancy):
while (!(TWCR & (1<<TWINT))) { } // wait for TWINT to be set
When the state change occurs, I do a switch (TWSR), and if TWSR is either TWI_SRX_ADR_DATA_ACK or TWI_SRX_GEN_DATA_ACK (received a byte of data), I read a byte from the TWDR and reset the control register with TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWEA) Then I do stuff based on what the byte I received was, for example run my "waiting" animation.
There is additional error handling (I basically ignore errors, as a dropped frame or animation matters little) and such, and it again works very well. So far so good.
The problem arises when I try to build a mechanism to abort my animation upon data received on the I2C bus. I quite simply cannot get it to abort. The pseudo code for a typical animation is very simplified:
switch (animation) {
case (1) : // "busy" animation
 for(iterations = 10; iterations >0; iterations--) {
 if (abortFlag) return; // if the abort flag (a volatile boolean) is set, we return back to reading data from the I2C bus
 drawBusyAnimationFram(iterations);
 }
break;
}
The abortFlag is set to false after each completed byte read from the I2C bus.
I then tried using the ISR (TIMER0_COMPA_vect) whose only purpose is to update the "screen", i.e. Peggy's 625LEDs, to put a check for new data on the I2C bus and update the abortFlag correspondingly:
if (TWDR == 0xde) abortFlag = true; did not work (never triggered) - I tried it as 0xde is the start of the header MegaMan sends every time it has new data
if (TWCR & (1<<TWINT)) abortFlag = true; did not work (triggers all the time and prevents animations from running)
if ( (TWCR & (1<<TWINT)) && (TWSR == TWI_SRX_ADR_DATA_ACK) ) abortFlag = true; did not work (never triggers)
if (TWSR == TWI_SRX_ADR_DATA_ACK) abortFlag = true; did not work (never triggers)
This could of course be done without interrupts - one could simply substitute the if (abortFlag) return; with one of the checks above, but that didn't make a difference, and I'd rather not miss something that happens while I'm doing the animation, so I suppose the timer interrupt is better. I also tried using a dedicated I2C interrupt, like so (with the checks above):
ISR (TWI_vect)
{
if (TWSR == TWI_SRX_ADR_ACK) {
abortFlag = true;
}
}
I here got the same result as putting it in the timer interrupt. I must admit I'm at my witt's end here. Is there a tried & tested way of simply answering the question: do I have data waiting for me at the I2C bus, or better yet: is the data waiting for me at the I2C bus 0xde?