Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADC raw values calibration #555

Merged
merged 12 commits into from
Jul 4, 2023
Merged

Conversation

katyo
Copy link
Contributor

@katyo katyo commented May 23, 2023

Based on #550
Related issue #326

This PR implements ADC calibration based on ESP IDF. The following schemes supported:

  • Basic calibration which sets initial (zero) bias
  • Line fitting which corrects gain (also includes basic)
  • Curve fitting which corrects non-linearity (also inclues basic and line fitting)

First two schemes uses factory values stored in efuse fields when it available.
As a fallback a runtime calibration is made by connecting ADC input to ground or reference voltage internally.

Third scheme uses pre-defined polynomial coefficients derived from IDF.
Also applied useful optimizations for runtime calculations and to coefficients representation to reduce operations and avoid division.

NOTE: Currently calibration supported only for riscv chips because I haven't xtensa-based boards for testing, but it will be relatively easy add support for such chips later.

Must

  • The code compiles without errors or warnings.
  • All examples work.
  • cargo fmt was run.
  • Your changes were added to the CHANGELOG.md in the proper section.
  • You updated existing examples or added examples (if applicable).
  • Added examples are checked in CI

Nice to have

  • You add a description of your work to this PR.
  • You added proper docs for your newly added features and code.

@JurajSadel
Copy link
Contributor

@katyo Thank you very much for working on this, is it something I can help you at this stage?

@katyo
Copy link
Contributor Author

katyo commented May 25, 2023

@JurajSadel,
Currently I search a way to avoid breaking changes for an API.
We need find a proper way to select calibration/fitting algorithm when enabling ADC pin.

@katyo katyo force-pushed the feature/adc_calibration branch 2 times, most recently from d53f1ce to 464e46a Compare May 26, 2023 04:55
@katyo katyo changed the title Feature/adc calibration ADC raw values calibration May 27, 2023
@bugadani
Copy link
Contributor

I know my opinion here isn't very important, but I'm pretty happy with where this is heading. From a pure design standpoint, I would be happier if the algorithms weren't tied to the efuses, but having calibration coupled to the ADCs here isn't bad either.

I am slightly worried about the hardcoded constants in curve fitting - how confident can we be that a single set of constants work well across different production runs?

@katyo
Copy link
Contributor Author

katyo commented May 29, 2023

@bugadani
I share your concerns about hardcoded curve coefficients which derived from ESP IDF source. I'm far away from chip manufacturing but curve-based error correction gives me best precision at the moment particularly near to top edge of a range. Seems coefficients represents non-linearity of attenuator and convertor itself.

@katyo katyo mentioned this pull request May 30, 2023
8 tasks
@katyo katyo force-pushed the feature/adc_calibration branch 2 times, most recently from b244042 to d700f09 Compare June 3, 2023 03:57
@katyo
Copy link
Contributor Author

katyo commented Jun 3, 2023

Now this PR uses efuse fields access from #567.

@katyo
Copy link
Contributor Author

katyo commented Jun 5, 2023

Because #567 is merged I rebased this pr and mark it ready for review.

@katyo katyo marked this pull request as ready for review June 5, 2023 18:05
Copy link
Contributor

@JurajSadel JurajSadel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for working on this!

I tried it, the C3 is working fine!

C6 example (adc_cal) is panicking for me with

!! A panic occured in 'C:\Users\JurajSadel\rust_test\esp-hal\esp-hal-common\src\analog\adc\cal_line.rs', at line 34, column 46

PanicInfo {
    payload: Any { .. },
    message: Some(
        No reference point V1 found in efuse,
    ),
    location: Location {
        file: "C:\\Users\\JurajSadel\\rust_test\\esp-hal\\esp-hal-common\\src\\analog\\adc\\cal_line.rs",
        line: 34,
        col: 46,
    },
    can_unwind: true,
}

Backtrace:

No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)

On C2, I think the voltage values are not correct (compared with my toy voltmeter and would be nice if someone could give it a try) and I couldn't compare it with the ESP-IDF because of revision mismatch.

C2 and C6 have a bunch of unused constants warnings, would be nice to clean them as well

@katyo katyo force-pushed the feature/adc_calibration branch 2 times, most recently from 062884e to f48ddeb Compare June 7, 2023 15:02
@katyo
Copy link
Contributor Author

katyo commented Jun 7, 2023

@JurajSadel Could you test it again?
I fixed calibration data version detection.
Also I added as a fallback the reference voltage calibration for linear scheme. But the last one is not so good because actual reference voltage may varies in range 1.0..=1.2V. This value currently is not present in efuse.

@jessebraham
Copy link
Member

jessebraham commented Jun 7, 2023

