stm32g0: add adc.

v2 "single" peripheral with a couple of tweaks :
 - added registers to configure two additionnal advanced analog watchdog.
 - different adc sampling time time based on channel groups.
 - 8 steps adc sequence injection, using chselr/chselrmode.

And a note on the rm explaining that after every configuration change to ADC_CFGR1's
SCANDIR or CHSELRMOD or CHSELR register, user need to check that configuration
is applied before any other modification / adc conversion start.. making adc_set_reqular
a bit painfull to read..
This commit is contained in:
Guillaume Revaillot 2019-01-21 15:36:49 +01:00 committed by Karl Palsson
parent a34da53c30
commit 38b45c8786
4 changed files with 534 additions and 1 deletions

View File

@ -36,6 +36,8 @@
# include <libopencm3/stm32/l1/adc.h>
#elif defined(STM32L4)
# include <libopencm3/stm32/l4/adc.h>
#elif defined(STM32G0)
# include <libopencm3/stm32/g0/adc.h>
#else
# error "stm32 family not defined."
#endif

View File

@ -0,0 +1,325 @@
/** @defgroup adc_defines ADC Defines
*
* @ingroup STM32G0xx_defines
*
* @author @htmlonly &copy; @endhtmlonly 2019 Guillaume Revaillot <g.revaillot@gmail.com>
*
* @brief <b>Defined Constants and Types for the STM32STM32G0xx Analog to Digital Converter</b>
*
* @version 1.0.0
*
* LGPL License Terms @ref lgpl_license
* */
/*
* This file is part of the libopencm3 project.
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#ifndef LIBOPENCM3_ADC_H
#define LIBOPENCM3_ADC_H
#include <libopencm3/stm32/common/adc_common_v2.h>
#include <libopencm3/stm32/common/adc_common_v2_single.h>
/** @defgroup adc_reg_base ADC register base addresses
*@{*/
#define ADC1 ADC1_BASE
/**@}*/
/** @defgroup adc_channel ADC Channel Numbers
*@{*/
#define ADC_CHANNEL_TEMP 12
#define ADC_CHANNEL_VREF 13
#define ADC_CHANNEL_VBAT 14
/**@}*/
/* ----- ADC registers -----------------------------------------------------*/
/** ADC_AWD1TR Watchdog 1 Threshold register */
#define ADC_AWD1TR(adc) MMIO32((adc) + 0x20)
/** ADC_AWD2TR Watchdog 2 Threshold register */
#define ADC_AWD2TR(adc) MMIO32((adc) + 0x22)
/** ADC_AWD3TR Watchdog 3 Threshold register */
#define ADC_AWD3TR(adc) MMIO32((adc) + 0x2c)
/** ADC_AWD2CR Watchdog 2 Configuration register */
#define ADC_AWD2CR(adc) MMIO32((adc) + 0xA0)
/** ADC_AWD3CR Watchdog 3 Configuration register */
#define ADC_AWD3CR(adc) MMIO32((adc) + 0xA4)
/** ADC_CALFACT Calibration factor register */
#define ADC_CALFACT(adc) MMIO32((adc) + 0xB4)
/** ADC_OR Option register */
#define ADC_OR(adc) MMIO32((adc) + 0xD0)
/* --- Register values -------------------------------------------------------*/
/** @addtogroup adc_isr
@{*/
/** CCRDY: Channel Configuration Ready flag */
#define ADC_ISR_CCRDY (1 << 13)
/**@}*/
/** @addtogroup adc_ier
@{*/
/** CCRDYIE: Channel Configuration Ready Interrupt enable bit */
#define ADC_IER_CCRDYIE (1 << 13)
/**@}*/
/** @addtogroup adc_ccr
@{*/
#define ADC_CCR_PRESC_MASK (0xf)
#define ADC_CCR_PRESC_SHIFT (18)
/** @defgroup adc_ccr_presc ADC clock prescaler
*@{*/
#define ADC_CCR_PRESC_NODIV (0x0)
#define ADC_CCR_PRESC_DIV1 (0x1)
#define ADC_CCR_PRESC_DIV2 (0x2)
#define ADC_CCR_PRESC_DIV6 (0x3)
#define ADC_CCR_PRESC_DIV8 (0x4)
#define ADC_CCR_PRESC_DIV10 (0x5)
#define ADC_CCR_PRESC_DIV12 (0x6)
#define ADC_CCR_PRESC_DIV16 (0x7)
#define ADC_CCR_PRESC_DIV32 (0x8)
#define ADC_CCR_PRESC_DIV64 (0x9)
#define ADC_CCR_PRESC_DIV128 (0x10)
#define ADC_CCR_PRESC_DIV256 (0x11)
/**@}*/
/**@}*/
/** @addtogroup adc_cr
@{*/
/** ADVREGEN: Voltage regulator enable bit */
#define ADC_CR_ADVREGEN (1 << 28)
/**@}*/
/** @addtogroup adc_cfgr1
@{*/
/** CHSELRMOD: Mode Selection of the ADC_CHSELR register */
#define ADC_CFGR1_CHSELRMOD (1 << 21)
/**@}*/
/** @addtogroup adc_cfgr2
@{*/
#define ADC_CFGR2_CKMODE_SHIFT (30)
#define ADC_CFGR2_CKMODE_MASK (0x3)
/** @defgroup adc_cfgr2_ckmode ADC Clock mode
*@{*/
#define ADC_CFGR2_CKMODE_ADCCLK (0x0)
#define ADC_CFGR2_CKMODE_PCLK_DIV2 (0x1)
#define ADC_CFGR2_CKMODE_PCLK_DIV4 (0x2)
#define ADC_CFGR2_CKMODE_PCLK (0x3)
/**@}*/
/** LFTRIG: Low Frequency Trigger Mode enable bit */
#define ADC_CFGR2_LFTRIG (1 << 29)
/** TOVS: Triggered Oversampling */
#define ADC_CFGR2_TOVS (1 << 9)
#define ADC_CFGR2_OVSS_SHIFT (5)
#define ADC_CFGR2_OVSS_MASK (0xf)
/** @defgroup adc_cfgr2_ovss ADC Oversampling shift
*@{*/
#define ADC_CFGR2_OVSS_BITS(bits) (bits)
/**@}*/
#define ADC_CFGR2_OVSR_SHIFT (2)
#define ADC_CFGR2_OVSR_MASK (0x7)
/** @defgroup adc_cfgr2_ovsr ADC Oversampling ratio
*@{*/
#define ADC_CFGR2_OVSR_2x (0x0)
#define ADC_CFGR2_OVSR_4x (0x1)
#define ADC_CFGR2_OVSR_8x (0x2)
#define ADC_CFGR2_OVSR_16x (0x3)
#define ADC_CFGR2_OVSR_32x (0x4)
#define ADC_CFGR2_OVSR_64x (0x5)
#define ADC_CFGR2_OVSR_128x (0x6)
#define ADC_CFGR2_OVSR_256x (0x7)
/**@}*/
/** OVSE: Oversampler mode enable bit */
#define ADC_CFGR2_OVSE (1 << 0)
/**@}*/
/** @addtogroup adc_smpr
@{*/
/* SMP1 ADC Channel Sample Time selection */
#define ADC_SMPR_SMPSEL_SHIFT 0x8
#define ADC_SMPR_SMPSEL_MASK 0x7ffff
#define ADC_SMPR_SMPSEL_CHANNEL_SHIFT(channel) ((channel) + ADC_SMPR_SMPSEL_SHIFT)
#define ADC_SMPR_SMPSEL_CHANNEL_MASK (1)
/** @defgroup adc_smpr_smpsel ADC Sample Time selection
@{*/
#define ADC_SMPR_SMPSEL_SMP1 0x0
#define ADC_SMPR_SMPSEL_SMP2 0x1
/**@}*/
/** SMP1 ADC Sample Time #1 selection */
#define ADC_SMPR_SMP1_SHIFT 0x0
#define ADC_SMPR_SMP1_MASK 0x7
/** SMP1 ADC Sample Time #2 selection */
#define ADC_SMPR_SMP2_SHIFT 0x4
#define ADC_SMPR_SMP2_MASK 0x7
/** @defgroup adc_smpr_smp ADC Sample Time selection values
@{*/
#define ADC_SMPR_SMPx_001DOT5CYC 0x0
#define ADC_SMPR_SMPx_003DOT5CYC 0x1
#define ADC_SMPR_SMPx_007DOT5CYC 0x2
#define ADC_SMPR_SMPx_012DOT5CYC 0x3
#define ADC_SMPR_SMPx_019DOT5CYC 0x4
#define ADC_SMPR_SMPx_039DOT5CYC 0x5
#define ADC_SMPR_SMPx_079DOT5CYC 0x6
#define ADC_SMPR_SMPx_160DOT5CYC 0x7
/**@}*/
/**@}*/
/** @defgroup adc_awdtr1 AWDTR1 ADC watchdog threshold register 1
* Shadows adc adc_tr1 register on other chips.
@{*/
#define ADC_AWDTR1_LT_SHIFT 0
#define ADC_AWDTR1_LT (0xFFF << ADC_TR1_LT_SHIFT)
#define ADC_AWDTR1_LT_VAL(x) ((x) << ADC_TR1_LT_SHIFT)
#define ADC_AWDTR1_HT_SHIFT 16
#define ADC_AWDTR1_HT (0xFFF << ADC_TR1_HT_SHIFT)
#define ADC_AWDTR1_HT_VAL(x) ((x) << ADC_TR1_HT_SHIFT)
/**@}*/
/** @defgroup adc_awdtr2 AWDTR2 ADC watchdog threshold register 2
@{*/
#define ADC_AWDTR2_LT_SHIFT 0
#define ADC_AWDTR2_LT (0xFFF << ADC_TR2_LT_SHIFT)
#define ADC_AWDTR2_LT_VAL(x) ((x) << ADC_TR2_LT_SHIFT)
#define ADC_AWDTR2_HT_SHIFT 16
#define ADC_AWDTR2_HT (0xFFF << ADC_TR2_HT_SHIFT)
#define ADC_AWDTR2_HT_VAL(x) ((x) << ADC_TR2_HT_SHIFT)
/**@}*/
/** @addtogroup adc_chselr CHSELR ADC Channel Selection register
@{*/
/** ADC_CHSELR_MAX_CHANNELS Maximum number of channel in regular sequence */
#define ADC_CHSELR_MAX_CHANNELS 18
/** ADC_CHSELR_MAX_SQ_CHANNELS Maximum number of sequences in fully configurable mode */
#define ADC_CHSELR_MAX_SQS 8
/** ADC_CHSELR_SQS_MAX_CHANNEL Maximum channel number in a fully configuralbe sequence */
#define ADC_CHSELR_SQS_MAX_CHANNEL 14
#define ADC_CHSELR_SQx_MASK 0xf
#define ADC_CHSELR_SQx_SHIFT(seqnum) (4 * ((seqnum)-1))
/** ADC_CHSELR_SQx Xth conversion of ADC sequence channel number value */
#define ADC_CHSELR_SQx(seqnum, value) ((value) << ADC_CHSELR_SQx_SHIFT(seqnum))
/** ADC_CHSELR_SQx_EOS End of Sequence */
#define ADC_CHSELR_SQx_EOS 0xf
/**@}*/
/** @defgroup adc_awdtr2 AWDTR2 ADC watchdog threshold register 2
@{*/
#define ADC_AWDTR3_LT_SHIFT 0
#define ADC_AWDTR3_LT (0xFFF << ADC_TR3_LT_SHIFT)
#define ADC_AWDTR3_LT_VAL(x) ((x) << ADC_TR3_LT_SHIFT)
#define ADC_AWDTR3_HT_SHIFT 16
#define ADC_AWDTR3_HT (0xFFF << ADC_TR3_HT_SHIFT)
#define ADC_AWDTR3_HT_VAL(x) ((x) << ADC_TR3_HT_SHIFT)
/**@}*/
/** @defgroup adc_awd2cr AWD2CR ADC Analog watchdog 2 configuration register
@{*/
/** AWD2CR Analog watchdog channel selection */
#define ADC_AW2CR_AWD2CHx_EN(x) (1 << x)
/**@}*/
/** @defgroup adc_awd3cr AWD3CR ADC Analog watchdog 3 configuration register
@{*/
/** AWD3CR Analog watchdog channel selection */
#define ADC_AW3CR_AWD3CHx_EN(x) (1 << x)
/**@}*/
/* --- API definition ----------------------------------------------------- */
/** @defgroup adc_api_clksource ADC clock source
*@{*/
#define ADC_CLKSOURCE_ADC ADC_CFGR2_CKMODE_ADCCLK
#define ADC_CLKSOURCE_PCLK ADC_CFGR2_CKMODE_PCLK
#define ADC_CLKSOURCE_PCLK_DIV2 ADC_CFGR2_CKMODE_PCLK_DIV2
#define ADC_CLKSOURCE_PCLK_DIV4 ADC_CFGR2_CKMODE_PCLK_DIV4
/**@}*/
/** @defgroup adc_api_smptime ADC Sampling Time
*@{*/
#define ADC_SMPTIME_001DOT5 ADC_SMPR_SMPx_001DOT5CYC
#define ADC_SMPTIME_003DOT5 ADC_SMPR_SMPx_003DOT5CYC
#define ADC_SMPTIME_007DOT5 ADC_SMPR_SMPx_007DOT5CYC
#define ADC_SMPTIME_012DOT5 ADC_SMPR_SMPx_012DOT5CYC
#define ADC_SMPTIME_019DOT5 ADC_SMPR_SMPx_019DOT5CYC
#define ADC_SMPTIME_039DOT5 ADC_SMPR_SMPx_039DOT5CYC
#define ADC_SMPTIME_079DOT5 ADC_SMPR_SMPx_079DOT5CYC
#define ADC_SMPTIME_160DOT5 ADC_SMPR_SMPx_160DOT5CYC
/**@}*/
/* --- Function prototypes ------------------------------------------------- */
BEGIN_DECLS
void adc_set_clk_source(uint32_t adc, uint32_t source);
void adc_set_clk_prescale(uint32_t adc, uint32_t prescale);
void adc_set_channel_sample_time_selection(uint32_t adc, uint8_t channel, uint8_t selection);
void adc_set_selection_sample_time(uint32_t adc, uint8_t selection, uint8_t time);
void adc_enable_regulator(uint32_t adc);
void adc_disable_regulator(uint32_t adc);
END_DECLS
/**@}*/
#endif

