Yesterday’s post turns out to uncover a lot of trouble spots and mistakes on my end w.r.t. switching small relays. Get ready for some scope shots ahead, to see what’s going on…
But first, the code I’ve been using:
void setup() {}
static void setLatch (bool on) {
pinMode(5, OUTPUT); // DIO2
pinMode(15, OUTPUT); // AIO2
pinMode(6, OUTPUT); // DIO3
pinMode(16, OUTPUT); // AIO3
digitalWrite(5, on ? 0 : 1);
digitalWrite(15, on ? 0 : 1);
digitalWrite(6, on ? 1 : 0);
digitalWrite(16, on ? 1 : 0);
delay(3);
pinMode(5, INPUT);
pinMode(15, INPUT);
pinMode(6, INPUT);
pinMode(16, INPUT);
}
void loop() {
setLatch(true);
delay(1000);
setLatch(false);
delay(1000);
}
Basic idea: set the pins as outputs, set the levels right, wait 3 ms, then set the pins as input again, i.e. in high-impedance mode, to stop driving the relays.
There are two very serious problems with this code. The first one is that the I/O pins are set to inputs at the end. This means there is no good path for the inductive kick energy to go, leading to this very nasty waveform across the relay – same as shown yesterday:

Note the extreme voltage levels across the coil – way beyond the ± 3.3V you’d want to see in a good 3.3V totem-pole configuration.
It turns out that there is a surprisingly simple solution for this – don’t make the I/O pins inputs, but put them all the same output level, so that no current flows through the relay (as before), but with the I/O pins still being outputs, i.e. able to conduct current.
Here’s is the entire pulse again, at maximum scale this time:

(the lines are so thick because the input is set to peak-detect mode in the scope)
No more funny business! A fairly clean on and off transition, just a bit of “sagging” as the two I/O pins in parallel struggle (successfully) to supply the required current. It looks like the inductive kickback is now absorbed by the output pins which are all set to “0”, i.e. conducting to GND.
I’m surprised by how well this seems to work. I’m guessing that the CMOS switches in the ATmega’s pin driver are able to conduct in both directions when enabled, and that the ESD protection diodes are absorbing any voltage excursions outside the 0 .. 3.3V range.
But a much bigger problem probably, is that the original sketch was shorting out the DIO and AIO pins against each other – producing a very bad intermediate voltage level:

The time scale is completely different now, and as you can see, the voltage level goes through a strange middle state. This is caused by the fact that the digitalWrite()
code is very slow, compared to the actual speed of the ATmega. It’s taking 4 µs to process each call, and as the first one is set to one value, the other pin connected to it can still be in the other state. So this is – briefly – shorting out two I/O pins, set to different values!
Very very bad – this may well reduce the lifetime of the ATmega µC. As it turns out, my choice of I/O pins makes it impossible to set both I/O pins simultaneously. But the least I can do without re-soldering, is to use the much faster bitSet() and bitClear() calls – even though this makes the code slightly harder to understand:
static void setLatch (bool on) {
if (on) {
bitSet(PORTD, 5); // D.5 = DIO2
bitSet(PORTC, 1); // A.1 = AIO2
} else {
bitSet(PORTD, 6); // D.6 = DIO3
bitSet(PORTC, 2); // A.2 = AIO3
}
delay(3);
bitClear(PORTD, 5); // D.5 = DIO2
bitClear(PORTC, 1); // A.1 = AIO2
bitClear(PORTD, 6); // D.6 = DIO3
bitClear(PORTC, 2); // A.2 = AIO3
}
The switching times are now dramatically closer together, more like 0.12 µS in fact:

Note that you’re looking at the superposition of both the rising and the falling switching waveforms in this case, as an easy way to see both in one screen shot.
For more information about inductive kickback and protection diodes: there’s an article by Douglas Jones – it’s full of useful information, even though it’s geared towards driving DC motors and stepper motors. The same principles apply when driving the coil in a relay.
So the conclusion seems to be that this specific relay could be driven from 4 I/O pins, without requiring any further circuitry. And since it’s a latching relay, this can easily be done on battery power: it merely takes a 3 ms pulse to make it switch, i.e. about the same amount of energy as sending out one RF12 packet!