Page 1 of 1

ADC on H7

Posted: Sat Mar 01, 2025 4:31 am
by hybridA
I need all 3 ADCs (ADC1, ADC2 and ADC3) for new project.
Currently, I can really only ADC1 work reliably. Once I switch to dual mode (for ADC2) I'm getting garbage data in my buffer. Switching to dual mode also messes with ADC3 readings.
I'm trying to read a total of 13 channels:
ADC1: 4 channels
ADC2: 2 channels
ADC3: 7 channels

Code: Select all

#if CACHE_LINE_SIZE > 0
CC_ALIGN_DATA(CACHE_LINE_SIZE)
#endif
CC_SECTION(".nocache") adcsample_t adcBuffer[ADC12_GRP_NUM_CHANNELS * ADC_OVERSAMPLE];

// ADC3 uses BDMA, which only has RAM4 access
CC_SECTION(".ram4_clear") adcsample_t adc3Buffer[CACHE_SIZE_ALIGN(adcsample_t, ADC3_GRP_NUM_CHANNELS * ADC_OVERSAMPLE)];

const ADCConfig adccfg = {.difsel = 0U, .calibration = 0U};

static constexpr ADCConversionGroup adc12grpcfg = {
      .circular = false,
      .num_channels = ADC12_GRP_NUM_CHANNELS,
      .end_cb = nullptr,
      .error_cb = nullptr,
      .cfgr = ADC_CFGR_CONT,
      .cfgr2 = 0U,
      .ccr = ADC_CCR_DUAL_REG_INTERL,
      .pcsel = ADC_SELMASK_IN2 | ADC_SELMASK_IN3 | ADC_SELMASK_IN5 | ADC_SELMASK_IN6 | ADC_SELMASK_IN9 | ADC_SELMASK_IN15,
      .ltr1 = 0U,
      .htr1 = 0U,
      .ltr2 = 0U,
      .htr2 = 0U,
      .ltr3 = 0U,
      .htr3 = 0U,
      .awd2cr = 0U,
      .awd3cr = 0U,
      .smpr = {   ADC_SMPR1_SMP_AN3(ADC_SMPR_SMP_32P5) | ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_32P5) |
                ADC_SMPR1_SMP_AN9(ADC_SMPR_SMP_32P5),
                ADC_SMPR2_SMP_AN15(ADC_SMPR_SMP_32P5)},
      .sqr = {    ADC_SQR1_SQ1_N(ADC_CHANNEL_IN3) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN5) |
               ADC_SQR1_SQ3_N(ADC_CHANNEL_IN9) | ADC_SQR1_SQ4_N(ADC_CHANNEL_IN15),
               0U,
               0U,
               0U},
      .ssmpr = {   ADC_SMPR1_SMP_AN2(ADC_SMPR_SMP_32P5) | ADC_SMPR1_SMP_AN6(ADC_SMPR_SMP_32P5),
               0U},
      .ssqr = {   ADC_SQR1_SQ1_N(ADC_CHANNEL_IN2) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN6),
               0U,
               0U,
               0U}
};

static const ADCConversionGroup adc3grpcfg = {
      .circular = false,
      .num_channels = ADC3_GRP_NUM_CHANNELS,
      .end_cb = nullptr,
      .error_cb = nullptr,
      .cfgr = ADC_CFGR_CONT,
      .cfgr2 = 0U,
      .ccr = ADC_CCR_DUAL_INDEPENDENT,
      .pcsel = ADC_SELMASK_IN0 | ADC_SELMASK_IN3 | ADC_SELMASK_IN4 | ADC_SELMASK_IN5 | ADC_SELMASK_IN6 | ADC_SELMASK_IN8 |
                   ADC_SELMASK_IN9,
      .ltr1 = 0U,
      .htr1 = 0U,
      .ltr2 = 0U,
      .htr2 = 0U,
      .ltr3 = 0U,
      .htr3 = 0U,
      .awd2cr = 0U,
      .awd3cr = 0U,
      .smpr = { ADC_SMPR1_SMP_AN0(ADC_SMPR_SMP_810P5) | ADC_SMPR1_SMP_AN3(ADC_SMPR_SMP_810P5) |
              ADC_SMPR1_SMP_AN4(ADC_SMPR_SMP_810P5) | ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_810P5) |
              ADC_SMPR1_SMP_AN6(ADC_SMPR_SMP_810P5) | ADC_SMPR1_SMP_AN8(ADC_SMPR_SMP_810P5) |
              ADC_SMPR1_SMP_AN9(ADC_SMPR_SMP_810P5),
              0U},
      .sqr =   { ADC_SQR1_SQ1_N(ADC_CHANNEL_IN0) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN3) | ADC_SQR1_SQ3_N(ADC_CHANNEL_IN4) |
              ADC_SQR1_SQ4_N(ADC_CHANNEL_IN5),
              ADC_SQR2_SQ5_N(ADC_CHANNEL_IN6) | ADC_SQR2_SQ6_N(ADC_CHANNEL_IN8) | ADC_SQR2_SQ7_N(ADC_CHANNEL_IN9),
              0U,
              0U},
      .ssmpr = {0U, 0U},
      .ssqr =  {0U, 0U, 0U, 0U}
};


