I have a potentiometer connected to an ADC input of an Arduino Leonardo. The ADC resolution is 10-bit (0..1024), while I need just 7-bit (0..127).
I use a simple filter to reduce the noise and then a map
to cut the unused bits:
#define ADC_CH 18
#define ADC_FILTER 0.90
uint16_t value;
uint16_t adc = analogRead(ADC_CH);
value = (uint16_t) ((float) ADC_FILTER * adc + (1. - ADC_FILTER) * value);
I show value
on an LCD. It's very stable, but it may happen that if the adc readings are in the nearby of two values, there is nothing that could prevent a change in the final value, even if the noise is of just few LSBs.
Is there a simple way to implement an hysteresis for each low-res value?
In this way the change between N and N+1 happens on a different ADC reading than between N and N-1, improving the noise rejection without increasing the filter delay (that would only increase the change interval).
1 Answer 1
When you reduce the resolution from 10 bits to 7 bits, every possible output corresponds to an interval of inputs. If you add some hysteresis, then those intervals should overlap. Below is a representation of the input intervals corresponding to outputs 0, 1,... 6, for an hysteresis width of 4:
input: 0 5 10 15 20 25 30 35 40 45 50 55 60
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴
├─────0────┤
├───────1──────┤
├───────2──────┤
├───────3──────┤
├───────4──────┤
├───────5──────┤
├───────6──────┤
To implement this in code, you have to remember the last output, and change it only if the input lies outside the interval assigned to this output. Here is an example implementation:
const int hysteresis = 4; // tune to taste
/* Remove 3 bits of resolution while adding some hysteresis. */
int resample(int adc)
{
static int value; // last output
if (adc < (value<<3) - hysteresis) {
value = (adc + hysteresis) >> 3;
} else if (adc >= ((value+1)<<3) + hysteresis) {
value = (adc - hysteresis) >> 3;
}
return value;
}
Note that a static
local variable keeps its value between successive
calls to the function.
-
I'm not supposed to do this but anyway: excellent answer! Nice graphic, well explained. :)2023年05月09日 06:04:10 +00:00Commented May 9, 2023 at 6:04
-
Your code works perfectly and it fits exactly the example in my question. Anyway, is there a more general approach? For example, what if I want to
map
the ADC input to [min, max] (always < 1024) ?Mark– Mark2023年05月12日 18:13:49 +00:00Commented May 12, 2023 at 18:13 -
1@Mark: Then you would replace the shifts (which are basically optimized multiplications and divisions) by arbitrary
map()
s. You may then want to do some tests in order to see how the rounding errors introduced by the map interfere with the hysteresis.Edgar Bonet– Edgar Bonet2023年05月12日 18:42:41 +00:00Commented May 12, 2023 at 18:42