Skip to content

wip: adc_ll refactor + dmm #96

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

bessman
Copy link
Contributor

@bessman bessman commented Jul 19, 2025

This is a work in progress to refactor the ADC driver and add a basic multimeter instrument.

Summary by Sourcery

Refactor the low-level ADC driver for flexible channel configuration, oversampling, and sample-rate queries, configure the platform to provide a dedicated ADC clock, introduce a new Digital Multimeter (DMM) instrument module with fixed-point support, and update the application to use DMM in place of legacy ADC logic.

New Features:

  • Introduce Digital Multimeter (DMM) module for single-sample voltage measurements using the ADC_LL API
  • Support oversampling and sample rate calculation in the low-level ADC driver
  • Add fixed-point Q16.16 arithmetic library for embedded computations

Enhancements:

  • Refactor ADC_LL driver to use a unified ADC_LL_Config struct, dynamic channel/trigger selection, and improved init/deinit procedures
  • Configure STM32H5 platform clock to provide a dedicated 75 MHz PLL2 clock for ADC and expose ADC clock speeds via PLATFORM_get_peripheral_clock_speed
  • Restructure CMake build to group ADC and instruments under a new instrument subsystem

Tests:

  • Add comprehensive unit tests for the DMM module using Unity and mocked ADC_LL

Copy link

sourcery-ai bot commented Jul 19, 2025

Reviewer's Guide

This PR revamps the ADC low-level driver by introducing dynamic channel and oversampling support, refactors platform clock setup to provide a dedicated 75 MHz ADC clock, replaces direct ADC usage in the application with a new Digital Multimeter (DMM) instrument module, and adds a fixed-point library and comprehensive DMM unit tests along with build script updates.

Class diagram for the new Digital Multimeter (DMM) instrument

classDiagram
    class DMM_Handle {
        +DMM_Config config
        +uint16_t adc_buffer
        +volatile bool conversion_complete
        +bool initialized
    }
    class DMM_Config {
        +DMM_Channel channel
        +uint32_t oversampling_ratio
        +uint32_t timeout_ms
        +Fixed reference_voltage
    }
    class DMM_Channel {
        <<enum>>
        DMM_CHANNEL_0
        DMM_CHANNEL_1
        DMM_CHANNEL_2
        DMM_CHANNEL_3
        DMM_CHANNEL_4
        DMM_CHANNEL_5
        DMM_CHANNEL_6
        DMM_CHANNEL_7
        DMM_CHANNEL_8
        DMM_CHANNEL_9
        DMM_CHANNEL_10
        DMM_CHANNEL_11
        DMM_CHANNEL_12
        DMM_CHANNEL_13
        DMM_CHANNEL_14
        DMM_CHANNEL_15
    }
    class Fixed {
        <<typedef>>
        int32_t (Q16.16 fixed-point)
    }
    DMM_Handle --> DMM_Config
    DMM_Config --> DMM_Channel
    DMM_Config --> Fixed
    DMM_Handle : +DMM_init(config)
    DMM_Handle : +DMM_deinit(handle)
    DMM_Handle : +DMM_read_voltage(handle, voltage_out)
Loading

Class diagram for the refactored ADC_LL driver

