Well, now that I managed to get my Duemilanove into & out of sleep mode (in being pretty easy, out took a while longer, was missing #interrupt library call in my sketch), I'm looking for a way to get an interrupt created while in Sleep mode. My goal is a wireless remote control to run a fencing scoring machine, so I want autowakeup on the first key press and not have a referee have to mess around waking up before something comes out.
I am using the keypad library to read a velleman 4x4 matrix keypad (which works great and is awesomely debounced, never misses a key press or puts out a false press with the default setup). When I look at the Row or Column inputs in sleep mode, I am not seeing any signal changes to work with. The column lines are good & high, sitting at 4.85V and driving 4mA across an LED and 680R load. The rows are also sitting high, at 4.93V but only driving 0.04mA across the same LED/680R load.
Putting a scope on them, the columns do not change at all, the rows wiggle down a few tenths, but not low enough to be recognized as anything but a good high logic 1. I need a LOW to get an interrupt into Int0 to recover from PWR_DOWN mode.
If you can arrange for all 4 columns to be low, then connect a 4-input AND to the rows and feed that into pin 3 for an interrupt. I believe all the Arduino digital outputs go high-Z state when you go into sleep modes, so you will need extra circuitry to hold the columns low. I think a set of pull down resistors and diodes would be enough to get the right logic level on the keyboard column lines when the AVR is asleep.
When you come out of sleep, you will stop worrying about the AND gate output and disable the circuit that held all the rows low, and go ahead scanning the keys normally.
I tried doing that - when is sleep mode, the columns are solid high - they had no problem driving an LED on - certainly not the floating/tri-state I thought they were going to be. The rows also seemed to be pulled weakly high - I tried pull downs there also, but any load that pulled it low enough then pulled it all the way low when not in sleep mode so the input appeared as a 0 all the time. Pressing a switch then just the row pin go good high like the column it gets connected to.
I wonder if some other setting needs to be made in the ATMega328 prior to entering sleep so that the ports really go into tristate.
Can't seem to download the atmel datasheet at work, just bogs down and never completes...
Interesting.... I think you're seeing input ports being clamped by the sleep logic, if I'm reading this right.
9.10.6 Port Pins
When entering a sleep mode, all port pins should be configured to use minimum power. The
most important is then to ensure that no pins drive resistive loads. In sleep modes where both
the I/O clock (clkI/O) and the ADC clock (clkADC) are stopped, the input buffers of the device will
be disabled. This ensures that no power is consumed by the input logic when not needed. In
some cases, the input logic is needed for detecting wake-up conditions, and it will then be
enabled. Refer to the section ”Digital Input Enable and Sleep Modes” on page 80 for details on
which pins are enabled. If the input buffer is enabled and the input signal is left floating or have
an analog signal level close to VCC/2, the input buffer will use excessive power.
13.2.5 Digital Input Enable and Sleep Modes
As shown in Figure 13-2, the digital input signal can be clamped to ground at the input of the
Schmitt Trigger. The signal denoted SLEEP in the figure, is set by the MCU Sleep Controller in
Power-down mode, Power-save mode, and Standby mode to avoid high power consumption if
some input signals are left floating, or have an analog signal level close to VCC/2.
SLEEP is overridden for port pins enabled as external interrupt pins. If the external interrupt
request is not enabled, SLEEP is active also for these pins. SLEEP is also overridden by various
other alternate functions as described in ”Alternate Port Functions” on page 81.
I think you should be able to use a sleep mode that picks up data pin changes, set your columns all high or low or whatever, set up the input row pins to interrupt on pin change. It would appear that AVR Port B pins (Arduino 0..7 I think) are the ones that can act as pin change interrupt sources. those ones should act normal during sleep.
Well, supposedly almost all the IO pins can be used for PCInts.
I have my row pins connected to D3-4-5-6 and the columns to D7-8-9-10 (D11/12 used by virtual wire, D13 for the standard activity LED, leaving D14-15-16-17 as available (and D18-19 on the Pro-Mini if one cares to fool around a little to arrange physical access to them, which I have done on the receiver side).
Thus leaving D2 free for interrupting and D0/D1 free for Tx/Rx in debug mode (via FTDI adapter) and re-programming without having to disassemble anything.
I'll take a look at the other unused pins later tonight, see if they are doing the same. As you say, they sure seem to be clamped high.
I think if they were low, then the Row pins could go low when pressed while in sleep mode.
I haven't tried the 4 other sleep modes, guess that'd be an easy enough thing to start with, just a little typing & downloading ...
My arduino experience has been limited to more standard stuff so far, getting deeper into the individual control registers and finding out how to write to them will be more of a challenge.
I haven't used the keypad library, so I'm at a bit of a disadvantage here.
But I gather it works pretty much like a standard hardware encoder: 4 output pins used to select along one axis, and 4 inputs that read along the other.
What you need to do is wire-AND the inputs to the interrupt pin (just like the 4-button example you linked to somewhere in your postings), and drive all 4 output pins low. That way, any keypress will cause an interrupt and wake up the CPU.
The trick will be to gracefully seize control of the output pins from the keypad library just before going to sleep, and relinquish it back upon waking up. This could be dead easy, or messy, depending on the design of the keypad library. If the library starts the scanning process afresh every time you call, then it probably won't care what state you leave the outputs in. If it's doing something more complicated, and expects to find the outputs in the same state it left them in, it might be more of a hassle to save and restore that state.
I'm in between fencing classes at the moment, be a couple more hours till I can tinker some more.
I'm going to try the simple approach first, just plop in the other sleep modes and see what the outputs are like. I guess after that can try declaring the pins as inputs before going to sleep, then declare as outputs again on waking? I wonder what that will make the digital signal look like as the read routine kicks in.
Sounds like maybe some library tinkering could be in order too ..
Your idea about changing the Column outputs was right on.
I went thru the various power modes, had no effect.
Made these changes in void Loop() before commanding sleep mode:
// try setting kepyad columns low before sleeping
// THE PROGRAM CONTINUEs FROM HERE after waking up in enterSleep()
Serial.println("recovered from sleep.");
and these changes in enterSleep() routine:
/* The program will continue from here after the interrupt. */
/* First thing to do is disable sleep. */
// set the keypad columns back high - need all of them set
// or only get key presses in the column that is high
// then go to the void Loop()
Now with the Rows diode-AND'ed together (with LEDs even, get a little flash as the button is pushed and pin2 is pulled low) feeding into Int0 it wakes up as envisioned and says its sending out the right data! Nice call :D
Now need the rest of my wires to show from pololu so I can complete assembly of the receiver side & make sure the data is actually getting transmitted).