I've confirmed that the examples build and run successfully on the C2, C3, and C6. The values for the C2 and C6 looked correct, however for me the C3 was reporting closer to 2.8V. Other than that seems good, though.

@katyo katyo force-pushed the feature/adc_calibration branch 3 times, most recently from 58af83a to 49abd51 Compare June 8, 2023 06:19
@bjoernQ
Copy link
Contributor

bjoernQ commented Jun 9, 2023

I just tried it and it works fine for me on C2 and C3. However C6 crashes in development mode with this

!! A panic occured in 'C:\projects\review\katyo\esp-hal\esp-hal-common\src\analog\adc\cal_line.rs', at line 73, column 15

PanicInfo {
    payload: Any { .. },
    message: Some(
        attempt to divide by zero,
    ),
    location: Location {
        file: "C:\\projects\\review\\katyo\\esp-hal\\esp-hal-common\\src\\analog\\adc\\cal_line.rs",
        line: 73,
        col: 15,
    },
    can_unwind: true,
}

And in release-mode it certainly shows weird values because of this

@katyo
Copy link
Contributor Author

katyo commented Jun 9, 2023

@bjoernQ Looks strange.

Seems code is zero here:

let gain = ((mv as u32 * GAIN_SCALE * 2 / code as u32 + 1) * 4096 / atten.ref_mv() as u32 + 1) / 2;

May be calibration data is missing (or version check failed)
or (a fallback way) runtime calibration failed.

@bjoernQ Cound you give me a values which return Efuse::get_block_version() call on a C6?

@bjoernQ
Copy link
Contributor

bjoernQ commented Jun 9, 2023

Maybe adding some debug or trace logs might help identifying what is going on here. We already have the log dependency in esp-hal-common

@katyo
Copy link
Contributor Author

katyo commented Jun 15, 2023

@elpiel The fact is that ADC input impedance on Espressif SoCs relatively low compared to many other MCUs (KOhms not MOhms).
I recommend you try use 10x-100x lower resistor values and add 10-100nF capacitor to input (after divider). You may need experimenting with it to get stable results.

@elpiel
Copy link
Contributor

elpiel commented Jun 23, 2023

Testing on the Olimex board with soldered jumpers for the battery meassurement on GPIO 3 indeed shows good results!
I need to double check the values with my multimeter and figure out a way to not charge the battery via USB when measuring it.

https://raw.githubusercontent.com/OLIMEX/ESP32-C3-DevKit-Lipo/main/HARDWARE/ESP32-C3-DevKit-Lipo_Rev_A/ESP32-C3-DevKit-Lipo_Rev_A.pdf

On the embassy project I still get a panic but I will check whether this is because of unhappy embassy crate versions

Copy link
Contributor

@bugadani bugadani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see the eFuse calibration versions (I commented on a few, but not all) named (a plain 1 is a bit confusing to read), but apart from that, this PR is in a really promising state.

I can see a few grammar issues (mine is probably incorrect, too!), and a few comments that look like they were copied from C or written to explain the C code during development. Really nothing major.

It would be nice if this PR could be merged in the near future 🙏

