Skip to content

kitanokitsune/rp2040adc_correction

Repository files navigation

RP2040/RP2350 A/D non-linearity correction

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.

RP2040/RP2350 ADC non-linearity Measurement

Data are measured by measure.py with this firmware and the circuit below. They are in testboard folder. Board design files are here.

ckt

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).

RP2040 ADC non-linearity

The raw data are here.

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

RP2350 ADC non-linearity

The raw data are here.

RP2350 is improved compared to RP2040, but there's still some non-linearity remaining.

General-Purpose Correction Code

By averaging the errors of the above five rp2040 devices and five rp2350 devices, I obtained the following code.

RP2040 GENERAL-PURPOSE 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 y

RP2350 GENERAL-PURPOSE CODE

def 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 y

Correction result by the general-purpose code

Y-axis is ADC error and X-axis is ADC input (not ADC readout).

RP2040 ADC correction result by the general-purpose code


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

RP2350 ADC correction result by the general-purpose code


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

Best Calibration by Individual Code

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:

Embed Individual Calibration Data in the Pico's Flash ROM

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);
}

Calibration result by individual code

Y-axis is ADC error and X-axis is ADC input (not ADC readout).

RP2040 ADC calibration result by individual code


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

RP2350 ADC calibration result by individual code


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

Releases

No releases published

Packages

 
 
 

Contributors