From 249aae587a1f35f5df55613c6765ae763db39107 Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Tue, 27 Apr 2010 10:11:52 +0000 Subject: FS#11172 - Fuzev2: Read the scrollwheel scrollwheel via IRQ git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25736 a1c6a512-1295-4272-9138-f99709370657 --- .../target/arm/as3525/sansa-fuzev2/button-fuzev2.c | 192 +++++++++++++++++++-- .../target/arm/as3525/sansa-fuzev2/button-target.h | 6 - 2 files changed, 180 insertions(+), 18 deletions(-) (limited to 'firmware/target/arm/as3525/sansa-fuzev2') diff --git a/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c b/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c index db08414ae5..9421076cce 100644 --- a/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c +++ b/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c @@ -24,37 +24,198 @@ #include "button.h" #include "backlight.h" -extern void scrollwheel(unsigned wheel_value); - #ifdef HAS_BUTTON_HOLD static bool hold_button = false; #endif + +#ifdef HAVE_SCROLLWHEEL +#define SCROLLWHEEL_BITS (1<<7|1<<6) + /* TIMER units */ +#define TIMER_TICK (TIMER_FREQ/HZ) /* how long a tick lasts */ +#define TIMER_MS (TIMER_TICK/(1000/HZ))/* how long a ms lasts */ + +#define WHEEL_REPEAT_INTERVAL (300*TIMER_MS) /* 300ms */ +#define WHEEL_FAST_ON_INTERVAL ( 20*TIMER_MS) /* 20ms */ +#define WHEEL_FAST_OFF_INTERVAL ( 60*TIMER_MS) /* 60ms */ +/* phsyical clicks per rotation * wheel value changes per phys click */ +#define WHEEL_CHANGES_PER_CLICK 4 +#define WHEELCLICKS_PER_ROTATION (12*WHEEL_CHANGES_PER_CLICK) + +/* + * based on button-e200.c, adjusted to the AMS timers and fuzev2's + * scrollwheel and cleaned up a little + */ +static void scrollwheel(unsigned int wheel_value) +{ + /* wheel values and times from the previous irq */ + static unsigned int old_wheel_value = 0; + static unsigned int wheel_repeat = BUTTON_NONE; + static long last_wheel_post = 0; + + /* We only post every 4th action, as this matches better with the physical + * clicks of the wheel */ + static unsigned int wheel_click_count = 0; + /* number of items to skip in lists, 1 in slow mode */ + static unsigned int wheel_delta = 0; + /* accumulated wheel rotations per second */ + static unsigned long wheel_velocity = 0; + /* fast or slow mode? */ + static int wheel_fast_mode = 0; + + /* Read wheel + * Bits 6 and 7 of GPIOA change as follows (Gray Code): + * Clockwise rotation 00 -> 01 -> 11 -> 10 -> 00 + * Counter-clockwise 00 -> 10 -> 11 -> 01 -> 00 + * + * For easy look-up, actual wheel values act as indicies also, + * which is why the table seems to be not ordered correctly + */ + static const unsigned char wheel_tbl[2][4] = + { + { 2, 0, 3, 1 }, /* Clockwise rotation */ + { 1, 3, 0, 2 }, /* Counter-clockwise */ + }; + + unsigned int btn = BUTTON_NONE; + + if (old_wheel_value == wheel_tbl[0][wheel_value]) + btn = BUTTON_SCROLL_FWD; + else if (old_wheel_value == wheel_tbl[1][wheel_value]) + btn = BUTTON_SCROLL_BACK; + + if (btn == BUTTON_NONE) + { + old_wheel_value = wheel_value; + return; + } + + int repeat = 1; /* assume repeat */ + long time = TIMER1_VALUE + current_tick*TIMER_TICK; /* to timer unit */ + long v = (time - last_wheel_post); + + /* interpolate velocity in timer_freq/timer_unit == 1/s */ + if (v) v = TIMER_FREQ / v; + + /* accumulate velocities over time with each v */ + wheel_velocity = (7*wheel_velocity + v) / 8; + + if (btn != wheel_repeat) + { + /* direction reversals nullify all fast mode states */ + wheel_repeat = btn; + repeat = + wheel_velocity = + wheel_click_count = 0; + } + + if (wheel_fast_mode != 0) + { + /* fast OFF happens immediately when velocity drops below + threshold */ + if (TIME_AFTER(time, + last_wheel_post + WHEEL_FAST_OFF_INTERVAL)) + { + /* moving out of fast mode */ + wheel_fast_mode = 0; + /* reset velocity */ + wheel_velocity = 0; + /* wheel_delta is always 1 in slow mode */ + wheel_delta = 1; + } + } + else + { + /* fast ON gets filtered to avoid inadvertent jumps to fast mode */ + if (repeat && wheel_velocity > TIMER_FREQ/WHEEL_FAST_ON_INTERVAL) + { + /* moving into fast mode */ + wheel_fast_mode = 1 << 31; + wheel_click_count = 0; + wheel_velocity = TIMER_FREQ/WHEEL_FAST_OFF_INTERVAL; + } + else if (++wheel_click_count < WHEEL_CHANGES_PER_CLICK) + { /* skip some wheel changes, so that 1 post represents + * 1 item in lists */ + btn = BUTTON_NONE; + } + + /* wheel_delta is always 1 in slow mode */ + wheel_delta = 1; + } + + if (btn != BUTTON_NONE) + { + wheel_click_count = 0; + + /* generate repeats if quick enough */ + if (repeat && TIME_BEFORE(time, + last_wheel_post + WHEEL_REPEAT_INTERVAL)) + btn |= BUTTON_REPEAT; + + last_wheel_post = time; + + if (queue_empty(&button_queue)) + { + queue_post(&button_queue, btn, wheel_fast_mode | + (wheel_delta << 24) | wheel_velocity*360/WHEELCLICKS_PER_ROTATION); + /* message posted - reset delta and poke backlight on*/ + wheel_delta = 1; + backlight_on(); + buttonlight_on(); + } + else + { + /* skipped post - increment delta */ + if (++wheel_delta > 0x7f) + wheel_delta = 0x7f; + } + } + + old_wheel_value = wheel_value; +} +#endif + void button_init_device(void) { +#if defined(HAVE_SCROLLWHEEL) GPIOA_DIR &= ~(1<<6|1<<7); GPIOC_DIR = 0; GPIOB_DIR |= (1<<4)|(1<<0); GPIOB_PIN(4) = 1<<4; /* activate the wheel */ -} -void get_scrollwheel(void) -{ -#if defined(HAVE_SCROLLWHEEL) && !defined(BOOTLOADER) - /* scroll wheel handling */ + /* setup scrollwheel isr */ + /* clear previous irq if any */ + GPIOA_IC = SCROLLWHEEL_BITS; + /* enable edge detecting */ + GPIOA_IS &= ~SCROLLWHEEL_BITS; + /* detect both raising and falling edges */ + GPIOA_IBE |= SCROLLWHEEL_BITS; + /* lastly, enable the interrupt */ + GPIOA_IE |= SCROLLWHEEL_BITS; +#endif +} + /* read the 2 bits at the same time */ #define GPIOA_PIN76_offset ((1<<(6+2)) | (1<<(7+2))) #define GPIOA_PIN76 (*(volatile unsigned char*)(GPIOA_BASE+GPIOA_PIN76_offset)) - scrollwheel(GPIOA_PIN76 >> 6); +void button_gpioa_isr(void) +{ +#if defined(HAVE_SCROLLWHEEL) + /* scroll wheel handling */ + if (GPIOA_MIS & SCROLLWHEEL_BITS) + scrollwheel(GPIOA_PIN76 >> 6); + + /* ack interrupt */ + GPIOA_IC = SCROLLWHEEL_BITS; #endif } + /* * Get button pressed from hardware */ - - int button_read_device(void) { int btn = 0; @@ -62,12 +223,12 @@ int button_read_device(void) static long power_counter = 0; unsigned gpiod6; - /* if we don't wait for the fifo to empty, we'll see screen corruption * (the higher the CPU frequency the higher the corruption) */ while ((DBOP_STAT & (1<<10)) == 0); - get_scrollwheel(); + int delay = 30; + while(delay--) nop; CCU_IO &= ~(1<<12); @@ -77,6 +238,7 @@ int button_read_device(void) gpiod6 = GPIOD_PIN(6); GPIOB_PIN(0) = 0; + udelay(1); if (GPIOC_PIN(1) & 1<<1) @@ -114,6 +276,12 @@ int button_read_device(void) { hold_button_old = hold_button; backlight_hold_changed(hold_button); + /* mask scrollwheel irq so we don't need to check for + * the hold button in the isr */ + if (hold_button) + GPIOA_IE &= ~SCROLLWHEEL_BITS; + else + GPIOA_IE |= SCROLLWHEEL_BITS; } #else (void)hold_button_old; diff --git a/firmware/target/arm/as3525/sansa-fuzev2/button-target.h b/firmware/target/arm/as3525/sansa-fuzev2/button-target.h index d7ef9623f9..c64c68f951 100644 --- a/firmware/target/arm/as3525/sansa-fuzev2/button-target.h +++ b/firmware/target/arm/as3525/sansa-fuzev2/button-target.h @@ -30,12 +30,6 @@ void button_init_device(void); bool button_hold(void); int button_read_device(void); -void get_scrollwheel(void); - -#define WHEEL_REPEAT_INTERVAL (HZ/5) -#define WHEEL_COUNTER_DIV 4 -#define ACCEL_INCREMENT 2 -#define ACCEL_SHIFT 2 /* Sandisk Sansa Fuze button codes */ /* Main unit's buttons */ -- cgit v1.2.3