esp-hal-common/src/analog/adc/cal_curve.rs Outdated Show resolved Hide resolved
esp-hal-common/src/analog/adc/cal_line.rs Outdated Show resolved Hide resolved
esp-hal-common/src/analog/adc/cal_line.rs Outdated Show resolved Hide resolved
pub fn get_rtc_calib_version() -> u8 {
let (major, _minor) = Self::get_block_version();
if major == 0 {
1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This 1 needs to be a named constant IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like avoid introduce new constants.
Ok, I'll think about it.

pub fn get_rtc_calib_init_code(_unit: u8, atten: Attenuation) -> Option<u16> {
let version = Self::get_rtc_calib_version();

if version != 1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This 1 needs to be the same constant as above, I think.

pub fn get_rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option<u16> {
let version = Self::get_rtc_calib_version();

if version != 1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

esp-hal-common/src/analog/adc/cal_line.rs Outdated Show resolved Hide resolved
esp-hal-common/src/analog/adc/cal_line.rs Outdated Show resolved Hide resolved
esp-hal-common/src/analog/adc/cal_curve.rs Outdated Show resolved Hide resolved
esp-hal-common/src/analog/adc/cal_line.rs Outdated Show resolved Hide resolved
pub fn get_rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option<u16> {
let version = Self::get_rtc_calib_version();

if version != 1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version here should also be replaced by a const

esp32c2-hal/examples/adc_cal.rs Show resolved Hide resolved
@katyo katyo force-pushed the feature/adc_calibration branch 2 times, most recently from 0039905 to 3f0e414 Compare June 30, 2023 08:41
@bugadani
Copy link
Contributor

bugadani commented Jul 2, 2023

I just want to say that I'm working on implementing this PR for the S3, just so others don't waste their time on that :) I'll post the results once this one gets merged.

Copy link
Member

@MabezDev MabezDev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After thinking some more, I don't think the consts are that big of a deal, we can always reference the ESP-IDF code to actually understand what's going on.

I think a quick rebase on main and this should be good to go from my perspective :).

@bugadani
Copy link
Contributor

bugadani commented Jul 3, 2023

After thinking some more, I don't think the consts are that big of a deal, we can always reference the ESP-IDF code to actually understand what's going on.

Honestly, I think the if major == 0 { 1 } pattern deserves some sort of explanation (be that a comment or a named constant) in the code as it looks like a mistake.

Buuuuut I also don't want to delay the PR 🫣

katyo added 12 commits July 4, 2023 21:49
- `AdcCalScheme<ADCI>` implemented for each calibration scheme (basic, linear, curved)
- `AdcCalEfuse` implemented for each ADC unit to get calibration data from efuse bits
Basic calibration is related to setting some initial bias value to ADC unit.
Such values usually is stored in efuse bit fields but also can be measured
in runtime by connecting ADC input to ground internally.
This scheme also includes basic calibration and implements gain correction based
on reference point.

Reference point is a pair of reference voltage and corresponding mean raw ADC
value. Such raw values usually is stored in efuse bit fields for each supported
attenuation.

Possibly it also can be measured in runtime by connecting ADC to reference
voltage internally.
This scheme also includes basic and linear and implements final polynomial error
correction.
This example uses line fitting calibration scheme by default.
It periodically prints both raw measured value and computed millivolts.
This example uses curve fitting calibration scheme by default.
It periodically prints both raw measured value and computed millivolts.
This example uses curve fitting calibration scheme by default.
It periodically prints both raw measured value and computed millivolts.
Copy link
Member

@jessebraham jessebraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could introduce some new constants here, as others have suggested, but in the interest of getting this PR merged I'm gonna say LGTM and we can come back to it at a later date. I also noticed that the H2 is missing from this, but again that can wait. I will create an issue to remind us, and may end up just tackling that myself.

Thanks for all your hard work on this, will be great to actually be able to use our ADCs now! 😁

@jessebraham jessebraham merged commit 74438fc into esp-rs:main Jul 4, 2023
@katyo
Copy link
Contributor Author

katyo commented Jul 4, 2023

@jessebraham Because esp-idf still does not support calibration for H2 so I don't know how we can add support for it just now.
(see efuse/esp32h2/esp_efuse_rtc_calib.c#L13)

@katyo katyo deleted the feature/adc_calibration branch July 4, 2023 17:31
@bugadani bugadani mentioned this pull request Jul 5, 2023
8 tasks
playfulFence pushed a commit to playfulFence/esp-hal that referenced this pull request Sep 26, 2023
* adc_cal: c2: Add efuse functions for reading calibration

* adc_cal: c3: Add efuse functions for reading calibration

* adc_cal: c6: Add efuse functions for reading calibration

* adc_cal: Add extra traits to support calibration

- `AdcCalScheme<ADCI>` implemented for each calibration scheme (basic, linear, curved)
- `AdcCalEfuse` implemented for each ADC unit to get calibration data from efuse bits

* adc_cal: Add basic ADC calibration scheme

Basic calibration is related to setting some initial bias value to ADC unit.
Such values usually is stored in efuse bit fields but also can be measured
in runtime by connecting ADC input to ground internally.

* adc_cal: Add line fitting ADC calibration scheme

This scheme also includes basic calibration and implements gain correction based
on reference point.

Reference point is a pair of reference voltage and corresponding mean raw ADC
value. Such raw values usually is stored in efuse bit fields for each supported
attenuation.

Possibly it also can be measured in runtime by connecting ADC to reference
voltage internally.

* adc_cal: Add curve fitting ADC calibration scheme

This scheme also includes basic and linear and implements final polynomial error
correction.

* adc_cal: riscv: Add ADC calibration implementation for riscv chips

* adc_cal: c2: Add calibrated ADC reading example

This example uses line fitting calibration scheme by default.
It periodically prints both raw measured value and computed millivolts.

* adc_cal: c3: Add calibrated ADC reading example

This example uses curve fitting calibration scheme by default.
It periodically prints both raw measured value and computed millivolts.

* adc_cal: c6: Add calibrated ADC reading example

This example uses curve fitting calibration scheme by default.
It periodically prints both raw measured value and computed millivolts.

* adc_cal: riscv: Add changelog entry for ADC calibration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants