Skip to content

Commit

Permalink
stm32h7_adc: Dynamically set clock prescaler and BOOST setting.
Browse files Browse the repository at this point in the history
    The ADC peripheral can only support up to
    50MHz on rev V silicon and 36MHz on Y silicon.
    The existing driver always used no prescaler
    and kept boost setting at 0.
  • Loading branch information
antmerlino authored and davids5 committed Dec 5, 2023
1 parent c97876a commit e056a58
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 13 deletions.
24 changes: 12 additions & 12 deletions arch/arm/src/stm32h7/hardware/stm32_adc.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,18 +243,18 @@
#define ADC_CR_ADSTP (1 << 4) /* Bit 4: ADC stop of regular conversion command */
#define ADC_CR_JADSTP (1 << 5) /* Bit 5: ADC stop of injected conversion command */
/* Bits 6-7: Reserved */
#if defined(ADC_DEVICE_VERSION_V)
# define ADC_CR_BOOST_SHIFT (8) /* Bits 8-9: ADC Boost mode control */
# define ADC_CR_BOOST_MASK (3 << ADC_CR_BOOST_SHIFT)
# define ADC_CR_BOOST_6p25_MHZ (0 << ADC_CR_BOOST_SHIFT)
# define ADC_CR_BOOST_12p5_MHZ (1 << ADC_CR_BOOST_SHIFT)
# define ADC_CR_BOOST_25_MHZ (2 << ADC_CR_BOOST_SHIFT)
# define ADC_CR_BOOST_50_MHZ (3 << ADC_CR_BOOST_SHIFT)
/* Bits 10-15: Reserved */
#else
# define ADC_CR_BOOST (1 << 8) /* Bit 8: ADC Boost mode control */
/* Bits 9-15: Reserved */
#endif

/* On rev V silicon, the BOOST setting is 2 bits. On Y silicon it's a single bit */

#define ADC_CR_BOOST_SHIFT (8) /* Bits 8-9: ADC Boost mode control */
#define ADC_CR_BOOST_MASK (3 << ADC_CR_BOOST_SHIFT)
# define ADC_CR_BOOST_6p25_MHZ (0 << ADC_CR_BOOST_SHIFT)
# define ADC_CR_BOOST_12p5_MHZ (1 << ADC_CR_BOOST_SHIFT)
# define ADC_CR_BOOST_25_MHZ (2 << ADC_CR_BOOST_SHIFT)
# define ADC_CR_BOOST_50_MHZ (3 << ADC_CR_BOOST_SHIFT)
#define ADC_CR_BOOST (1 << 8) /* Bit 8: ADC Boost mode control */
/* Bits 10-15: Reserved */

#define ADC_CR_ADCALLIN (1 << 16) /* Bit 16: ADC Linearity calibration */
/* Bits 17-21: Reserved */
#define ADC_CR_LINCALRDYW1 (1 << 22) /* Bit 22: ADC Linearity calibration ready Word 1 */
Expand Down
148 changes: 147 additions & 1 deletion arch/arm/src/stm32h7/stm32_adc.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include "stm32_tim.h"
#include "stm32_dma.h"
#include "stm32_adc.h"
#include "stm32_dbgmcu.h"

/* ADC "upper half" support must be enabled */

Expand Down Expand Up @@ -228,6 +229,7 @@ static void tim_dumpregs(struct stm32_dev_s *priv,
/* ADC Miscellaneous Helpers */

static void adc_rccreset(struct stm32_dev_s *priv, bool reset);
static void adc_setupclock(struct stm32_dev_s *priv);
static void adc_enable(struct stm32_dev_s *priv);
static uint32_t adc_sqrbits(struct stm32_dev_s *priv, int first,
int last, int offset);
Expand Down Expand Up @@ -277,6 +279,7 @@ static void adc_shutdown(struct adc_dev_s *dev);
static void adc_rxint(struct adc_dev_s *dev, bool enable);
static int adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg);


/****************************************************************************
* Private Data
****************************************************************************/
Expand Down Expand Up @@ -1413,12 +1416,14 @@ static int adc_setup(struct adc_dev_s *dev)

clrbits = ADC_CCR_PRESC_MASK | ADC_CCR_VREFEN |
ADC_CCR_VSENSEEN | ADC_CCR_VBATEN;
setbits = ADC_CCR_PRESC_NOT_DIV | ADC_CCR_CKMODE_ASYCH;
setbits = ADC_CCR_CKMODE_ASYCH;

