Map 10 bit analog read to a higher resolution

Hello!

I must be doing something really weird (or a poor search), cause I couldn’t find a similar topic.

BACKGROUND:

I am reading a potentiometer value, getting the full 10 bit resolution (0-1023), and then I want to send MIDI NRPN messages with this value, which are 14 bits (0-16383).

For the application I am developing this, I need the value of the pot to be able to have flexible LEFT and RIGHT values, depending on the user preference. This MIN and MAX values that the user chooses, are stored in EEPROM.

Due to a memory restriction, this LEFT and RIGHT values are selected from a list of 128 values (I store the index to the list in EEPROM, and the array in FLASH).

The list is the following, and it’s not random, but was generated with an algorithm which spread the numbers logarithmically (higher density at the beggining, and lower at the end). This is a design decision based on EEPROM space available and usage needs.

const uint16_t nrpn_min_max[] PROGMEM = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 26, 27, 29, 31, 33, 35, 38, 40, 43, 45, 48, 51, 55, 58, 62, 66, 70, 75, 79, 85, 90, 96, 102, 108, 115, 127, 128, 139, 148, 157, 168, 178, 190, 202, 225, 254, 255, 275, 293, 312, 331, 353, 375, 399, 425, 452, 481, 511, 512, 552, 616, 655, 697, 742, 789, 839, 893, 950, 1023, 1075, 1144, 1217, 1295, 1378, 1466, 1559, 1659, 1765, 1877, 1997, 2125, 2261, 2405, 2558, 2722, 2896, 3081, 3277, 3487, 3709, 3946, 4198, 4466, 4751, 5055, 5377, 5721, 6086, 6474, 6888, 7328, 7795, 8293, 8823, 9386, 9985, 10623, 11301, 12022, 12790, 13606, 14475, 15399, 16383};

A user could choose any number from the list to be the LEFT extreme value and any to be the RIGHT value.

Examples:

  1. LEFT: 0 RIGHT: 10623
  2. LEFT: 10623 RIGHT: 0
  3. LEFT: 5055 RIGHT: 14475
  4. LEFT: 14475 RIGHT: 5055

THE CODE:

      rawValue= constrain(rawValue, CONST_LOW_LIMIT, CONST_HIGH_LIMIT);
      if (IsNoise(rawValue, prevRawValue[numInput], numInput, 2))   // Pre-filter with raw 10 bit value
        return;
      prevRawValue[numInput] = rawValue;  
      Serial.print("Raw value: "); Serial.print(rawValue); Serial.println();
      
      if (mode == KMS::M_NRPN){
        minMidiNRPN = pgm_read_word_near(KMS::nrpn_min_max + minMidi);
        maxMidiNRPN = pgm_read_word_near(KMS::nrpn_min_max + maxMidi);

        mapValue = map(rawValue, CONST_LOW_LIMIT, CONST_HIGH_LIMIT, minMidiNRPN, maxMidiNRPN); 
       ...
     }

The complete code is here, and the previous are lines 820-830.

I constrain to take the extremes as a suggestion from another post to ignore bad hop-on region of a pot by removing 8 values at both extremes.

Then the IsNoise() function is an hysteresis filter to prevent the typical fluctuations.

THE PROBLEM:

Whenever LEFT > RIGHT and the output value range is higher than the original 10 bit resolution (LEFT-RIGHT > 1023), the output doesn’t always reach the extremes.

If we take EXAMPLE 2 (LEFT=10623 and RIGHT=0), i sometimes get values close to the extremes like 10582 or 10603.

This happens mostly if I move the pot slowly towards the extreme. If I move it rapidly it almost always is ok.

I noticed another thing, and it is that for EXAMPLES 1 and 3, for every raw 10bit value, there is an output value, but for EXAMPLES 2 and 4 there isn’t always an output value for every raw.

There should be always an output when mapping to a higher range. If, for example, we double the range (0->1023 to 0->2047), if raw moves from 0 → 1, output moves from 0 → 2, if raw goes from 1 → 2, output moves from 2 → 4.

So the absence of some output values there is odd. See the attached images for the serial monitor output.

Appreciate your time and help!

Due to a memory restriction, this LEFT and RIGHT values are selected from a list of 128 values (I store the index to the list in EEPROM, and the array in FLASH).

That curve can be approximated by 5 or 6 linear segments. You don't need to have the whole thing in memory. But if you really need the select-from-list function then it's a good way to do it.

This happens mostly if I move the pot slowly towards the extreme. If I move it rapidly it almost always is ok.

This seems like a mechanical problem. The Arduino is thousands of times faster than you are. Either way is "slow" for the Arduino. Twisting the knob with a motor will still give the Arduino time to take hundreds or thousands of measurements of the position.