* arch/arm/src/stm32f7/stm32_qencoder.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/spinlock.h>
#include <nuttx/sensors/qencoder.h>
#include <arch/board/board.h>
#include "chip.h"
#include "arm_internal.h"
#include "stm32_rcc.h"
#include "stm32_gpio.h"
#include "stm32_tim.h"
#include "stm32_qencoder.h"
#ifdef CONFIG_SENSORS_QENCODER
* Pre-processor Definitions
****************************************************************************/
#undef HAVE_32BIT_TIMERS
#undef HAVE_16BIT_TIMERS
#if defined(CONFIG_STM32F7_TIM2_QE) || defined(CONFIG_STM32F7_TIM5_QE)
# define HAVE_32BIT_TIMERS 1
#endif
#if defined(CONFIG_STM32F7_TIM1_QE) || defined(CONFIG_STM32F7_TIM3_QE) || \
defined(CONFIG_STM32F7_TIM4_QE) || defined(CONFIG_STM32F7_TIM8_QE)
# define HAVE_16BIT_TIMERS 1
#endif
#define TIM1_BITWIDTH 16
#define TIM2_BITWIDTH 32
#define TIM3_BITWIDTH 16
#define TIM4_BITWIDTH 16
#define TIM5_BITWIDTH 32
#define TIM8_BITWIDTH 16
#undef HAVE_MIXEDWIDTH_TIMERS
#if defined(HAVE_16BIT_TIMERS) && defined(HAVE_32BIT_TIMERS)
# define HAVE_MIXEDWIDTH_TIMERS 1
#endif
#ifdef CONFIG_STM32F7_QENCODER_FILTER
# if defined(CONFIG_STM32F7_QENCODER_SAMPLE_FDTS)
# if defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_1)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_NOFILT
# endif
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_CKINT)
# if defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_2)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FCKINT2
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_4)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FCKINT4
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_8)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FCKINT8
# endif
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_FDTS_2)
# if defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_6)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd26
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_8)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd28
# endif
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_FDTS_4)
# if defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_6)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd46
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_8)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd48
# endif
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_FDTS_8)
# if defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_6)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd86
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_8)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd88
# endif
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_FDTS_16)
# if defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_5)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd165
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_6)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd166
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_8)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd168
# endif
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_FDTS_32)
# if defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_5)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd325
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_6)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd326
# elif defined(CONFIG_STM32F7_QENCODER_SAMPLE_EVENT_8)
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_FDTSd328
# endif
# endif
# ifndef STM32_QENCODER_ICF
# warning "Invalid encoder filter combination, filter disabled"
# endif
#endif
#ifndef STM32_QENCODER_ICF
# define STM32_QENCODER_ICF GTIM_CCMR_ICF_NOFILT
#endif
#define STM32_GPIO_INPUT_FLOAT (GPIO_INPUT | GPIO_FLOAT)
* encoder
*/
#ifndef CONFIG_DEBUG_FEATURES
# undef CONFIG_DEBUG_SENSORS
#endif
#ifdef CONFIG_DEBUG_SENSORS
# ifdef CONFIG_DEBUG_INFO
# define qe_dumpgpio(p,m) stm32_dumpgpio(p,m)
# else
# define qe_dumpgpio(p,m)
# endif
#else
# define qe_dumpgpio(p,m)
#endif
* Private Types
****************************************************************************/
struct stm32_qeconfig_s
{
uint8_t timid;
uint8_t irq;
#ifdef HAVE_MIXEDWIDTH_TIMERS
uint8_t width;
#endif
#ifdef CONFIG_STM32_STM32F10XX
uint16_t ti1cfg;
uint16_t ti2cfg;
#else
uint32_t ti1cfg;
uint32_t ti2cfg;
#endif
uintptr_t regaddr;
uint32_t enable;
uint32_t base;
uint32_t psc;
};
struct stm32_lowerhalf_s
{
* half callback structure:
*/
const struct qe_ops_s *ops;
const struct stm32_qeconfig_s *config;
bool inuse;
#ifdef HAVE_16BIT_TIMERS
volatile int32_t position;
#endif
spinlock_t lock;
};
* Private Function Prototypes
****************************************************************************/
static uint16_t stm32_getreg16(struct stm32_lowerhalf_s *priv,
int offset);
static void stm32_putreg16(struct stm32_lowerhalf_s *priv, int offset,
uint16_t value);
static uint32_t stm32_getreg32(struct stm32_lowerhalf_s *priv,
int offset);
static void stm32_putreg32(struct stm32_lowerhalf_s *priv, int offset,
uint32_t value);
#if defined(CONFIG_DEBUG_SENSORS) && defined(CONFIG_DEBUG_INFO)
static void stm32_dumpregs(struct stm32_lowerhalf_s *priv,
const char *msg);
#else
# define stm32_dumpregs(priv,msg)
#endif
static struct stm32_lowerhalf_s *stm32_tim2lower(int tim);
#ifdef HAVE_16BIT_TIMERS
static int stm32_interrupt(int irq, void *context, void *arg);
#endif
static int stm32_setup(struct qe_lowerhalf_s *lower);
static int stm32_shutdown(struct qe_lowerhalf_s *lower);
static int stm32_position(struct qe_lowerhalf_s *lower,
int32_t *pos);
static int stm32_reset(struct qe_lowerhalf_s *lower);
static int stm32_ioctl(struct qe_lowerhalf_s *lower, int cmd,
unsigned long arg);
* Private Data
****************************************************************************/
static const struct qe_ops_s g_qecallbacks =
{
.setup = stm32_setup,
.shutdown = stm32_shutdown,
.position = stm32_position,
.setposmax = NULL,
.reset = stm32_reset,
.setindex = NULL,
.ioctl = stm32_ioctl,
};
#ifdef CONFIG_STM32F7_TIM1_QE
static const struct stm32_qeconfig_s g_tim1config =
{
.timid = 1,
.irq = STM32_IRQ_TIM1UP,
#ifdef HAVE_MIXEDWIDTH_TIMERS
.width = TIM1_BITWIDTH,
#endif
.regaddr = STM32_RCC_APB2ENR,
.enable = RCC_APB2ENR_TIM1EN,
.base = STM32_TIM1_BASE,
.psc = CONFIG_STM32F7_TIM1_QEPSC,
.ti1cfg = GPIO_TIM1_CH1IN,
.ti2cfg = GPIO_TIM1_CH2IN,
};
static struct stm32_lowerhalf_s g_tim1lower =
{
.ops = &g_qecallbacks,
.config = &g_tim1config,
.inuse = false,
.lock = SP_UNLOCKED,
};
#endif
#ifdef CONFIG_STM32F7_TIM2_QE
static const struct stm32_qeconfig_s g_tim2config =
{
.timid = 2,
.irq = STM32_IRQ_TIM2,
#ifdef HAVE_MIXEDWIDTH_TIMERS
.width = TIM2_BITWIDTH,
#endif
.regaddr = STM32_RCC_APB1ENR,
.enable = RCC_APB1ENR_TIM2EN,
.base = STM32_TIM2_BASE,
.psc = CONFIG_STM32F7_TIM2_QEPSC,
.ti1cfg = GPIO_TIM2_CH1IN,
.ti2cfg = GPIO_TIM2_CH2IN,
};
static struct stm32_lowerhalf_s g_tim2lower =
{
.ops = &g_qecallbacks,
.config = &g_tim2config,
.inuse = false,
.lock = SP_UNLOCKED,
};
#endif
#ifdef CONFIG_STM32F7_TIM3_QE
static const struct stm32_qeconfig_s g_tim3config =
{
.timid = 3,
.irq = STM32_IRQ_TIM3,
#ifdef HAVE_MIXEDWIDTH_TIMERS
.width = TIM3_BITWIDTH,
#endif
.regaddr = STM32_RCC_APB1ENR,
.enable = RCC_APB1ENR_TIM3EN,
.base = STM32_TIM3_BASE,
.psc = CONFIG_STM32F7_TIM3_QEPSC,
.ti1cfg = GPIO_TIM3_CH1IN,
.ti2cfg = GPIO_TIM3_CH2IN,
};
static struct stm32_lowerhalf_s g_tim3lower =
{
.ops = &g_qecallbacks,
.config = &g_tim3config,
.inuse = false,
.lock = SP_UNLOCKED,
};
#endif
#ifdef CONFIG_STM32F7_TIM4_QE
static const struct stm32_qeconfig_s g_tim4config =
{
.timid = 4,
.irq = STM32_IRQ_TIM4,
#ifdef HAVE_MIXEDWIDTH_TIMERS
.width = TIM4_BITWIDTH,
#endif
.regaddr = STM32_RCC_APB1ENR,
.enable = RCC_APB1ENR_TIM4EN,
.base = STM32_TIM4_BASE,
.psc = CONFIG_STM32F7_TIM4_QEPSC,
.ti1cfg = GPIO_TIM4_CH1IN,
.ti2cfg = GPIO_TIM4_CH2IN,
};
static struct stm32_lowerhalf_s g_tim4lower =
{
.ops = &g_qecallbacks,
.config = &g_tim4config,
.inuse = false,
.lock = SP_UNLOCKED,
};
#endif
#ifdef CONFIG_STM32F7_TIM5_QE
static const struct stm32_qeconfig_s g_tim5config =
{
.timid = 5,
.irq = STM32_IRQ_TIM5,
#ifdef HAVE_MIXEDWIDTH_TIMERS
.width = TIM5_BITWIDTH,
#endif
.regaddr = STM32_RCC_APB1ENR,
.enable = RCC_APB1ENR_TIM5EN,
.base = STM32_TIM5_BASE,
.psc = CONFIG_STM32F7_TIM5_QEPSC,
.ti1cfg = GPIO_TIM5_CH1IN,
.ti2cfg = GPIO_TIM5_CH2IN,
};
static struct stm32_lowerhalf_s g_tim5lower =
{
.ops = &g_qecallbacks,
.config = &g_tim5config,
.inuse = false,
.lock = SP_UNLOCKED,
};
#endif
#ifdef CONFIG_STM32F7_TIM8_QE
static const struct stm32_qeconfig_s g_tim8config =
{
.timid = 8,
.irq = STM32_IRQ_TIM8UP,
#ifdef HAVE_MIXEDWIDTH_TIMERS
.width = TIM8_BITWIDTH,
#endif
.regaddr = STM32_RCC_APB2ENR,
.enable = RCC_APB2ENR_TIM8EN,
.base = STM32_TIM8_BASE,
.psc = CONFIG_STM32F7_TIM8_QEPSC,
.ti1cfg = GPIO_TIM8_CH1IN,
.ti2cfg = GPIO_TIM8_CH2IN,
};
static struct stm32_lowerhalf_s g_tim8lower =
{
.ops = &g_qecallbacks,
.config = &g_tim8config,
.inuse = false,
.lock = SP_UNLOCKED,
};
#endif
* Private Functions
****************************************************************************/
* Name: stm32_getreg16
*
* Description:
* Read the value of a 16-bit timer register.
*
* Input Parameters:
* priv - A reference to the lower half status
* offset - The offset to the register to read
*
* Returned Value:
* The current contents of the specified register
*
****************************************************************************/
static uint16_t stm32_getreg16(struct stm32_lowerhalf_s *priv, int offset)
{
return getreg16(priv->config->base + offset);
}
* Name: stm32_putreg16
*
* Description:
* Write a value to a 16-bit timer register.
*
* Input Parameters:
* priv - A reference to the lower half status
* offset - The offset to the register to read
*
* Returned Value:
* None
*
****************************************************************************/
static void stm32_putreg16(struct stm32_lowerhalf_s *priv, int offset,
uint16_t value)
{
putreg16(value, priv->config->base + offset);
}
* Name: stm32_getreg32
*
* Description:
* Read the value of a 32-bit timer register. This applies for the STM32
* F7 32-bit registers (CNT, ARR, CRR1-4) in the 32-bit timers TIM2-5 (but
* works OK with the 16-bit TIM1,8 as well).
*
* Input Parameters:
* priv - A reference to the lower half status
* offset - The offset to the register to read
*
* Returned Value:
* The current contents of the specified register
*
****************************************************************************/
static uint32_t stm32_getreg32(struct stm32_lowerhalf_s *priv,
int offset)
{
return getreg32(priv->config->base + offset);
}
* Name: stm32_putreg32
*
* Description:
* Write a value to a 32-bit timer register. This applies for the STM32 F7
* 32-bit registers (CNT, ARR, CRR1-4) in the 32-bit timers TIM2-5 (but
* works OK with the 16-bit TIM1,8 as well).
*
* Input Parameters:
* priv - A reference to the lower half status
* offset - The offset to the register to read
*
* Returned Value:
* None
*
****************************************************************************/
static void stm32_putreg32(struct stm32_lowerhalf_s *priv, int offset,
uint32_t value)
{
putreg32(value, priv->config->base + offset);
}
* Name: stm32_dumpregs
*
* Description:
* Dump all timer registers.
*
* Input Parameters:
* priv - A reference to the QENCODER block status
*
* Returned Value:
* None
*
****************************************************************************/
#if defined(CONFIG_DEBUG_SENSORS) && defined(CONFIG_DEBUG_INFO)
static void stm32_dumpregs(struct stm32_lowerhalf_s *priv,
const char *msg)
{
sninfo("%s:\n", msg);
sninfo(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n",
stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET),
stm32_getreg16(priv, STM32_GTIM_CR2_OFFSET),
stm32_getreg16(priv, STM32_GTIM_SMCR_OFFSET),
stm32_getreg16(priv, STM32_GTIM_DIER_OFFSET));
sninfo(" SR: %04x EGR: %04x CCMR1: %04x CCMR2: %04x\n",
stm32_getreg16(priv, STM32_GTIM_SR_OFFSET),
stm32_getreg16(priv, STM32_GTIM_EGR_OFFSET),
stm32_getreg16(priv, STM32_GTIM_CCMR1_OFFSET),
stm32_getreg16(priv, STM32_GTIM_CCMR2_OFFSET));
sninfo(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n",
stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET),
stm32_getreg16(priv, STM32_GTIM_CNT_OFFSET),
stm32_getreg16(priv, STM32_GTIM_PSC_OFFSET),
stm32_getreg16(priv, STM32_GTIM_ARR_OFFSET));
sninfo(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n",
stm32_getreg16(priv, STM32_GTIM_CCR1_OFFSET),
stm32_getreg16(priv, STM32_GTIM_CCR2_OFFSET),
stm32_getreg16(priv, STM32_GTIM_CCR3_OFFSET),
stm32_getreg16(priv, STM32_GTIM_CCR4_OFFSET));
#if defined(CONFIG_STM32F7_TIM1_QE) || defined(CONFIG_STM32F7_TIM8_QE)
if (priv->config->timid == 1 || priv->config->timid == 8)
{
sninfo(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n",
stm32_getreg16(priv, STM32_ATIM_RCR_OFFSET),
stm32_getreg16(priv, STM32_ATIM_BDTR_OFFSET),
stm32_getreg16(priv, STM32_ATIM_DCR_OFFSET),
stm32_getreg16(priv, STM32_ATIM_DMAR_OFFSET));
}
else
#endif
{
sninfo(" DCR: %04x DMAR: %04x\n",
stm32_getreg16(priv, STM32_GTIM_DCR_OFFSET),
stm32_getreg16(priv, STM32_GTIM_DMAR_OFFSET));
}
}
#endif
* Name: stm32_tim2lower
*
* Description:
* Map a timer number to a device structure
*
****************************************************************************/
static struct stm32_lowerhalf_s *stm32_tim2lower(int tim)
{
switch (tim)
{
#ifdef CONFIG_STM32F7_TIM1_QE
case 1:
return &g_tim1lower;
#endif
#ifdef CONFIG_STM32F7_TIM2_QE
case 2:
return &g_tim2lower;
#endif
#ifdef CONFIG_STM32F7_TIM3_QE
case 3:
return &g_tim3lower;
#endif
#ifdef CONFIG_STM32F7_TIM4_QE
case 4:
return &g_tim4lower;
#endif
#ifdef CONFIG_STM32F7_TIM5_QE
case 5:
return &g_tim5lower;
#endif
#ifdef CONFIG_STM32F7_TIM8_QE
case 8:
return &g_tim8lower;
#endif
default:
return NULL;
}
}
* Name: stm32_interrupt
*
* Description:
* Common timer interrupt handling. NOTE: Only 16-bit timers require timer
* interrupts.
*
****************************************************************************/
#ifdef HAVE_16BIT_TIMERS
static int stm32_interrupt(int irq, void *context, void *arg)
{
struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)arg;
uint16_t regval;
DEBUGASSERT(priv != NULL);
regval = stm32_getreg16(priv, STM32_GTIM_SR_OFFSET);
DEBUGASSERT((regval & ATIM_SR_UIF) != 0);
stm32_putreg16(priv, STM32_GTIM_SR_OFFSET, regval & ~GTIM_SR_UIF);
* maximum value + 1, as appropriate.
*/
regval = stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET);
if ((regval & ATIM_CR1_DIR) != 0)
{
priv->position -= (int32_t)0x00010000;
}
else
{
priv->position += (int32_t)0x00010000;
}
return OK;
}
#endif
* Name: stm32_setup
*
* Description:
* This method is called when the driver is opened. The lower half driver
* should configure and initialize the device so that it is ready for use.
* The initial position value should be zero. *
*
****************************************************************************/
static int stm32_setup(struct qe_lowerhalf_s *lower)
{
struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
uint16_t dier;
uint32_t smcr;
uint32_t ccmr1;
uint16_t ccer;
uint16_t cr1;
#ifdef HAVE_16BIT_TIMERS
uint16_t regval;
int ret;
#endif
modifyreg32(priv->config->regaddr, 0, priv->config->enable);
cr1 = stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET);
* (0=Edge aligned) (Timers 2-5 and 1-8 only)
*/
cr1 &= ~(GTIM_CR1_DIR | GTIM_CR1_CMS_MASK);
stm32_putreg16(priv, STM32_GTIM_CR1_OFFSET, cr1);
#if defined(HAVE_MIXEDWIDTH_TIMERS)
if (priv->config->width == 32)
{
stm32_putreg32(priv, STM32_GTIM_ARR_OFFSET, 0xffffffff);
}
else
{
stm32_putreg16(priv, STM32_GTIM_ARR_OFFSET, 0xffff);
}
#elif defined(HAVE_32BIT_TIMERS)
stm32_putreg32(priv, STM32_GTIM_ARR_OFFSET, 0xffffffff);
#else
stm32_putreg16(priv, STM32_GTIM_ARR_OFFSET, 0xffff);
#endif
*
* If we are doing precise shaft positioning, each qe pulse is important.
* So the STM32 has direct config control on the pulse count prescaler.
* This input clock just limits the incoming pulse rate, which should be
* lower than the peripheral clock due to resynchronization, but it is the
* responsibility of the system designer to decide the correct prescaler
* value, because it has a direct influence on the encoder resolution.
*/
stm32_putreg16(priv, STM32_GTIM_PSC_OFFSET, (uint16_t)priv->config->psc);
#if defined(CONFIG_STM32F7_TIM1_QE) || defined(CONFIG_STM32F7_TIM8_QE)
if (priv->config->timid == 1 || priv->config->timid == 8)
{
stm32_putreg16(priv, STM32_ATIM_RCR_OFFSET, 0);
}
#endif
* and the repetition counter (only for TIM1 and TIM8) value immediately
*/
stm32_putreg16(priv, STM32_GTIM_EGR_OFFSET, GTIM_EGR_UG);
stm32_configgpio(priv->config->ti1cfg);
stm32_configgpio(priv->config->ti2cfg);
smcr = stm32_getreg32(priv, STM32_GTIM_SMCR_OFFSET);
smcr &= ~GTIM_SMCR_SMS_MASK;
smcr |= GTIM_SMCR_ENCMD3;
stm32_putreg32(priv, STM32_GTIM_SMCR_OFFSET, smcr);
ccer = stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET);
ccer &= ~GTIM_CCER_CC1E;
stm32_putreg16(priv, STM32_GTIM_CCER_OFFSET, ccer);
ccmr1 = stm32_getreg32(priv, STM32_GTIM_CCMR1_OFFSET);
ccer = stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET);
ccmr1 &= ~(GTIM_CCMR1_CC1S_MASK | GTIM_CCMR1_IC1F_MASK);
ccmr1 |= GTIM_CCMR_CCS_CCIN1 << GTIM_CCMR1_CC1S_SHIFT;
ccmr1 |= STM32_QENCODER_ICF << GTIM_CCMR1_IC1F_SHIFT;
ccer &= ~(GTIM_CCER_CC1P | GTIM_CCER_CC1NP);
ccer |= GTIM_CCER_CC1E;
stm32_putreg32(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1);
stm32_putreg16(priv, STM32_GTIM_CCER_OFFSET, ccer);
* edge is detected on the capture input.
*/
ccmr1 = stm32_getreg32(priv, STM32_GTIM_CCMR1_OFFSET);
ccmr1 &= ~GTIM_CCMR1_IC1PSC_MASK;
ccmr1 |= (GTIM_CCMR_ICPSC_NOPSC << GTIM_CCMR1_IC1PSC_SHIFT);
stm32_putreg32(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1);
ccer = stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET);
ccer &= ~GTIM_CCER_CC2E;
stm32_putreg16(priv, STM32_GTIM_CCER_OFFSET, ccer);
ccmr1 = stm32_getreg32(priv, STM32_GTIM_CCMR1_OFFSET);
ccer = stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET);
ccmr1 &= ~(GTIM_CCMR1_CC2S_MASK | GTIM_CCMR1_IC2F_MASK);
ccmr1 |= GTIM_CCMR_CCS_CCIN1 << GTIM_CCMR1_CC2S_SHIFT;
ccmr1 |= STM32_QENCODER_ICF << GTIM_CCMR1_IC2F_SHIFT;
ccer &= ~(GTIM_CCER_CC2P | GTIM_CCER_CC2NP);
ccer |= GTIM_CCER_CC2E;
stm32_putreg32(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1);
stm32_putreg16(priv, STM32_GTIM_CCER_OFFSET, ccer);
* edge is detected on the capture input.
*/
ccmr1 = stm32_getreg32(priv, STM32_GTIM_CCMR1_OFFSET);
ccmr1 &= ~GTIM_CCMR1_IC2PSC_MASK;
ccmr1 |= (GTIM_CCMR_ICPSC_NOPSC << GTIM_CCMR1_IC2PSC_SHIFT);
stm32_putreg32(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1);
dier = stm32_getreg16(priv, STM32_GTIM_DIER_OFFSET);
dier &= ~GTIM_DIER_UIE;
stm32_putreg16(priv, STM32_GTIM_DIER_OFFSET, dier);
#ifdef HAVE_16BIT_TIMERS
#ifdef HAVE_MIXEDWIDTH_TIMERS
if (priv->config->width != 32)
#endif
{
ret = irq_attach(priv->config->irq, stm32_interrupt, priv);
if (ret < 0)
{
stm32_shutdown(lower);
return ret;
}
up_enable_irq(priv->config->irq);
}
#endif
cr1 = stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET);
cr1 &= ~GTIM_CR1_UDIS;
stm32_putreg16(priv, STM32_GTIM_CR1_OFFSET, cr1);
cr1 &= ~GTIM_CR1_URS;
stm32_putreg16(priv, STM32_GTIM_CR1_OFFSET, cr1);
#ifdef HAVE_16BIT_TIMERS
#ifdef HAVE_MIXEDWIDTH_TIMERS
if (priv->config->width != 32)
#endif
{
regval = stm32_getreg16(priv, STM32_GTIM_SR_OFFSET);
stm32_putreg16(priv, STM32_GTIM_SR_OFFSET, regval & ~GTIM_SR_UIF);
dier = stm32_getreg16(priv, STM32_GTIM_DIER_OFFSET);
dier |= GTIM_DIER_UIE;
stm32_putreg16(priv, STM32_GTIM_DIER_OFFSET, dier);
}
#endif
cr1 = stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET);
cr1 |= GTIM_CR1_CEN;
stm32_putreg16(priv, STM32_GTIM_CR1_OFFSET, cr1);
stm32_dumpregs(priv, "After setup");
return OK;
}
* Name: stm32_shutdown
*
* Description:
* This method is called when the driver is closed. The lower half driver
* should stop data collection, free any resources, disable timer hardware,
* and put the system into the lowest possible power usage state *
*
****************************************************************************/
static int stm32_shutdown(struct qe_lowerhalf_s *lower)
{
struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
irqstate_t flags;
uint32_t regaddr;
uint32_t regval;
uint32_t resetbit;
uint32_t pincfg;
flags = enter_critical_section();
up_disable_irq(priv->config->irq);
irq_detach(priv->config->irq);
* to prevent any concurrent access to the reset register.
*/
stm32_putreg16(priv, STM32_GTIM_DIER_OFFSET, 0);
stm32_putreg16(priv, STM32_GTIM_SR_OFFSET, 0);
switch (priv->config->timid)
{
#ifdef CONFIG_STM32F7_TIM1_QE
case 1:
regaddr = STM32_RCC_APB2RSTR;
resetbit = RCC_APB2RSTR_TIM1RST;
break;
#endif
#ifdef CONFIG_STM32F7_TIM2_QE
case 2:
regaddr = STM32_RCC_APB1RSTR;
resetbit = RCC_APB1RSTR_TIM2RST;
break;
#endif
#ifdef CONFIG_STM32F7_TIM3_QE
case 3:
regaddr = STM32_RCC_APB1RSTR;
resetbit = RCC_APB1RSTR_TIM3RST;
break;
#endif
#ifdef CONFIG_STM32F7_TIM4_QE
case 4:
regaddr = STM32_RCC_APB1RSTR;
resetbit = RCC_APB1RSTR_TIM4RST;
break;
#endif
#ifdef CONFIG_STM32F7_TIM5_QE
case 5:
regaddr = STM32_RCC_APB1RSTR;
resetbit = RCC_APB1RSTR_TIM5RST;
break;
#endif
#ifdef CONFIG_STM32F7_TIM8_QE
case 8:
regaddr = STM32_RCC_APB2RSTR;
resetbit = RCC_APB2RSTR_TIM8RST;
break;
#endif
default:
leave_critical_section(flags);
return -EINVAL;
}
* into a state where stm32_start() can be called.
*/
regval = getreg32(regaddr);
regval |= resetbit;
putreg32(regval, regaddr);
regval &= ~resetbit;
putreg32(regval, regaddr);
leave_critical_section(flags);
sninfo("regaddr: %08" PRIx32 " resetbit: %08" PRIx32 "\n",
regaddr, resetbit);
stm32_dumpregs(priv, "After stop");
modifyreg32(priv->config->regaddr, priv->config->enable, 0);
pincfg = priv->config->ti1cfg & (GPIO_PORT_MASK | GPIO_PIN_MASK);
pincfg |= STM32_GPIO_INPUT_FLOAT;
stm32_configgpio(pincfg);
pincfg = priv->config->ti2cfg & (GPIO_PORT_MASK | GPIO_PIN_MASK);
pincfg |= STM32_GPIO_INPUT_FLOAT;
stm32_configgpio(pincfg);
return OK;
}
* Name: stm32_position
*
* Description:
* Return the current position measurement.
*
****************************************************************************/
static int stm32_position(struct qe_lowerhalf_s *lower, int32_t *pos)
{
struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
#ifdef HAVE_16BIT_TIMERS
irqstate_t flags;
int32_t position;
int32_t verify;
uint32_t count;
DEBUGASSERT(lower && priv->inuse);
flags = spin_lock_irqsave(&priv->lock);
do
{
position = priv->position;
count = stm32_getreg32(priv, STM32_GTIM_CNT_OFFSET);
verify = priv->position;
}
while (position != verify);
spin_unlock_irqrestore(&priv->lock, flags);
*pos = position + (int32_t)count;
#else
*pos = (int32_t)stm32_getreg32(priv, STM32_GTIM_CNT_OFFSET);
#endif
return OK;
}
* Name: stm32_reset
*
* Description:
* Reset the position measurement to zero.
*
****************************************************************************/
static int stm32_reset(struct qe_lowerhalf_s *lower)
{
struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
#ifdef HAVE_16BIT_TIMERS
irqstate_t flags;
sninfo("Resetting position to zero\n");
DEBUGASSERT(lower && priv->inuse);
* atomic (if possible)
*/
flags = enter_critical_section();
stm32_putreg32(priv, STM32_GTIM_CNT_OFFSET, 0);
priv->position = 0;
leave_critical_section(flags);
#else
sninfo("Resetting position to zero\n");
DEBUGASSERT(lower && priv->inuse);
stm32_putreg32(priv, STM32_GTIM_CNT_OFFSET, 0);
#endif
return OK;
}
* Name: stm32_ioctl
*
* Description:
* Lower-half logic may support platform-specific ioctl commands
*
****************************************************************************/
static int stm32_ioctl(struct qe_lowerhalf_s *lower, int cmd,
unsigned long arg)
{
return -ENOTTY;
}
* Public Functions
****************************************************************************/
* Name: stm32_qeinitialize
*
* Description:
* Initialize a quadrature encoder interface. This function must be
* called from board-specific logic.
*
* Input Parameters:
* devpath - The full path to the driver to register. E.g., "/dev/qe0"
* tim - The timer number to used. 'tim' must be an element of
* {1,2,3,4,5,8}
*
* Returned Value:
* Zero on success; A negated errno value is returned on failure.
*
****************************************************************************/
int stm32_qeinitialize(const char *devpath, int tim)
{
struct stm32_lowerhalf_s *priv;
int ret;
* timer
*/
priv = stm32_tim2lower(tim);
if (!priv)
{
snerr("ERROR: TIM%d support not configured\n", tim);
return -ENXIO;
}
if (priv->inuse)
{
snerr("ERROR: TIM%d is in-use\n", tim);
return -EBUSY;
}
ret = qe_register(devpath, (struct qe_lowerhalf_s *)priv);
if (ret < 0)
{
snerr("ERROR: qe_register failed: %d\n", ret);
return ret;
}
stm32_shutdown((struct qe_lowerhalf_s *)priv);
priv->inuse = true;
return OK;
}
#endif