mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/efm32/adc: add support for Gecko Series 2
Series 2 features IADCs instead of ADCs.
This commit is contained in:
parent
ba3ccfe32e
commit
18e9167c73
@ -25,7 +25,11 @@
|
||||
|
||||
#include "cpu_conf.h"
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
#include "em_iadc.h"
|
||||
#else
|
||||
#include "em_adc.h"
|
||||
#endif
|
||||
#include "em_cmu.h"
|
||||
#include "em_device.h"
|
||||
#include "em_gpio.h"
|
||||
@ -57,57 +61,6 @@ typedef struct {
|
||||
CMU_ClkDiv_TypeDef div; /**< Divisor */
|
||||
} clk_div_t;
|
||||
|
||||
#if (defined(ADC_COUNT) && (ADC_COUNT > 0)) || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Internal macro for combining ADC resolution (x) with number of
|
||||
* shifts (y).
|
||||
*/
|
||||
#define ADC_MODE(x, y) ((y << 4) | x)
|
||||
|
||||
/**
|
||||
* @brief Internal define to note that resolution is not supported.
|
||||
*/
|
||||
#define ADC_MODE_UNDEF(x) (ADC_MODE(x, 15))
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/**
|
||||
* @brief Possible ADC resolution settings
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_ADC_RES_T
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = ADC_MODE(adcRes6Bit, 0), /**< ADC resolution: 6 bit */
|
||||
ADC_RES_8BIT = ADC_MODE(adcRes8Bit, 0), /**< ADC resolution: 8 bit */
|
||||
ADC_RES_10BIT = ADC_MODE(adcRes12Bit, 2), /**< ADC resolution: 10 bit (shifted from 12 bit) */
|
||||
ADC_RES_12BIT = ADC_MODE(adcRes12Bit, 0), /**< ADC resolution: 12 bit */
|
||||
ADC_RES_14BIT = ADC_MODE_UNDEF(0), /**< ADC resolution: 14 bit (unsupported) */
|
||||
ADC_RES_16BIT = ADC_MODE_UNDEF(1), /**< ADC resolution: 16 bit (unsupported) */
|
||||
} adc_res_t;
|
||||
/** @} */
|
||||
#endif /* ndef DOXYGEN */
|
||||
|
||||
/**
|
||||
* @brief ADC device configuration
|
||||
*/
|
||||
typedef struct {
|
||||
ADC_TypeDef *dev; /**< ADC device used */
|
||||
CMU_Clock_TypeDef cmu; /**< the device CMU channel */
|
||||
} adc_conf_t;
|
||||
|
||||
/**
|
||||
* @brief ADC channel configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t dev; /**< device index */
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
ADC_SingleInput_TypeDef input; /**< input channel */
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
ADC_PosSel_TypeDef input; /**< input channel */
|
||||
#endif
|
||||
ADC_Ref_TypeDef reference; /**< channel voltage reference */
|
||||
ADC_AcqTime_TypeDef acq_time; /**< channel acquisition time */
|
||||
} adc_chan_conf_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Length of CPU ID in octets.
|
||||
@ -245,6 +198,183 @@ typedef enum {
|
||||
/** @} */
|
||||
#endif /* ndef DOXYGEN */
|
||||
|
||||
#if defined(_SILICON_LABS_32B_SERIES_2)
|
||||
/**
|
||||
* @brief Internal macro for combining over-sampling rate (osr), digital
|
||||
* averaging count (avg) and output resolution (res).
|
||||
*
|
||||
* @note The efr32xg23 reference manual provides this folumar:
|
||||
* res = 11 bit + log_2(osr * avg) bit
|
||||
*/
|
||||
#if defined(_IADC_CFG_DIGAVG_MASK)
|
||||
#define ADC_MODE(osr, avg, res) ((osr << 16) | (avg << 8) | res)
|
||||
#else
|
||||
#define ADC_MODE(osr, res) ((osr << 16) | res)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Internal macro to extract averaging count
|
||||
*/
|
||||
#define ADC_MODE_OSR(mode) ((mode & 0xff0000) >> 16)
|
||||
|
||||
#if defined(_IADC_CFG_DIGAVG_MASK)
|
||||
/**
|
||||
* @brief Internal macro to extract over-sampling rate
|
||||
*/
|
||||
#define ADC_MODE_AVG(mode) ((mode & 0x00ff00) >> 8)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Internal macro to extract output resolution
|
||||
*/
|
||||
#define ADC_MODE_RES(mode) ((mode & 0x0000ff) >> 0)
|
||||
|
||||
/**
|
||||
* @brief Possible ADC resolution settings
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_ADC_RES_T
|
||||
#if defined(_IADC_CFG_DIGAVG_MASK)
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = ADC_MODE(iadcCfgOsrHighSpeed2x, iadcDigitalAverage1, 6),
|
||||
ADC_RES_8BIT = ADC_MODE(iadcCfgOsrHighSpeed2x, iadcDigitalAverage1, 8),
|
||||
ADC_RES_10BIT = ADC_MODE(iadcCfgOsrHighSpeed2x, iadcDigitalAverage1, 10),
|
||||
ADC_RES_12BIT = ADC_MODE(iadcCfgOsrHighSpeed2x, iadcDigitalAverage1, 12),
|
||||
ADC_RES_14BIT = ADC_MODE(iadcCfgOsrHighSpeed8x, iadcDigitalAverage1, 14),
|
||||
ADC_RES_16BIT = ADC_MODE(iadcCfgOsrHighSpeed16x, iadcDigitalAverage2, 16),
|
||||
} adc_res_t;
|
||||
#else
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = ADC_MODE(iadcCfgOsrHighSpeed2x, 6),
|
||||
ADC_RES_8BIT = ADC_MODE(iadcCfgOsrHighSpeed2x, 8),
|
||||
ADC_RES_10BIT = ADC_MODE(iadcCfgOsrHighSpeed2x, 10),
|
||||
ADC_RES_12BIT = ADC_MODE(iadcCfgOsrHighSpeed2x, 12),
|
||||
ADC_RES_14BIT = ADC_MODE(iadcCfgOsrHighSpeed8x, 14),
|
||||
ADC_RES_16BIT = ADC_MODE(iadcCfgOsrHighSpeed32x, 16),
|
||||
} adc_res_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ADC device configuration
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* IADC device configuration
|
||||
*/
|
||||
IADC_TypeDef *dev;
|
||||
|
||||
/**
|
||||
* CMU gate for the IADC device
|
||||
*/
|
||||
CMU_Clock_TypeDef cmu;
|
||||
|
||||
/**
|
||||
* Voltage reference to use
|
||||
*/
|
||||
IADC_CfgReference_t reference;
|
||||
|
||||
/**
|
||||
* Voltage of the reference in mV
|
||||
*
|
||||
* @note Required internally for offset correction.
|
||||
*/
|
||||
uint32_t reference_mV;
|
||||
|
||||
/**
|
||||
* Ampilfication of the analog input signal
|
||||
*
|
||||
* @note The maximum input voltage is
|
||||
* \ref adc_conf_t.gain * \ref adc_conf_t.reference_mV
|
||||
*/
|
||||
IADC_CfgAnalogGain_t gain;
|
||||
|
||||
/**
|
||||
* Available resoltions
|
||||
*
|
||||
* @note Resolutions made available to the applications have to be
|
||||
* specified during \ref adc_init. This will configure the IADC
|
||||
* accordingly and allows for quick \ref adc_sample calls.
|
||||
*/
|
||||
adc_res_t available_res[IADC0_CONFIGNUM];
|
||||
} adc_conf_t;
|
||||
|
||||
/**
|
||||
* @brief ADC channel configuration
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* \ref adc_conf_t device index
|
||||
*/
|
||||
uint8_t dev;
|
||||
|
||||
/**
|
||||
* Positive analog input
|
||||
*/
|
||||
gpio_t input_pos;
|
||||
|
||||
/**
|
||||
* Negative analog input.
|
||||
* Can be set to \ref GPIO_UNDEF for single-ended ADC lines.
|
||||
*
|
||||
* @note For differential inputs make sure that
|
||||
* \ref adc_chan_conf_t.input_pos is an even pin number and
|
||||
* \ref adc_chan_conf_t.input_neg is an odd pin number or the other
|
||||
* way around.
|
||||
*/
|
||||
gpio_t input_neg;
|
||||
} adc_chan_conf_t;
|
||||
#else /* defined(_SILICON_LABS_32B_SERIES_2) */
|
||||
/**
|
||||
* @brief Internal macro for combining ADC resolution (x) with number of
|
||||
* shifts (y).
|
||||
*/
|
||||
#define ADC_MODE(x, y) ((y << 4) | x)
|
||||
|
||||
/**
|
||||
* @brief Internal define to note that resolution is not supported.
|
||||
*/
|
||||
#define ADC_MODE_UNDEF(x) (ADC_MODE(x, 15))
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/**
|
||||
* @brief Possible ADC resolution settings
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_ADC_RES_T
|
||||
typedef enum {
|
||||
ADC_RES_6BIT = ADC_MODE(adcRes6Bit, 0), /**< ADC resolution: 6 bit */
|
||||
ADC_RES_8BIT = ADC_MODE(adcRes8Bit, 0), /**< ADC resolution: 8 bit */
|
||||
ADC_RES_10BIT = ADC_MODE(adcRes12Bit, 2), /**< ADC resolution: 10 bit (shifted from 12 bit) */
|
||||
ADC_RES_12BIT = ADC_MODE(adcRes12Bit, 0), /**< ADC resolution: 12 bit */
|
||||
ADC_RES_14BIT = ADC_MODE_UNDEF(0), /**< ADC resolution: 14 bit (unsupported) */
|
||||
ADC_RES_16BIT = ADC_MODE_UNDEF(1), /**< ADC resolution: 16 bit (unsupported) */
|
||||
} adc_res_t;
|
||||
/** @} */
|
||||
#endif /* ndef DOXYGEN */
|
||||
|
||||
/**
|
||||
* @brief ADC device configuration
|
||||
*/
|
||||
typedef struct {
|
||||
ADC_TypeDef *dev; /**< ADC device used */
|
||||
CMU_Clock_TypeDef cmu; /**< the device CMU channel */
|
||||
} adc_conf_t;
|
||||
|
||||
/**
|
||||
* @brief ADC channel configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t dev; /**< device index */
|
||||
#if defined(_SILICON_LABS_32B_SERIES_0)
|
||||
ADC_SingleInput_TypeDef input; /**< input channel */
|
||||
#elif defined(_SILICON_LABS_32B_SERIES_1)
|
||||
ADC_PosSel_TypeDef input; /**< input channel */
|
||||
#endif
|
||||
ADC_Ref_TypeDef reference; /**< channel voltage reference */
|
||||
ADC_AcqTime_TypeDef acq_time; /**< channel acquisition time */
|
||||
} adc_chan_conf_t;
|
||||
#endif /* !defined(_SILICON_LABS_32B_SERIES_2) */
|
||||
|
||||
/**
|
||||
* @brief Override hardware crypto supported methods.
|
||||
* @{
|
||||
|
@ -1,5 +1,14 @@
|
||||
include $(RIOTCPU)/efm32/efm32-info.mk
|
||||
|
||||
# Select the correct implementation for `periph_adc`
|
||||
ifneq (,$(filter periph_adc,$(USEMODULE)))
|
||||
ifeq (2,$(EFM32_SERIES))
|
||||
SRC += adc_series2.c
|
||||
else
|
||||
SRC += adc_series01.c
|
||||
endif
|
||||
endif
|
||||
|
||||
# Select the correct implementation for `periph_timer`
|
||||
ifneq (,$(filter periph_hwrng,$(USEMODULE)))
|
||||
ifeq (1,$(EFM32_SERIES))
|
||||
|
148
cpu/efm32/periph/adc_series2.c
Normal file
148
cpu/efm32/periph/adc_series2.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2022 SSV Software Systems GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_efm32
|
||||
* @ingroup drivers_periph_adc
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level ADC driver implementation
|
||||
*
|
||||
* @author Juergen Fitschen <me@jue.yt>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mutex.h"
|
||||
|
||||
#include "periph_conf.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#include "em_cmu.h"
|
||||
#include "em_gpio.h"
|
||||
#include "em_iadc.h"
|
||||
|
||||
static mutex_t adc_lock[ADC_DEV_NUMOF];
|
||||
|
||||
int adc_init(adc_t line)
|
||||
{
|
||||
assert(line < ADC_NUMOF);
|
||||
uint8_t dev = adc_channel_config[line].dev;
|
||||
assert(dev < ADC_DEV_NUMOF);
|
||||
|
||||
/* initialize lock */
|
||||
mutex_init(&adc_lock[dev]);
|
||||
|
||||
/* enable clock */
|
||||
CMU_ClockEnable(adc_config[dev].cmu, true);
|
||||
|
||||
/* make sure we're in a known state */
|
||||
IADC_reset(adc_config[dev].dev);
|
||||
|
||||
/* init IADC periph */
|
||||
const IADC_Init_t init = IADC_INIT_DEFAULT;
|
||||
IADC_AllConfigs_t configs = { 0 };
|
||||
for (size_t i = 0; i < IADC0_CONFIGNUM; i++) {
|
||||
configs.configs[i].adcMode = iadcCfgModeNormal;
|
||||
configs.configs[i].osrHighSpeed = ADC_MODE_OSR(adc_config[dev].available_res[i]);
|
||||
configs.configs[i].digAvg = ADC_MODE_AVG(adc_config[dev].available_res[i]);
|
||||
configs.configs[i].analogGain = adc_config[dev].gain;
|
||||
configs.configs[i].reference = adc_config[dev].reference;
|
||||
configs.configs[i].vRef = adc_config[dev].reference_mV;
|
||||
configs.configs[i].twosComplement = iadcCfgTwosCompUnipolar;
|
||||
}
|
||||
IADC_init(adc_config[dev].dev, &init, &configs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline GPIO_Port_TypeDef _port_num(gpio_t pin)
|
||||
{
|
||||
return ((pin & 0xf0) >> 4);
|
||||
}
|
||||
|
||||
static inline uint8_t _pin_num(gpio_t pin)
|
||||
{
|
||||
return (pin & 0x0f);
|
||||
}
|
||||
|
||||
static void _setup_abus(gpio_t pin) {
|
||||
const uint32_t alloc = GPIO_ABUSALLOC_AEVEN0_ADC0
|
||||
| GPIO_ABUSALLOC_AODD0_ADC0;
|
||||
switch (_port_num(pin)) {
|
||||
case gpioPortA:
|
||||
GPIO->ABUSALLOC = alloc;
|
||||
break;
|
||||
case gpioPortB:
|
||||
GPIO->BBUSALLOC = alloc;
|
||||
break;
|
||||
case gpioPortC:
|
||||
case gpioPortD:
|
||||
GPIO->CDBUSALLOC = alloc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t adc_sample(adc_t line, adc_res_t res)
|
||||
{
|
||||
assert(line < ADC_NUMOF);
|
||||
uint8_t dev = adc_channel_config[line].dev;
|
||||
|
||||
/* find config to given resolution */
|
||||
uint8_t config;
|
||||
for (config = 0; config < IADC0_CONFIGNUM; config++) {
|
||||
if (adc_config[dev].available_res[config] == res) {
|
||||
/* we found the corresponding config */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* unsopported resolution */
|
||||
if (config >= IADC0_CONFIGNUM) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* lock device */
|
||||
mutex_lock(&adc_lock[dev]);
|
||||
|
||||
/* setup channel and start sampling */
|
||||
static const IADC_InitSingle_t init = {
|
||||
.alignment = iadcAlignRight20,
|
||||
.showId = false,
|
||||
.dataValidLevel = iadcFifoCfgDvl4,
|
||||
.fifoDmaWakeup = false,
|
||||
.triggerSelect = iadcTriggerSelImmediate,
|
||||
.triggerAction = iadcTriggerActionOnce,
|
||||
.start = true
|
||||
};
|
||||
IADC_SingleInput_t input = IADC_SINGLEINPUT_DEFAULT;
|
||||
input.configId = config;
|
||||
input.posInput = IADC_portPinToPosInput(_port_num(adc_channel_config[line].input_pos),
|
||||
_pin_num(adc_channel_config[line].input_pos));
|
||||
_setup_abus(adc_channel_config[line].input_pos);
|
||||
if (adc_channel_config[line].input_neg != GPIO_UNDEF) {
|
||||
input.negInput = IADC_portPinToNegInput(_port_num(adc_channel_config[line].input_neg),
|
||||
_pin_num(adc_channel_config[line].input_neg));
|
||||
_setup_abus(adc_channel_config[line].input_neg);
|
||||
}
|
||||
|
||||
IADC_initSingle(adc_config[dev].dev, &init, &input);
|
||||
|
||||
/* wait for the conservation to provide the result in the fifo */
|
||||
while ((IADC_getStatus(adc_config[dev].dev) & IADC_STATUS_SINGLEFIFODV) == 0) {}
|
||||
uint32_t result = IADC_pullSingleFifoData(adc_config[dev].dev);
|
||||
|
||||
/* unlock device */
|
||||
mutex_unlock(&adc_lock[dev]);
|
||||
|
||||
return result >> (20 - ADC_MODE_RES(res));
|
||||
}
|
Loading…
Reference in New Issue
Block a user