summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2010-04-27 10:11:52 +0000
committerThomas Martitz <kugel@rockbox.org>2010-04-27 10:11:52 +0000
commit249aae587a1f35f5df55613c6765ae763db39107 (patch)
treedcc7f100fcdc7af8b09a7e3daa96b852a1986c74 /firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c
parentee291a5e9b12651ffa658bef15e09902a82285f9 (diff)
downloadrockbox-249aae587a1f35f5df55613c6765ae763db39107.tar.gz
rockbox-249aae587a1f35f5df55613c6765ae763db39107.zip
FS#11172 - Fuzev2: Read the scrollwheel scrollwheel via IRQ
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25736 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c')
-rw-r--r--firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c192
1 files changed, 180 insertions, 12 deletions
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;