nuttx/arch/arm/src/stm32/stm32_foc.c
Nathan Hartman ce20211357 Fix various typos in comments and documentation
Fix typos in these files:
    * Documentation/components/drivers/character/foc.rst
    * Documentation/guides/cpp_cmake.rst
    * Kconfig
    * arch/arm/src/imxrt/imxrt_lpspi.c
    * arch/arm/src/kinetis/kinetis_spi.c
    * arch/arm/src/kl/kl_spi.c
    * arch/arm/src/lpc31xx/lpc31_spi.c
    * arch/arm/src/nrf52/nrf52_radio.h
    * arch/arm/src/s32k1xx/s32k1xx_lpspi.c
    * arch/arm/src/stm32/Kconfig
    * arch/arm/src/stm32/stm32_adc.c
    * arch/arm/src/stm32/stm32_foc.c
    * arch/arm/src/stm32/stm32_foc.h
    * arch/arm/src/stm32/stm32_pwm.c
    * arch/arm/src/stm32/stm32_spi.c
    * arch/arm/src/stm32f0l0g0/stm32_spi.c
    * arch/arm/src/stm32f7/Kconfig
    * arch/arm/src/stm32f7/stm32_spi.c
    * arch/arm/src/stm32h7/Kconfig
    * arch/arm/src/stm32h7/stm32_allocateheap.c
    * arch/arm/src/stm32h7/stm32_fmc.c
    * arch/arm/src/stm32h7/stm32_fmc.h
    * arch/arm/src/stm32h7/stm32_pwm.c
    * arch/arm/src/stm32h7/stm32_qspi.c
    * arch/arm/src/stm32h7/stm32_spi.c
    * arch/arm/src/stm32l4/stm32l4_pwm.c
    * arch/arm/src/stm32l4/stm32l4_spi.c
    * arch/arm/src/stm32l5/Kconfig
    * arch/arm/src/stm32l5/stm32l5_spi.c
    * arch/renesas/src/rx65n/rx65n_dtc.c
    * arch/renesas/src/rx65n/rx65n_usbdev.c
    * arch/risc-v/src/rv32m1/rv32m1_serial.c
    * boards/arm/stm32/b-g431b-esc1/src/stm32_foc.c
    * boards/arm/stm32/nucleo-f103rb/src/stm32_foc_ihm07m1.c
    * boards/arm/stm32/nucleo-f302r8/src/stm32_foc_ihm07m1.c
    * boards/arm/stm32h7/nucleo-h743zi2/README.txt
    * boards/risc-v/rv32m1/rv32m1-vega/README.txt
    * boards/sim/sim/sim/scripts/Make.defs
    * drivers/1wire/1wire.c
    * drivers/1wire/1wire_internal.h
    * drivers/lcd/Kconfig
    * drivers/syslog/ramlog.c
    * fs/fat/Kconfig
    * libs/libc/debug/Kconfig
    * libs/libc/machine/Kconfig
    * libs/libc/stdio/lib_libvsprintf.c
    * libs/libc/stdlib/lib_div.c
    * libs/libc/stdlib/lib_ldiv.c
    * libs/libc/stdlib/lib_lldiv.c
    * libs/libdsp/lib_observer.c
2021-07-04 11:23:26 -05:00

2427 lines
63 KiB
C

