Merged in juniskane/nuttx_stm32l4/dfsdm_adc_work_pr (pull request #487)

STM32L4 ADC, DFSDM: add routing of ADC data to DFSDM filters

* configs/nucleo-l496zg: add DFSDM initialization

* STM32L4 ADC: add option for routing ADC data to DFSDM, fix DFSDM DMA

Approved-by: Gregory Nutt <gnutt@nuttx.org>
This commit is contained in:
Juha Niskanen 2017-09-20 12:20:45 +00:00 committed by Gregory Nutt
parent ae2a1d07b3
commit abcaedb990
11 changed files with 335 additions and 18 deletions

View File

@ -3308,6 +3308,28 @@ config STM32L4_ADC3_DMA
DMA transfer, which is necessary if multiple channels are read
or if very high trigger frequencies are used.
config STM32L4_ADC1_OUTPUT_DFSDM
bool "ADC1 output to DFSDM"
depends on STM32L4_ADC1 && STM32L4_DFSDM1 && (STM32L4_STM32L496XX || \
STM32L4_STM32L451XX || STM32L4_STM32L452XX || STM32L4_STM32L462XX)
default n
---help---
Route ADC1 output directly to DFSDM parallel inputs.
config STM32L4_ADC2_OUTPUT_DFSDM
bool "ADC2 output to DFSDM"
depends on STM32L4_ADC2 && STM32L4_DFSDM1 && STM32L4_STM32L496XX
default n
---help---
Route ADC2 output directly to DFSDM parallel inputs.
config STM32L4_ADC3_OUTPUT_DFSDM
bool "ADC3 output to DFSDM"
depends on STM32L4_ADC3 && STM32L4_DFSDM1 && STM32L4_STM32L496XX
default n
---help---
Route ADC3 output directly to DFSDM parallel inputs.
endmenu
menu "DAC Configuration"

View File

@ -73,7 +73,7 @@
#define STM32L4_ADC_OFR1_OFFSET 0x0060 /* ADC offset register 1 */
#define STM32L4_ADC_OFR2_OFFSET 0x0064 /* ADC offset register 2 */
#define STM32L4_ADC_OFR3_OFFSET 0x0068 /* ADC offset register 3 */
#define STM32L4_ADC_OFR4_OFFSET 0x006c /* ADC data offset register 4 */
#define STM32L4_ADC_OFR4_OFFSET 0x006c /* ADC offset register 4 */
#define STM32L4_ADC_JDR1_OFFSET 0x0080 /* ADC injected data register 1 */
#define STM32L4_ADC_JDR2_OFFSET 0x0084 /* ADC injected data register 2 */
#define STM32L4_ADC_JDR3_OFFSET 0x0088 /* ADC injected data register 3 */
@ -220,6 +220,7 @@
#define ADC_CFGR_DMAEN (1 << 0) /* Bit 0: Direct memory access enable */
#define ADC_CFGR_DMACFG (1 << 1) /* Bit 1: Direct memory access configuration */
#define ADC_CFGR_DFSDMCFG (1 << 2) /* Bit 2: DFSDM mode configuration */
#define ADC_CFGR_RES_SHIFT (3) /* Bits 3-4: Data resolution */
#define ADC_CFGR_RES_MASK (3 << ADC_CFGR_RES_SHIFT)
# define ADC_CFGR_RES_12BIT (0 << ADC_CFGR_RES_SHIFT) /* 12-bit resolution */

View File

@ -176,11 +176,14 @@ struct stm32_dev_s
uint8_t current; /* Current ADC channel being converted */
#ifdef ADC_HAVE_DMA
uint8_t dmachan; /* DMA channel needed by this ADC */
bool hasdma; /* True: This channel supports DMA */
bool hasdma; /* True: This ADC supports DMA */
#endif
#ifdef ADC_HAVE_DFSDM
bool hasdfsdm; /* True: This ADC routes its output to DFSDM */
#endif
#ifdef ADC_HAVE_TIMER
uint8_t trigger; /* Timer trigger channel: 0=CC1, 1=CC2, 2=CC3,
* 3=CC4, 4=TRGO */
* 3=CC4, 4=TRGO, 5=TRGO2 */
#endif
xcpt_t isr; /* Interrupt handler for this ADC block */
uint32_t base; /* Base address of registers unique to this ADC
@ -248,6 +251,11 @@ static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t isr,
FAR void *arg);
#endif
#ifdef ADC_HAVE_DFSDM
static int adc_setoffset(FAR struct stm32_dev_s *priv, uint8_t ch, uint8_t i,
uint16_t offset);
#endif
static void adc_startconv(FAR struct stm32_dev_s *priv, bool enable);
/* ADC Interrupt Handler */
@ -306,6 +314,9 @@ static struct stm32_dev_s g_adcpriv1 =
.dmachan = ADC1_DMA_CHAN,
.hasdma = true,
#endif
#ifdef ADC1_HAVE_DFSDM
.hasdfsdm = true,
#endif
};
static struct adc_dev_s g_adcdev1 =
@ -335,6 +346,9 @@ static struct stm32_dev_s g_adcpriv2 =
.dmachan = ADC2_DMA_CHAN,
.hasdma = true,
#endif
#ifdef ADC2_HAVE_DFSDM
.hasdfsdm = true,
#endif
};
static struct adc_dev_s g_adcdev2 =
@ -364,6 +378,9 @@ static struct stm32_dev_s g_adcpriv3 =
.dmachan = ADC3_DMA_CHAN,
.hasdma = true,
#endif
#ifdef ADC3_HAVE_DFSDM
.hasdfsdm = true,
#endif
};
static struct adc_dev_s g_adcdev3 =
@ -1230,6 +1247,19 @@ static int adc_setup(FAR struct adc_dev_s *dev)
}
#endif
#ifdef ADC_HAVE_DFSDM
if (priv->hasdfsdm)
{
/* Disable DMA */
clrbits |= ADC_CFGR_DMAEN;
/* Enable routing to DFSDM */
setbits |= ADC_CFGR_DFSDMCFG;
}
#endif
/* Disable continuous mode and set align to right */
clrbits |= ADC_CFGR_CONT | ADC_CFGR_ALIGN;
@ -1455,6 +1485,35 @@ static bool adc_internal(FAR struct stm32_dev_s * priv, uint32_t *adc_ccr)
return internal;
}
/****************************************************************************
* Name: adc_set_offset
****************************************************************************/
#ifdef ADC_HAVE_DFSDM
static int adc_setoffset(FAR struct stm32_dev_s *priv, uint8_t ch, uint8_t i,
uint16_t offset)
{
uint32_t reg;
uint32_t regval;
if (i >= 4)
{
/* There are only four offset registers. */
return -E2BIG;
}
reg = STM32L4_ADC_OFR1_OFFSET + i * 4;
regval = ADC_OFR_OFFSETY_EN;
adc_putreg(priv, reg, regval);
regval |= ADC_OFR_OFFSETY_CH(ch) | ADC_OFR_OFFSETY(offset);
adc_putreg(priv, reg, regval);
return OK;
}
#endif
/****************************************************************************
* Name: adc_set_ch
*
@ -1509,6 +1568,25 @@ static int adc_set_ch(FAR struct adc_dev_s *dev, uint8_t ch)
adc_sqrbits(priv, ADC_SQR1_FIRST, ADC_SQR1_LAST, ADC_SQR1_SQ_OFFSET);
adc_modifyreg(priv, STM32L4_ADC_SQR1_OFFSET, ~ADC_SQR1_RESERVED, bits);
#ifdef ADC_HAVE_DFSDM
if (priv->hasdfsdm)
{
/* Convert 12-bit ADC result to signed 16-bit. */
if (ch == 0)
{
for (i = 0; i < priv->cchannels; i++)
{
adc_setoffset(priv, priv->chanlist[i], i, 0x800);
}
}
else
{
adc_setoffset(priv, priv->current, 0, 0x800);
}
}
#endif
return OK;
}

