• Status Assigned
  • Percent Complete
  • Task Type Patches
  • Category Drivers
  • Assigned To
  • Operating System iriver H10
  • Severity Low
  • Priority Very Low
  • Reported Version Release 3.0
  • Due in Version Undecided
  • Due Date Undecided
  • Votes
  • Private
Attached to Project: Rockbox
Opened by Lazerman - 2006-09-21
Last edited by Marc_Guay - 2008-12-12

FS#6041 - H10 Scrollpad

The scrollpad on the H10 (20gb, not sure about 5/10gb) has always bothered me with being jumpy, inaccurate, and unintuitive. At the moment (in rockbox) it seems as though it is working as a simple two button device (up/down), with no real dragging capabilities. I am wondering if this is a limitation of the hardware or current software implementation.

The original firmware seemed to work as follows:
split slider into upper and lower field. Tapping in either upper or lower half would create click-type event for up/down. Dragging in upper half would produce scroll up, draggin in lower half would produce scroll down events. The problem was that upper and lower halves are really kinda arbitrary and not easily distinguishable.

What I think would work nicely:
Tapping top and bottom, as before. Remove upper and lower restrictions for dragging, i.e. if dragging down (anywhere on the scrollpad) create scroll down event, if dragging up (again, anywhere) create scroll up event. Drag and hold will scroll in desired direction proportional to the drag speed (as in pushing a puck on ice). Hold (just hold, no tap or drag) on the touchpad creates a sliding value that can be used as a coordinate for different types of continuous input.

To implement this, we’d need some kind of knowledge about what signals the actual hardware provides, what resolution the pad has, i.e. what the physical and logical implementations of the device are. I am not sure how the Ipod scrollwheel works, but it seems that some of that code could be re-purposed, maybe?

The scrollpad currently provides 10-bit values (0x0 - 0x3ff) for the position where it is touched. These values are read from the ADC which i think is built in to the PP5020 chip itself.

Unfortunately, it looks like we don't fully understand how to use the ADC. The values we read are quite jumpy, so it will be quite difficult to implement your suggestion until we can read stable values from the ADC. Alternatively, it may be possible to implement some sort of filter on the ADC values read?

You can see the signal read from the ADC yourself in the I/O Ports debug screen (note that you have to press Power and Right at the same time to exit that screen). If you feel you can get a working implementation of your idea with these values, then please feel free to submit a patch.

Is there anyway to steal the code on how it works from the original firmware? Or atleast use that to get a rough idea on how it works?

The problem is that the original firmware isn't great anyway. The hardware just seems to provide very jumpy values. These should be smoothed out (averaged) to provide better handling. I'd suggest using a bilateral filtering approach because that will smooth out small jitters, but preserve large variations. I can supply code for the filtering if someone else wants to do the actual compiling/testing (I don't have the setup available here).

If only i could code… unfortunately… at this stage in life.. i dont have the time to learn , though i wish i did, and even if i did id take a while

Lazerman, it would be great if you could supply some filtering code. Hopefully someone else here will be able to pick it up and integrate it into the H10 button driver.

I will try to cut some code out of another project that I have done. In short, the idea for bilateral filtering is similar to simple Gaussian filtering. What happens there works as follows. Let's assume you have a kernel of size 5, then you have five sample values that you want to average. You could do this by adding them all up and dividing by the number of samples, 5. You could also say you multiply each sample by 0.2 before adding them together. This would be a box filter. In the general case you multiply each element by different values (weights), depending on how close to the center they are. For Gaussian filtering these weights have a Gaussian bell shape, e.g. 0.05 0.2 0.5 0.2 0.05 - note that they weights always add up to one. This is important so that the entire system doesn't loose or gain energy (become darker/brighter, louder/quiter, …).

So in short, Output = W1*S1 + W2*S2 + W3*S3 + W4*S4 + W5*S5, where Wn are the weights, and Sn are the samples.

The problem for this type of (linear) filtering is that large differences between adjacent samples can get severely smoothed, but often these differences are important and should be kept.

Bilateral filtering doesn't just take the proximity of samples into account (how close to the center they are), but also how different they are from the center value. This introduces another weight (let's call it vertical; and the other one horizontal) that must be included

V1 = f(S3-S1)
V2 = f(S3-S2)
V3 = 1
V4 = f(S3-S4)
V5 = f(S3-S5)

the function, f, is generally also a Gaussian shape (i.e. exponential, f~e^(-x^2/d^2)). The parameter d changes the width of the Gaussian bell shape. The effect is that if S3-S1 is very large, then V1 is very small and the much different S1 will not influence the output very much.

The new sum becomes:
Output = W1*V1*S1 + W2*V2*S2 + W3*V3*S3 + W4*V4*S4 + W5*V5*S5

Obivously, the output now has to be normalized differently, by dividing the sum by W1*V1 + W2*V2 + W3*V3 + W4*V4 + W5*V5

