1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #18933 from jue89/feature/cpu_efm32_series_2_adc

cpu/efm32: add periph_adc support for Gecko Series 2
This commit is contained in:
Juergen Fitschen 2022-11-24 11:32:56 +01:00 committed by GitHub
commit a6ff838a4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 378 additions and 51 deletions

View File

@ -13,6 +13,7 @@ config BOARD_XG23_PK6068A
select CPU_MODEL_EFR32ZG23A020F512GM48
# Put defined MCU peripherals here (in alphabetical order)
select HAS_PERIPH_ADC
select HAS_PERIPH_I2C
select HAS_PERIPH_SPI
select HAS_PERIPH_TIMER

View File

@ -3,6 +3,7 @@ CPU_FAM = efr32zg23
CPU_MODEL = efr32zg23a020f512gm48
# Put defined MCU peripherals here (in alphabetical order)
FEATURES_PROVIDED += periph_adc
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer

View File

@ -67,6 +67,8 @@ PIN 1 is the bottom-left contact when the header faces you horizontally.
| Timer | 0 | TIMER0 | | Default ztimer backend | EM1 |
| | 1 | LETIMER0 | | | EM3 |
| UART | 0 | EUSART1 | RX: PA9, TX: PA8 | Default STDIO output | EM1 |
| ADC_LINE | 0 | IADC0 | IN+: PA10, IN-: GND | Single-ended, V_ref: 2 * 1.21V | EM1 |
| | 1 | IADC0 | IN+: PA0, IN-:PA5 | Differential, V_ref: 2 * 1.21V | EM1 |
### User interface
| Peripheral | Mapped to | Pin | Comments |

View File

@ -55,6 +55,38 @@ static const clk_div_t clk_div_config[] = {
#define CLK_DIV_NUMOF ARRAY_SIZE(clk_div_config)
/** @} */
/**
* @name ADC configuration
* @{
*/
static const adc_conf_t adc_config[] = {
{
.dev = IADC0,
.cmu = cmuClock_IADC0,
.reference = iadcCfgReferenceInt1V2,
.reference_mV = 1210,
.gain = iadcCfgAnalogGain0P5x,
.available_res = { ADC_RES_10BIT, ADC_RES_16BIT }
}
};
static const adc_chan_conf_t adc_channel_config[] = {
{
.dev = 0,
.input_pos = GPIO_PIN(PA, 10),
.input_neg = GPIO_UNDEF
},
{
.dev = 0,
.input_pos = GPIO_PIN(PA, 0),
.input_neg = GPIO_PIN(PA, 5)
},
};
#define ADC_DEV_NUMOF ARRAY_SIZE(adc_config)
#define ADC_NUMOF ARRAY_SIZE(adc_channel_config)
/** @} */
/**
* @name I2C configuration
* @{

View File

@ -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.
* @{

View File

@ -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))

View 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));
}

View File

@ -13133,3 +13133,7 @@ boards/xg23\-pk6068a/include/periph_conf\.h:[0-9]+: warning: Member timer_config
boards/xg23\-pk6068a/include/periph_conf\.h:[0-9]+: warning: Member UART_0_ISR_RX \(macro definition\) of file periph_conf\.h is not documented\.
boards/xg23\-pk6068a/include/periph_conf\.h:[0-9]+: warning: Member UART_NUMOF \(macro definition\) of file periph_conf\.h is not documented\.
boards/xg23\-pk6068a/include/periph_conf\.h:[0-9]+: warning: Member uart_config\[\] \(variable\) of file periph_conf\.h is not documented\.
boards/xg23\-pk6068a/include/periph_conf\.h:[0-9]+: warning: Member ADC_DEV_NUMOF \(macro definition\) of file periph_conf\.h is not documented\.
boards/xg23\-pk6068a/include/periph_conf\.h:[0-9]+: warning: Member ADC_NUMOF \(macro definition\) of file periph_conf\.h is not documented\.
boards/xg23\-pk6068a/include/periph_conf\.h:[0-9]+: warning: Member adc_config\[\] \(variable\) of file periph_conf\.h is not documented\.
boards/xg23\-pk6068a/include/periph_conf\.h:[0-9]+: warning: Member adc_channel_config\[\] \(variable\) of file periph_conf\.h is not documented\.