Skip to content

New ADC V2 with Async support #814

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

Merged
merged 2 commits into from
Jun 17, 2025
Merged

New ADC V2 with Async support #814

merged 2 commits into from
Jun 17, 2025

Conversation

rnd-ash
Copy link
Contributor

@rnd-ash rnd-ash commented Jan 23, 2025

Summary

This PR adds a new ADC API, based around a similar system to channels used by EIC and DMA peripherals.

At the moment I'll keep this as a draft PR for feedback whilst I am still finalizing bits and also adding SAMD11/21 support

Features

  • A new settings builder system for configuring the ADC
  • Channels system (Each Analog capable pin has its own channel)
  • Both blocking and Async read support
  • Buffer filling support (Free running continuous mode)
  • CPU temperature reading
  • Internal voltages reading

Progress checklist

  • SAMx51 support
  • SAMx11/21 support
  • Channels API
  • Settings builder (Mostly completed, need to add settings for Vref calibration)
  • Async/Blocking single reads
  • Async/Blocking buffer reads
  • CPU temperature reading (Restricted to primary ADC if applicable to chip)
  • CPU internal voltages reading (Restricted to primary ADC if applicable to chip)

Copy link
Contributor

@jbeaurivage jbeaurivage left a comment

Choose a reason for hiding this comment

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

I just finished a first pass at reviewing this. In general, I would say this is an excellent implementation. Given your willingness to learn best practices, my review is more detailed than usual, so feel free to ignore some of these comments if you feel they're too nitpicky.

That being said, I do have some other comments that I feel deserve some attention:

  • SAMD11 support. I feel we're so close it makes sense to finish the implementation for SAMD11 chips as well. As far as I can tell, the ADC is exactly the same between D21 and D11 targets. The only thing that might change and that I'm unsure of, is the maximum supported clock speed.
  • We've already discussed this a little, but I'm not so sure the Channel API is so necessary after all. When I initially suggested this, I was under the impression that the ADC could perform conversions simultaneously on multiple channels, similar to EIC or DAMC. That's obviously not the case. As far as I can tell, there is also a 1:1 relationship between ADC channels and physical pins. So while the ADC can mux between channels, there is no pin-channel muxing going on. I think the ADC could simply take &mut refs to already-configured pins instead. This would cut down on complexity a whole bunch.
  • Before merging, the Tier 1 BSP examples will have to be updated, and clippy warnings resolved. Ideally we would show examples where the CPU clock is ran at full speed, and a second GCLK is started at a slower speed to clock the ADC.
  • I like that the d51 implementation takes advantage of the v2 clock system. However, it seems like the new adc module is not taking fully advantage of the typesafe clocking system: one needs to (unsafely) steal the peripherals to create a MCLK instance in order to configure the ADC. We should instead use the clocks system in its intended way, which is to exchange an ApbToken<Adcx> using Apb::enable to get a configured clock type, which can then be passed to the Adc peripheral, thus proving the clock is enabled. This is somewhat inverse to what the module is currently doing, which is enabling the ADC clock inside the constructor using the MCLK peripheral.

@jbeaurivage
Copy link
Contributor

Here's a proof of concept of what I mean by using the typesafe clocking system. It's still untested and needs some more feature gating to accomodate D11 and D21 targets, but I think it gets the idea across: https://github.com/jbeaurivage/atsamd/tree/adc-v2-clocking

With an example usage:

let pins = Pins::new(cx.device.port);
let (buses, clocks, tokens) = clock_system_at_reset(
    cx.device.oscctrl,
    cx.device.osc32kctrl,
    cx.device.gclk,
    cx.device.mclk,
    &mut cx.device.nvmctrl,
);
let mut apb = buses.apb;
let adc0_clk = apb.enable(tokens.apbs.adc0);

let adc0_settings = Config::new()
    .clock_cycles_per_sample(5)
    .clock_divider(Prescalerselect::Div2)
    .sample_resolution(AdcResolution::_12bit)
    .accumulation_method(AdcAccumulation::Single);

let gclk0 = clocks.gclk0;
let (pclk_adc0, gclk0) = Pclk::enable(tokens.pclks.adc0, gclk0);
let (_pclk_adc1, _gclk0) = Pclk::enable(tokens.pclks.adc1, gclk0);

let (adc0, channels_adc0) =
    Adc::new(cx.device.adc0, adc0_settings, adc0_clk, pclk_adc0.into()).unwrap();