And that's already it. The smoothing behavior then depends mostly on the kernel size (5 in this case) and the parameters for the horizontal and vertical gaussian distributions. Note that while the horizontal weights can be pre-computed, the vertical weights are data-dependent and therefore need to be computed on the fly.

An implementation should keep a circular array of size 5 or 7 or something, update the latest values and smooth over them using the above filter.

I hope that helps for now. If nobody comes up with an implementation I will provide one after my vacation. Have fun.

Is a way to touch the pad and see (xample: with a rock application) the value returned ?

To see the value read from the touchpad, go into the menu, then Info→Debug→View I/O Ports. It's called ADC_SCROLLPAD.

where is the code of the driver ?

The code to read the value is in firmware/target/arm/iriver/h10/adc-h10.c and the code for button reading is in firmware/target/arm/iriver/h10/button-h10.c

Can you just ommit few less significant bits in this value form ADC? It would give you quite stable value, and I think that 256 levels (8-bit) is enough for start and would be less CPU consuming than solving that mathematically.

Well for a temporary fix, couldnt you add some code saying like ignore the first command if its scrolled down to the bottom… like if you start at the top and scroll down… instead of make it go jerky as… it just scrolls down?

I made some tests a while ago and just tooke the average of 5 values given by the ADC.
I also divided the touchpad into 5 boundings and it worked at least in the plugin API. I gave
it up because of my exams and because it wasnt 100% and i am not a good programmer in C.

EMonk commented on 2007-09-09 21:15

I'm new to RockBox, so bear with me. I won't try to build my own patch since I don't have enough knowledge about the code as yet to do so.

I've been playing around with the OF to see if I can figure out what it's doing in the background. Here's some *PSEUDO-CODE* for what I've figured out so far


while no input from scrollpad { do nothing }
wait up to 0.5s for scrollpad change
if changed to no input then 
  send up/down based on previous position
  goto HandleScrollPad
while not changed
  send up/down based on current position 
  wait (repeat_delay) for change
if changed to valid value (not open)
  while not open
    send up/down based on direction and size of changes
goto HandleScrollPad

It's a bit messy, but I think that gives a basic idea. There's all sorts of ADC settling times and so on missing, as well as some way of checking if a change is heading for the ADC_OPEN state, etc. Someone with more familiarity with the ADC code should be able to fill that in fairly well, and adjust the above pseudo-code to fit in with the way the key drivers work at the moment.

Ok. I finally had a look at the erratic touchpad values and ways to smooth them out, and I am quite happy with the solution. Since I couldn't think of an easy way to get ground-truth vs. read-out values for the touchpad, I wrote a little program (attached - in C#) that adds a certain amount of noise to a slider-value, and then tries to recover a "clean" signal. When designing this, I watched out for the following:
– Absolute value is not as important as relative values (we want to register up as up, and down as down, but we don't need to be running at full resolution… my fingers are much too thick for that anyway :-))
– We must filter out noise at the expected noise level, but NOT large jumps (i.e. simple averaging doesn't work)
– We don't want to filter over too big a time-window. This requires resources and introduces delay

So the solution I came up with are two very simple functions: A smoothing pass, followed by a quantization step. Both only consider the last known value and are trivial to implement.

One caveat is that the system will self-correct over time. While this is a nice feature, it can result in a delayed signal being registered. This really only matter when you keep your finger on the control for a long time. What could happen is this: E.g. you scroll up and the display or cursor moves up, as expected. After you stop and hold for a little bit, another spurious up event is fired. One could possibly fix that by increasing the quantization size a bit, or moving the quantization center in accordance with the current sliding speed, but I would have to see this in action to really recommend a solution.

Smoothing (Smoother.cs): An exponential terms that weights the difference in signal by the amount of difference. Large differences are passed through, while small differences are heavily attenuated.
Quantization (SlidingQuantizer.cs): Only register value changes over a certain threshold

The parameter values for these would have to depend on how noisy the trackpad actually is, somebody would have to test that out, but there are only two values to consider - the smoothing value and the quantization value.

Note that the code is written to demonstrate the smoothing, not for integration into rockbox. Someone would have to take the 10 or so relevant lines of code and put that into the firmware. Then, one would have to work at using those values somehow in the UI. So - Godspeed to whomever takes on that task! Go Team :-)

   H10Pad.7z (14.7 KiB)

Thanks. Judging from the test application, this looks like it might work quite well if it can be implemented on the device. The main issues I see for integration into Rockbox are:

1. We can't use floating point.
2. I don't think we currently have an exponential function. Maybe we could just get by with a couple of terms from the taylor series? For example 1-exp(-x*x) ~ x*x so maybe we could use max(x*x,1) instead?

Ok, done. I replaced the weighting function with a smoothstep function. Have a look at the PDF for why that is a fair approximation.
I replaced the smoothing and quantization functions with Integer versions. No floating points needed.


Available keyboard shortcuts


Task Details

Task Editing