summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Schodet2011-09-27 21:04:35 +0200
committerNicolas Schodet2011-09-27 21:31:05 +0200
commitc4cb248eead0c403442cc018f2ebe3123fb2d395 (patch)
treed3bb7e25801761dd828daab594d7c27343f66cae
parent062258441f674e3c77605a479e35d38646076788 (diff)
src/common: use system clock prescaler to save power
-rw-r--r--src/binwatch/avrconfig.h6
-rw-r--r--src/common/button.c2
-rw-r--r--src/common/led.c5
-rw-r--r--src/common/power.c2
-rw-r--r--src/common/power.h51
5 files changed, 63 insertions, 3 deletions
diff --git a/src/binwatch/avrconfig.h b/src/binwatch/avrconfig.h
index a6e4fdb..539bedb 100644
--- a/src/binwatch/avrconfig.h
+++ b/src/binwatch/avrconfig.h
@@ -29,6 +29,12 @@
* 8000000, 11059200, 14745600, 16000000, 18432000, 20000000. */
#define AC_FREQ 1000000
+/* power */
+/** Initial clock divider, giving the AC_FREQ frequency. */
+#define AC_POWER_CLKPS_INITIAL 3
+/** Low speed clock divider to save battery. */
+#define AC_POWER_CLKPS_LOW 8
+
/* twi - TWI module. */
/** Driver to implement TWI: HARD, SOFT, or USI. */
#define AC_TWI_DRIVER SOFT
diff --git a/src/common/button.c b/src/common/button.c
index 7209eff..51941d2 100644
--- a/src/common/button.c
+++ b/src/common/button.c
@@ -56,7 +56,7 @@ button_wait (void)
uint8_t debounce_ms = 0;
while (debounce_ms < BUTTON_DEBOUNCE_MS)
{
- utils_delay_ms (1);
+ power_delay_ms (1);
if (IO_GET (BUTTON_IO))
debounce_ms++;
else
diff --git a/src/common/led.c b/src/common/led.c
index bb8acd8..fb41f06 100644
--- a/src/common/led.c
+++ b/src/common/led.c
@@ -27,6 +27,7 @@
#include "led.h"
#include "button.h"
+#include "power.h"
/** Time a led is lit. */
#define LED_UP_MS 0.05
@@ -95,11 +96,11 @@ led_display (uint16_t leds, uint16_t duration)
PORTB = portb | led_tab[l].port;
DDRB = ddrb | led_tab[l].ddr;
}
- utils_delay_ms (LED_UP_MS);
+ power_delay_ms (LED_UP_MS);
/* Turn off, leds are too bright. */
DDRB = ddrb;
PORTB = portb | LED_MASK;
- utils_delay_ms (LED_DOWN_MS);
+ power_delay_ms (LED_DOWN_MS);
/* Stop now if button is pressed, it disturbs leds. */
if (button_pressed ())
return;
diff --git a/src/common/power.c b/src/common/power.c
index c7a5aab..3ca0d1c 100644
--- a/src/common/power.c
+++ b/src/common/power.c
@@ -30,6 +30,8 @@
void
power_init (void)
{
+ /* Change initial clock. */
+ power_clock_prescale_set (AC_POWER_CLKPS_INITIAL);
/* Shutdown unused peripherals. */
/* Analog comparator. */
ACSR = _BV (ACD);
diff --git a/src/common/power.h b/src/common/power.h
index 279a26e..ac1f03d 100644
--- a/src/common/power.h
+++ b/src/common/power.h
@@ -23,6 +23,8 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
* }}} */
+#include <util/delay_basic.h>
+#include "io.h"
/** Initialise power reduction. */
void
@@ -33,4 +35,53 @@ power_init (void);
void
power_sleep (void);
+/** Own version of clock_prescale_set which does not touch I bit. */
+static inline void __attribute__((__always_inline__))
+power_clock_prescale_set (uint8_t x)
+{
+ uint8_t tmp = _BV (CLKPCE);
+ __asm__ __volatile__ (
+ "out %1, %0" "\n\t"
+ "out %1, %2" "\n\t"
+ : /* no outputs */
+ : "d" (tmp),
+ "I" (_SFR_IO_ADDR (CLKPR)),
+ "d" (x)
+ : "r0");
+}
+
+#define CLOCK_COEF (1 << (AC_POWER_CLKPS_LOW - AC_POWER_CLKPS_INITIAL))
+/** Wait for a given duration, trying to reduce clock speed. */
+static inline void __attribute__((always_inline))
+power_delay_ms (double ms)
+{
+ double slow_ticks = (AC_FREQ / 1e3 / CLOCK_COEF) * ms;
+ if (slow_ticks > 3.0)
+ {
+ /* Can use slow clock, round down as some extra instructions will be
+ * executed using slow clock. */
+ if (slow_ticks / 3.0 <= 256)
+ {
+ uint8_t loops = (uint8_t) (slow_ticks / 3.0);
+ power_clock_prescale_set (AC_POWER_CLKPS_LOW);
+ _delay_loop_1 (loops);
+ power_clock_prescale_set (AC_POWER_CLKPS_INITIAL);
+ }
+ else
+ {
+ uint16_t loops = (uint16_t) (slow_ticks / 4.0);
+ power_clock_prescale_set (AC_POWER_CLKPS_LOW);
+ _delay_loop_2 (loops);
+ power_clock_prescale_set (AC_POWER_CLKPS_INITIAL);
+ }
+ /* Do not support huge delay. */
+ }
+ else
+ {
+ /* Here, the delay is small, use a short loop. */
+ uint8_t loops = (uint8_t) (ms * AC_FREQ / 3e3);
+ _delay_loop_1 (loops);
+ }
+}
+
#endif /* power_h */