The purpose of this project is to improve the non-linearity of the ADC in the RP2040 microcontroller, as described by the errata RP2040-E11, using software.
Furthermore, the project will also cover RP2350.
Note Lost information cannot be restored. Only improving linearity is possible.
Data are measured by measure.py with this firmware and the circuit below. They are in testboard folder. Board design files are here.
The following graphs show ADC errors on actual Raspberry Pi Pico/Pico W/Pico 2/Pico 2W devices which I possess.
Y-axis is ADC error and X-axis is ADC readout (not ADC input).
The raw data are here.

The enormous discontinuities are observed at 512, 1536, 2560 and 3584.
The raw data are here.

RP2350 is improved compared to RP2040, but there's still some non-linearity remaining.
By averaging the errors of the above five rp2040 devices and five rp2350 devices, I obtained the following code.
def rp2040adc_correction(d):
'''
rp2040 ADC Correction
d : 12 bit ADC readout (0~4095)
return: corrected ADC value
'''
if d >= 4081:
y = 4095
elif d >= 3711:
y = d + 14
elif d >= 3584:
y = d + 15
elif d == 3583:
y = 3583 + 10
elif d >= 3455:
y = d + 5
elif d >= 2943:
y = d + 6
elif d >= 2560:
y = d + 7
elif d == 2559:
y = 2559 + 3
elif d >= 2431:
y = d - 2
elif d >= 2048:
y = d - 1
elif d == 2047:
y = 2047 + 0
elif d >= 1664:
y = d + 1
elif d >= 1536:
y = d + 2
elif d == 1535:
y = 1535 - 3
elif d >= 1407:
y = d - 7
elif d >= 895:
y = d - 6
elif d >= 512:
y = d - 5
elif d == 511:
y = 511 - 10
elif d >= 16:
y = d - 15
else:
y = 0
return ydef rp2350adc_correction(d):
'''
rp2350 ADC Correction
d : 12 bit ADC readout (0~4095)
return: corrected ADC value
'''
if d >= 4079:
y = 4095
elif d >= 3905:
y = d + 16
elif d >= 3839:
y = d + 15
elif d >= 3489:
y = d + 14
elif d >= 3327:
y = d + 13
elif d >= 3105:
y = d + 12
elif d >= 2816:
y = d + 11
elif d >= 2783:
y = d + 10
elif d >= 2559:
y = d + 9
elif d >= 2304:
y = d + 10
elif d >= 2144:
y = d + 9
elif d >= 2047:
y = d + 8
elif d >= 1824:
y = d + 9
elif d >= 1792:
y = d + 8
elif d >= 1535:
y = d + 7
elif d >= 1280:
y = d + 8
elif d >= 1089:
y = d + 7
elif d >= 833:
y = d + 6
elif d >= 767:
y = d + 5
elif d >= 576:
y = d + 4
elif d >= 319:
y = d + 3
elif d >= 255:
y = d + 2
elif d >= 160:
y = d + 1
else:
y = d
return yY-axis is ADC error and X-axis is ADC input (not ADC readout).

The red line shows the error with correction, and the cyan line shows the error without correction.

The red line shows the error with correction, and the cyan line shows the error without correction.
If you can use individual code for each device, that means each device has its own customized firmware, you can use the following formula that gives better correction:
ADC_CORRECTED = ADC_READOUT + rp2040adc_err[ADC_READOUT]
Where rp2040adc_err[] is the array created by calc_err.py, and has error data for specific device measured by yourself using measure.py and the firmware. (Each device needs its own rp2040adc_err[].)
Individual code templates:
- C/C++ template (template_rp2040adc.h, template_rp2350adc.h)
- Python template (template_rp2040adc.py, template_rp2350adc.py)
You can embed the individual ADC calibration data in the last 4KB block of a flash ROM on a pico board, using pico_adc_chk.py and the firmware.
The embedded calibration data area can be accessed as an emulated EEPROM using Arduino-Pico.
Here is the example sketch of Arduino-Pico.
#include "hardware/adc.h"
#include "EEPROM.h"
int8_t const *adc_cal_data;
void setup()
{
EEPROM.begin(4096); /* start emulated EEPROM */
adc_cal_data = reinterpret_cast<int8_t const *>(EEPROM.getConstDataPtr());
Serial.begin(9600);
adc_init();
adc_gpio_init(26); /* A0 = GPIO26 */
analogReadResolution(12); /* resolution = 12bit */
}
void loop()
{
uint16_t n;
n = analogRead(A0);
Serial.println(n+adc_cal_data[n]);
delay(100);
}Y-axis is ADC error and X-axis is ADC input (not ADC readout).

The red line shows the error with correction, and the cyan line shows the error without correction.

The red line shows the error with correction, and the cyan line shows the error without correction.
