diff --git a/boards/arm/stm32/b-g474e-dpow1/configs/buckboost/defconfig b/boards/arm/stm32/b-g474e-dpow1/configs/buckboost/defconfig new file mode 100644 index 0000000000..1c21cb0514 --- /dev/null +++ b/boards/arm/stm32/b-g474e-dpow1/configs/buckboost/defconfig @@ -0,0 +1,90 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_LEDS is not set +CONFIG_ADC=y +CONFIG_ANALOG=y +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="b-g474e-dpow1" +CONFIG_ARCH_BOARD_B_G474E_DPOW1=y +CONFIG_ARCH_BUTTONS=y +CONFIG_ARCH_CHIP="stm32" +CONFIG_ARCH_CHIP_STM32=y +CONFIG_ARCH_CHIP_STM32G474R=y +CONFIG_ARCH_HIPRI_INTERRUPT=y +CONFIG_ARCH_RAMVECTORS=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILTIN=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DISABLE_ENVIRON=y +CONFIG_DISABLE_MQUEUE=y +CONFIG_DISABLE_POSIX_TIMERS=y +CONFIG_DISABLE_PTHREAD=y +CONFIG_DRIVERS_SMPS=y +CONFIG_EXAMPLES_SMPS=y +CONFIG_EXAMPLES_SMPS_DEVPATH="/dev/smps0" +CONFIG_EXAMPLES_SMPS_IN_VOLTAGE_LIMIT=10000 +CONFIG_EXAMPLES_SMPS_OUT_CURRENT_LIMIT=100 +CONFIG_EXAMPLES_SMPS_OUT_POWER_LIMIT=100 +CONFIG_EXAMPLES_SMPS_OUT_VOLTAGE_DEFAULT=5000 +CONFIG_EXAMPLES_SMPS_OUT_VOLTAGE_LIMIT=10000 +CONFIG_EXAMPLES_SMPS_TIME_DEFAULT=10 +CONFIG_FDCLONE_STDIO=y +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=1024 +CONFIG_INTELHEX_BINARY=y +CONFIG_LIBDSP=y +CONFIG_LIBM=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=256 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_READLINE=y +CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE=512 +CONFIG_PTHREAD_STACK_DEFAULT=1024 +CONFIG_PTHREAD_STACK_MIN=1024 +CONFIG_RAM_SIZE=98304 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_SMPS_HAVE_INPUT_VOLTAGE=y +CONFIG_SMPS_HAVE_OUTPUT_VOLTAGE=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_STDIO_BUFFER_SIZE=128 +CONFIG_STM32_ADC1=y +CONFIG_STM32_ADC1_INJECTED_CHAN=2 +CONFIG_STM32_ADC_CHANGE_SAMPLETIME=y +CONFIG_STM32_ADC_LL_OPS=y +CONFIG_STM32_ADC_NOIRQ=y +CONFIG_STM32_CCMEXCLUDE=y +CONFIG_STM32_HRTIM1=y +CONFIG_STM32_HRTIM_ADC1_TRG2=y +CONFIG_STM32_HRTIM_ADC=y +CONFIG_STM32_HRTIM_DEADTIME=y +CONFIG_STM32_HRTIM_DISABLE_CHARDRV=y +CONFIG_STM32_HRTIM_PWM=y +CONFIG_STM32_HRTIM_TIMC=y +CONFIG_STM32_HRTIM_TIMC_DT=y +CONFIG_STM32_HRTIM_TIMC_PWM=y +CONFIG_STM32_HRTIM_TIMC_PWM_CH1=y +CONFIG_STM32_HRTIM_TIMC_PWM_CH2=y +CONFIG_STM32_HRTIM_TIMD=y +CONFIG_STM32_HRTIM_TIMD_DT=y +CONFIG_STM32_HRTIM_TIMD_PWM=y +CONFIG_STM32_HRTIM_TIMD_PWM_CH1=y +CONFIG_STM32_HRTIM_TIMD_PWM_CH2=y +CONFIG_STM32_JTAG_SW_ENABLE=y +CONFIG_STM32_PWR=y +CONFIG_STM32_USART3=y +CONFIG_SYSTEM_NSH=y +CONFIG_TASK_NAME_SIZE=0 +CONFIG_USART3_SERIAL_CONSOLE=y diff --git a/boards/arm/stm32/b-g474e-dpow1/include/board.h b/boards/arm/stm32/b-g474e-dpow1/include/board.h index 4b4f25788a..d1219cdb45 100644 --- a/boards/arm/stm32/b-g474e-dpow1/include/board.h +++ b/boards/arm/stm32/b-g474e-dpow1/include/board.h @@ -185,6 +185,63 @@ #define GPIO_USART3_TX GPIO_USART3_TX_3 /* PC10 */ #define GPIO_USART3_RX GPIO_USART3_RX_3 /* PC11 */ -/* Pin Multiplexing Disambiguation ******************************************/ +/* Board configuration for SMPS example: + * PB12 - HRTIM1_CHC1 + * PB13 - HRTIM1_CHC2 + * PB14 - HRTIM1_CHD1 + * PB15 - HRTIM1_CHD2 + * VIN - ADC Channel 2 (PA1) + * VOUT - ADC Channel 4 (PA3) + */ + +#if defined(CONFIG_EXAMPLES_SMPS) + +/* HRTIM configuration ******************************************************/ + +/* Timer C configuration - Buck operations */ + +#define HRTIM_TIMC_PRESCALER HRTIM_PRESCALER_1 +#define HRTIM_TIMC_MODE HRTIM_MODE_CONT +#define HRTIM_TIMC_UPDATE 0 +#define HRTIM_TIMC_RESET 0 + +#define HRTIM_TIMC_CH1_SET HRTIM_OUT_SET_NONE +#define HRTIM_TIMC_CH1_RST HRTIM_OUT_RST_NONE +#define HRTIM_TIMC_CH2_SET HRTIM_OUT_SET_NONE +#define HRTIM_TIMC_CH2_RST HRTIM_OUT_RST_NONE + +#define HRTIM_TIMC_DT_FSLOCK HRTIM_DT_LOCK +#define HRTIM_TIMC_DT_RSLOCK HRTIM_DT_LOCK +#define HRTIM_TIMC_DT_FVLOCK HRTIM_DT_RW +#define HRTIM_TIMC_DT_RVLOCK HRTIM_DT_RW +#define HRTIM_TIMC_DT_FSIGN HRTIM_DT_SIGN_POSITIVE +#define HRTIM_TIMC_DT_RSIGN HRTIM_DT_SIGN_POSITIVE +#define HRTIM_TIMC_DT_PRESCALER HRTIM_DEADTIME_PRESCALER_1 + +/* Timer D configuration - Boost operations */ + +#define HRTIM_TIMD_PRESCALER HRTIM_PRESCALER_1 +#define HRTIM_TIMD_MODE HRTIM_MODE_CONT +#define HRTIM_TIMD_UPDATE 0 +#define HRTIM_TIMD_RESET 0 + +#define HRTIM_TIMD_CH1_SET HRTIM_OUT_SET_NONE +#define HRTIM_TIMD_CH1_RST HRTIM_OUT_RST_NONE +#define HRTIM_TIMD_CH2_SET HRTIM_OUT_SET_NONE +#define HRTIM_TIMD_CH2_RST HRTIM_OUT_RST_NONE + +#define HRTIM_TIMD_DT_FSLOCK HRTIM_DT_LOCK +#define HRTIM_TIMD_DT_RSLOCK HRTIM_DT_LOCK +#define HRTIM_TIMD_DT_FVLOCK HRTIM_DT_RW +#define HRTIM_TIMD_DT_RVLOCK HRTIM_DT_RW +#define HRTIM_TIMD_DT_FSIGN HRTIM_DT_SIGN_POSITIVE +#define HRTIM_TIMD_DT_RSIGN HRTIM_DT_SIGN_POSITIVE +#define HRTIM_TIMD_DT_PRESCALER HRTIM_DEADTIME_PRESCALER_1 + +#define HRTIM_ADC_TRG2 HRTIM_ADCTRG24_CC4 + +/* DMA channels *************************************************************/ + +#endif /* CONFIG_EXAMPLES_SMPS */ #endif /* __BOARDS_ARM_STM32_B_G474E_DPOW1_INCLUDE_BOARD_H */ diff --git a/boards/arm/stm32/b-g474e-dpow1/src/CMakeLists.txt b/boards/arm/stm32/b-g474e-dpow1/src/CMakeLists.txt index d0c35a88d0..ced983e42c 100644 --- a/boards/arm/stm32/b-g474e-dpow1/src/CMakeLists.txt +++ b/boards/arm/stm32/b-g474e-dpow1/src/CMakeLists.txt @@ -30,6 +30,10 @@ if(CONFIG_BOARDCTL) list(APPEND SRCS stm32_appinit.c) endif() +if(CONFIG_DRIVERS_SMPS) + list(APPEND SRCS stm32_smps.c) +endif() + target_sources(board PRIVATE ${SRCS}) set_property(GLOBAL PROPERTY LD_SCRIPT "${NUTTX_BOARD_DIR}/scripts/ld.script") diff --git a/boards/arm/stm32/b-g474e-dpow1/src/Make.defs b/boards/arm/stm32/b-g474e-dpow1/src/Make.defs index fbc74033c1..8c7dc5159f 100644 --- a/boards/arm/stm32/b-g474e-dpow1/src/Make.defs +++ b/boards/arm/stm32/b-g474e-dpow1/src/Make.defs @@ -33,6 +33,10 @@ ifeq ($(CONFIG_BOARDCTL),y) CSRCS += stm32_appinit.c endif +ifeq ($(CONFIG_DRIVERS_SMPS),y) +CSRCS += stm32_smps.c +endif + DEPPATH += --dep-path board VPATH += :board CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)board diff --git a/boards/arm/stm32/b-g474e-dpow1/src/b-g474e-dpow1.h b/boards/arm/stm32/b-g474e-dpow1/src/b-g474e-dpow1.h index 2871324a5f..9cee308419 100644 --- a/boards/arm/stm32/b-g474e-dpow1/src/b-g474e-dpow1.h +++ b/boards/arm/stm32/b-g474e-dpow1/src/b-g474e-dpow1.h @@ -74,4 +74,16 @@ * Public Function Prototypes ****************************************************************************/ +/**************************************************************************** + * Name: stm32_smps_setup + * + * Description: + * Initialize SMPS peripheral for the board. + * + ****************************************************************************/ + +#ifdef CONFIG_DRIVERS_SMPS +int stm32_smps_setup(void); +#endif + #endif /* __BOARDS_ARM_STM32_B_G474E_DPOW1_SRC_B_G474E_DPOW1_H */ diff --git a/boards/arm/stm32/b-g474e-dpow1/src/stm32_appinit.c b/boards/arm/stm32/b-g474e-dpow1/src/stm32_appinit.c index 224060a955..6badc2865c 100644 --- a/boards/arm/stm32/b-g474e-dpow1/src/stm32_appinit.c +++ b/boards/arm/stm32/b-g474e-dpow1/src/stm32_appinit.c @@ -86,6 +86,16 @@ int board_app_initialize(uintptr_t arg) } #endif +#ifdef CONFIG_DRIVERS_SMPS + /* Initialize smps and register the smps driver */ + + ret = stm32_smps_setup(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_smps_setup failed: %d\n", ret); + } +#endif + UNUSED(ret); return OK; } diff --git a/boards/arm/stm32/b-g474e-dpow1/src/stm32_smps.c b/boards/arm/stm32/b-g474e-dpow1/src/stm32_smps.c new file mode 100644 index 0000000000..9cf7c5f527 --- /dev/null +++ b/boards/arm/stm32/b-g474e-dpow1/src/stm32_smps.c @@ -0,0 +1,1262 @@ +/**************************************************************************** + * boards/arm/stm32/b-g474e-dpow1/src/stm32_smps.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 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "arm_internal.h" +#include "ram_vectors.h" + +#include "stm32_hrtim.h" +#include "stm32_adc.h" + +#include + +#if defined(CONFIG_EXAMPLES_SMPS) && defined(CONFIG_DRIVERS_SMPS) + +#ifndef CONFIG_LIBDSP +# error CONFIG_LIBDSP is required +#endif + +#ifndef CONFIG_ARCH_HIPRI_INTERRUPT +# error CONFIG_ARCH_HIPRI_INTERRUPT is required +#endif + +#ifndef CONFIG_ARCH_RAMVECTORS +# error CONFIG_ARCH_RAMVECTORS is required +#endif + +#ifndef CONFIG_ARCH_IRQPRIO +# error CONFIG_ARCH_IRQPRIO is required +#endif + +#ifndef CONFIG_ARCH_FPU +# warning Set CONFIG_ARCH_FPU for hardware FPU support +#endif + +#if !defined(CONFIG_STM32_HRTIM1) || !defined(CONFIG_STM32_HRTIM) +# error "SMPS example requires HRTIM1 support" +#endif + +#if !defined(CONFIG_STM32_ADC1) || !defined(CONFIG_ADC) +# error "SMPS example requires ADC1 support" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* ADC1 channels used in this example */ + +#define ADC1_NCHANNELS 2 + +/* ADC1 injected channels numeration */ + +#define V_IN_ADC_INJ_CHANNEL 0 +#define V_OUT_ADC_INJ_CHANNEL 1 + +/* Voltage reference for ADC */ + +#define ADC_REF_VOLTAGE ((float)3.3) + +/* ADC resolution */ + +#define ADC_VAL_MAX 4095 + +/* Input voltage conversion ratio - 6.8k/(6.8k + 27k) */ + +#define V_IN_RATIO (float)((float)(6800+27000)/(float)6800) + +/* Output voltage conversion ratio - 3.3k/(3.3k + 13.3k) */ + +#define V_OUT_RATIO (float)((float)(3300+13300)/(float)3300) + +/* Some absolute limits */ + +#define SMPS_ABSOLUTE_OUT_CURRENT_LIMIT_mA 250 +#define SMPS_ABSOLUTE_OUT_VOLTAGE_LIMIT_mV 15000 +#define SMPS_ABSOLUTE_IN_VOLTAGE_LIMIT_mV 15000 + +#if CONFIG_EXAMPLES_SMPS_OUT_CURRENT_LIMIT > SMPS_ABSOLUTE_OUT_CURRENT_LIMIT_mA +# error "Output current limit great than absolute limit!" +#endif +#if CONFIG_EXAMPLES_SMPS_OUT_VOLTAGE_LIMIT > SMPS_ABSOLUTE_OUT_VOLTAGE_LIMIT_mV +# error "Output voltage limit greater than absolute limit!" +#endif +#if CONFIG_EXAMPLES_SMPS_IN_VOLTAGE_LIMIT > SMPS_ABSOLUTE_IN_VOLTAGE_LIMIT_mV +# error "Input voltage limit greater than absolute limit!" +#endif + +/* Maximum output voltage for boost converter in float */ + +#define BOOST_VOLT_MAX ((float)CONFIG_EXAMPLES_SMPS_OUT_VOLTAGE_LIMIT/1000.0) + +/* At this time only PID controller implemented */ + +#define SMPS_CONTROLLER_PID 1 + +/* Converter's finite accuracy */ + +#define SMPS_VOLTAGE_ACCURACY ((float)0.01) + +/* Buck-boost mode threshold */ + +#define SMPS_BUCKBOOST_RANGE ((float)0.5) + +/* PID controller configuration */ + +#define PID_KP ((float)1.0) +#define PID_KI ((float)0.1) +#define PID_KD ((float)0.0) + +/* Converter frequencies: + * - TIMC_PWM_FREQ - buck converter 250kHz + * - TIMD_PWM_FREQ - boost converter 250kHz + */ + +#define TIMC_PWM_FREQ 250000 +#define TIMD_PWM_FREQ 250000 + +/* Deadtime configuration */ + +#define DT_RISING 0x0B0 +#define DT_FALLING 0x0B0 + +/* Helper macros */ + +#define HRTIM_ALL_OUTPUTS_ENABLE(hrtim, state) \ + HRTIM_OUTPUTS_ENABLE(hrtim, HRTIM_OUT_TIMC_CH1|HRTIM_OUT_TIMC_CH2| \ + HRTIM_OUT_TIMD_CH1|HRTIM_OUT_TIMD_CH2, state); + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Current converter mode */ + +enum converter_mode_e +{ + CONVERTER_MODE_INIT, /* Initial mode */ + CONVERTER_MODE_BUCK, /* Buck mode operations (V_in > V_out) */ + CONVERTER_MODE_BOOST, /* Boost mode operations (V_in < V_out) */ + CONVERTER_MODE_BUCKBOOST, /* Buck-boost operations (V_in near V_out) */ +}; + +/* SMPS lower drivers structure */ + +struct smps_lower_dev_s +{ + struct hrtim_dev_s *hrtim; /* PWM generation */ + struct stm32_adc_dev_s *adc; /* input and output voltage sense */ + struct comp_dev_s *comp; /* not used in this demo - only as reference */ + struct dac_dev_s *dac; /* not used in this demo - only as reference */ + struct opamp_dev_s *opamp; /* not used in this demo - only as reference */ +}; + +/* Private data for smps */ + +struct smps_priv_s +{ + uint8_t conv_mode; /* Converter mode */ + uint16_t v_in_raw; /* Voltage input RAW value */ + uint16_t v_out_raw; /* Voltage output RAW value */ + float v_in; /* Voltage input real value in V */ + float v_out; /* Voltage output real value in V */ + bool running; /* Running flag */ + pid_controller_f32_t pid; /* PID controller */ + float *c_limit_tab; /* Current limit tab */ +}; + +/**************************************************************************** + * Private Function Protototypes + ****************************************************************************/ + +static int smps_setup(struct smps_dev_s *dev); +static int smps_shutdown(struct smps_dev_s *dev); +static int smps_start(struct smps_dev_s *dev); +static int smps_stop(struct smps_dev_s *dev); +static int smps_params_set(struct smps_dev_s *dev, + struct smps_params_s *param); +static int smps_mode_set(struct smps_dev_s *dev, uint8_t mode); +static int smps_limits_set(struct smps_dev_s *dev, + struct smps_limits_s *limits); +static int smps_state_get(struct smps_dev_s *dev, + struct smps_state_s *state); +static int smps_fault_set(struct smps_dev_s *dev, uint8_t fault); +static int smps_fault_get(struct smps_dev_s *dev, + uint8_t *fault); +static int smps_fault_clean(struct smps_dev_s *dev, + uint8_t fault); +static int smps_ioctl(struct smps_dev_s *dev, int cmd, + unsigned long arg); + +static void smps_conv_mode_set(struct smps_priv_s *priv, + struct smps_lower_dev_s *lower, + uint8_t mode); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct smps_lower_dev_s g_smps_lower; +struct smps_priv_s g_smps_priv; +struct smps_s g_smps; + +struct smps_ops_s g_smps_ops = +{ + .setup = smps_setup, + .shutdown = smps_shutdown, + .start = smps_start, + .stop = smps_stop, + .params_set = smps_params_set, + .mode_set = smps_mode_set, + .limits_set = smps_limits_set, + .fault_set = smps_fault_set, + .state_get = smps_state_get, + .fault_get = smps_fault_get, + .fault_clean = smps_fault_clean, + .ioctl = smps_ioctl +}; + +struct smps_dev_s g_smps_dev = +{ + .ops = &g_smps_ops, + .priv = &g_smps, + .lower = NULL +}; + +/* ADC configuration: + * - Input voltage (V_IN) - ADC1 Channel 2 (PA1) + * - Output voltage (V_OUT) - ADC1 Channel 4 (PA3) + * + * ADC channels configured in injected mode. + * + * Transistors configuration in buck mode: + * - T6 - ON + * - T2 - OFF + * - T5 and T1 - buck operation + * Transistors configuration in boost mode: + * - T5 - ON + * - T1 - OFF + * - T6 and T2 - boost operation + * Transistors configuration in buck-boost mode: + * - T5 and T1 - buck operation + * - T6 and T2 - boost operation + * + * HRTIM outputs configuration: + * - T5 -> PB12 -> HRTIM_CHC1 + * - T6 -> PB14 -> HRTIM_CHD1 + * - T1 -> PB13 -> HRTIM_CHC2 + * - T2 -> PB15 -> HRTIM_CHD2 + */ + +/* ADC channel list */ + +static const uint8_t g_adc1chan[ADC1_NCHANNELS] = +{ + 2, + 4 +}; + +/* Configurations of pins used by ADC channel */ + +static const uint32_t g_adc1pins[ADC1_NCHANNELS] = +{ + GPIO_ADC1_IN2, /* PA1 - V_IN */ + GPIO_ADC1_IN4, /* PA3 - V_OUT */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: smps_setup + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_setup(struct smps_dev_s *dev) +{ + struct smps_lower_dev_s *lower = dev->lower; + struct smps_s *smps = (struct smps_s *)dev->priv; + struct hrtim_dev_s *hrtim = NULL; + struct stm32_adc_dev_s *adc = NULL; + struct smps_priv_s *priv; + struct adc_channel_s channels[ADC1_NCHANNELS]; + struct adc_sample_time_s stime; + int ret = OK; + int i = 0; + + /* Initialize smps structure */ + + smps->opmode = SMPS_OPMODE_INIT; + smps->state.state = SMPS_STATE_INIT; + smps->priv = &g_smps_priv; + + /* Check lower half drivers */ + + hrtim = lower->hrtim; + if (hrtim == NULL) + { + pwrerr("ERROR: Failed to get hrtim "); + ret = ERROR; + goto errout; + } + + adc = lower->adc; + if (adc == NULL) + { + pwrerr("ERROR: Failed to get ADC lower level interface"); + ret = ERROR; + goto errout; + } + + /* Update ADC sample time */ + + for (i = 0; i < ADC1_NCHANNELS; i += 1) + { + channels[i].sample_time = ADC_SMPR_92p5; + channels[i].channel = g_adc1chan[i]; + } + + memset(&stime, 0, sizeof(struct adc_sample_time_s)); + + stime.channels_nbr = ADC1_NCHANNELS; + stime.channel = channels; + + STM32_ADC_SAMPLETIME_SET(adc, &stime); + STM32_ADC_SAMPLETIME_WRITE(adc); + + /* TODO: create current limit table */ + + UNUSED(priv); + +errout: + return ret; +} + +/**************************************************************************** + * Name: smps_shutdown + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_shutdown(struct smps_dev_s *dev) +{ + struct smps_s *smps = (struct smps_s *)dev->priv; + struct smps_priv_s *priv = (struct smps_priv_s *)smps->priv; + + /* Stop smps if running */ + + if (priv->running == true) + { + smps_stop(dev); + } + + /* Reset smps structure */ + + memset(smps, 0, sizeof(struct smps_s)); + + return OK; +} + +/**************************************************************************** + * Name: smps_start + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_start(struct smps_dev_s *dev) +{ + struct smps_lower_dev_s *lower = dev->lower; + struct smps_s *smps = (struct smps_s *)dev->priv; + struct smps_priv_s *priv = (struct smps_priv_s *)smps->priv; + struct hrtim_dev_s *hrtim = lower->hrtim; + struct stm32_adc_dev_s *adc = lower->adc; + volatile uint64_t per = 0; + uint64_t fclk = 0; + int ret = OK; + + /* Disable HRTIM outputs */ + + HRTIM_ALL_OUTPUTS_ENABLE(hrtim, false); + + /* Reset SMPS private structure */ + + memset(priv, 0, sizeof(struct smps_priv_s)); + +#ifdef SMPS_CONTROLLER_PID + /* Initialize PID controller */ + + pid_controller_init(&priv->pid, PID_KP, PID_KI, PID_KD); + + /* Set PID controller saturation */ + + pid_saturation_set(&priv->pid, 0.0, BOOST_VOLT_MAX); + + /* Reset PI integral if saturated */ + + pi_ireset_enable(&priv->pid, true); +#endif + + /* Get TIMC period value for given frequency */ + + fclk = HRTIM_FCLK_GET(hrtim, HRTIM_TIMER_TIMC); + per = fclk / TIMC_PWM_FREQ; + if (per > HRTIM_PER_MAX) + { + pwrerr("ERROR: Can not achieve timc pwm " + "freq=%" PRIu32 " if fclk=%" PRIu64 "\n", + (uint32_t)TIMC_PWM_FREQ, (uint64_t)fclk); + ret = -EINVAL; + goto errout; + } + + /* Set TIMC period value */ + + HRTIM_PER_SET(hrtim, HRTIM_TIMER_TIMC, (uint16_t)per); + + /* Get TIMD period value for given frequency */ + + fclk = HRTIM_FCLK_GET(hrtim, HRTIM_TIMER_TIMD); + per = fclk / TIMD_PWM_FREQ; + if (per > HRTIM_PER_MAX) + { + pwrerr("ERROR: Can not achieve timd pwm " + "freq=%" PRIu32 " if fclk=%" PRIu64 "\n", + (uint32_t)TIMD_PWM_FREQ, (uint64_t)fclk); + ret = -EINVAL; + goto errout; + } + + /* Set TIMD period value */ + + HRTIM_PER_SET(hrtim, HRTIM_TIMER_TIMD, (uint16_t)per); + + /* ADC trigger on TIMC CMP4 */ + + HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMC, HRTIM_CMP4, 10000); + + /* Configure TIMER C and TIMER D deadtime mode + * + * NOTE: In deadtime mode we have to configure output 1 only + * (SETx1, RSTx1), output 2 configuration is not significant. + */ + + HRTIM_DEADTIME_UPDATE(hrtim, HRTIM_TIMER_TIMC, HRTIM_DT_EDGE_RISING, + DT_RISING); + HRTIM_DEADTIME_UPDATE(hrtim, HRTIM_TIMER_TIMC, HRTIM_DT_EDGE_FALLING, + DT_FALLING); + HRTIM_DEADTIME_UPDATE(hrtim, HRTIM_TIMER_TIMD, HRTIM_DT_EDGE_RISING, + DT_RISING); + HRTIM_DEADTIME_UPDATE(hrtim, HRTIM_TIMER_TIMD, HRTIM_DT_EDGE_FALLING, + DT_FALLING); + + /* Set T5 and T2 to a low state. + * Deadtime mode force T1 and T6 to a high state. + */ + + HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMC_CH1, HRTIM_OUT_SET_NONE); + HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMC_CH1, HRTIM_OUT_RST_PER); + + HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMD_CH1, HRTIM_OUT_SET_NONE); + HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMD_CH1, HRTIM_OUT_RST_PER); + + /* Set running flag */ + + priv->running = true; + + HRTIM_ALL_OUTPUTS_ENABLE(hrtim, true); + + /* Enable ADC JEOS interrupts */ + + STM32_ADC_INT_ENABLE(adc, ADC_INT_JEOS); + + /* Enable ADC12 interrupts */ + + up_enable_irq(STM32_IRQ_ADC12); + + /* Start injected conversion */ + + STM32_ADC_INJ_STARTCONV(adc, true); + +errout: + return ret; +} + +/**************************************************************************** + * Name: smps_stop + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_stop(struct smps_dev_s *dev) +{ + struct smps_lower_dev_s *lower = dev->lower; + struct smps_s *smps = (struct smps_s *)dev->priv; + struct smps_priv_s *priv = (struct smps_priv_s *)smps->priv; + struct hrtim_dev_s *hrtim = lower->hrtim; + struct stm32_adc_dev_s *adc = lower->adc; + + /* Disable HRTIM outputs */ + + HRTIM_ALL_OUTPUTS_ENABLE(hrtim, false); + + /* Stop injected conversion */ + + STM32_ADC_INJ_STARTCONV(adc, false); + + /* Disable ADC JEOS interrupts */ + + STM32_ADC_INT_DISABLE(adc, ADC_INT_JEOS); + + /* Disable ADC12 interrupts */ + + up_disable_irq(STM32_IRQ_ADC12); + + /* Reset running flag */ + + priv->running = false; + + return OK; +} + +/**************************************************************************** + * Name: smps_params_set + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_params_set(struct smps_dev_s *dev, + struct smps_params_s *param) +{ + struct smps_s *smps = (struct smps_s *)dev->priv; + int ret = OK; + + /* Only output voltage */ + + smps->param.v_out = param->v_out; + + /* REVISIT: use current and power parameters ? */ + + if (param->i_out > 0) + { + pwrwarn("WARNING: Output current parameters not used in this demo\n"); + } + + if (param->p_out > 0) + { + pwrwarn("WARNING: Output power parameters not used in this demo\n"); + } + + return ret; +} + +/**************************************************************************** + * Name: smps_mode_set + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_mode_set(struct smps_dev_s *dev, uint8_t mode) +{ + struct smps_s *smps = (struct smps_s *)dev->priv; + int ret = OK; + + /* Only constant voltage mode supported */ + + if (mode == SMPS_OPMODE_CV) + { + smps->opmode = mode; + } + else + { + pwrerr("ERROR: Unsupported SMPS mode %d!\n", mode); + ret = ERROR; + goto errout; + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: smps_limits_set + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_limits_set(struct smps_dev_s *dev, + struct smps_limits_s *limits) +{ + struct smps_s *smps = (struct smps_s *)dev->priv; + int ret = OK; + + /* Some assertions */ + + if (limits->v_out <= 0) + { + pwrerr("ERROR: Output voltage limit must be set!\n"); + ret = ERROR; + goto errout; + } + + if (limits->v_in <= 0) + { + pwrerr("ERROR: Input voltage limit must be set!\n"); + ret = ERROR; + goto errout; + } + + if (limits->i_out <= 0) + { + pwrerr("ERROR: Output current limit must be set!\n"); + ret = ERROR; + goto errout; + } + + if (limits->v_out * 1000 > CONFIG_EXAMPLES_SMPS_OUT_VOLTAGE_LIMIT) + { + limits->v_out = (float)CONFIG_EXAMPLES_SMPS_OUT_VOLTAGE_LIMIT / 1000.0; + pwrwarn("WARNING: " + "SMPS output voltage limiit > SMPS absolute output voltage " + "limit. Set output voltage limit to %.2f.\n", + limits->v_out); + } + + if (limits->v_in * 1000 > CONFIG_EXAMPLES_SMPS_IN_VOLTAGE_LIMIT) + { + limits->v_in = (float)CONFIG_EXAMPLES_SMPS_IN_VOLTAGE_LIMIT / 1000.0; + pwrwarn("WARNING: " + "SMPS input voltage limiit > SMPS absolute input voltage " + "limit. Set input voltage limit to %.2f.\n", + limits->v_in); + } + + if (limits->i_out * 1000 > CONFIG_EXAMPLES_SMPS_OUT_CURRENT_LIMIT) + { + limits->i_out = (float)CONFIG_EXAMPLES_SMPS_OUT_CURRENT_LIMIT / 1000.0; + pwrwarn("WARNING: " + "SMPS output current limiit > SMPS absolute output current " + "limit. Set output current limit to %.2f.\n", + limits->i_out); + } + + /* Set output voltage limit */ + + smps->limits.v_out = limits->v_out; + + /* Set input voltage limit */ + + smps->limits.v_in = limits->v_in; + + /* Set current limit */ + + smps->limits.i_out = limits->i_out; + + /* Lock limits */ + + smps->limits.lock = true; + +errout: + return ret; +} + +/**************************************************************************** + * Name: smps_state_get + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_state_get(struct smps_dev_s *dev, + struct smps_state_s *state) +{ + struct smps_s *smps = (struct smps_s *)dev->priv; + + /* Copy localy stored feedbacks data to status structure */ + + smps->state.fb.v_in = g_smps_priv.v_in; + smps->state.fb.v_out = g_smps_priv.v_out; + + /* Return state structure to caller */ + + memcpy(state, &smps->state, sizeof(struct smps_state_s)); + + return OK; +} + +/**************************************************************************** + * Name: smps_fault_set + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_fault_set(struct smps_dev_s *dev, uint8_t fault) +{ + return OK; +} + +/**************************************************************************** + * Name: smps_fault_get + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_fault_get(struct smps_dev_s *dev, uint8_t *fault) +{ + return OK; +} + +/**************************************************************************** + * Name: smps_fault_clean + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_fault_clean(struct smps_dev_s *dev, uint8_t fault) +{ + return OK; +} + +/**************************************************************************** + * Name: smps_state_get + * + * Description: + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +static int smps_ioctl(struct smps_dev_s *dev, int cmd, unsigned long arg) +{ + return OK; +} + +/**************************************************************************** + * Name: smps_controller + * + * Description: + * + * Returned Value: + * + ****************************************************************************/ + +static float smps_controller(struct smps_priv_s *priv, float err) +{ + float out = 0.0; + +#ifdef SMPS_CONTROLLER_PID + out = pid_controller(&priv->pid, err); +#else +# error "At this time only PID controller implemented" +#endif + + return out; +} + +/**************************************************************************** + * Name: smps_duty_set + * + * Description: + * + * Returned Value: + * + ****************************************************************************/ + +static void smps_duty_set(struct smps_priv_s *priv, + struct smps_lower_dev_s *lower, + float out) +{ + struct hrtim_dev_s *hrtim = lower->hrtim; + uint8_t mode = priv->conv_mode; + uint16_t cmp = 0; + float duty = 0.0; + uint16_t per = 0; + + switch (mode) + { + case CONVERTER_MODE_INIT: + { + /* Do nothing */ + + break; + } + + case CONVERTER_MODE_BUCK: + { + if (out >= priv->v_in) out = priv->v_in; + if (out < 0.0) out = 0.0; + + duty = out / priv->v_in; + +#warning TODO: current limit in buck mode + + per = HRTIM_PER_GET(hrtim, HRTIM_TIMER_TIMC); + + cmp = (uint16_t)(per * duty); + + if (cmp > per - 30) cmp = per - 30; + + /* Set T5 duty cycle. T1 is complementary to T5 */ + + HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMC, HRTIM_CMP1, cmp); + + break; + } + + case CONVERTER_MODE_BOOST: + { + per = HRTIM_PER_GET(hrtim, HRTIM_TIMER_TIMC); + + if (out < priv->v_in) out = priv->v_in; + if (out >= BOOST_VOLT_MAX) out = BOOST_VOLT_MAX; + + duty = 1.0 - priv->v_in / out; + +#warning TODO: current limit in boost mode + + cmp = (uint16_t)(per * duty); + + /* Set T2 duty cycle. T6 is complementary to T2 */ + + HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMD, HRTIM_CMP1, cmp); + + break; + } + + case CONVERTER_MODE_BUCKBOOST: + { + /* Buck converter is set to fixed duty cycle (80%). + * Now we need set boost converter + */ + + per = HRTIM_PER_GET(hrtim, HRTIM_TIMER_TIMC); + + if (out < priv->v_in) out = priv->v_in; + if (out >= BOOST_VOLT_MAX) out = BOOST_VOLT_MAX; + + duty = 1.0 - priv->v_in / out; + +#warning TODO: current limit in buck boost mode + + cmp = (uint16_t)(per * duty); + + /* Set T2 duty cycle. T6 is complementary to T2 */ + + HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMD, HRTIM_CMP1, cmp); + + break; + } + + default: + { + pwrerr("ERROR: Unknown converter mode %d!\n", mode); + break; + } + } +} + +/**************************************************************************** + * Name: smps_conv_mode_set + * + * Description: + * Change converter mode (buck/boost/buck-boost). + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void smps_conv_mode_set(struct smps_priv_s *priv, + struct smps_lower_dev_s *lower, + uint8_t mode) +{ + struct hrtim_dev_s *hrtim = lower->hrtim; + + /* Disable all outputs */ + + HRTIM_ALL_OUTPUTS_ENABLE(hrtim, false); + + switch (mode) + { + case CONVERTER_MODE_INIT: + { + break; + } + + case CONVERTER_MODE_BUCK: + { + /* Set T2 low (T6 high) on the next PER */ + + HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMD_CH1, + HRTIM_OUT_SET_NONE); + HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMD_CH1, + HRTIM_OUT_RST_PER); + + /* Set T5 to a high state on PER and reset on CMP1. + * T1 is complementary to T5. + */ + + HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMC_CH1, + HRTIM_OUT_SET_PER); + HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMC_CH1, + HRTIM_OUT_RST_CMP1); + + break; + } + + case CONVERTER_MODE_BOOST: + { + /* Set T4 high (T11 low) on the next PER */ + + HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMC_CH1, + HRTIM_OUT_SET_PER); + HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMC_CH1, + HRTIM_OUT_RST_NONE); + + /* Set T12 to a high state on PER and reset on CMP1. + * T5 is complementary to T12. + */ + + HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMD_CH1, + HRTIM_OUT_SET_PER); + HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMD_CH1, + HRTIM_OUT_RST_CMP1); + + break; + } + + case CONVERTER_MODE_BUCKBOOST: + { + /* Set T4 to a high state on PER and reset on CMP1. + * T11 is complementary to T4. + */ + + HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMC_CH1, + HRTIM_OUT_SET_PER); + HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMC_CH1, + HRTIM_OUT_RST_CMP1); + + /* Set T12 to a high state on PER and reset on CMP1. + * T5 is complementary to T12. + */ + + HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMD_CH1, + HRTIM_OUT_SET_PER); + HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMD_CH1, + HRTIM_OUT_RST_CMP1); + + /* Set fixed duty cycle (80%) on buck converter (T4 and T11) */ + + HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMC, HRTIM_CMP1, + 0.8 * ((uint16_t)HRTIM_PER_GET(hrtim, + HRTIM_TIMER_TIMC))); + + break; + } + + default: + { + pwrerr("ERROR: Unknown converter mode %d!\n", mode); + break; + } + } + + /* Set mode in private data */ + + priv->conv_mode = mode; + + /* Enable outputs */ + + HRTIM_ALL_OUTPUTS_ENABLE(hrtim, true); +} + +/**************************************************************************** + * Name: adc12_handler + ****************************************************************************/ + +static void adc12_handler(void) +{ + struct smps_dev_s *dev = &g_smps_dev; + struct smps_s *smps = (struct smps_s *)dev->priv; + struct smps_priv_s *priv = (struct smps_priv_s *)smps->priv; + struct smps_lower_dev_s *lower = dev->lower; + struct stm32_adc_dev_s *adc = lower->adc; + uint32_t pending; + float ref = ADC_REF_VOLTAGE; + float bit = ADC_VAL_MAX; + float err; + float out; + uint8_t mode; + + pending = STM32_ADC_INT_GET(adc); + + if (pending & ADC_INT_JEOC && priv->running == true) + { + /* Get raw ADC values */ + + priv->v_out_raw = STM32_ADC_INJDATA_GET(adc, V_OUT_ADC_INJ_CHANNEL); + priv->v_in_raw = STM32_ADC_INJDATA_GET(adc, V_IN_ADC_INJ_CHANNEL); + + /* Convert raw values to real values */ + + priv->v_out = (priv->v_out_raw * ref / bit) * V_OUT_RATIO; + priv->v_in = (priv->v_in_raw * ref / bit) * V_IN_RATIO; + + /* According to measured voltages we set converter + * in appropriate mode + */ + + if (smps->param.v_out > (priv->v_in + SMPS_BUCKBOOST_RANGE)) + { + /* Desired output voltage greater than input voltage - set + * boost converter + */ + + mode = CONVERTER_MODE_BOOST; + } + + else if (smps->param.v_out < (priv->v_in - SMPS_BUCKBOOST_RANGE)) + { + /* Desired output voltage lower than input voltage - set + * buck converter + */ + + mode = CONVERTER_MODE_BUCK; + } + + else + { + /* Desired output voltage close to input voltage - set + * buck-boost converter + */ + + mode = CONVERTER_MODE_BUCKBOOST; + } + + /* Configure converter to the new mode if needed */ + + if (priv->conv_mode != mode) + { + smps_conv_mode_set(priv, lower, mode); + } + + /* Get regulator error */ + + err = smps->param.v_out - priv->v_out; + + if (err >= SMPS_VOLTAGE_ACCURACY || err <= (-SMPS_VOLTAGE_ACCURACY)) + { + /* PID controller */ + + out = smps_controller(priv, err); + + /* Update duty cycle */ + + smps_duty_set(priv, lower, out); + } + } + + /* Clear pending */ + + STM32_ADC_INT_ACK(adc, pending); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_smps_setup + * + * Description: + * Initialize SMPS driver. + * + * This function should be call by board_app_initialize(). + * + * Returned Value: + * 0 on success, a negated errno value on failure + * + ****************************************************************************/ + +int stm32_smps_setup(void) +{ + struct smps_lower_dev_s *lower = &g_smps_lower; + struct smps_dev_s *smps = &g_smps_dev; + struct hrtim_dev_s *hrtim = NULL; + struct adc_dev_s *adc = NULL; + static bool initialized = false; + int ret = OK; + int i; + + /* Initialize only once */ + + if (!initialized) + { + /* Get the HRTIM interface */ + + hrtim = stm32_hrtiminitialize(); + if (hrtim == NULL) + { + pwrerr("ERROR: Failed to get HRTIM1 interface\n"); + return -ENODEV; + } + + /* Configure the pins as analog inputs for the selected channels */ + + for (i = 0; i < ADC1_NCHANNELS; i++) + { + stm32_configgpio(g_adc1pins[i]); + } + + /* Get the ADC interface */ + + adc = stm32_adcinitialize(1, g_adc1chan, ADC1_NCHANNELS); + if (adc == NULL) + { + pwrerr("ERROR: Failed to get ADC %d interface\n", 1); + return -ENODEV; + } + + /* Initialize SMPS lower driver interfaces */ + + lower->hrtim = hrtim; + lower->adc = adc->ad_priv; + lower->comp = NULL; + lower->dac = NULL; + lower->opamp = NULL; + + /* Attach ADC12 ram vector */ + + ret = arm_ramvec_attach(STM32_IRQ_ADC12, adc12_handler); + if (ret < 0) + { + pwrerr("ERROR: arm_ramvec_attach failed: %d\n", ret); + ret = EXIT_FAILURE; + goto errout; + } + + /* Set the priority of the ADC12 interrupt vector */ + + ret = up_prioritize_irq(STM32_IRQ_ADC12, NVIC_SYSH_HIGH_PRIORITY); + if (ret < 0) + { + pwrerr("ERROR: up_prioritize_irq failed: %d\n", ret); + ret = EXIT_FAILURE; + goto errout; + } + + /* Setup ADC hardware */ + + adc->ad_ops->ao_setup(adc); + + /* We do not need register character drivers for SMPS lower + * peripherals. All control should be done via SMPS character + * driver. + */ + + ret = smps_register(CONFIG_EXAMPLES_SMPS_DEVPATH, smps, (void *)lower); + if (ret < 0) + { + pwrerr("ERROR: smps_register failed: %d\n", ret); + return ret; + } + + initialized = true; + } + +errout: + return ret; +} + +#endif /* CONFIG_EXAMPLE_SMPS && CONFIG_DRIVERS_SMPS*/