classDiagram
    class ADC_LL_Config {
        +ADC_LL_Channel channel
        +ADC_LL_TriggerSource trigger_source
        +uint16_t* output_buffer
        +uint32_t buffer_size
        +uint32_t oversampling_ratio
    }
    class ADC_LL_Channel {
        <<enum>>
        ADC_LL_CHANNEL_0
        ADC_LL_CHANNEL_1
        ADC_LL_CHANNEL_2
        ADC_LL_CHANNEL_3
        ADC_LL_CHANNEL_4
        ADC_LL_CHANNEL_5
        ADC_LL_CHANNEL_6
        ADC_LL_CHANNEL_7
        ADC_LL_CHANNEL_8
        ADC_LL_CHANNEL_9
        ADC_LL_CHANNEL_10
        ADC_LL_CHANNEL_11
        ADC_LL_CHANNEL_12
        ADC_LL_CHANNEL_13
        ADC_LL_CHANNEL_14
        ADC_LL_CHANNEL_15
    }
    class ADC_LL_TriggerSource {
        <<enum>>
        ADC_TRIGGER_TIMER1
        ADC_TRIGGER_TIMER1_TRGO2
        ADC_TRIGGER_TIMER2
        ADC_TRIGGER_TIMER3
        ADC_TRIGGER_TIMER4
        ADC_TRIGGER_TIMER6
        ADC_TRIGGER_TIMER8
        ADC_TRIGGER_TIMER8_TRGO2
        ADC_TRIGGER_TIMER15
    }
    ADC_LL_Config --> ADC_LL_Channel
    ADC_LL_Config --> ADC_LL_TriggerSource
    class ADCInstance {
        +ADC_HandleTypeDef* adc_handle
        +DMA_HandleTypeDef* dma_handle
        +ADC_ChannelConfTypeDef adc_config
        +uint16_t* adc_buffer_data
        +uint32_t adc_buffer_size
        +ADC_LL_CompleteCallback adc_complete_callback
        +ADC_LL_Channel current_channel
        +uint32_t oversampling_ratio
        +bool initialized
    }
    ADCInstance --> ADC_LL_Channel
Loading

Class diagram for the new fixed-point library

classDiagram
    class Fixed {
        <<typedef>>
        int32_t (Q16.16 fixed-point)
    }
    Fixed : +FIXED_FROM_INT(x)
    Fixed : +FIXED_TO_INT(x)
    Fixed : +FIXED_FROM_FLOAT(f)
    Fixed : +FIXED_TO_FLOAT(x)
    Fixed : +fixed_add(a, b)
    Fixed : +fixed_sub(a, b)
    Fixed : +fixed_mul(a, b)
    Fixed : +fixed_div(a, b)
    Fixed : +fixed_mul_int(a, b)
    Fixed : +fixed_div_int(a, b)
    Fixed : +fixed_from_fraction(num, den)
    Fixed : +fixed_from_millivolts(mv)
    Fixed : +fixed_to_millivolts(volts)
    Fixed : +fixed_get_fractional_part(x)
    Fixed : +fixed_get_integer_part(x)
Loading

File-Level Changes

Change Details Files
Generalize and extend ADC_LL driver
  • Replaced flat init API with ADC_LL_Config struct and validated inputs
  • Added ADC_LL_Channel enum, dynamic pin lookup, and per-channel GPIO/DMA setup
  • Implemented HAL channel and trigger mapping functions
  • Added oversampling support with validation and configuration in HAL init
  • Introduced ADC_LL_get_sample_rate() and error callback logging
src/platform/h563xx/adc_ll.c
src/platform/adc_ll.h
Configure dedicated PLL2 ADC clock and update clock API
  • Configured PLL2 to generate a 75 MHz ADC clock and set ADC clocks to PLL2R
  • Updated RCC and peripheral clock setup in system_clock_config
  • Extended PLATFORM_get_peripheral_clock_speed() for ADC1/2 and added new clock enums
src/platform/h563xx/platform.c
src/platform/platform.h
Migrate main application to use DMM instrument
  • Removed direct ADC init/start/stop in main.c
  • Initialized DMM with default config and read voltage instead of manual DMA loops
  • Updated callbacks and logging to use DMM API
src/application/main.c
Add Digital Multimeter (DMM) module
  • Implemented DMM_init, DMM_deinit, and DMM_read_voltage in dmm.c/dmm.h
  • Added config validation, ADC callback, timer integration, and error handling
  • Provided DMM_Channel enum and default configuration
src/system/instrument/dmm.c
src/system/instrument/dmm.h
Introduce fixed-point arithmetic utility
  • Added Q16.16 fixed-point type, conversion macros, and basic math operations
src/util/fixed_point.h
Add DMM unit tests and update build scripts
  • Created comprehensive test_dmm.c covering DMM edge cases and error paths
  • Renamed ADC subsystem CMakeLists to instrument and updated test inclusion