@rnd-ash rnd-ash marked this pull request as ready for review February 2, 2025 08:35
@jbeaurivage
Copy link
Contributor

jbeaurivage commented Feb 4, 2025

@rnd-ash, your additional commits look good.

You can check out This PR, which implements some of the larger changes mentioned here

I also want to point out that when testing on a SAMD51 board (metro M4), I regularly get the ADC stalling on waiting for the RESRDY flag to get set. So far the only way I managed to get ADC readings was:

  • Using the clocks at reset (48 MHz), and
  • Compiling in release mode.

To me this sounds like there is still a clock synchronization issue or a race condition somewhere.

So, among the things left to do:

@rnd-ash rnd-ash requested a review from jbeaurivage March 10, 2025 05:05
@jbeaurivage
Copy link
Contributor

jbeaurivage commented Mar 11, 2025

@rnd-ash, if you're happy with this, I am too. I think you've been doing extensive testing recently. Only two things to get CI green and get this merged:

  • Fix a small clippy lint in calibration/d11.rs (line 44).
  • Fix the pygamer examples

@rnd-ash
Copy link
Contributor Author

rnd-ash commented Mar 12, 2025

I'll be able to fix these next week or this weekend when I'm back in the UK and have access to all my test boards (SAMD21 I need to verify it doesn't read old results in the ADC during sequential reads)

@rnd-ash
Copy link
Contributor Author

rnd-ash commented Mar 19, 2025

@ianrrees @jbeaurivage As far as I am concerned now, this is ready to Merge. If you guys want to do 1 final review feel free.

As discussed with Ian, I fixed the pygamer example my deleting the neopixel examples

Copy link
Contributor

@ianrrees ianrrees left a comment

Choose a reason for hiding this comment

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

With stable Rust, I note cargo doc flags several dead links in the documentation, so please do have a look at those and ensure the rendered docs are what you intend.

Thanks for all this work! Looks pretty good.


/// This adjusts the number of ADC clock cycles taken to sample a single
/// sample. The higher this number, the longer it will take the ADC to
/// sample each sample.
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the maximum value, and assuming it's not 8b what happens if a config is specified that's over it?

@jbeaurivage
Copy link
Contributor

jbeaurivage commented Apr 16, 2025

@rnd-ash, @ianrrees still had a few comments that are worth being addressed. I've fixed a bunch in rnd-ash#10. The only outstanding item that might require more discussion is the handling of the sampling parameters, ie, if we should force users to explicitly specify everything or not.

To simplify things a bit, I suggest that if you want add convenience methods to setup the ADC to a specific number of cycles per sample, it could be done in a follow-up PR.

@jbeaurivage jbeaurivage mentioned this pull request Apr 17, 2025
2 tasks
@ianrrees
Copy link
Contributor

I've been working on an ESP32 based project for a couple weeks now, and TBF it's reinforced my feelings that for things like UARTs (and I'd say ADC/DAC as well - just haven't used them in ESP32), it's really best to make the API explicit about settings where there is no clear default. However, I'm just one person, and I respect that the ESP32 HAL is quite successful, so maybe it's not worth worrying too much about...

@rnd-ash - it would be great to get this merged, what's your feeling about the next steps with it? If changing the config builder is too much of a pain, I'm happy either leaving it as-is or having a crack at it myself if you'd like.

@rnd-ash
Copy link
Contributor Author

rnd-ash commented May 15, 2025

@ianrrees i think we are ready to merge this, just 1 minor bug with the SAMD21's temperature sensor not reading accurately, but I can (For now at least), just disable the cpu temperature function for SAMD21 if it proves to be a blocker.

I have not tested this on a D11 target either, as I don't have one, would it be possible for you to verify functionality?

@ianrrees
Copy link
Contributor

Awesome, thanks! Yep, my next few days are going to be a bit variable in terms of computer time, but will try to get to it soon.

@ianrrees
Copy link
Contributor

ianrrees commented May 19, 2025

Alrighty, have put a little bit of time in to this and have a few thoughts in no particular order:

  1. The work looks really good!
  2. I've fiddled with the Git history of this work, making a branch that's as-if this work was all done using rebases instead of merges, then rebased that on to the current master and made a couple slight improvements. The new branch is at https://github.com/ianrrees/atsamd/tree/adcv2_rebase . I'd suggest that we look at whether it makes sense to keep the work in >1 commit (I guess it does, in particular removing the problematic examples) and if so clean up the history. Regardless, we should update as needed via rebasing on to master. Happy for it to live in my GH, or @rnd-ash if you'd like to update your branch that would look like (presuming you've not added more commits in the mean time):
git checkout adc_v2
git branch adc_v2_backup
git remote add ianrrees [email protected]:ianrrees/atsamd.git
git fetch ianrrees
git reset ianrrees/adcv2_rebase
git push --force
  1. Unless it gets fixed in the meantime, I'd like to have a play with the SAMD21 temperature sensor issue before merging - should be able to put some time in to it in the next couple days. Not really a show stopper, just while we're in the area it seems like a good thing to sort out.

@rnd-ash
Copy link
Contributor Author

rnd-ash commented May 19, 2025

Done @ianrrees , should be up to date with your branch

rnd-ash added 2 commits June 17, 2025 21:59
atsamd-rs#814

This work was done over about 6 months by:
Ashcon Mohseninia <[email protected]>
and
Justin Beaurivage <[email protected]>
with input from the atsamd-rs community.

Begin constructing an async ADC concept for D5X

Draft up new ADC API

Test implementation of async read for ADC

Resolve rest of the adc read future

Move to type based ADC implementation

Broken read_blocking implementation

Begin implementing async reading ADC

Automatically convert pins to correct mode in with_pins

Remove explicit reference to a chip in favor of PAC

Split async and sync ADC into different implementations

Clean up ADC

Add inlines everywhere

Add wait_flags method and do some extra cleanup

Add blocking and async buffered read methods

Derive debug for Adc Error

Fix ADC read_buffer not ever starting

Fix ADC reading blocking buffer always returning BufferOverrun

Implement more ADCSettings

Add in temperature calibration parameters to calibration

Add in CPU Temperature reading

Add CPU internal sensors reading, and setting VREF for adc

Restrict internal CPU sensors to primary CPU ADC

Remove breaking comments in Cargo.toml

Allow blocking methods on async-enabled ADCs

Derive defmt::Format for error type

Remove duplicate methods

ADC: Add support for samd21 line

Remove curiosity nano reference

Make names more consistent with the rest of the crate

samd21 - Fix ADC results being multiplied by 2x

Error if ADC is configured for summation/averaging but user has selected an incorrect bit width

Improve ADC comments

Resolve clippy warnings

Add ADC Pins for D11

Improve imports and hal_cfg usage for ADC

Leave workspace Cargo.toml unchanged

Make good use of the typelevel guarantees provided by clock::v2

Remove the need for the Channel machinery

D5x - Document TH and TL parameters better

Improve D11/D21 ADC clocking

Cleanup config type aliases

Add doc comment for ADC::new (thumbv6)

Simplify some snippets, add missing doc comment, and add missing sync calls

Minimal working ADC example for feather_m0

Fix some bugs in conversion methods

Slightly improve docs

Revert workspace Cargo.toml

Add D11 ADC calibration

Update T1 BSPs

Fix misaligned flash read

Make internal calibration API safer

Fix math error in ADC bias calibration

Improve ADC reading results by powering down and up ADC

Fix fmt and clippy warns

Start fixing pygamer examples

Fix overrun errors on thumbv6 targets

Fix dividers in examples

Fix Samd21g build

First round of ADC Doc fixes

Construct ADC from ADC Builder

D5x - Correct all calibration values

D11 - Correct all calibrations and add temp cals

Cleanup and finalize ADC

Refactor the check_and_clear_flags method

Improve, standardize examples and make them more robust

Mention that async ADC is supported in repo README

Doc and formatting fixes

Fix warning when building without async feature

Supply vref in SAMD11 adc example

Fix Calibration parts_to_f32

Stabilize ADC reading after a setting change

Document TSEN enable

correct calibration floating point values

rework calibration decimal numbers

Document tsen enabling procedure for d11 too

ADC docs

Remove disused parts_to_f32()

Update docstrings for d5x temperature methods

SAMD21 - Expose bandgap voltage and rename vbat to IO

D2x - Optimize ADC temp cals

D21 - Remove raw field on read_cpu_temperature

Make ADC examples use single reads rather than buffer reads

Fix output averages being wrong

Calibration bug fixes

Fine tune sensor reading settings

Remove useless read from ADC when discarding value

D21 - Document how to NOT nuke the VREF register when enabling TSEN

Make reading CPU temperature infaillible

Make cpu sensor/temp reading actually async for the async implementation

Tidy comments in examples

clippy lints, formatting
Issues around the clockv2 transition and the current state of neopixel
drivers makes these examples untenable.  See:
atsamd-rs#752
@jbeaurivage jbeaurivage merged commit e9d120d into atsamd-rs:master Jun 17, 2025
91 of 109 checks passed
jbeaurivage pushed a commit that referenced this pull request Jun 17, 2025
#814

This work was done over about 6 months by:
Ashcon Mohseninia <[email protected]>
and
Justin Beaurivage <[email protected]>
with input from the atsamd-rs community.

Begin constructing an async ADC concept for D5X

Draft up new ADC API

Test implementation of async read for ADC

Resolve rest of the adc read future

Move to type based ADC implementation

Broken read_blocking implementation

Begin implementing async reading ADC

Automatically convert pins to correct mode in with_pins

Remove explicit reference to a chip in favor of PAC

Split async and sync ADC into different implementations

Clean up ADC

Add inlines everywhere

Add wait_flags method and do some extra cleanup

Add blocking and async buffered read methods

Derive debug for Adc Error

Fix ADC read_buffer not ever starting

Fix ADC reading blocking buffer always returning BufferOverrun

Implement more ADCSettings

Add in temperature calibration parameters to calibration

Add in CPU Temperature reading

Add CPU internal sensors reading, and setting VREF for adc

Restrict internal CPU sensors to primary CPU ADC

Remove breaking comments in Cargo.toml

Allow blocking methods on async-enabled ADCs

Derive defmt::Format for error type

Remove duplicate methods

ADC: Add support for samd21 line

Remove curiosity nano reference

Make names more consistent with the rest of the crate

samd21 - Fix ADC results being multiplied by 2x

Error if ADC is configured for summation/averaging but user has selected an incorrect bit width

Improve ADC comments

Resolve clippy warnings

Add ADC Pins for D11

Improve imports and hal_cfg usage for ADC

Leave workspace Cargo.toml unchanged

Make good use of the typelevel guarantees provided by clock::v2

Remove the need for the Channel machinery

D5x - Document TH and TL parameters better

Improve D11/D21 ADC clocking

Cleanup config type aliases

Add doc comment for ADC::new (thumbv6)

Simplify some snippets, add missing doc comment, and add missing sync calls

Minimal working ADC example for feather_m0

Fix some bugs in conversion methods

Slightly improve docs

Revert workspace Cargo.toml

Add D11 ADC calibration

Update T1 BSPs

Fix misaligned flash read

Make internal calibration API safer

Fix math error in ADC bias calibration

Improve ADC reading results by powering down and up ADC

Fix fmt and clippy warns

Start fixing pygamer examples

Fix overrun errors on thumbv6 targets

Fix dividers in examples

Fix Samd21g build

First round of ADC Doc fixes

Construct ADC from ADC Builder

D5x - Correct all calibration values

D11 - Correct all calibrations and add temp cals

Cleanup and finalize ADC

Refactor the check_and_clear_flags method

Improve, standardize examples and make them more robust

Mention that async ADC is supported in repo README

Doc and formatting fixes

Fix warning when building without async feature

Supply vref in SAMD11 adc example

Fix Calibration parts_to_f32

Stabilize ADC reading after a setting change

Document TSEN enable

correct calibration floating point values

rework calibration decimal numbers

Document tsen enabling procedure for d11 too

ADC docs

Remove disused parts_to_f32()

Update docstrings for d5x temperature methods

SAMD21 - Expose bandgap voltage and rename vbat to IO

D2x - Optimize ADC temp cals

D21 - Remove raw field on read_cpu_temperature

Make ADC examples use single reads rather than buffer reads

Fix output averages being wrong

Calibration bug fixes

Fine tune sensor reading settings

Remove useless read from ADC when discarding value

D21 - Document how to NOT nuke the VREF register when enabling TSEN

Make reading CPU temperature infaillible

Make cpu sensor/temp reading actually async for the async implementation

Tidy comments in examples

clippy lints, formatting
jbeaurivage added a commit that referenced this pull request Jun 20, 2025
* atsamd-hal release v0.22.0

* Add missing commits to HAL changelog

* Update wio_terminal changelog to include #874

* Update HAL changelog to include #881

* Update HAL changelog to include #883

* Updade pygamer and HAL changelogs following #814

* Update HAL changelog to include #880

* Update HAL changelog to include #878

* Update HAL changelog to include #875

* Update HAL changelog to include #845

* Bump atsamd-hal-macros (#875)

* Changelog formatting
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.

3 participants