adc_internal(priv, &setbits);

adc_modifyregm(priv, STM32_ADC_CCR_OFFSET, clrbits, setbits);

adc_setupclock(priv);

#ifdef ADC_HAVE_DMA

/* Enable DMA */
Expand Down Expand Up @@ -1571,6 +1576,147 @@ static void adc_rxint(struct adc_dev_s *dev, bool enable)
adc_putreg(priv, STM32_ADC_IER_OFFSET, regval);
}

/****************************************************************************
* Name: adc_setupclock
****************************************************************************/

static void adc_setupclock(struct stm32_dev_s *priv)
{
uint32_t max_clock = 36000000;
uint32_t src_clock;
uint32_t adc_clock;
uint32_t setbits;

/* The maximum clock is different for rev Y devices and rev V devices.
* rev V can support an ADC clock of up to 50MHz. rev Y only supports
* up to 36MHz.
*/

if ((getreg32(STM32_DEBUGMCU_BASE) & DBGMCU_IDCODE_REVID_MASK) ==
STM32_IDCODE_REVID_V)
{
/* The max fadc is 50MHz, but there is an always-present /2 divider after
* the configurable prescaler. Therefore, the max clock out of the
* prescaler block is 2*50=100MHz
*/

max_clock = 100000000;
}

#if STM32_RCC_D3CCIPR_ADCSRC == RCC_D3CCIPR_ADCSEL_PLL2
src_clock = STM32_PLL2P_FREQUENCY;
#elif STM32_RCC_D3CCIPR_ADCSRC == RCC_D3CCIPR_ADCSEL_PLL3
src_clock = STM32_PLL3R_FREQUENCY;
#elif STM32_RCC_D3CCIPR_ADCSRC == RCC_D3CCIPR_ADCSEL_PER
# error ADCSEL_PER not supported
#else
src_clock = STM32_PLL2P_FREQUENCY;
#endif

if (src_clock <= max_clock)
{
setbits = ADC_CCR_PRESC_NOT_DIV;
adc_clock = src_clock;
}
else if (src_clock/2 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV2;
adc_clock = src_clock/2;
}
else if (src_clock/4 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV4;
adc_clock = src_clock/4;
}
else if (src_clock/6 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV6;
adc_clock = src_clock/6;
}
else if (src_clock/8 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV8;
adc_clock = src_clock/8;
}
else if (src_clock/10 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV10;
adc_clock = src_clock/10;
}
else if (src_clock/12 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV12;
adc_clock = src_clock/12;
}
else if (src_clock/16 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV16;
adc_clock = src_clock/16;
}
else if (src_clock/32 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV32;
adc_clock = src_clock/32;
}
else if (src_clock/64 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV64;
adc_clock = src_clock/64;
}
else if (src_clock/128 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV128;
adc_clock = src_clock/128;
}
else if (src_clock/256 <= max_clock)
{
setbits = ADC_CCR_PRESC_DIV256;
adc_clock = src_clock/256;
}
else
{
aerr("ERROR: source clock too high\n");
}

/* Write the prescaler to the CCR register */

adc_modifyregm(priv, STM32_ADC_CCR_OFFSET, ADC_CCR_PRESC_MASK, setbits);

if ((getreg32(STM32_DEBUGMCU_BASE) & DBGMCU_IDCODE_REVID_MASK) ==
STM32_IDCODE_REVID_V)
{
if (adc_clock >= 25000000)
{
setbits = ADC_CR_BOOST_50_MHZ;
}
else if (adc_clock >= 12500000)
{
setbits = ADC_CR_BOOST_25_MHZ;
}
else if (adc_clock >= 6250000)
{
setbits = ADC_CR_BOOST_12p5_MHZ;
}
else
{
setbits = ADC_CR_BOOST_6p25_MHZ;
}
}
else
{
if (adc_clock >= 20000000)
{
setbits = ADC_CR_BOOST;
}
else
{
setbits = 0;
}
}

adc_modifyregm(priv, STM32_ADC_CR_OFFSET, ADC_CR_BOOST_MASK, setbits);
}

/****************************************************************************
* Name: adc_sqrbits
****************************************************************************/
Expand Down

0 comments on commit e056a58

Please sign in to comment.