mcuconf.h:

Code: Select all

/*
 * ADC driver system settings.
 */
#define STM32_ADC_DUAL_MODE                 TRUE
#define STM32_ADC_SAMPLES_SIZE              32
#define STM32_ADC_USE_ADC12                 TRUE
#define STM32_ADC_ADC12_DMA_STREAM          STM32_DMA_STREAM_ID_ANY
#define STM32_ADC_ADC12_DMA_PRIORITY        2
#define STM32_ADC_ADC12_IRQ_PRIORITY        5
#define STM32_ADC_ADC12_CLOCK_MODE          ADC_CCR_CKMODE_ADCCK
#define STM32_ADC_USE_ADC3                  TRUE
#define STM32_ADC_ADC3_BDMA_STREAM          STM32_BDMA_STREAM_ID_ANY
#define STM32_ADC_ADC3_BDMA_PRIORITY        2
#define STM32_ADC_ADC3_IRQ_PRIORITY         5
#define STM32_ADC_ADC3_CLOCK_MODE           ADC_CCR_CKMODE_ADCCK


Doing this in a loop at around 500hz:

Code: Select all

adcConvert(&ADCD1, &adc12grpcfg, adcBuffer, ADC_OVERSAMPLE);
adcConvert(&ADCD3, &adc3grpcfg, adc3Buffer, ADC_OVERSAMPLE);
cacheBufferInvalidate(adc3Buffer, sizeof (adc3Buffer) / sizeof (adcsample_t));

Re: ADC on H7

Posted: Sat Mar 01, 2025 6:24 am
by Giovanni
Hi,

Are you sure this is not cache-related? try to disable cache globally and see if you get the same problems.

Giovanni

Re: ADC on H7

Posted: Sat Mar 01, 2025 6:55 am
by hybridA
Getting the same results by disabling cache with

Code: Select all

SCB_DisableICache();
SCB_DisableDCache();


The ADC3 readings appear all to be garbage, first 42-entries in the buffer are all number much larger than 12-bit. rest is all zeros.

When I change the sampling depth to 1, I can see that all the ADC1 readings in the ADC1/2 buffer are correct, but all ADC2 values are garbage and much larger than 16-bit.

Re: ADC on H7

Posted: Sun Mar 02, 2025 9:19 pm
by hybridA
Some additional test results:

ADC12
For testing, I changed oversampling to 1, just to make reading the buffer a little easier during debugging.
Goal is to read from these pins:

Code: Select all

ADC12_INP3    PA6
ADC12_INP5    PB1
ADC12_INP9    PB0
ADC12_INP15   PA3
ADC2_INP2     PF13
ADC2_INP6     PF14


ADCConfig:

Code: Select all

