Question About Implementing System Timeouts

Hello people!

I'm trying to build a quadrotor (a form of aircraft) and have managed to connect a gyroscope, accelerometer and magnetometer via I2C to two ATmega328Ps. The gyroscope goes to the first ATmega (master) and the other two sensors go to the second (slave). The two ATmegas communicate via SPI.

I've managed to make them to do all the necessary math, but I face a problem: Occasionally, a sensor becomes unresponsive and jams the I2C line, stopping the entire system. While I'm attempting to get to the root of this problem, I decided this is probably inevitable and so I should be prepared for it. Anyway, I've made the two ATmegas completely synchronised, which means if anything bad happens to one of them, both of them will stop functioning.

So here's the question: How do I implement a timeout or several timeouts that are activated if a specific event takes longer than expected? Ultimately, I would like to use this to avoid running the appropriate portion of the code (such as communication with sensors)?

I'm aware of the existence of watchdog timers. Unfortunately, a system reset will most likely result in a crash, and I'm not sure an interrupt can be used to achieve my objective. For example, in the case of being stuck during I2C communication, even if the interrupt handler is successfully completed, wouldn't the system return to the point in the code where it was stuck in the I2C loop? Considering I need the controller to function partially, I still need to have the timeout break the system out of the offending loop.

I would greatly appreciate any ideas! Thank you!

Bo Xuan

How did you ensure that both of them are really 100% in sync? Do they share the same clock? How do you ensure that they will start in sync after a reset? How do you ensure that any prescalers are 100% in sync?

Just curious.

With regard to reset/watchdog. I would think about how to recover from a watchdog triggered reset without a crash. Also I suggest to figure out a solution without requiring two controllers to be 100% in sync.

Udo

Thanks for replying!

The microcontrollers not strictly in sync, but they do wait for each other at certain points in the code. I used 2 pins on each (one to tell the other when its ready and the other to poll whether the other is ready) to do this. I had to do this because I want the full duplex communication (SPI) between the microcontrollers, which means I need them to start communicating only after both microcontrollers have prepared the necessary bytes for sending. If I had to make them out of sync, it would mean I have to forgo full duplex communication or SPI communication completely.

About preventing a crash after a watchdog triggered reset, that's quite difficult! While I might be able to set a flag so the start up process skips all unnecessary setup/calibration sequences, the necessary sequences (setting up sensors etc) still takes up a bit of time. Also, for safety, I've made sure the motors on the quadrotor will not start spinning up without a specific set of instructions. Having the quadrotor continue flying immediately after a reset sounds quite dangerous!

use the internal timers

// this code assumes the timer0 is already running

TCNT0 = 0; // reset
while (!hasReplied() && TCNT0 < 50); // wait for reply or timeout
if (TCNT0 >= 50)
{
// timed out
}
else
{
// replied
}

I’ve just done some code that does something similar, two Arduinos talking via SPI and I have a sync function on the slave that waits for a clock pulse from the master. Trouble is if the master is not there (an expected situation) or just crashed the pulses never come. So I’ve done the following using the watchdog timer.

Part of the sync function.

	server_sync_abort = false;  // declared as volatile elsewhere
	WDTCSR |= (1<<WDIE); // enable WD

	//////////////////////////////////////////
	// wait for low to end, if pin is already low this will
	// drop through to the next line straight away
	do {} while ((PINB &= low_mask) == 0 && !server_sync_abort);  

	//////////////////////////////////////////
	// wait for high to end
	do {} while ((PINB &= high_mask) == 0xFF && !server_sync_abort); 

	cli();
	WDTCSR &= ~(1<<WDIE); // disable WD
	sei();

if (server_sync_abort) {
     // Houston we had a problem
}

And the watch dog ISR

ISR (WDT_vect) {
	asm ("wdr");
	server_sync_abort = true;
}

This will kill the sync after 16mS, however the WD time can be set to longer (but not shorter).

The WDT won’t reset the processor unless the WDEN bit is set, it will go to the ISR.

So you can use the WD to do this, but you need access to the function doing the waiting and I don’t know if you can do that with what you are doing.

NOTE: A spare timer could do the same thing.


Rob

Sorry about the extremely slow reply, I had some serious hardware issues that occupied all my time the past few days.

Anyway, thanks for the code snippets! I'll see if I can implement something similar. Also, I found (somewhere in the forum) a method to timeout I2C reads with the Wire library. A slight modification of the library was required but it works well. Whenever the the bus gets stuck for 5ms, it'll automatically restart the entire bus!

Bo Xuan