View File

@ -120,6 +120,38 @@
#if defined(CONFIG_STM32L4_ADC1) || defined(CONFIG_STM32L4_ADC2) || \
defined(CONFIG_STM32L4_ADC3)
/* ADC output to DFSDM support. Note that DFSDM and DMA are
* mutually exclusive.
*/
#undef ADC_HAVE_DFSDM
#if defined(CONFIG_STM32L4_ADC1_OUTPUT_DFSDM) || \
defined(CONFIG_STM32L4_ADC2_OUTPUT_DFSDM) || \
defined(CONFIG_STM32L4_ADC3_OUTPUT_DFSDM)
# define ADC_HAVE_DFSDM
#endif
#if defined(CONFIG_STM32L4_ADC1_OUTPUT_DFSDM)
# define ADC1_HAVE_DFSDM 1
# undef CONFIG_STM32L4_ADC1_DMA
#else
# undef ADC1_HAVE_DFSDM
#endif
#if defined(CONFIG_STM32L4_ADC2_OUTPUT_DFSDM)
# define ADC2_HAVE_DFSDM 1
# undef CONFIG_STM32L4_ADC2_DMA
#else
# undef ADC2_HAVE_DFSDM
#endif
#if defined(CONFIG_STM32L4_ADC3_OUTPUT_DFSDM)
# define ADC3_HAVE_DFSDM 1
# undef CONFIG_STM32L4_ADC3_DMA
#else
# undef ADC3_HAVE_DFSDM
#endif
/* DMA support */
#undef ADC_HAVE_DMA

