From 62ff88b71735c01384e23d915bf1c29167425cf5 Mon Sep 17 00:00:00 2001 From: Tobias Diedrich Date: Tue, 23 Mar 2010 05:27:32 +0000 Subject: Implement software pwm to control c200v2 display brightness. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25300 a1c6a512-1295-4272-9138-f99709370657 --- firmware/target/arm/as3525/kernel-as3525.c | 122 ++++++++++++++++++++- .../arm/as3525/sansa-c200v2/backlight-c200v2.c | 37 ++++++- .../arm/as3525/sansa-c200v2/backlight-target.h | 7 ++ 3 files changed, 161 insertions(+), 5 deletions(-) diff --git a/firmware/target/arm/as3525/kernel-as3525.c b/firmware/target/arm/as3525/kernel-as3525.c index 9250f320b9..4c421e50fc 100644 --- a/firmware/target/arm/as3525/kernel-as3525.c +++ b/firmware/target/arm/as3525/kernel-as3525.c @@ -36,7 +36,7 @@ /* The scrollwheel is polled every 5 ms (the tick tasks only every 10) */ static int poll_scrollwheel = 0; -void INT_TIMER2(void) +static inline void do_scrollwheel(void) { if (!poll_scrollwheel) call_tick_tasks(); /* Run through the list of tick tasks @@ -48,16 +48,131 @@ void INT_TIMER2(void) } poll_scrollwheel ^= 1; +} +#else +static inline void do_scrollwheel(void) +{ + call_tick_tasks(); /* Run through the list of tick tasks */ +} +#endif + +#if defined(SANSA_C200V2) +#include "backlight-target.h" + +static int timer2_cycles_per_tick = 0; +static int timer2_cycles_pwmon = 0; +static int timer2_cycles_pwmoff = 0; +static int timer2_pwm_state = 0; +static int timer2_pwm_on = 0; + +void _set_timer2_pwm_ratio(int ratio) +{ + int cycles = timer2_cycles_per_tick; + + /* + * Rather arbitrary limits, but since the CPU + * needs some to time in the interrupt handler + * there sure is some limit. + * More specifically, if the cycles needed to do + * the pwm handling are more than the reloaded counter needs + * to reach 0 again it will reload to the old value most + * likely leading to a (slight) slowdown in tick rate. + */ + + if (ratio < 10) { + /* + * Permanent off, reduce interrupt rate to save power + */ + TIMER2_BGLOAD = cycles; + timer2_pwm_on = 0; + _backlight_pwm(0); + return; + } + + if (ratio > 990) { + /* + * Permanent on, reduce interrupt rate to save power + */ + TIMER2_BGLOAD = cycles; + timer2_pwm_on = 0; + _backlight_pwm(1); + return; + } + + timer2_cycles_pwmon = cycles*ratio/1000; + timer2_cycles_pwmoff = cycles*(1000-ratio)/1000; + + if (timer2_pwm_on == 0) { + timer2_pwm_state = 0; + timer2_pwm_on = 1; + TIMER2_BGLOAD = timer2_cycles_pwmoff; + } +} + +static void set_timer2_cycles_per_tick(int cycles) +{ + timer2_cycles_per_tick = cycles; +} + +static inline void do_sw_pwm(void) +{ + if (!timer2_pwm_on) { + do_scrollwheel(); /* Handle scrollwheel and tick tasks */ + TIMER2_INTCLR = 0; /* clear interrupt */ + return; + } + + timer2_pwm_state ^= 1; + if (timer2_pwm_state) { + TIMER2_BGLOAD = timer2_cycles_pwmoff; + _backlight_pwm(1); + /* + * Always do scrollwheel and tick tasks during the longer cycle for safety, + * since the short cycle can be quite short. + * (minimum: 1us if ratio is 10 or 990 or 0.5us with scrollwheel, + * or just about 6000 clock cycles at 60MHz) + */ + if (timer2_cycles_pwmon > timer2_cycles_pwmoff) + do_scrollwheel(); /* Handle scrollwheel and tick tasks */ + } else { + TIMER2_BGLOAD = timer2_cycles_pwmon; + _backlight_pwm(0); + if (!(timer2_cycles_pwmon > timer2_cycles_pwmoff)) + do_scrollwheel(); /* Handle scrollwheel and tick tasks */ + } + TIMER2_INTCLR = 0; /* clear interrupt */ } #else +static inline void do_sw_pwm(void) +{ + do_scrollwheel(); /* Handle scrollwheel and tick tasks */ +} + +static void set_timer2_cycles_per_tick(int cycles) +{ + (void)cycles; +} +#endif + + void INT_TIMER2(void) { - call_tick_tasks(); /* Run through the list of tick tasks */ + /* + * Timer is stacked as follows: + * Lowest layer: Software PWM (if configured) + * Alternates timer2 reload value to implement + * software pwm at 100Hz (no scrollwheel) + * or 200Hz (scrollwheel) with variable pulse width 1% to 99% + * Middle layer: Scrollwheel handling (if configured, 200Hz) + * Alternate between polling scrollwheel and running tick + * tasks (includes scrollwheel polling). + * Top layer: Run tick tasks at 100Hz + */ + do_sw_pwm(); TIMER2_INTCLR = 0; /* clear interrupt */ } -#endif void tick_start(unsigned int interval_in_ms) { @@ -66,6 +181,7 @@ void tick_start(unsigned int interval_in_ms) CGU_PERI |= CGU_TIMER2_CLOCK_ENABLE; /* enable peripheral */ VIC_INT_ENABLE = INTERRUPT_TIMER2; /* enable interrupt */ + set_timer2_cycles_per_tick(cycles); TIMER2_LOAD = TIMER2_BGLOAD = cycles; /* timer period */ /* /!\ bit 4 (reserved) must not be modified diff --git a/firmware/target/arm/as3525/sansa-c200v2/backlight-c200v2.c b/firmware/target/arm/as3525/sansa-c200v2/backlight-c200v2.c index d38c068ca2..e094cca8fe 100644 --- a/firmware/target/arm/as3525/sansa-c200v2/backlight-c200v2.c +++ b/firmware/target/arm/as3525/sansa-c200v2/backlight-c200v2.c @@ -27,6 +27,32 @@ #include "as3514.h" int buttonlight_is_on = 0; +int backlight_is_on = 0; +static int backlight_level = 0; + +/* logarithmic lookup table for brightness s*/ +static const int brightness_table[MAX_BRIGHTNESS_SETTING+1] = { + 0, 21, 47, 78, 118, 165, 224, 296, 386, 495, 630, 796, 1000 +}; + +static void _ll_backlight_on(void) +{ + GPIOA_PIN(5) = 1<<5; +} + +static void _ll_backlight_off(void) +{ + GPIOA_PIN(5) = 0; +} + +void _backlight_pwm(int on) +{ + if (on) { + _ll_backlight_on(); + } else { + _ll_backlight_off(); + } +} bool _backlight_init(void) { @@ -36,6 +62,8 @@ bool _backlight_init(void) void _backlight_set_brightness(int brightness) { + backlight_level = brightness_table[brightness]; + if (brightness > 0) _backlight_on(); else @@ -47,12 +75,17 @@ void _backlight_on(void) #ifdef HAVE_LCD_ENABLE lcd_enable(true); /* power on lcd + visible display */ #endif - GPIOA_PIN(5) = 1<<5; + if (!backlight_is_on) + _ll_backlight_on(); + _set_timer2_pwm_ratio(backlight_level); + backlight_is_on = 1; } void _backlight_off(void) { - GPIOA_PIN(5) = 0; + backlight_is_on = 0; + _set_timer2_pwm_ratio(0); + _ll_backlight_off(); #ifdef HAVE_LCD_ENABLE lcd_enable(false); /* power off visible display */ #endif diff --git a/firmware/target/arm/as3525/sansa-c200v2/backlight-target.h b/firmware/target/arm/as3525/sansa-c200v2/backlight-target.h index 84a6ea6179..c09e759138 100644 --- a/firmware/target/arm/as3525/sansa-c200v2/backlight-target.h +++ b/firmware/target/arm/as3525/sansa-c200v2/backlight-target.h @@ -24,6 +24,7 @@ #include bool _backlight_init(void); +void _backlight_pwm(int on); void _backlight_on(void); void _backlight_off(void); void _backlight_set_brightness(int brightness); @@ -31,4 +32,10 @@ int __backlight_is_on(void); void _buttonlight_on(void); void _buttonlight_off(void); + +/* + * FIXME: This may be better off in kernel.h, but... + */ +void _set_timer2_pwm_ratio(int ratio); + #endif -- cgit v1.2.3