NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.
Microcontroller Programming » How-To: Sleep
October 12, 2009 by mikedoug |
So my journey with the nerdkit continues to move forward day by day. I'm more of a programming geek than an electronics one -- but I have a couple electronics books on order at the library to fill in those gaps. Today, driven by years strict programming philosophies of efficiency, I got the sleep mode working with my MCU. I am using the watchdog timer (perhaps the timer timers would be better? I just haven't read up on them yet, and that's next to investigate) set to interrupt mode only, and then powering down the MCU. The savings in power is astonishing -- 7 mA vs 24mA. Talk about getting your battery to last longer -- 3.4 times longer! One thing I discovered was that if you do NOT register an interrupt handler for the watchdog (WDT), then the effect will be the MPU restarting (even when you put it in interrupt-only mode). Also, READ the MPU document VERY closely... I missed a key point that took me a while to figure out. Here's the simple code that allows my project to sleep for a second by powering down the MPU:
Now, I'm not entirely certain that you need the cli()/sei() -- but the documentation does state that in order to change the WDTCSR register, you MUST set WDCE and WDE to 1, and then "within the next 4 clock cycles" set the WDTCSR to the desired settings -- given that, it would seem to me that you do not want this code to be interrupted. This sleeps for 1 second. If you want to sleep otherwise, you'll want to see page 56 of the ATmega168.pdf for your options. |
---|---|
October 18, 2009 by mikedoug |
A BUG! If you are transmitting any data via the USART prior to calling sleep() here, you'll get corrupted output. I think this is because the USART is getting powered down. You can fix that by doing this in places of power down sleep mode:
Results aren't as nice -- 18-22mA of current. The next thing I will try is to figure out how to wait for transmission to complete and THEN enter the power down state. MikeDoug |
October 19, 2009 by mikedoug |
I did a little more research, and it all has to do with shutting down power to the USART. If you use the PRR register to turn off the USART and then turn it back on, you get a little corruption -- just like when you turn on the MPU you get some garbage. I think the only real way around this is to use two pins, on the MPU, solder four wires to the DB9 header from the nerdkit (on the RTS/CTS and DSR/DTR pins); then implement that protocol in the uart.c file, and enable hardware flow control. In theory, I think, that should prevent the garbage as long at RTS is low when you power on the USART because the receiving end won't be looking for you to be sending data when RTS is low. So, if you want to save 4 pins, then use the IDLE mode which does not affect power to the USART. In testing today that change uses 18-19mA, which is still a drop from 24mA otherwise. MikeDoug |
October 20, 2009 by rusirius |
not having looked into that part much I have no clue what I'm talking about... But could you not check to make sure no data was being sent/received via (UCSR0A & (1<<UDRE0))==0) and (UCSR0A & (1<<RXC0)==0) before sleeping? |
October 20, 2009 by mikedoug |
rusirius: That's a seriously good idea... I had tried that (as well as checking for TXC0) to no avail. I'd put a while loop in place, nothing helped. It just seems that cutting power to the USART circuitry and then restoring it causes garbage output. Based on the behavior of the LEDs on the little RS232 circuitboard from the USB nerdkit, it looks like the MPU may be sending a break when the USART is re-powered back on (the TX light burns solid for about a second). MikeDoug |
October 21, 2009 by mrobbins (NerdKits Staff) |
Hi MikeDoug, I think that part of the issue here is that when the USART module is disabled, pins #2 and 3 revert to their "normal" state. Take a look at the ATmega168 datasheet, page 193: "The Transmitter will override normal port operation for the TxDn pin when enabled." However, since when we wrote the original uart.c code we expected that the USART module would be left on, we never really cared to define the state of those pins, with the result that it ends up reverting to being a floating input pin. This means noise is free to drive that line into the serial inverter board and into your computer. To fix this, you should just add two lines somewhere in your code -- perhaps right before the uart_init():
That way, when the USART module is turned off, the transmit serial port line will stay idle (asserted high), not floating and susceptible to noise. Let me know if that helps! Mike |
October 24, 2009 by mikedoug |
Nope -- that did not work, it still gets garbage. |
October 24, 2009 by mikedoug |
Okay, here's revised code:
This update of the code handle the case where you have other interrupts that wake up the processor. Those interrupts can take care of whatever they want to do, and then we'll go back to sleep here. I found this flaw when I added the real-time-clock code (from elsewhere in the forum) to my project (because I like keeping time). I tried to NOT use a variable, but I could not find anything that changed with the WDTCSR -- I thought maybe I could clear WDE or WDIE inside the WDT_vect -- but I wasn't sure what that would do to the operations of the chip -- so I went with this route. |
October 26, 2009 by mikedoug |
Here's, yet again, another way to do this... I'm using the 16-bit timer1 -- but you could retrofit it to use the 8-bit timer (but you'd need to lower the 4.5 second cycles to a much lower number...
Note: There is a potential race condition IF the balloon timer (think balloon loan payment) is a REALLY small number and the timer races past that number inside the signal handler before it sets OCR1A to the new max. That said... I'm running a test right now with a continual loop of 4.51 second (451 hsecs) sleeps and I've done 84 of them with no race condition. So it would seem that, under simple conditions, this race condition isn't a concern. However... A condition which MIGHT cause this would be:
Worst case scenario under this SHOULD be an extra 4.58 second delay as timer1 overflows and wraps back around. Note: The printf_P(...) statements are there for debug, but were pretty fun to watch the operations of the function so I left them in. The 2 PORTB lines are in there so you can easily see the sleep vs. execution time of the application -- change this to a port you want to use -- OR remove it all together. Don't forget you also have to do this somewhere to enable output on that port:
Note: I picked the same 1024 clock divider that was used in the real-time-clock given elsewhere in this forum. This keeps the same divider -- which, as I read tonight, is pretty important since timer0 and timer1 must be the same click divider. (pg. 138) |
October 27, 2009 by mikedoug |
I ran a test overnight and had over 6900 4.5 second pauses with no race conditions triggered. I do have one other interrupt triggering (the real time clock counter) every 1/100th of a second. While this is not terribly conclusive, it does point to the fact that it seems to be a hard race condition to trigger. It's been running for almost 7.75 hours, and I'm going to let it keep running. |
October 27, 2009 by mikedoug |
Probably about as good as it needs to be -- 16130 4.5 sleep iterations, and NONE have triggered the race condition. |
Please log in to post a reply.
Did you know that an electroluminescent backlight for an LCD panel requires hundreds of volts AC to run? Learn more...
|