/****************************************************************************
* arch/arm/src/stm32/stm32_foc.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 <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/arch.h>
#include "arm_arch.h"
#include "stm32_pwm.h"
#include "stm32_adc.h"
#include "stm32_dma.h"
#include <arch/irq.h>
#include <arch/chip/chip.h>
#include "stm32_foc.h"
#include "hardware/stm32_dbgmcu.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Verify peripheral configuration ******************************************/
/* This is the lower-half implementation for the STM32 FOC devices.
*
* We currently support a current sensing topology with two and three shunts.
* Configuration with a single-shunt is not supported at the moment and will
* require additional current reconstruction logic.
*
* A single FOC device uses one advanced timer to generate a center-aligned
* PWM which control phase switches bridge. Phase currents must be sampled
* at vector 0 (all low-side switches are on and the current flows through
* current sensors).
*
* This implementation uses one ADC per controller and we only use injected
* conversion to sample currents. ADC regular conversion is not used
* and can be used to other tasks with the help of DMA transfer.
* For ADC regular conversion, only DMA transfer is possible since ADC
* interrupt handler is reserved for the FOC only.
*
* The ADC conversion trigger is configurable. Available options are:
* 1. TRGO events generated on update event
* 2. CCR4 events
*
* There are some differences in implementation depending on the peripherals
* supported by the chip.
*
* There is no differences according to TIMER IPv1 and IPv2 affecting
* this implementation.
*
* For STM32 ADC cores there are some dissimilarities that had to be taken
* into account. It is:
*
* 1. For ADC IPv1 (F2, F4, F7, L1)
* - all ADC instances coupled in single block
* - single entry point for ADC123 interrupts
*
* 2. For ADC IPv1 basic (F1, F37x)
* - ADC instances are no coupled in blocks
* - common interrupts for ADC1 and ADC2
*
* 3. For ADC IPv2 (F3 (without F37x), H7, L4, L4+, G4)
* - ADC grouped in slave-master configuration (ADC12, ADC34)
*
* This code will not work for chips with ADC IPv2 basic (F0, L0, G0).
* For these, only regular channels are available and we cannot use injected
* conversion.
*
* Currently, up to two FOC instances are supported.
*/
/* Verify system configuration **********************************************/
/* This is not for ADC IPv2 basic */
#if defined(CONFIG_STM32_HAVE_IP_ADC_V2) && defined(HAVE_BASIC_ADC)
# error Not supported ADC IP core
#endif
/* Multi instances support tested only on IP_ADC_V1 */
#if CONFIG_MOTOR_FOC_INST > 1
# if defined(CONFIG_STM32_HAVE_IP_ADC_V2)
# error Not tested yet
# endif
#endif
/* PWM lower-half ops and ADC lower-half ops must be enabled */
#ifndef CONFIG_STM32_PWM_LL_OPS
# error PWM low-level operations interface must be enabled
#endif
#ifndef CONFIG_STM32_ADC_LL_OPS
# error ADC low-level operations interface must be enabled
#endif
/* We don't want start conversion during ADC setup */
#ifndef CONFIG_STM32_ADC_NO_STARTUP_CONV
# error ADC startup conversion must be disabled
#endif
/* We need interface to change ADC sample-time */
#ifndef CONFIG_STM32_ADC_CHANGE_SAMPLETIME
# error ADC sample-time configuration interface must be enabled
#endif
/* Debug register for PWM timers */
#if defined(CONFIG_STM32_HAVE_IP_DBGMCU_V2) || \
defined(CONFIG_STM32_HAVE_IP_DBGMCU_V3)
# define FOC_PWM_FZ_REG (STM32_DBGMCU_APB2_FZ)
#elif defined(CONFIG_STM32_HAVE_IP_DBGMCU_V1)
# define FOC_PWM_FZ_REG (STM32_DBGMCU_CR)
#endif
/* FOC0 always use TIMER1 for PWM */
#ifdef CONFIG_STM32_FOC_FOC0
# define FOC0_PWM (1)
# define FOC0_PWM_NCHANNELS (PWM_TIM1_NCHANNELS)
# define FOC0_PWM_BASE (STM32_TIM1_BASE)
# if defined(CONFIG_STM32_HAVE_IP_DBGMCU_V2) || \
defined(CONFIG_STM32_HAVE_IP_DBGMCU_V3)
# define FOC0_PWM_FZ_BIT (DBGMCU_APB2_TIM1STOP)
# elif defined(CONFIG_STM32_HAVE_IP_DBGMCU_V1)
# define FOC0_PWM_FZ_BIT (DBGMCU_CR_TIM1STOP)
# endif
# if CONFIG_STM32_TIM1_MODE != 2
# error TIM1 must be configured in center-aligned mode 1
# endif
#endif /* CONFIG_STM32_FOC_FOC0 */
/* FOC1 always use TIMER8 for PWM */
#ifdef CONFIG_STM32_FOC_FOC1
# define FOC1_PWM (8)
# define FOC1_PWM_NCHANNELS (PWM_TIM8_NCHANNELS)
# define FOC1_PWM_BASE (STM32_TIM8_BASE)
# if defined(CONFIG_STM32_HAVE_IP_DBGMCU_V2)
# define FOC1_PWM_FZ_BIT (DBGMCU_APB2_TIM8STOP)
# elif defined(CONFIG_STM32_HAVE_IP_DBGMCU_V1)
# define FOC1_PWM_FZ_BIT (DBGMCU_CR_TIM8STOP)
# endif
# if CONFIG_STM32_TIM8_MODE != 2
# error TIM8 must be configured in center-aligned mode 1
# endif
#endif
/* The maximum supported number of phases depends on the ADC trigger */
#if defined(CONFIG_STM32_FOC_ADC_CCR4)
# if CONFIG_MOTOR_FOC_PHASES > 3
# error max 3 phases supported
# endif
#elif defined(CONFIG_STM32_FOC_ADC_TRGO)
# if CONFIG_MOTOR_FOC_PHASES > 4
# error max 4 phases supported
# endif
#else
# error
#endif
/* Tested only for 3-phase devices */
#if CONFIG_MOTOR_FOC_PHASES != 3
# error Tested only for 3-phase devices
#endif
/* Only one ADC trigger must be selected */
#if defined(CONFIG_STM32_FOC_ADC_CCR4) && defined(CONFIG_STM32_FOC_ADC_TRGO)
# error Invalid ADC trigger configuration
#endif
/* Phase currents can only be sampled when all low-side switches are off.
* This is only valid for the V0 vector in the SVM.
*
* For PWM mode 1:
* V7 for CNTR = 0
* V0 for CNTR = ARR
*
* For PWM mode 2:
* V7 for CNTR = ARR
* V0 for CNTR = 0
*/
#if defined(CONFIG_STM32_FOC_ADC_CCR4)
/* FOC ADC trigger on CCR4 **************************************************/
/* PWM channels configuration:
* - n channels for phases PWM (CCR1, CCR2, CCR3)
* - 1 channel for ADC injection sequence trigger (CCR4)
*/
# if defined(CONFIG_STM32_FOC_FOC0)
# if FOC0_PWM_NCHANNELS != (CONFIG_MOTOR_FOC_PHASES + 1)
# error Invalid channels configuration
# endif
# endif
# if defined(CONFIG_STM32_FOC_FOC1)
# if FOC1_PWM_NCHANNELS != (CONFIG_MOTOR_FOC_PHASES + 1)
# error Invalid channels configuration
# endif
# endif
/* Generalize JEXTSEL bits for CCR4 trigger.
*
* ADC trigger event on PWM timer CCR4 (rising edge).
*
* This implementation uses PWM mode 1 so:
* TIMx CCR4 = (ARR - trigger_offset)
*/
# if defined(CONFIG_STM32_HAVE_IP_ADC_V2)
# ifdef CONFIG_STM32_HAVE_TIM1
# define ADC_JEXTSEL_T1CC4 (ADC12_JSQR_JEXTSEL_T1CC4)
# endif
# ifdef CONFIG_STM32_HAVE_TIM8
# define ADC_JEXTSEL_T8CC4 (ADC12_JSQR_JEXTSEL_T8CC4)
# endif
# elif defined(CONFIG_STM32_HAVE_IP_ADC_V1)
# ifdef CONFIG_STM32_HAVE_TIM1
# define ADC_JEXTSEL_T1CC4 (ADC_CR2_JEXTSEL_T1CC4)
# endif
# ifdef CONFIG_STM32_HAVE_TIM8
# define ADC_JEXTSEL_T8CC4 (ADC_CR2_JEXTSEL_T8CC4)
# endif
# else
# error Not supported
# endif
/* ADC trigger offset - must be greater than 0! */
# define ADC_TRIGGER_OFFSET (1)
# ifdef CONFIG_STM32_FOC_FOC0
# define FOC0_ADC_JEXTSEL (ADC_JEXTSEL_T1CC4)
# endif
# ifdef CONFIG_STM32_FOC_FOC1
# define FOC1_ADC_JEXTSEL (ADC_JEXTSEL_T8CC4)
# endif
#elif defined(CONFIG_STM32_FOC_ADC_TRGO)
/* FOC ADC trigger on TRGO **************************************************/
/* PWM TRGO support must be enabled */
# ifndef CONFIG_STM32_PWM_TRGO
# error PWM TRGO support must be enabled
# endif
/* TRGO on update event = ATIM_CR2_MMS_UPDATE (2) */
# define FOC_PWM_TRGO (2)
/* PWM channels configuration:
* - n channels for phases PWM (CCR1, CCR2, CCR3, CCR4)
*/
# if defined(CONFIG_STM32_FOC_FOC0)
# if FOC0_PWM_NCHANNELS != (CONFIG_MOTOR_FOC_PHASES)
# error Invalid channels configuration
# endif
# endif
# if defined(CONFIG_STM32_FOC_FOC1)
# if FOC1_PWM_NCHANNELS != (CONFIG_MOTOR_FOC_PHASES)
# error Invalid channels configuration
# endif
# endif
/* Generalize JEXTSEL bits for TRGO trigger.
*
* ADC trigger event on PWM timer TRGO (rising edge).
*
* This implementation uses PWM mode 1 so:
* TIMx TRGO = (ARR)
*/
# if defined(CONFIG_STM32_HAVE_IP_ADC_V2)
# ifdef CONFIG_STM32_FOC_USE_TIM1
# define ADC_JEXTSEL_T1TRGO (ADC12_JSQR_JEXTSEL_T1TRGO)
# endif
# ifdef CONFIG_STM32_FOC_USE_TIM8
# define ADC_JEXTSEL_T8TRGO (ADC12_JSQR_JEXTSEL_T8TRGO)
# endif
# elif defined(CONFIG_STM32_HAVE_IP_ADC_V1)
# ifdef CONFIG_STM32_FOC_USE_TIM1
# define ADC_JEXTSEL_T1TRGO (ADC_CR2_JEXTSEL_T1TRGO)
# endif
# ifdef CONFIG_STM32_FOC_USE_TIM8
# error TIM8 and TRGO trigger not supported for ADC IPv1
# endif
# else
# error Not supported
# endif
# ifdef CONFIG_STM32_FOC_FOC0
# define FOC0_ADC_JEXTSEL (ADC_JEXTSEL_T1TRGO)
# endif
# ifdef CONFIG_STM32_FOC_FOC1
# define FOC1_ADC_JEXTSEL (ADC_JEXTSEL_T8TRGO)
# endif
#else
/* No trigger selected ******************************************************/
# error Invalid FOC ADC trigger
#endif
/* Phase current samples for FOC0 */
#ifdef CONFIG_STM32_FOC_FOC0
# ifdef CONFIG_STM32_FOC_FOC0_ADC1
# define FOC0_ADC 1
# endif
# ifdef CONFIG_STM32_FOC_FOC0_ADC2
# define FOC0_ADC 2
# endif
# ifdef CONFIG_STM32_FOC_FOC0_ADC3
# define FOC0_ADC 3
# endif
# ifdef CONFIG_STM32_FOC_FOC0_ADC4
# define FOC0_ADC 4
# endif
#endif
/* Phase current samples for FOC1 */
#ifdef CONFIG_STM32_FOC_FOC1
# ifdef CONFIG_STM32_FOC_FOC1_ADC1
# define FOC1_ADC 1
# endif
# ifdef CONFIG_STM32_FOC_FOC1_ADC2
# define FOC1_ADC 2
# endif
# ifdef CONFIG_STM32_FOC_FOC1_ADC3
# define FOC1_ADC 3
# endif
# ifdef CONFIG_STM32_FOC_FOC1_ADC4
# define FOC1_ADC 4
# endif
#endif
/* The number of required injected channels */
#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND
# define FOC_ADC_INJ_CHAN_REQUIRED (CONFIG_MOTOR_FOC_SHUNTS + 1)
#else
# define FOC_ADC_INJ_CHAN_REQUIRED (CONFIG_MOTOR_FOC_SHUNTS)
#endif
/* Validate ADC configuration:
* 1. ADC must be supported by chip,
* 2. ADC support for injected channels must be enabled,
* 3. ADC software trigger starts only regular conversion.
*/
#ifdef CONFIG_STM32_FOC_USE_ADC1
# ifndef CONFIG_STM32_HAVE_ADC1
# error ADC1 not supported !
# endif
# ifndef ADC1_HAVE_JEXTCFG
# error ADC1 must support JEXTCFG
# endif
# if CONFIG_STM32_ADC1_ANIOC_TRIGGER != 1
# error CONFIG_STM32_ADC1_ANIOC_TRIGGER must be 1
# endif
# if CONFIG_STM32_ADC1_INJECTED_CHAN != FOC_ADC_INJ_CHAN_REQUIRED
# error Invalid configuration for ADC1 injected channels
# endif
#endif
#ifdef CONFIG_STM32_FOC_USE_ADC2
# ifndef CONFIG_STM32_HAVE_ADC2
# error ADC2 not supported !
# endif
# ifndef ADC2_HAVE_JEXTCFG
# error ADC2 must support JEXTCFG
# endif
# if CONFIG_STM32_ADC2_ANIOC_TRIGGER != 1
# error CONFIG_STM32_ADC2_ANIOC_TRIGGER must be 1
# endif
# if CONFIG_STM32_ADC2_INJECTED_CHAN != FOC_ADC_INJ_CHAN_REQUIRED
# error Invalid configuration for ADC2 injected channels
# endif
#endif
#ifdef CONFIG_STM32_FOC_USE_ADC3
# ifndef CONFIG_STM32_HAVE_ADC3
# error ADC3 not supported !
# endif
# ifndef ADC3_HAVE_JEXTCFG
# error ADC3 must support JEXTCFG
# endif
# if CONFIG_STM32_ADC3_ANIOC_TRIGGER != 1
# error CONFIG_STM32_ADC3_ANIOC_TRIGGER must be 1
# endif
# if CONFIG_STM32_ADC3_INJECTED_CHAN != FOC_ADC_INJ_CHAN_REQUIRED
# error Invalid configuration for ADC3 injected channels
# endif
#endif
#ifdef CONFIG_STM32_FOC_USE_ADC4
# ifndef CONFIG_STM32_HAVE_ADC4
# error ADC4 not supported !
# endif
# ifndef ADC4_HAVE_JEXTCFG
# error ADC4 must support JEXTCFG
# endif
# if CONFIG_STM32_ADC4_ANIOC_TRIGGER != 1
# error CONFIG_STM32_ADC4_ANIOC_TRIGGER must be 1
# endif
# if CONFIG_STM32_ADC4_INJECTED_CHAN != FOC_ADC_INJ_CHAN_REQUIRED
# error Invalid configuration for ADC4 injected channels
# endif
#endif
/* Max 3 shunts supported if STM32G4 ADC CHAN0 workaround enabled */
#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND
# if CONFIG_MOTOR_FOC_SHUNTS > 3
# error
# endif
#endif
/* Combine JEXTSEL with JEXTEN default */
#ifdef CONFIG_STM32_FOC_FOC0
# define FOC0_ADC_JEXT (ADC_JEXTREG_JEXTEN_DEFAULT | FOC0_ADC_JEXTSEL)
#endif
#ifdef CONFIG_STM32_FOC_FOC1
# define FOC1_ADC_JEXT (ADC_JEXTREG_JEXTEN_DEFAULT | FOC1_ADC_JEXTSEL)
#endif
/* Generalize ADC interrupt flags */
#if defined(CONFIG_STM32_HAVE_IP_ADC_V2)
# define FOC_ADC_ISR_FOC ADC_ISR_JEOS
# define FOC_ADC_IER_FOC ADC_IER_JEOS
# define FOC_ADC_ISR_OVR ADC_INT_OVR
#elif defined(CONFIG_STM32_HAVE_IP_ADC_V1)
# define FOC_ADC_ISR_FOC ADC_ISR_JEOC
# define FOC_ADC_IER_FOC ADC_IER_JEOC
# define FOC_ADC_ISR_OVR ADC_SR_OVR
#else
# error Not supported
#endif
/* We have 3 possible ADC IRQ configuration */
#if defined(STM32_IRQ_ADC1)
/* Only ADC1 supported */
# define STM32_IRQ_ADC1_FOC STM32_IRQ_ADC1
#elif defined(STM32_IRQ_ADC12)
/* ADC1 + ADC2 interrupt */
# define STM32_IRQ_ADC1_FOC STM32_IRQ_ADC12
# define STM32_IRQ_ADC2_FOC STM32_IRQ_ADC12
/* ADC3 + ADC4 interrupt */
# define STM32_IRQ_ADC3_FOC STM32_IRQ_ADC34
# define STM32_IRQ_ADC4_FOC STM32_IRQ_ADC34
#elif defined(STM32_IRQ_ADC)
/* ADC1 + ADC2 + ADC3 interrupt */
# define STM32_IRQ_ADC1_FOC STM32_IRQ_ADC
# define STM32_IRQ_ADC2_FOC STM32_IRQ_ADC
# define STM32_IRQ_ADC3_FOC STM32_IRQ_ADC
#endif
/* ADC common ***************************************************************/
/* Common for ADCv1 */
#if defined(CONFIG_STM32_HAVE_IP_ADC_V1) && !defined(HAVE_BASIC_ADC)
# define FOC_ADC_HAVE_CMN (1)
# ifdef CONFIG_STM32_HAVE_ADC1
# define FOC_ADC1_CMN (&g_stm32_foc_adccmn123)
# endif
# ifdef CONFIG_STM32_HAVE_ADC2
# define FOC_ADC2_CMN (&g_stm32_foc_adccmn123)
# endif
# ifdef CONFIG_STM32_HAVE_ADC3
# define FOC_ADC3_CMN (&g_stm32_foc_adccmn123)
# endif
#endif
/* Common for ADCv1 basic */
#if defined(CONFIG_STM32_HAVE_IP_ADC_V1) && defined(HAVE_BASIC_ADC)
# undef FOC_ADC_HAVE_CMN
# ifdef CONFIG_STM32_HAVE_ADC1
# define FOC_ADC1_CMN (0)
# endif
# ifdef CONFIG_STM32_HAVE_ADC2
# define FOC_ADC2_CMN (0)
# endif
# ifdef CONFIG_STM32_HAVE_ADC3
# define FOC_ADC3_CMN (0)
# endif
#endif
/* Common for ADCv2 */
#ifdef CONFIG_STM32_HAVE_IP_ADC_V2
# define FOC_ADC_HAVE_CMN (1)
# ifdef CONFIG_STM32_HAVE_ADC1
# define FOC_ADC1_CMN (&g_stm32_foc_adccmn12)
# endif
# ifdef CONFIG_STM32_HAVE_ADC2
# define FOC_ADC2_CMN (&g_stm32_foc_adccmn12)
# endif
# ifdef CONFIG_STM32_HAVE_ADC3
# define FOC_ADC3_CMN (&g_stm32_foc_adccmn34)
# endif
# ifdef CONFIG_STM32_HAVE_ADC4
# define FOC_ADC4_CMN (&g_stm32_foc_adccmn34)
# endif
#endif
/* FOC ADC configuration ****************************************************/
#ifdef CONFIG_STM32_FOC_FOC0
# ifdef CONFIG_STM32_FOC_FOC0_ADC1
# define FOC0_ADC_IRQ STM32_IRQ_ADC1_FOC
# define FOC0_ADC_CMN FOC_ADC1_CMN
# endif
# ifdef CONFIG_STM32_FOC_FOC0_ADC2
# define FOC0_ADC_IRQ STM32_IRQ_ADC2_FOC
# define FOC0_ADC_CMN FOC_ADC2_CMN
# endif
# ifdef CONFIG_STM32_FOC_FOC0_ADC3
# define FOC0_ADC_IRQ STM32_IRQ_ADC3_FOC
# define FOC0_ADC_CMN FOC_ADC3_CMN
# endif
# ifdef CONFIG_STM32_FOC_FOC0_ADC4
# define FOC0_ADC_IRQ STM32_IRQ_ADC4_FOC
# define FOC0_ADC_CMN FOC_ADC4_CMN
# endif
#endif
#ifdef CONFIG_STM32_FOC_FOC1
# ifdef CONFIG_STM32_FOC_FOC1_ADC1
# define FOC1_ADC_IRQ STM32_IRQ_ADC1_FOC
# define FOC1_ADC_CMN FOC_ADC1_CMN
# endif
# ifdef CONFIG_STM32_FOC_FOC1_ADC2
# define FOC1_ADC_IRQ STM32_IRQ_ADC2_FOC
# define FOC1_ADC_CMN FOC_ADC2_CMN
# endif
# ifdef CONFIG_STM32_FOC_FOC1_ADC3
# define FOC1_ADC_IRQ STM32_IRQ_ADC3_FOC
# define FOC1_ADC_CMN FOC_ADC3_CMN
# endif
# ifdef CONFIG_STM32_FOC_FOC1_ADC4
# define FOC1_ADC_IRQ STM32_IRQ_ADC4_FOC
# define FOC1_ADC_CMN FOC_ADC4_CMN
# endif
#endif
/* Helper macros ************************************************************/
/* Get arch-specific FOC private part */
#define STM32_FOC_PRIV_FROM_DEV_GET(d) \
((FAR struct stm32_foc_priv_s *)(d)->lower->data)
/* Get board-specific FOC data */
#define STM32_FOC_BOARD_FROM_DEV_GET(d) \
((STM32_FOC_PRIV_FROM_DEV_GET(d))->board)
/* Get arch-specific FOC devices */
#define STM32_FOC_DEV_FROM_DEV_GET(d) \
((STM32_FOC_PRIV_FROM_DEV_GET(d))->dev)
/* Get PWM device */
#define PWM_FROM_FOC_DEV_GET(d) (STM32_FOC_DEV_FROM_DEV_GET(d)->pwm)
/* Get ADC device */
#define ADC_FROM_FOC_DEV_GET(d) (STM32_FOC_DEV_FROM_DEV_GET(d)->adc)
/* Define PWM all outputs */
#ifdef CONFIG_STM32_FOC_HAS_PWM_COMPLEMENTARY
# define PMW_OUTPUTS_ALL_COMP (STM32_PWM_OUT1N| \
STM32_PWM_OUT2N| \
STM32_PWM_OUT3N)
#else
# define PMW_OUTPUTS_ALL_COMP (0)
#endif
#if defined(CONFIG_STM32_FOC_ADC_CCR4) || (CONFIG_MOTOR_FOC_PHASES > 3)
# define PMW_OUTPUTS_ALL_OUT4 (STM32_PWM_OUT4)
#else
# define PMW_OUTPUTS_ALL_OUT4 (0)
#endif
#define PWM_OUTPUTS_ALL (STM32_PWM_OUT1| \
STM32_PWM_OUT2| \
STM32_PWM_OUT3| \
PMW_OUTPUTS_ALL_COMP| \
PMW_OUTPUTS_ALL_OUT4)
/* Enable all PWM outputs at once (include CHAN4 for ADC trigger) */
#define PWM_ALL_OUTPUTS_ENABLE(pwm, state) \
PWM_OUTPUTS_ENABLE(pwm, PWM_OUTPUTS_ALL, state);
/* Enable/disable ADC interrupts (FOC worker loop) */
#define STM32_ADC_ENABLEINT(adc) STM32_ADC_INT_ENABLE(adc, FOC_ADC_IER_FOC)
#define STM32_ADC_DISABLEINT(adc) STM32_ADC_INT_DISABLE(adc, FOC_ADC_IER_FOC)
/* ADC calibration samples */
#define CAL_SAMPLES (5000)
/* ADC calibration frequency */
#define CAL_FREQ (10000)
/* Define PWM modes to control H-bridge.
*
* Any H-bridge specific configuration can be done with PWM_CHxPOL
* and PWM_CHxIDLE configuration options
*/
#define PWM_MODE_FOC STM32_CHANMODE_PWM1
#define PWM_MODE_ADC_TRG STM32_CHANMODE_PWM1
#define PWM_MODE_HSLO_LSHI STM32_CHANMODE_OCREFHI
#define PWM_MODE_HSHI_LSLO STM32_CHANMODE_OCREFLO
#define PWM_MODE_HIZ STM32_CHANMODE_FRZN
/****************************************************************************
* Private Types
****************************************************************************/
/* STM32 FOC devices.
* This strucutre gathers all low level drivers required by FOC device.
*/
struct stm32_foc_dev_s
{
uint8_t pwm_inst; /* PWM timer instance */
uint8_t adc_inst; /* ADC timer instance */
uint32_t pwm_base; /* PWM timer base */
uint32_t adc_irq; /* ADC irq */
uint32_t jextval; /* JEXT configuration */
FAR struct stm32_pwm_dev_s *pwm; /* PWM device reference */
FAR struct adc_dev_s *adc_dev; /* ADC device reference */
FAR struct stm32_adc_dev_s *adc; /* STM32 ADC device reference */
/* Interrupt handler for FOC device */
CODE int (*adc_isr)(FAR struct foc_dev_s *dev);
};
/* STM32 FOC common data */
struct stm32_foc_adccmn_s
{
uint8_t cntr; /* ADC common counter */
sem_t sem; /* Lock data */
};
/* STM32 FOC volatile data */
struct stm32_foc_data_s
{
foc_current_t curr[CONFIG_MOTOR_FOC_PHASES]; /* Current */
uint8_t notifier_div; /* FOC notifier prescaler */
uint32_t adc_freq; /* ADC interrupts frequency */
uint32_t per; /* PWM timer period (ARR) */
uint32_t adcint_cntr; /* ADC interrupt counter */
uint32_t curr_offset[CONFIG_MOTOR_FOC_SHUNTS]; /* ADC current offset */
int16_t curr_raw[CONFIG_MOTOR_FOC_SHUNTS]; /* ADC current RAW */
};
/* STM32 FOC private */
struct stm32_foc_priv_s
{
/* Volatile data */
struct stm32_foc_data_s data;
/* ADC calbration done */
sem_t cal_done_sem;
/* STM32 FOC devices */
FAR struct stm32_foc_dev_s *dev;
/* Board-specific data */
FAR struct stm32_foc_board_s *board;
/* Upper-half FOC controller callbacks */
FAR const struct foc_callbacks_s *cb;
#ifdef FOC_ADC_HAVE_CMN
/* Common data */
FAR struct stm32_foc_adccmn_s *adc_cmn;
#endif
};
/****************************************************************************
* Private Function Protototypes
****************************************************************************/
/* FOC lower-half operations */
static int stm32_foc_configure(FAR struct foc_dev_s *dev,
FAR struct foc_cfg_s *cfg);
static int stm32_foc_setup(FAR struct foc_dev_s *dev);
static int stm32_foc_shutdown(FAR struct foc_dev_s *dev);
static int stm32_foc_start(FAR struct foc_dev_s *dev, bool state);
static int stm32_foc_pwm_duty_set(FAR struct foc_dev_s *dev,
FAR foc_duty_t *duty);
static int stm32_foc_ioctl(FAR struct foc_dev_s *dev, int cmd,
unsigned long arg);
static int stm32_foc_bind(FAR struct foc_dev_s *dev,
FAR struct foc_callbacks_s *cb);
static int stm32_foc_fault_clear(FAR struct foc_dev_s *dev);
#ifdef CONFIG_MOTOR_FOC_TRACE
int stm32_foc_trace_init(FAR struct foc_dev_s *dev);
void stm32_foc_trace(FAR struct foc_dev_s *dev, int type, bool state);
#endif
/* ADC handlers */
static int stm32_foc_adc_handler(int irq, FAR void *context, FAR void *arg);
static int stm32_foc_adc_calibration_handler(FAR struct foc_dev_s *dev);
static int stm32_foc_worker_handler(FAR struct foc_dev_s *dev);
/* Helpers */
static void stm32_foc_curr_get(FAR struct foc_dev_s *dev,
FAR int16_t *curr, int shunts);
static int stm32_foc_notifier_cfg(FAR struct foc_dev_s *dev, uint32_t freq);
static int stm32_foc_pwm_cfg(FAR struct foc_dev_s *dev, uint32_t freq);
static int stm32_foc_adc_cfg(FAR struct foc_dev_s *dev);
static int stm32_foc_pwm_start(FAR struct foc_dev_s *dev, bool state);
static int stm32_foc_adc_start(FAR struct foc_dev_s *dev, bool state);
static int stm32_foc_calibration_start(FAR struct foc_dev_s *dev);
static int stm32_foc_pwm_freq_set(FAR struct foc_dev_s *dev, uint32_t freq);
#if defined(CONFIG_STM32_FOC_ADC_CCR4)
static void stm32_foc_adc_ccr4_trg_set(FAR struct foc_dev_s *dev,
uint32_t offset);
#elif defined(CONFIG_STM32_FOC_ADC_TRGO)
static void stm32_foc_adc_trgo_trg_set(FAR struct foc_dev_s *dev,
uint8_t rcr);
#else
# error Invalid FOC ADC trigger
#endif
static void stm32_foc_hw_config_get(FAR struct foc_dev_s *dev);
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef FOC_ADC_HAVE_CMN
# ifdef CONFIG_STM32_HAVE_IP_ADC_V1
/* Common for ADC123 */
static struct stm32_foc_adccmn_s g_stm32_foc_adccmn123 =
{
.cntr = 0
};
# endif /* CONFIG_STM32_HAVE_IP_ADC_V1 */
# ifdef CONFIG_STM32_HAVE_IP_ADC_V2
# if defined(CONFIG_STM32_HAVE_ADC1) || defined(CONFIG_STM32_HAVE_ADC2)
/* Common for ADC12 */
static struct stm32_foc_adccmn_s g_stm32_foc_adccmn12 =
{
.cntr = 0
};
# endif /* CONFIG_STM32_HAVE_ADC1 || CONFIG_STM32_HAVE_ADC2 */
# if defined(CONFIG_STM32_HAVE_ADC3) || defined(CONFIG_STM32_HAVE_ADC4)
/* Common for ADC34 */
static struct stm32_foc_adccmn_s g_stm32_foc_adccmn34 =
{
.cntr = 0
};
# endif /* CONFIG_STM32_HAVE_ADC3 || CONFIG_STM32_HAVE_ADC4 */
# endif /* CONFIG_STM32_HAVE_IP_ADC_V2 */
#endif /* FOC_ADC_HAVE_CMN */
/* STM32 specific FOC data */
static struct stm32_foc_dev_s g_stm32_foc_dev[CONFIG_MOTOR_FOC_INST];
static struct stm32_foc_priv_s g_stm32_foc_priv[CONFIG_MOTOR_FOC_INST];
/* STM32 specific FOC ops */
static struct foc_lower_ops_s g_stm32_foc_ops =
{
.configure = stm32_foc_configure,
.setup = stm32_foc_setup,
.shutdown = stm32_foc_shutdown,
.start = stm32_foc_start,
.pwm_duty_set = stm32_foc_pwm_duty_set,
.ioctl = stm32_foc_ioctl,
.bind = stm32_foc_bind,
.fault_clear = stm32_foc_fault_clear,
#ifdef CONFIG_MOTOR_FOC_TRACE
.trace = stm32_foc_trace
#endif
};
/* FOC lower-half */
static struct foc_lower_s g_stm32_foc_lower[CONFIG_MOTOR_FOC_INST];
/* FOC upper-half device data */
static struct foc_dev_s g_foc_dev[CONFIG_MOTOR_FOC_INST];
/****************************************************************************
* Private Functions
****************************************************************************/
#if (CONFIG_MOTOR_FOC_INST > 1)
/****************************************************************************
* Name: stm32_foc_sync_all
*
* Description:
* Synchronise all FOC PWM timers
*
****************************************************************************/
void stm32_foc_sync_all(void)
{
FAR struct foc_dev_s *dev = NULL;
FAR struct stm32_foc_dev_s *foc_dev = NULL;
uint32_t egr_reg[CONFIG_MOTOR_FOC_INST];
int i = 0;
/* Get registers to write */
for (i = 0; i < CONFIG_MOTOR_FOC_INST; i += 1)
{
/* Get FOC device */
dev = &g_foc_dev[i];
/* Get FOC lower half devices */
foc_dev = STM32_FOC_DEV_FROM_DEV_GET(dev);
/* Store EGR register address */
egr_reg[i] = foc_dev->pwm_base + STM32_GTIM_EGR_OFFSET;
}
/* Write all registers at once */
for (i = 0; i < CONFIG_MOTOR_FOC_INST; i += 1)
{
/* Force update event to reset CNTR */
putreg32(GTIM_EGR_UG, egr_reg[i]);
}
}
#endif
/****************************************************************************
* Name: stm32_foc_pwm_cfg
*
* Description:
* PWM configuration for the FOC device
*
****************************************************************************/
static int stm32_foc_pwm_cfg(FAR struct foc_dev_s *dev, uint32_t freq)
{
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
FAR struct stm32_pwm_dev_s *pwm = PWM_FROM_FOC_DEV_GET(dev);
int ret = OK;
DEBUGASSERT(dev);
DEBUGASSERT(board);
DEBUGASSERT(pwm);
DEBUGASSERT(freq > 0);
/* Set phases PWM frequency */
ret = stm32_foc_pwm_freq_set(dev, freq);
if (ret < 0)
{
goto errout;
}
#ifdef CONFIG_STM32_FOC_HAS_PWM_COMPLEMENTARY
/* Configure deadtime */
PWM_DT_UPDATE(pwm, (uint8_t)board->data->pwm_dt);
#else
UNUSED(board);
#endif
/* Configure PWM mode for PWM outputs */
PWM_MODE_UPDATE(pwm, STM32_PWM_CHAN1, PWM_MODE_FOC);
PWM_MODE_UPDATE(pwm, STM32_PWM_CHAN2, PWM_MODE_FOC);
#if CONFIG_MOTOR_FOC_PHASES > 2
PWM_MODE_UPDATE(pwm, STM32_PWM_CHAN3, PWM_MODE_FOC);
#endif
#if CONFIG_MOTOR_FOC_PHASES > 3
PWM_MODE_UPDATE(pwm, STM32_PWM_CHAN4, PWM_MODE_FOC);
#endif
/* Dump PWM regs */
PWM_DUMP_REGS(pwm, NULL);
errout:
return ret;
}
/****************************************************************************
* Name: stm32_foc_pwm_freq_set
*
* Description:
* Configure the PWM frequency for the FOC device
*
****************************************************************************/
static int stm32_foc_pwm_freq_set(FAR struct foc_dev_s *dev, uint32_t freq)
{
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
FAR struct stm32_pwm_dev_s *pwm = PWM_FROM_FOC_DEV_GET(dev);
int ret = OK;
DEBUGASSERT(dev);
DEBUGASSERT(priv);
DEBUGASSERT(pwm);
DEBUGASSERT(freq > 0);
/* Update the PWM frequency.
* IMPORTANT: must be x2 as the PWM is in center-aligned mode.
*/
ret = PWM_FREQ_UPDATE(pwm, (freq * 2));
if (ret < 0)
{
goto errout;
}
/* Store the PWM period to improve some future calculations */
priv->data.per = PWM_ARR_GET(pwm);
errout:
return ret;
}
/****************************************************************************
* Name: stm32_foc_start
*
* Description:
* Start or stop the FOC lower-half operations
*
****************************************************************************/
static int stm32_foc_start(FAR struct foc_dev_s *dev, bool state)
{
int ret = OK;
DEBUGASSERT(dev);
/* Start PWM */
ret = stm32_foc_pwm_start(dev, state);
if (ret < 0)
{
mtrerr("stm32_foc_pwm_start failed %d\n", ret);
goto errout;
}
/* Start ADC */
ret = stm32_foc_adc_start(dev, state);
if (ret < 0)
{
mtrerr("stm32_foc_adc_start failed %d\n", ret);
goto errout;
}
errout:
return ret;
}
/****************************************************************************
* Name: stm32_foc_pwm_start
*
* Description:
* Start or stop PWM
*
****************************************************************************/
static int stm32_foc_pwm_start(FAR struct foc_dev_s *dev, bool state)
{
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
FAR struct stm32_pwm_dev_s *pwm = PWM_FROM_FOC_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(board);
DEBUGASSERT(pwm);
/* Configure outputs state */
PWM_ALL_OUTPUTS_ENABLE(pwm, state);
/* Call board-specific logic */
board->ops->pwm_start(dev, state);
return OK;
}
/****************************************************************************
* Name: stm32_foc_adc_start
*
* Description:
* Start or stop ADC
*
****************************************************************************/
static int stm32_foc_adc_start(FAR struct foc_dev_s *dev, bool state)
{
FAR struct stm32_foc_dev_s *foc_dev = STM32_FOC_DEV_FROM_DEV_GET(dev);
FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(foc_dev);
DEBUGASSERT(adc);
if (state == false)
{
/* Disable ADC interrupts */
STM32_ADC_DISABLEINT(adc);
/* Disable ADC injected conversion */
STM32_ADC_INJ_STARTCONV(adc, false);
/* Reset ADC injected trigger */
STM32_ADC_JEXTCFG_SET(adc, foc_dev->jextval);
}
else
{
/* Configure ADC injected trigger */
STM32_ADC_JEXTCFG_SET(adc, foc_dev->jextval);
/* Enable ADC interrupts */
STM32_ADC_ENABLEINT(adc);
/* Enable ADC injected conversion */
STM32_ADC_INJ_STARTCONV(adc, true);
}
return OK;
}
/****************************************************************************
* Name: stm32_foc_adc_cfg
*
* Description:
* Configure ADC for FOC worker
*
****************************************************************************/
static int stm32_foc_adc_cfg(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_dev_s *foc_dev = STM32_FOC_DEV_FROM_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(foc_dev);
/* Set ADC interrupt handler to FOC worker */
foc_dev->adc_isr = stm32_foc_worker_handler;
return OK;
}
#if defined(CONFIG_STM32_FOC_ADC_CCR4)
/****************************************************************************
* Name: stm32_foc_adc_ccr4_trg_set
*
* Description:
* Configure ADC CCR4 trigger for FOC controller
*
****************************************************************************/
static void stm32_foc_adc_ccr4_trg_set(FAR struct foc_dev_s *dev,
uint32_t offset)
{
FAR struct stm32_pwm_dev_s *pwm = PWM_FROM_FOC_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(pwm);
DEBUGASSERT(offset > 0);
/* Configure PWM mode for ADC trigger
* NOTE:
* For PWM mode 1 we have V7 when CRR=0 and V0 when CRR = ARR
* For PWM mode 2 we have V7 when CRR=ARR and V0 when CRR = 0
*/
PWM_MODE_UPDATE(pwm, STM32_PWM_CHAN4, PWM_MODE_ADC_TRG);
/* Set CCR4 */
PWM_CCR_UPDATE(pwm, STM32_PWM_CHAN4, offset);
}
#elif defined(CONFIG_STM32_FOC_ADC_TRGO)
/****************************************************************************
* Name: stm32_foc_adc_trgo_trg_set
*
* Description:
* Configure ADC TRGO trigger for FOC controller
*
****************************************************************************/
static void stm32_foc_adc_trgo_trg_set(FAR struct foc_dev_s *dev,
uint8_t rcr)
{
FAR struct stm32_pwm_dev_s *pwm = PWM_FROM_FOC_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(pwm);
/* We want TRGO on update events only if overflow (ARR):
* 1. RCR must be configured when timer is enabled
* 2. RCR must be odd value
*/
if (rcr % 2 == 0)
{
rcr -= 1;
}
/* Configure RCR */
PWM_RCR_UPDATE(pwm, rcr);
/* Configure TRGO */
PWM_TRGO_SET(pwm, FOC_PWM_TRGO);
}
#else
# error Invalid FOC ADC trigger
#endif
/****************************************************************************
* Name: stm32_foc_configure
*
* Description:
* Arch-specific FOC device configuration
*
****************************************************************************/
static int stm32_foc_configure(FAR struct foc_dev_s *dev,
FAR struct foc_cfg_s *cfg)
{
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
int ret = OK;
DEBUGASSERT(dev);
DEBUGASSERT(cfg);
DEBUGASSERT(priv);
DEBUGASSERT(cfg->pwm_freq > 0);
DEBUGASSERT(cfg->notifier_freq > 0);
/* Configure ADC */
ret = stm32_foc_adc_cfg(dev);
if (ret < 0)
{
mtrerr("stm32_foc_adc_cfg failed %d\n", ret);
goto errout;
}
/* Configure PWM */
ret = stm32_foc_pwm_cfg(dev, cfg->pwm_freq);
if (ret < 0)
{
mtrerr("stm32_foc_pwm_cfg failed %d\n", ret);
goto errout;
}
/* Configure FOC notifier */
ret = stm32_foc_notifier_cfg(dev, cfg->notifier_freq);
if (ret < 0)
{
mtrerr("stm32_foc_notifier_cfg failed %d\n", ret);
goto errout;
}
/* Configure ADC trigger - must be after PWM frequency set */
DEBUGASSERT(priv->data.per != 0);
#if defined(CONFIG_STM32_FOC_ADC_CCR4)
stm32_foc_adc_ccr4_trg_set(dev, (priv->data.per - ADC_TRIGGER_OFFSET));
#elif defined(CONFIG_STM32_FOC_ADC_TRGO)
stm32_foc_adc_trgo_trg_set(dev, (dev->cfg.pwm_freq /
priv->data.adc_freq) * 2);
#else
# error Invalid FOC ADC trigger
#endif
/* Reset ADC interrupts counter */
priv->data.adcint_cntr = 0;
/* REVISIT: synchronise instances if TRGO trigger selected */
#if (CONFIG_MOTOR_FOC_INST > 1)
# if defined(CONFIG_STM32_FOC_ADC_TRGO)
# error stm32_foc_sync_all breaks TRGO event on V0 vector
# endif
/* Sync all FOC PWM timers instances.
* IMPORTANT: This must be done after PWM frequency update !
*/
stm32_foc_sync_all();
#endif
errout:
return ret;
}
/****************************************************************************
* Name: stm32_foc_setup
*
* Description:
* Arch-specific FOC device setup
*
****************************************************************************/
static int stm32_foc_setup(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_dev_s *foc_dev = STM32_FOC_DEV_FROM_DEV_GET(dev);
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev);
struct adc_sample_time_s stime;
int ret = OK;
DEBUGASSERT(dev);
DEBUGASSERT(foc_dev);
DEBUGASSERT(board);
DEBUGASSERT(priv);
DEBUGASSERT(adc);
#ifdef FOC_ADC_HAVE_CMN
DEBUGASSERT(priv->adc_cmn);
#endif
/* Call board-specific setup - must be done before TIM enable */
ret = board->ops->setup(dev);
if (ret < 0)
{
mtrerr("board->setup failed %d\n", ret);
goto errout;
}
/* Setup ADC */
STM32_ADC_SETUP(foc_dev->adc);
#ifdef FOC_ADC_HAVE_CMN
/* Lock ADC common data */
ret = nxsem_wait_uninterruptible(&priv->adc_cmn->sem);
if (ret < 0)
{
goto errout;
}
/* Only if first device */
if (priv->adc_cmn->cntr == 0)
{
/* Enable ADC interrupts */
up_enable_irq(foc_dev->adc_irq);
}
/* Increase counter */
priv->adc_cmn->cntr += 1;
/* Unlock ADC common data */
nxsem_post(&priv->adc_cmn->sem);
#endif
/* Setup PWM */
PWM_SETUP(foc_dev->pwm);
PWM_TIM_ENABLE(foc_dev->pwm, true);
/* Stop ADC and PWM */
stm32_foc_pwm_start(dev, false);
stm32_foc_adc_start(dev, false);
/* Reset ADC handler */
foc_dev->adc_isr = NULL;
/* Configure sample times for ADC channels */
memset(&stime, 0, sizeof(struct adc_sample_time_s));
stime.channels_nbr = board->data->adc_cfg->nchan;
stime.channel = board->data->adc_cfg->stime;
STM32_ADC_SAMPLETIME_SET(adc, &stime);
STM32_ADC_SAMPLETIME_WRITE(adc);
/* Set the priority of the ADC interrupt vector */
ret = up_prioritize_irq(foc_dev->adc_irq, NVIC_SYSH_PRIORITY_DEFAULT);
if (ret < 0)
{
mtrerr("up_prioritize_irq failed: %d\n", ret);
goto errout;
}
/* Attach the ADC interrupt handler */
ret = irq_attach(foc_dev->adc_irq, stm32_foc_adc_handler, NULL);
if (ret < 0)
{
mtrerr("irq_attach failed: %d\n", ret);
goto errout;
}
/* Get HW configuration */
stm32_foc_hw_config_get(dev);
#ifdef CONFIG_MOTOR_FOC_TRACE
/* Initialize trace interface */
ret = stm32_foc_trace_init(dev);
if (ret < 0)
{
mtrerr("stm32_foc_trace_init failed %d\n", ret);
goto errout;
}
#endif
/* Start hardware calibration */
ret = stm32_foc_calibration_start(dev);
if (ret < 0)
{
mtrerr("stm32_foc_calibration_start failed %d\n", ret);
goto errout;
}
/* Dump ADC regs */
STM32_ADC_DUMP_REGS(adc);
errout:
return ret;
}
/****************************************************************************
* Name: stm32_foc_shutdown
*
* Description:
* Arch-specific FOC device shutdown
*
****************************************************************************/
static int stm32_foc_shutdown(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_dev_s *foc_dev = STM32_FOC_DEV_FROM_DEV_GET(dev);
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
int ret = OK;
DEBUGASSERT(dev);
DEBUGASSERT(foc_dev);
DEBUGASSERT(board);
DEBUGASSERT(priv);
/* Disable PWM */
PWM_TIM_ENABLE(foc_dev->pwm, false);
PWM_SHUTDOWN(foc_dev->pwm);
/* Reset ADC interrupt handler */
foc_dev->adc_isr = NULL;
/* Deinitialize ADC */
STM32_ADC_SHUTDOWN(foc_dev->adc);
#ifdef FOC_ADC_HAVE_CMN
/* Lock ADC common data */
ret = nxsem_wait_uninterruptible(&priv->adc_cmn->sem);
if (ret < 0)
{
goto errout;
}
/* Decrease counter */
priv->adc_cmn->cntr -= 1;
/* Deinitialize ADC only if last device */
if (priv->adc_cmn->cntr == 0)
#endif
{
/* Disable ADC interrupts */
up_disable_irq(foc_dev->adc_irq);
}
#ifdef FOC_ADC_HAVE_CMN
/* Unlock ADC common data */
nxsem_post(&priv->adc_cmn->sem);
#endif
/* Call board-specific shutdown */
board->ops->shutdown(dev);
/* Reset STM32 FOC volatile data */
memset(&priv->data, 0, sizeof(struct stm32_foc_data_s));
#ifdef FOC_ADC_HAVE_CMN
errout:
#endif
return ret;
}
/****************************************************************************
* Name: stm32_foc_ioctl
*
* Description:
* Arch-specific FOC device IOCTL
*
****************************************************************************/
static int stm32_foc_ioctl(FAR struct foc_dev_s *dev, int cmd,
unsigned long arg)
{
return -1;
}
/****************************************************************************
* Name: stm32_foc_calibration_handler
*
* Description:
* ADC interrupt handler for FOC calibration
*
****************************************************************************/
static int stm32_foc_adc_calibration_handler(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
int i = 0;
DEBUGASSERT(dev);
DEBUGASSERT(priv);
if (priv->data.adcint_cntr < CAL_SAMPLES)
{
/* Get raw current samples */
stm32_foc_curr_get(dev, priv->data.curr_raw, CONFIG_MOTOR_FOC_SHUNTS);
/* Get sum */
for (i = 0; i < CONFIG_MOTOR_FOC_SHUNTS; i += 1)
{
priv->data.curr_offset[i] += priv->data.curr_raw[i];
}
}
else if (priv->data.adcint_cntr == CAL_SAMPLES)
{
/* Get average offset */
for (i = 0; i < CONFIG_MOTOR_FOC_SHUNTS; i += 1)
{
priv->data.curr_offset[i] =
(priv->data.curr_offset[i] / CAL_SAMPLES);
}
/* Post semaphore that calibration is done */
sem_post(&priv->cal_done_sem);
}
else
{
/* Calibration completed */
}
return OK;
}
/****************************************************************************
* Name: stm32_foc_adc_handler
*
* Description:
* ADC interrupt handler
*
****************************************************************************/
static int stm32_foc_adc_handler(int irq, FAR void *context, FAR void *arg)
{
FAR struct foc_dev_s *dev = NULL;
FAR struct stm32_foc_priv_s *priv = NULL;
#ifdef CONFIG_MOTOR_FOC_TRACE
FAR struct stm32_foc_board_s *board = NULL;
#endif
FAR struct stm32_adc_dev_s *adc = NULL;
FAR struct stm32_foc_dev_s *foc_dev = NULL;
uint32_t pending = 0;
int ret = OK;
int i = 0;
UNUSED(irq);
UNUSED(context);
UNUSED(arg);
/* Loop through all FOC instances to prevent context switching if
* all instances are synchronized.
*/
for (i = 0; i < CONFIG_MOTOR_FOC_INST; i += 1)
{
/* Reset pointer to a device */
dev = NULL;
/* Get ADC device associated with FOC device */
adc = ADC_FROM_FOC_DEV_GET(&g_foc_dev[i]);
DEBUGASSERT(adc);
/* Get ADC pending interrupts */
pending = STM32_ADC_INT_GET(adc);
/* Only if end of injected sequence */
if (pending & FOC_ADC_ISR_FOC)
{
/* Found device with penidng ADC interrupt */
dev = &g_foc_dev[i];
}
/* Handle pending interrupt for device */
if (dev != NULL)
{
priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
DEBUGASSERT(priv);
foc_dev = STM32_FOC_DEV_FROM_DEV_GET(dev);
DEBUGASSERT(foc_dev);
#ifdef CONFIG_MOTOR_FOC_TRACE
board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
DEBUGASSERT(board);
board->ops->trace(dev, FOC_TRACE_LOWER, true);
#endif
/* Clear pending */
STM32_ADC_INT_ACK(adc, pending);
/* Call interrupt handler if registerd */
if (foc_dev->adc_isr != NULL)
{
ret = foc_dev->adc_isr(dev);
if (ret < 0)
{
DEBUGASSERT(0);
}
}
/* Increase interrupt counter */
priv->data.adcint_cntr += 1;
#ifdef CONFIG_MOTOR_FOC_TRACE
board->ops->trace(dev, FOC_TRACE_LOWER, false);
#endif
}
}
return ret;
}
/****************************************************************************
* Name: stm32_foc_worker_handler
*
* Description:
* Handle ADC conversion and do FOC device work.
*
****************************************************************************/
static int stm32_foc_worker_handler(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev);
int ret = OK;
DEBUGASSERT(dev);
DEBUGASSERT(priv);
DEBUGASSERT(adc);
DEBUGASSERT(board);
DEBUGASSERT(priv->cb);
DEBUGASSERT(priv->cb->notifier);
if (priv->data.adcint_cntr % priv->data.notifier_div == 0)
{
/* Get raw current samples */
stm32_foc_curr_get(dev, priv->data.curr_raw, CONFIG_MOTOR_FOC_SHUNTS);
/* Get phase currents */
ret = board->ops->current_get(dev,
priv->data.curr_raw,
priv->data.curr);
/* Call upper-half worker callback */
priv->cb->notifier(dev, priv->data.curr);
}
return ret;
}
/****************************************************************************
* Name: stm32_foc_calibration_start
*
* Description:
* Start FOC hardware calibration (ADC offsets)
*
****************************************************************************/
static int stm32_foc_calibration_start(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_dev_s *foc_dev = STM32_FOC_DEV_FROM_DEV_GET(dev);
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
FAR struct stm32_pwm_dev_s *pwm = PWM_FROM_FOC_DEV_GET(dev);
FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev);
uint8_t i = 0;
uint8_t ch = 0;
int ret = OK;
DEBUGASSERT(dev);
DEBUGASSERT(foc_dev);
DEBUGASSERT(priv);
DEBUGASSERT(board);
DEBUGASSERT(pwm);
DEBUGASSERT(adc);
/* Call board-specific */
board->ops->calibration(dev, true);
/* Force high side transistors to low state and
* low side tranisstors to high state
*/
PWM_MODE_UPDATE(pwm, STM32_PWM_CHAN1, PWM_MODE_HSLO_LSHI);
PWM_MODE_UPDATE(pwm, STM32_PWM_CHAN2, PWM_MODE_HSLO_LSHI);
#if CONFIG_MOTOR_FOC_PHASES > 2
PWM_MODE_UPDATE(pwm, STM32_PWM_CHAN3, PWM_MODE_HSLO_LSHI);
#endif
#if CONFIG_MOTOR_FOC_PHASES > 3
PWM_MODE_UPDATE(pwm, STM32_PWM_CHAN4, PWM_MODE_HSLO_LSHI);
#endif
/* Set PWM to trigger ADC */
ret = stm32_foc_pwm_freq_set(dev, CAL_FREQ);
if (ret < 0)
{
goto errout;
}
/* Configure ADC interrupt handler to calibration */
foc_dev->adc_isr = stm32_foc_adc_calibration_handler;
/* Configure ADC trigger - must be after PWM frequency set */
DEBUGASSERT(priv->data.per != 0);
#if defined(CONFIG_STM32_FOC_ADC_CCR4)
stm32_foc_adc_ccr4_trg_set(dev, (priv->data.per - ADC_TRIGGER_OFFSET));
#elif defined(CONFIG_STM32_FOC_ADC_TRGO)
stm32_foc_adc_trgo_trg_set(dev, 1);
#else
# error Invalid FOC ADC trigger
#endif
/* Reset ADC interrupts counter */
priv->data.adcint_cntr = 0;
/* Start ADC and PWM */
stm32_foc_adc_start(dev, true);
stm32_foc_pwm_start(dev, true);
/* Wait for calibration done semaphore
* All work is done in adc_calibration_handler
*/
ret = nxsem_wait_uninterruptible(&priv->cal_done_sem);
if (ret < 0)
{
goto errout;
}
/* Stop ADC and PWM */
stm32_foc_pwm_start(dev, false);
stm32_foc_adc_start(dev, false);
/* Reset ADC interrupt handler */
foc_dev->adc_isr = NULL;
/* Clear last ADC data */
for (i = 0; i < CONFIG_MOTOR_FOC_SHUNTS; i += 1)
{
priv->data.curr_raw[i] = 0;
}
/* Set ADC hardware offset for current channels (only injected channels) */
for (i = 0; i < CONFIG_MOTOR_FOC_SHUNTS; i += 1)
{
/* Get channel */
ch = board->data->adc_cfg->chan[board->data->adc_cfg->regch + i];
/* Write offset */
STM32_ADC_OFFSET_SET(adc, ch, i, priv->data.curr_offset[i]);
}
mtrinfo("ADC offset calibration - DONE!\n");
errout:
/* Call board-specific */
board->ops->calibration(dev, false);
/* Reset ADC interrupts counter */
priv->data.adcint_cntr = 0;
return ret;
}
/****************************************************************************
* Name: stm32_foc_pwm_duty_set
*
* Description:
* Set the 3-phase PWM duty cycle
*
****************************************************************************/
static int stm32_foc_pwm_duty_set(FAR struct foc_dev_s *dev,
FAR foc_duty_t *duty)
{
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
FAR struct stm32_foc_dev_s *foc_dev = STM32_FOC_DEV_FROM_DEV_GET(dev);
uint16_t ccr[CONFIG_MOTOR_FOC_PHASES];
DEBUGASSERT(dev);
DEBUGASSERT(duty);
DEBUGASSERT(priv);
DEBUGASSERT(foc_dev);
DEBUGASSERT(priv->data.per != 0);
/* Get the CCR for a given duty cycle */
DEBUGASSERT(duty[0] >= 0);
DEBUGASSERT(duty[1] >= 0);
#if CONFIG_MOTOR_FOC_PHASES > 2
DEBUGASSERT(duty[2] >= 0);
#endif
#if CONFIG_MOTOR_FOC_PHASES > 3
DEBUGASSERT(duty[3] >= 0);
#endif
ccr[0] = (uint16_t)b16toi(b16muli(duty[0], priv->data.per));
ccr[1] = (uint16_t)b16toi(b16muli(duty[1], priv->data.per));
#if CONFIG_MOTOR_FOC_PHASES > 2
ccr[2] = (uint16_t)b16toi(b16muli(duty[2], priv->data.per));
#endif
#if CONFIG_MOTOR_FOC_PHASES > 3
ccr[3] = (uint16_t)b16toi(b16muli(duty[3], priv->data.per));
#endif
/* Write directly to timer registers.
* We are not using the PWM_CCR_UPDATE interface as it is too slow
*/
putreg32(ccr[0], (foc_dev->pwm_base + STM32_GTIM_CCR1_OFFSET));
putreg32(ccr[1], (foc_dev->pwm_base + STM32_GTIM_CCR2_OFFSET));
#if CONFIG_MOTOR_FOC_PHASES > 2
putreg32(ccr[2], (foc_dev->pwm_base + STM32_GTIM_CCR3_OFFSET));
#endif
#if CONFIG_MOTOR_FOC_PHASES > 3
putreg32(ccr[3], (foc_dev->pwm_base + STM32_GTIM_CCR4_OFFSET));
#endif
return OK;
}
/****************************************************************************
* Name: stm32_foc_hw_config_get
*
* Description:
* Get HW configuration for FOC device
*
****************************************************************************/
static void stm32_foc_hw_config_get(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(board);
/* Get data from board configuration */
dev->info.hw_cfg.pwm_dt_ns = board->data->pwm_dt_ns;
dev->info.hw_cfg.pwm_max = board->data->duty_max;
}
/****************************************************************************
* Name: stm32_foc_curr_get
*
* Description:
* Get current samples from ADC
*
****************************************************************************/
static void stm32_foc_curr_get(FAR struct foc_dev_s *dev,
FAR int16_t *curr, int shunts)
{
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
FAR struct stm32_adc_dev_s *adc = ADC_FROM_FOC_DEV_GET(dev);
int i = 0;
DEBUGASSERT(dev);
DEBUGASSERT(priv);
DEBUGASSERT(adc);
DEBUGASSERT(curr);
for (i = 0; i < shunts; i += 1)
{
/* Get raw current samples.
* We have ADC offset enabled for injected channels so this
* gives us signed values.
* NOTE: ADC value is 11 bits + sign.
*/
#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND
/* Ignore first channel */
curr[i] = (int16_t)STM32_ADC_INJDATA_GET(adc, (i + 1));
#else
curr[i] = (int16_t)STM32_ADC_INJDATA_GET(adc, i);
#endif
}
}
/****************************************************************************
* Name: stm32_foc_notifier_cfg
*
* Description:
* Configure FOC notifier
*
****************************************************************************/
static int stm32_foc_notifier_cfg(FAR struct foc_dev_s *dev, uint32_t freq)
{
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
int ret = OK;
DEBUGASSERT(dev);
DEBUGASSERT(priv);
DEBUGASSERT(freq > 0);
DEBUGASSERT(dev->cfg.pwm_freq > 0);
/* Validate input:
* 1. must be fraction of PWM frequency
*/
if (dev->cfg.pwm_freq % freq != 0)
{
ret = -EINVAL;
goto errout;
}
#if defined(CONFIG_STM32_FOC_ADC_CCR4)
/* ADC interrupts frequency is PWM frequency */
priv->data.adc_freq = dev->cfg.pwm_freq;
/* Get worker divider */
priv->data.notifier_div = (dev->cfg.pwm_freq / freq);
#elif defined(CONFIG_STM32_FOC_ADC_TRGO)
/* Call work on every ADC interrupt */
priv->data.notifier_div = 1;
/* ADC interrupts frequency is notifier frequency */
priv->data.adc_freq = freq;
#else
# error Invalid FOC ADC trigger
#endif
errout:
return ret;
}
/****************************************************************************
* Name: stm32_foc_bind
*
* Description:
* Bind lower-half FOC device with upper-half FOC logic
*
****************************************************************************/
static int stm32_foc_bind(FAR struct foc_dev_s *dev,
FAR struct foc_callbacks_s *cb)
{
FAR struct stm32_foc_priv_s *priv = STM32_FOC_PRIV_FROM_DEV_GET(dev);
int ret = OK;
DEBUGASSERT(dev);
DEBUGASSERT(cb);
DEBUGASSERT(priv);
/* Do we support given FOC instance? */
if (dev->devno > CONFIG_MOTOR_FOC_INST)
{
mtrerr("Unsupported STM32 FOC instance %d\n", dev->devno);
ret = -EINVAL;
goto errout;
}
/* Validate callbacks */
DEBUGASSERT(cb->notifier);
/* Bind upper-half FOC device callbacks */
priv->cb = cb;
errout:
return ret;
}
/****************************************************************************
* Name: stm32_foc_fault_clear
*
* Description:
* Arch-specific fault clear
*
****************************************************************************/
static int stm32_foc_fault_clear(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(board);
return board->ops->fault_clear(dev);
}
#ifdef CONFIG_MOTOR_FOC_TRACE
/****************************************************************************
* Name: stm32_foc_trace
*
* Description:
* Arch-specific trace initialization
*
****************************************************************************/
int stm32_foc_trace_init(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(board);
/* Call board-specific logic */
return board->ops->trace_init(dev);
}
/****************************************************************************
* Name: stm32_foc_trace
*
* Description:
* Arch-specific trace
*
****************************************************************************/
void stm32_foc_trace(FAR struct foc_dev_s *dev, int type, bool state)
{
FAR struct stm32_foc_board_s *board = STM32_FOC_BOARD_FROM_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(board);
/* Call board-specific logic */
board->ops->trace(dev, type, state);
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: stm32_foc_initialize
*
* Description:
* Initialize the FOC lower-half.
*
* Input Parameters:
* inst - FOC instance number
* board - FOC board-specific data
*
* Returned Value:
* Valid lower-half FOC controller structure reference on success;
* NULL on failure
*
****************************************************************************/
FAR struct foc_dev_s *
stm32_foc_initialize(int inst, FAR struct stm32_foc_board_s *board)
{
FAR struct foc_dev_s *dev = NULL;
FAR struct stm32_foc_adc_s *adc_cfg = NULL;
FAR struct foc_lower_s *foc_lower = NULL;
FAR struct stm32_foc_dev_s *foc_dev = NULL;
FAR struct stm32_foc_priv_s *foc_priv = NULL;
#ifdef FOC_ADC_HAVE_CMN
FAR struct stm32_foc_adccmn_s *adc_cmn = NULL;
#endif
uint32_t adc_irq = 0;
uint32_t pwm_base = 0;
uint32_t jextval = 0;
uint8_t pwm_inst = 0;
uint8_t adc_inst = 0;
uint32_t pwmfzbit = 0;
int i = 0;
#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND
FAR uint8_t *adc_chan = NULL;
uint8_t adc_nchan = 0;
#endif
DEBUGASSERT(board != NULL);
DEBUGASSERT(board->ops != NULL);
DEBUGASSERT(board->data != NULL);
/* Assert board-specific ops */
DEBUGASSERT(board->ops->setup);
DEBUGASSERT(board->ops->shutdown);
DEBUGASSERT(board->ops->calibration);
DEBUGASSERT(board->ops->fault_clear);
DEBUGASSERT(board->ops->pwm_start);
DEBUGASSERT(board->ops->current_get);
#ifdef CONFIG_MOTOR_FOC_TRACE
DEBUGASSERT(board->ops->trace_init);
DEBUGASSERT(board->ops->trace);
#endif
/* Get ADC configuration from board data */
adc_cfg = board->data->adc_cfg;
DEBUGASSERT(adc_cfg);
/* Get FOC instance configuration */
switch (inst)
{
#ifdef CONFIG_STM32_FOC_FOC0
case 0:
{
pwm_inst = FOC0_PWM;
adc_inst = FOC0_ADC;
adc_irq = FOC0_ADC_IRQ;
pwm_base = FOC0_PWM_BASE;
jextval = FOC0_ADC_JEXT;
pwmfzbit = FOC0_PWM_FZ_BIT;
#ifdef FOC_ADC_HAVE_CMN
adc_cmn = FOC0_ADC_CMN;
#endif
break;
}
#endif
#ifdef CONFIG_STM32_FOC_FOC1
case 1:
{
pwm_inst = FOC1_PWM;
adc_inst = FOC1_ADC;
adc_irq = FOC1_ADC_IRQ;
pwm_base = FOC1_PWM_BASE;
jextval = FOC1_ADC_JEXT;
pwmfzbit = FOC1_PWM_FZ_BIT;
#ifdef FOC_ADC_HAVE_CMN
adc_cmn = FOC1_ADC_CMN;
#endif
break;
}
#endif
default:
{
mtrerr("Unsupported STM32 FOC instance %d\n", inst);
set_errno(EINVAL);
goto errout;
}
}
/* Get STM32 FOC lower-half */
foc_lower = &g_stm32_foc_lower[inst];
/* Connect STM32 FOC private data with ops and data */
foc_lower->data = &g_stm32_foc_priv[inst];
foc_lower->ops = &g_stm32_foc_ops;
foc_priv = foc_lower->data;
/* Reset STM32 FOC private data */
memset(foc_lower->data, 0, sizeof(struct stm32_foc_priv_s));
/* Connect STM32 FOC devices */
foc_priv->dev = &g_stm32_foc_dev[inst];
/* Connect board data */
foc_priv->board = board;
#ifdef FOC_ADC_HAVE_CMN
/* Connect ADC common data */
foc_priv->adc_cmn = adc_cmn;
#endif
/* Get arch-specific device */
foc_dev = (struct stm32_foc_dev_s *)foc_priv->dev;
DEBUGASSERT(foc_dev);
/* Store STM32 FOC devices data */
foc_dev->adc_inst = adc_inst;
foc_dev->pwm_inst = pwm_inst;
foc_dev->pwm_base = pwm_base;
foc_dev->jextval = jextval;
foc_dev->adc_irq = adc_irq;
/* Get the advanced timer PWM interface */
foc_dev->pwm = (FAR struct stm32_pwm_dev_s *)stm32_pwminitialize(pwm_inst);
if (foc_dev->pwm == NULL)
{
mtrerr("Failed to get PWM%d interface\n", pwm_inst);
set_errno(EINVAL);
goto errout;
}
/* Configure pins as analog inputs for the selected channels */
DEBUGASSERT(adc_cfg != NULL);
DEBUGASSERT(adc_cfg->pins != NULL);
DEBUGASSERT(adc_cfg->chan != NULL);
for (i = 0; i < adc_cfg->nchan; i++)
{
stm32_configgpio(adc_cfg->pins[i]);
}
/* Make sure that we are using the appropriate ADC interface */
if (adc_inst != adc_cfg->intf)
{
mtrerr("Configuration doesn't match %d, %d\n",
adc_inst, adc_cfg->intf);
set_errno(EINVAL);
goto errout;
}
/* STM32G4 ADC channel 0 unwanted conversion workaround */
#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND
/* Add one dummy channel to conversion */
adc_nchan = (adc_cfg->nchan + 1);
/* Allocate memory for the extended list of channels */
adc_chan = zalloc(adc_nchan);
if (adc_chan == NULL)
{
goto errout;
}
/* Copy regular channels first */
for (i = 0; i < adc_cfg->regch; i += 1)
{
adc_chan[i] = adc_cfg->chan[i];
}
/* Add dummy channel at the beginning of injected channels */
adc_chan[adc_cfg->regch] = 0;
/* Copy injected channels */
for (i = (adc_cfg->regch + 1); i < adc_nchan; i += 1)
{
adc_chan[i] = adc_cfg->chan[i - 1];
}
#endif /* CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND */
/* Get the ADC interface */
#ifdef CONFIG_STM32_FOC_G4_ADCCHAN0_WORKAROUND
foc_dev->adc_dev = stm32_adcinitialize(adc_inst,
adc_chan,
adc_nchan);
free(adc_chan);
#else
foc_dev->adc_dev = stm32_adcinitialize(adc_inst,
adc_cfg->chan,
adc_cfg->nchan);
#endif
if (foc_dev->adc_dev == NULL)
{
mtrerr("Failed to get ADC%d interface\n", adc_cfg->intf);
set_errno(EINVAL);
goto errout;
}
/* Get ADC private part */
foc_dev->adc = (FAR struct stm32_adc_dev_s *)foc_dev->adc_dev->ad_priv;
/* Froze timer and reset outputs when core is halted.
* TODO: move this to stm32_pwm.c and configure from Kconfig
*/
modifyreg32(FOC_PWM_FZ_REG, 0, pwmfzbit);
#ifdef FOC_ADC_HAVE_CMN
/* Initialize ADC common data semaphore */
nxsem_init(&foc_priv->adc_cmn->sem, 0, 1);
#endif
/* Initialize calibration semaphore */
nxsem_init(&foc_priv->cal_done_sem, 0, 0);
nxsem_set_protocol(&foc_priv->cal_done_sem, SEM_PRIO_NONE);
/* Get FOC device */
dev = &g_foc_dev[inst];
/* Connect the lower-half device with the upper-half device */
dev->lower = (FAR void *)foc_lower;
/* Return upper-half driver instance */
return dev;
errout:
return NULL;
}
/****************************************************************************
* Name: stm32_foc_adcget
*
* Description:
* Get a handler for ADC device associated with a given FOC device.
*
* The FOC lower-half logic uses only injected ADC channels for operations.
* We are using a custom ADC interrupt logic that cannot handle
* additional regular channels conversion. This limitation can be overcome
* with the DMA transfer.
* With this function we can get a handler to the ADC device and use it
* to register a standard ADC character device.
*
* Input Parameters:
* lower - a pointer to the uperr-half FOC device
*
* Returned Value:
* Valid ADC device structure reference on success; a NULL on failure
*
****************************************************************************/
FAR struct adc_dev_s *stm32_foc_adcget(FAR struct foc_dev_s *dev)
{
FAR struct stm32_foc_dev_s *foc_dev = STM32_FOC_DEV_FROM_DEV_GET(dev);
DEBUGASSERT(dev);
DEBUGASSERT(foc_dev);
/* Return STM32 ADC device */
return foc_dev->adc_dev;
}