static constexpr ADCConversionGroup adc12grpcfg = {
   .circular = false,
   .num_channels = ADC12_GRP_NUM_CHANNELS,
   .end_cb = nullptr,
   .error_cb = nullptr,
   .cfgr = ADC_CFGR_CONT,
   .cfgr2 = 0U,
   .ccr = ADC_CCR_DUAL_REG_INTERL,
   .pcsel = ADC_SELMASK_IN2 | ADC_SELMASK_IN3 | ADC_SELMASK_IN5 | ADC_SELMASK_IN6 | ADC_SELMASK_IN9 | ADC_SELMASK_IN15,
   .ltr1 = 0U,
   .htr1 = 0U,
   .ltr2 = 0U,
   .htr2 = 0U,
   .ltr3 = 0U,
   .htr3 = 0U,
   .awd2cr = 0U,
   .awd3cr = 0U,
   .smpr = {   ADC_SMPR1_SMP_AN3(ADC_SMPR_SMP_32P5) | ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_32P5) |
           ADC_SMPR1_SMP_AN9(ADC_SMPR_SMP_32P5),
           ADC_SMPR2_SMP_AN15(ADC_SMPR_SMP_32P5)},
   .sqr = {    ADC_SQR1_SQ1_N(ADC_CHANNEL_IN3) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN5) |
          ADC_SQR1_SQ3_N(ADC_CHANNEL_IN9) | ADC_SQR1_SQ4_N(ADC_CHANNEL_IN15),
          0U,
          0U,
          0U},
   .ssmpr = {   ADC_SMPR1_SMP_AN2(ADC_SMPR_SMP_32P5) | ADC_SMPR1_SMP_AN6(ADC_SMPR_SMP_32P5),
          0U},
   .ssqr = {   ADC_SQR1_SQ1_N(ADC_CHANNEL_IN2) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN6),
          0U,
          0U,
          0U}
};


Applying test-signal (0-3V3) to pin PF13 which should be adcBuffer[0], but doesn't change the value in array at all.
However, applying voltage to pin PA6 does change adcBuffer[0] and it goes full scale 0-16-bit with 0-3V3 input.
I'm also noticing adcBuffer[3] and adcBuffer[4] at a very high number [3] = 0xc1000000 and [4] = 0x2400121c which never changes. adcBuffer[5] is static at 0.

When turning off dual mode in mcuconf.h
#define STM32_ADC_DUAL_MODE FALSE

and using this ADCConfig:

Code: Select all

static constexpr ADCConversionGroup adc12grpcfg = {
      .circular = false,
      .num_channels = ADC12_GRP_NUM_CHANNELS,
      .end_cb = nullptr,
      .error_cb = nullptr,
      .cfgr = ADC_CFGR_CONT,
      .cfgr2 = 0U,
      .ccr = 0U,
      .pcsel = ADC_SELMASK_IN2 | ADC_SELMASK_IN3 | ADC_SELMASK_IN5 | ADC_SELMASK_IN6 | ADC_SELMASK_IN9 | ADC_SELMASK_IN15,
      .ltr1 = 0U,
      .htr1 = 0U,
      .ltr2 = 0U,
      .htr2 = 0U,
      .ltr3 = 0U,
      .htr3 = 0U,
      .awd2cr = 0U,
      .awd3cr = 0U,
      .smpr = {ADC_SMPR1_SMP_AN2(ADC_SMPR_SMP_32P5) | ADC_SMPR1_SMP_AN3(ADC_SMPR_SMP_32P5) | ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_32P5) |
             ADC_SMPR1_SMP_AN6(ADC_SMPR_SMP_32P5) | ADC_SMPR1_SMP_AN9(ADC_SMPR_SMP_32P5),
             ADC_SMPR2_SMP_AN15(ADC_SMPR_SMP_32P5)},
      .sqr = {ADC_SQR1_SQ1_N(ADC_CHANNEL_IN2) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN3) |
            ADC_SQR1_SQ3_N(ADC_CHANNEL_IN5) | ADC_SQR1_SQ4_N(ADC_CHANNEL_IN6),
            ADC_SQR2_SQ5_N(ADC_CHANNEL_IN9) | ADC_SQR2_SQ6_N(ADC_CHANNEL_IN15),
            0U,
            0U},
};


All ADC1 inputs are read in correctly, even at oversampling of 12.


ADC3
ADC3 works fine, but only if using oversampling of 1, e.g.

Code: Select all

adcConvert(&ADCD3, &adc3grpcfg, adc3Buffer, 1);


Does dual mode need to be explicitly turned off for ADC3?
if enabled via mcuconf.h, all readings on ADC3 are garbage, even without any oversampling.