From 38b45c87861e5461283609f3aa2df31b141faae6 Mon Sep 17 00:00:00 2001 From: Guillaume Revaillot Date: Mon, 21 Jan 2019 15:36:49 +0100 Subject: [PATCH] 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.. --- include/libopencm3/stm32/adc.h | 2 + include/libopencm3/stm32/g0/adc.h | 325 ++++++++++++++++++++++++++++++ lib/stm32/g0/Makefile | 2 +- lib/stm32/g0/adc.c | 206 +++++++++++++++++++ 4 files changed, 534 insertions(+), 1 deletion(-) create mode 100644 include/libopencm3/stm32/g0/adc.h create mode 100644 lib/stm32/g0/adc.c diff --git a/include/libopencm3/stm32/adc.h b/include/libopencm3/stm32/adc.h index 1c3d6662..fed4a3b7 100644 --- a/include/libopencm3/stm32/adc.h +++ b/include/libopencm3/stm32/adc.h @@ -36,6 +36,8 @@ # include #elif defined(STM32L4) # include +#elif defined(STM32G0) +# include #else # error "stm32 family not defined." #endif diff --git a/include/libopencm3/stm32/g0/adc.h b/include/libopencm3/stm32/g0/adc.h new file mode 100644 index 00000000..f7130558 --- /dev/null +++ b/include/libopencm3/stm32/g0/adc.h @@ -0,0 +1,325 @@ +/** @defgroup adc_defines ADC Defines + * + * @ingroup STM32G0xx_defines +* + * @author @htmlonly © @endhtmlonly 2019 Guillaume Revaillot + * + * @brief Defined Constants and Types for the STM32STM32G0xx Analog to Digital Converter + * + * @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 . + */ + +/**@{*/ + +#ifndef LIBOPENCM3_ADC_H +#define LIBOPENCM3_ADC_H + +#include +#include + +/** @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 + diff --git a/lib/stm32/g0/Makefile b/lib/stm32/g0/Makefile index d88703e9..979ddcee 100644 --- a/lib/stm32/g0/Makefile +++ b/lib/stm32/g0/Makefile @@ -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 diff --git a/lib/stm32/g0/adc.c b/lib/stm32/g0/adc.c new file mode 100644 index 00000000..7d4d1c93 --- /dev/null +++ b/lib/stm32/g0/adc.c @@ -0,0 +1,206 @@ +/** @addtogroup adc_file ADC peripheral API + * @ingroup peripheral_apis + * + * @author @htmlonly © @endhtmlonly 2019 Guillaume Revaillot + * + * @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 . + */ + +/**@{*/ + +#include +#include + +/** @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; +} + +/**@}*/