View File

@ -33,7 +33,7 @@ TGT_CFLAGS += $(DEBUG_FLAGS)
TGT_CFLAGS += $(STANDARD_FLAGS)
ARFLAGS = rcs
OBJS += adc.o adc_common_v2.o
OBJS += crc_common_all.o
OBJS += dma_common_l1f013.o
OBJS += dmamux.o

206
lib/stm32/g0/adc.c Normal file
View File

@ -0,0 +1,206 @@
/** @addtogroup adc_file ADC peripheral API
* @ingroup peripheral_apis
*
* @author @htmlonly &copy; @endhtmlonly 2019 Guillaume Revaillot <g.revaillot@gmail.com>
*
* @date 10 January 2019
*
* LGPL License Terms @ref lgpl_license
*/
/*
* This file is part of the libopencm3 project.
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#include <libopencm3/stm32/adc.h>
#include <libopencm3/cm3/assert.h>
/** @brief ADC Set Clock Source
*
* @param[in] adc ADC base address (@ref adc_reg_base)
* @param[in] source Source (@ref adc_cfgr2_ckmode)
*/
void adc_set_clk_source(uint32_t adc, uint32_t source)
{
uint32_t reg32 = ADC_CFGR2(adc);
reg32 &= ~(ADC_CFGR2_CKMODE_MASK << ADC_CFGR2_CKMODE_SHIFT);
ADC_CFGR2(adc) = (reg32 | (source << ADC_CFGR2_CKMODE_SHIFT));
}
/** @brief ADC Set Clock Prescale
*
* @param[in] adc ADC base address (@ref adc_reg_base)
* @param[in] prescale Prescale value for ADC Async Clock (@ref adc_ccr_presc)
*/
void adc_set_clk_prescale(uint32_t adc, uint32_t prescale)
{
uint32_t reg32 = ADC_CCR(adc);
reg32 &= ~(ADC_CCR_PRESC_MASK << ADC_CCR_PRESC_SHIFT);
ADC_CCR(adc) = (reg32 | (prescale << ADC_CCR_PRESC_SHIFT));
}
/** @brief ADC Set the Sample Time for All Channels
*
* Setup all ADC channels to use a single ADC sampling time.
*
* @param[in] adc ADC base address (@ref adc_reg_base)
* @param[in] time ADC Sampling Time (@ref adc_api_smptime)
*/
void adc_set_sample_time_on_all_channels(uint32_t adc, uint8_t time)
{
uint32_t reg32;
reg32 = ADC_SMPR1(adc);
/* set all channels on ADC_SMPR_SMPSEL_SMP1 first @ref adc_smpr_smpsel sample time selection, and clear its value */
reg32 &= ~((ADC_SMPR_SMPSEL_MASK << ADC_SMPR_SMP1_SHIFT) | (ADC_SMPR_SMP1_MASK << ADC_SMPR_SMP1_SHIFT));
/* setup ADC_SMPR_SMPSEL_SMP1 sample time */
reg32 |= (time << ADC_SMPR_SMP1_SHIFT);
ADC_SMPR1(adc) = reg32;
}
/** @brief ADC Set the Sample Time Selection for a Single Channel
*
* @param[in] adc ADC base address (@ref adc_reg_base)
* @param[in] channel ADC Channel (0..18 or @ref adc_channel)
* @param[in] selection Sampling time selection (@ref adc_smpr_smpsel)
*/
void adc_set_channel_sample_time_selection(uint32_t adc, uint8_t channel, uint8_t selection)
{
uint32_t reg32;
reg32 = ADC_SMPR1(adc);
reg32 &= ~(ADC_SMPR_SMPSEL_CHANNEL_MASK << ADC_SMPR_SMPSEL_CHANNEL_SHIFT(channel));
reg32 |= (selection << ADC_SMPR_SMPSEL_CHANNEL_SHIFT(channel));
ADC_SMPR1(adc) = reg32;
}
/** @brief ADC Set the Sample Time for Given Selection.
*
* @param[in] adc ADC base address (@ref adc_reg_base)
* @param[in] selection Sampling Time Selection (@ref adc_smpr_smpsel)
* @param[in] time Sampling Time (@ref adc_smpr_smp)
*/
void adc_set_selection_sample_time(uint32_t adc, uint8_t selection, uint8_t time)
{
uint32_t reg32;
reg32 = ADC_SMPR1(adc);
switch (selection) {
case ADC_SMPR_SMPSEL_SMP1:
reg32 &= ~(ADC_SMPR_SMP1_MASK << ADC_SMPR_SMP1_SHIFT);
reg32 |= (time << ADC_SMPR_SMP1_SHIFT);
break;
case ADC_SMPR_SMPSEL_SMP2:
reg32 &= ~(ADC_SMPR_SMP2_MASK << ADC_SMPR_SMP2_SHIFT);
reg32 |= (time << ADC_SMPR_SMP2_SHIFT);
break;
}
ADC_SMPR1(adc) = reg32;
}
/** @brief ADC Set a Regular Channel Conversion Sequence
*
* Define a simple sequence of channels to be converted.
* ADCSTART must be de-asserted before sequence setup.
*
* @param[in] adc ADC base address (@ref adc_reg_base)
* @param[in] length Number of channels in the group, range 0..18
* @param[in] channel Set of channels in sequence (0..18 or @ref adc_channel)
*/
void adc_set_regular_sequence(uint32_t adc, uint8_t length, uint8_t channel[])
{
uint32_t reg32 = 0;
bool stepup = false, stepdn = false;
if (length > ADC_CHSELR_MAX_CHANNELS) {
return;
}
if (length == 0) {
ADC_CHSELR(adc) = 0;
return;
}
reg32 |= (1 << channel[0]);
for (uint8_t i = 1; i < length; i++) {
reg32 |= ADC_CHSELR_CHSEL(channel[i]);
stepup |= channel[i-1] < channel[i];
stepdn |= channel[i-1] > channel[i];
}
/* Check if the channel list is in order */
if (stepup && stepdn) {
cm3_assert_not_reached();
}
/* Each modification to ADC_CFGR1's SCANDIR or CHSELRMOD bits or
ADC_CHSELR register must be done after previous configuration change
being properly applied: We have to clear ccrdy bit before and poll for
it being assert back after, before going on. We also need to wait for
configuration applied before starting conversion, or start will be
ignored. */
/* Setup scandir, if needed, waiting for configuration be applied.. */
if (stepdn && (!(ADC_CFGR1(adc) & ADC_CFGR1_SCANDIR))) {
ADC_ISR(adc) &= ~ADC_ISR_CCRDY;
ADC_CFGR1(adc) |= ADC_CFGR1_SCANDIR;
while (!(ADC_ISR(adc) & ADC_ISR_CCRDY));
} else if (stepup && ((ADC_CFGR1(adc) & ADC_CFGR1_SCANDIR))) {
ADC_ISR(adc) &= ~ADC_ISR_CCRDY;
ADC_CFGR1(adc) &= ~ADC_CFGR1_SCANDIR;
while (!(ADC_ISR(adc) & ADC_ISR_CCRDY));
}
/* Setup ADC in simple, not configurable, mode, if needed. */
if ((ADC_CFGR1(adc) & ADC_CFGR1_CHSELRMOD)) {
ADC_ISR(adc) &= ~ADC_ISR_CCRDY;
ADC_CFGR1(adc) &= ~ADC_CFGR1_CHSELRMOD;
while (!(ADC_ISR(adc) & ADC_ISR_CCRDY));
}
if (ADC_CHSELR(adc) != reg32) {
ADC_ISR(adc) &= ~ADC_ISR_CCRDY;
ADC_CHSELR(adc) = reg32;
while (!(ADC_ISR(adc) & ADC_ISR_CCRDY));
}
}
/**
* @brief Enable the ADC Voltage regulator
*
* @param[in] adc ADC base address (@ref adc_reg_base)
*/
void adc_enable_regulator(uint32_t adc)
{
ADC_CR(adc) |= ADC_CR_ADVREGEN;
}
/**
* @brief Disable the ADC Voltage regulator
*
* @param[in] adc ADC base address (@ref adc_reg_base)
*/
void adc_disable_regulator(uint32_t adc)
{
ADC_CR(adc) &= ~ADC_CR_ADVREGEN;
}
/**@}*/