tests/test_dmm.c
src/system/instrument/CMakeLists.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @bessman - I've reviewed your changes - here's some feedback:

  • The repetitive channel-to-pin, channel-to-HAL constant, and trigger mappings could be consolidated into static lookup tables or helper macros to reduce boilerplate and simplify future updates.
  • ADC_LL_get_sample_rate currently does multiple runtime lookups—consider computing and caching the sample rate once during init to avoid the overhead of rebuilding lookup tables on each call.
  • The DMM read_raw function uses a busy-wait loop for conversion completion, which ties up the CPU—consider leveraging interrupt-driven synchronization or a hardware timer/semaphore instead of active polling.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The repetitive channel-to-pin, channel-to-HAL constant, and trigger mappings could be consolidated into static lookup tables or helper macros to reduce boilerplate and simplify future updates.
- ADC_LL_get_sample_rate currently does multiple runtime lookups—consider computing and caching the sample rate once during init to avoid the overhead of rebuilding lookup tables on each call.
- The DMM read_raw function uses a busy-wait loop for conversion completion, which ties up the CPU—consider leveraging interrupt-driven synchronization or a hardware timer/semaphore instead of active polling.

## Individual Comments

### Comment 1
<location> `src/platform/h563xx/adc_ll.c:312` </location>
<code_context>
     }

+    // Validate oversampling ratio
+    if (config->oversampling_ratio != 1 && config->oversampling_ratio != 2 &&
+        config->oversampling_ratio != 4 && config->oversampling_ratio != 8 &&
+        config->oversampling_ratio != 16 && config->oversampling_ratio != 32 &&
+        config->oversampling_ratio != 64 && config->oversampling_ratio != 128 &&
+        config->oversampling_ratio != 256) {
+        THROW(ERROR_INVALID_ARGUMENT);
+        return;
</code_context>

<issue_to_address>
Oversampling ratio validation is unnecessarily verbose.

Consider replacing the explicit checks with a single condition that verifies the value is a power of two between 1 and 256. This will improve maintainability and reduce potential errors when supporting more ratios.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    // Validate oversampling ratio
    if (config->oversampling_ratio != 1 && config->oversampling_ratio != 2 &&
        config->oversampling_ratio != 4 && config->oversampling_ratio != 8 &&
        config->oversampling_ratio != 16 && config->oversampling_ratio != 32 &&
        config->oversampling_ratio != 64 && config->oversampling_ratio != 128 &&
        config->oversampling_ratio != 256) {
        THROW(ERROR_INVALID_ARGUMENT);
        return;
=======
    // Validate oversampling ratio: must be a power of two between 1 and 256
    if (config->oversampling_ratio == 0 ||
        config->oversampling_ratio > 256 ||
        (config->oversampling_ratio & (config->oversampling_ratio - 1)) != 0) {
        THROW(ERROR_INVALID_ARGUMENT);
        return;
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 311 to 318
// Validate oversampling ratio
if (config->oversampling_ratio != 1 && config->oversampling_ratio != 2 &&
config->oversampling_ratio != 4 && config->oversampling_ratio != 8 &&
config->oversampling_ratio != 16 && config->oversampling_ratio != 32 &&
config->oversampling_ratio != 64 && config->oversampling_ratio != 128 &&
config->oversampling_ratio != 256) {
THROW(ERROR_INVALID_ARGUMENT);
return;
Copy link

Choose a reason for hiding this comment

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

suggestion: Oversampling ratio validation is unnecessarily verbose.

Consider replacing the explicit checks with a single condition that verifies the value is a power of two between 1 and 256. This will improve maintainability and reduce potential errors when supporting more ratios.

Suggested change
// Validate oversampling ratio
if (config->oversampling_ratio != 1 && config->oversampling_ratio != 2 &&
config->oversampling_ratio != 4 && config->oversampling_ratio != 8 &&
config->oversampling_ratio != 16 && config->oversampling_ratio != 32 &&
config->oversampling_ratio != 64 && config->oversampling_ratio != 128 &&
config->oversampling_ratio != 256) {
THROW(ERROR_INVALID_ARGUMENT);
return;
// Validate oversampling ratio: must be a power of two between 1 and 256
if (config->oversampling_ratio == 0 ||
config->oversampling_ratio > 256 ||
(config->oversampling_ratio & (config->oversampling_ratio - 1)) != 0) {
THROW(ERROR_INVALID_ARGUMENT);
return;

@bessman bessman marked this pull request as draft July 19, 2025 22:35
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.

1 participant