View File

@ -152,8 +152,8 @@
* parallel inputs to DFSDM_CHyDATINR register.
*/
#define DFSDM_DMA_CONTROL_WORD (DMA_CCR_MSIZE_16BITS | \
DMA_CCR_PSIZE_16BITS | \
#define DFSDM_DMA_CONTROL_WORD (DMA_CCR_MSIZE_32BITS | \
DMA_CCR_PSIZE_32BITS | \
DMA_CCR_MINC | \
DMA_CCR_CIRC)
@ -1371,13 +1371,16 @@ static int dfsdm_set_ch(FAR struct adc_dev_s *dev, uint8_t ch)
regval |= DFSDM_FLTCR1_RCH(priv->current);
dfsdm_putreg(priv, FLTCR1_OFFSET(priv), regval);
#if 0 /* TODO: for testing */
/* Set CHCFGR1 input data configuration */
regval = dfsdm_getreg(priv, CHCFGR1_OFFSET(priv));
regval |= DFSDM_CHCFGR1_DATMPX_DATINR;
dfsdm_putreg(priv, CHCFGR1_OFFSET(priv), regval);
#ifdef ADC_HAVE_DFSDM
regval |= DFSDM_CHCFGR1_DATMPX_ADC;
#else
regval |= DFSDM_CHCFGR1_DATMPX_EXT;
// regval |= DFSDM_CHCFGR1_DATMPX_DATINR;
#endif
dfsdm_putreg(priv, CHCFGR1_OFFSET(priv), regval);
/* Enable the channel */
@ -1488,7 +1491,7 @@ static int dfsdm_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg)
static int dfsdm_interrupt(FAR struct adc_dev_s *dev, uint32_t isr)
{
FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
int32_t value;
uint32_t value;
/* Identifies the interruption AWD or OVR */
@ -1516,6 +1519,7 @@ static int dfsdm_interrupt(FAR struct adc_dev_s *dev, uint32_t isr)
dfsdm_putreg(priv, FLTAWCFR_OFFSET(priv), DFSDM_INT_ROVR);
}
#if 0
if ((isr & DFSDM_INT_JOVR) != 0)
{
awarn("WARNING: Injected conversion overrun has occurred!\n");
@ -1524,6 +1528,7 @@ static int dfsdm_interrupt(FAR struct adc_dev_s *dev, uint32_t isr)
dfsdm_putreg(priv, FLTAWCFR_OFFSET(priv), DFSDM_INT_JOVR);
}
#endif
/* EOC: End of conversion */
@ -1689,6 +1694,7 @@ static void dfsdm_dmaconvcallback(DMA_HANDLE handle, uint8_t isr, FAR void *arg)
{
FAR struct adc_dev_s *dev = (FAR struct adc_dev_s *)arg;
FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv;
uint32_t value;
int i;
/* Verify that the upper-half driver has bound its callback functions */
@ -1699,7 +1705,10 @@ static void dfsdm_dmaconvcallback(DMA_HANDLE handle, uint8_t isr, FAR void *arg)
for (i = 0; i < priv->nchannels; i++)
{
priv->cb->au_receive(dev, priv->chanlist[priv->current], priv->dmabuffer[priv->current]);
value = priv->dmabuffer[priv->current];
value = (value & DFSDM_FLTRDATAR_RDATA_MASK) >> DFSDM_FLTRDATAR_RDATA_SHIFT;
priv->cb->au_receive(dev, priv->chanlist[priv->current], value);
priv->current++;
if (priv->current >= priv->nchannels)
{

View File

@ -89,6 +89,15 @@
# define DFSDM_HAVE_DMA 1
#endif
/* ADC output to DFSDM support */
#undef ADC_HAVE_DFSDM
#if defined(CONFIG_STM32L4_ADC1_OUTPUT_DFSDM) || \
defined(CONFIG_STM32L4_ADC2_OUTPUT_DFSDM) || \
defined(CONFIG_STM32L4_ADC3_OUTPUT_DFSDM)
# define ADC_HAVE_DFSDM
#endif
/* Timer configuration: If a timer trigger is specified, then get
* information about the timer.
*/

View File

@ -74,13 +74,10 @@
* LSE: 32.768 kHz
*/
#define STM32_BOARD_XTAL 8000000ul
#define STM32_HSI_FREQUENCY 16000000ul
#define STM32_LSI_FREQUENCY 32000
#define STM32_HSE_FREQUENCY STM32_BOARD_XTAL
#define STM32_LSE_FREQUENCY 32768
#define STM32L4_HSI_FREQUENCY 16000000ul
#define STM32L4_LSI_FREQUENCY 32000
#define STM32L4_HSE_FREQUENCY 8000000ul /* 8 MHz from MCO output */
#define STM32L4_LSE_FREQUENCY 32768
#define HSI_CLOCK_CONFIG

View File

@ -46,7 +46,7 @@ CSRCS += stm32_userleds.c
endif
ifeq ($(CONFIG_ARCH_BUTTONS),y)
CSRCS += stm32_buttons.c
CSRCS += stm32_buttons.c
endif
ifeq ($(CONFIG_LIB_BOARDCTL),y)
@ -59,6 +59,9 @@ endif
ifeq ($(CONFIG_ADC),y)
CSRCS += stm32_adc.c
ifeq ($(CONFIG_STM32L4_DFSDM),y)
CSRCS += stm32_dfsdm.c
endif
endif
ifeq ($(CONFIG_DAC),y)

View File

@ -293,5 +293,17 @@ int stm32_adc_setup(void);
int stm32_dac_setup(void);
#endif
/************************************************************************************
* Name: stm32_dfsdm_setup
*
* Description:
* Initialize DFSDM and register the ADC drivers for DFSDM filters.
*
************************************************************************************/
#if defined(CONFIG_ADC) && defined(CONFIG_STM32L4_DFSDM)
int stm32_dfsdm_setup(void);
#endif
#endif /* __ASSEMBLY__ */
#endif /* __CONFIGS_NUCLEO_L496ZG_SRC_NUCLEO_144_H */

View File

@ -130,7 +130,18 @@ int board_app_initialize(uintptr_t arg)
{
syslog(LOG_ERR, "ERROR: stm32_adc_setup failed: %d\n", ret);
}
#ifdef CONFIG_STM32L4_DFSDM
/* Initialize DFSDM and register its filters as additional ADC devices. */
ret = stm32_dfsdm_setup();
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: stm32_dfsdm_setup failed: %d\n", ret);
}
#endif
#endif /* CONFIG_ADC */
#ifdef CONFIG_DAC
/* Initialize DAC and register the DAC driver. */

View File

@ -0,0 +1,143 @@
/*****************************************************************************
* configs/nucleo-l496zg/src/stm32_dfsdm.c
*
* Copyright (C) 2017 Haltian Ltd. All rights reserved.
* Authors: Juha Niskanen <juha.niskanen@haltian.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************************/
/************************************************************************************
* Included Files
************************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/board.h>
#include <nuttx/analog/adc.h>
#include <arch/board/board.h>
#include "stm32l4_gpio.h"
#include "stm32l4_dfsdm.h"
#include "nucleo-144.h"
#if defined(CONFIG_ADC) && defined(CONFIG_STM32L4_DFSDM)
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: stm32_dfsdm_setup
************************************************************************************/
int stm32_dfsdm_setup(void)
{
static bool initialized = false;
if (!initialized)
{
int ret;
struct adc_dev_s *adc;
ainfo("Initializing DFSDM\n");
/* TODO: just some arbitrary channels selected, missing input pin
* configuration and DFSDM mode selection: SPI/Manchester or internal
* parallel inputs (CPU/DMA/ADC).
*/
#ifdef CONFIG_STM32L4_DFSDM1_FLT0
adc = stm32l4_dfsdm_initialize(0, (const uint8_t[1]){0}, 1);
if (adc == NULL)
{
aerr("Failed to get DFSDM FLT0 interface\n");
return -ENODEV;
}
ret = adc_register("/dev/adc_flt0", adc);
if (ret < 0)
{
aerr("adc_register failed: %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_STM32L4_DFSDM1_FLT1
adc = stm32l4_dfsdm_initialize(1, (const uint8_t[2]){0,1}, 2);
if (adc == NULL)
{
aerr("Failed to get DFSDM FLT1 interface\n");
return -ENODEV;
}
ret = adc_register("/dev/adc_flt1", adc);
if (ret < 0)
{
aerr("adc_register failed: %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_STM32L4_DFSDM1_FLT2
adc = stm32l4_dfsdm_initialize(2, (const uint8_t[8]){0,1,2,3,4,5,6,7}, 8);
if (adc == NULL)
{
aerr("Failed to get DFSDM FLT2 interface\n");
return -ENODEV;
}
ret = adc_register("/dev/adc_flt2", adc);
if (ret < 0)
{
aerr("adc_register failed: %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_STM32L4_DFSDM1_FLT3
adc = stm32l4_dfsdm_initialize(3, (const uint8_t[4]){6,5,4,3}, 4);
if (adc == NULL)
{
aerr("Failed to get DFSDM FLT3 interface\n");
return -ENODEV;
}
ret = adc_register("/dev/adc_flt3", adc);
if (ret < 0)
{
aerr("adc_register failed: %d\n", ret);
return ret;
}
#endif
initialized = true;
}
return OK;
}
#endif /* CONFIG_ADC && CONFIG_STM32L4_DFSDM */