From c4cb248eead0c403442cc018f2ebe3123fb2d395 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Tue, 27 Sep 2011 21:04:35 +0200 Subject: src/common: use system clock prescaler to save power --- src/binwatch/avrconfig.h | 6 ++++++ src/common/button.c | 2 +- src/common/led.c | 5 +++-- src/common/power.c | 2 ++ src/common/power.h | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 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: * }}} */ +#include +#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 */ -- cgit v1.2.3