nuttx/configs/stm32f334-disco/src/stm32_smps.c

1084 lines
29 KiB
C

/****************************************************************************
* configs/stm32f334-disco/src/stm32_smps.c
*
* Copyright (C) 2017, 2018 Gregory Nutt. All rights reserved.
* Author: Mateusz Szafoni <raiden00@railab.me>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <debug.h>
#include <dsp.h>
#include <sys/boardctl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <nuttx/arch.h>
#include <nuttx/board.h>
#include <nuttx/fs/fs.h>
#include "up_internal.h"
#include "ram_vectors.h"
#include <nuttx/analog/adc.h>
#include <nuttx/analog/ioctl.h>
#include <nuttx/power/smps.h>
#include "stm32_hrtim.h"
#include "stm32_adc.h"
#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 convertion ratio - 6.8k/(6.8k + 27k) */
#define V_IN_RATIO (float)((float)(6800+27000)/(float)6800)
/* Output voltage convertion 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 conveter in float */
#define BOOST_VOLT_MAX ((float)CONFIG_EXAMPLES_SMPS_OUT_VOLTAGE_LIMIT/1000.0)
/* Current limit table dimmension */
#define SMPS_CURRENT_LIMIT_TAB_DIM 15
/* 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 frequncies:
* - TIMA_PWM_FREQ - buck converter 250kHz
* - TIMB_PWM_FREQ - boost converter 250kHz
*/
#define TIMA_PWM_FREQ 250000
#define TIMB_PWM_FREQ 250000
/* Deadtime configuration */
#define DT_RISING 0x0A0
#define DT_FALLING 0x0A0
/* Helper macros */
#define HRTIM_ALL_OUTPUTS_ENABLE(hrtim, state) \
HRTIM_OUTPUTS_ENABLE(hrtim, HRTIM_OUT_TIMA_CH1|HRTIM_OUT_TIMA_CH2| \
HRTIM_OUT_TIMB_CH1|HRTIM_OUT_TIMB_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
{
FAR struct hrtim_dev_s *hrtim; /* PWM generation */
FAR struct adc_dev_s *adc; /* input and output voltage sense */
FAR struct comp_dev_s *comp; /* not used in this demo - only as reference */
FAR struct dac_dev_s *dac; /* not used in this demo - only as reference */
FAR 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_t pid; /* PID controller */
float *c_limit_tab; /* Current limit tab */
};
/****************************************************************************
* Private Function Protototypes
****************************************************************************/
static int smps_setup(FAR struct smps_dev_s *dev);
static int smps_shutdown(FAR struct smps_dev_s *dev);
static int smps_start(FAR struct smps_dev_s *dev);
static int smps_stop(FAR struct smps_dev_s *dev);
static int smps_params_set(FAR struct smps_dev_s *dev,
FAR struct smps_params_s *param);
static int smps_mode_set(FAR struct smps_dev_s *dev, uint8_t mode);
static int smps_limits_set(FAR struct smps_dev_s *dev,
FAR struct smps_limits_s *limits);
static int smps_state_get(FAR struct smps_dev_s *dev,
FAR struct smps_state_s *state);
static int smps_fault_set(FAR struct smps_dev_s *dev, uint8_t fault);
static int smps_fault_get(FAR struct smps_dev_s *dev,
FAR uint8_t *fault);
static int smps_fault_clean(FAR struct smps_dev_s *dev,
uint8_t fault);
static int smps_ioctl(FAR struct smps_dev_s *dev, int cmd,
unsigned long arg);
/****************************************************************************
* 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:
* - T5 - ON
* - T12 - OFF
* - T4 and T11 - buck operation
* Transistors configuration in boost mode:
* - T4 - ON
* - T11 - OFF
* - T5 and T12 - boost operation
* Transistors configuration in buck-boost mode:
* - T4, T11 - buck operation
* - T5 and T12 - boost operation
*
* HRTIM outputs configuration:
* - T4 -> PA8 -> HRTIM_CHA1
* - T5 -> PA11 -> HRTIM_CHB2
* - T11 -> PA9 -> HRTIM_CHA2
* - T12 -> PA10 -> HRTIM_CHB1
*
*/
/* 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
****************************************************************************/
static int smps_shutdown(FAR struct smps_dev_s *dev)
{
FAR struct smps_s *smps = (FAR struct smps_s *)dev->priv;
FAR 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_setup
*
* Description:
*
* Returned Value:
* 0 on success, a negated errno value on failure
*
****************************************************************************/
static int smps_setup(FAR struct smps_dev_s *dev)
{
FAR struct smps_lower_dev_s *lower = dev->lower;
FAR struct smps_s *smps = (FAR struct smps_s *)dev->priv;
FAR struct hrtim_dev_s *hrtim = NULL;
FAR struct adc_dev_s *adc = NULL;
FAR struct smps_priv_s *priv;
/* 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 ");
}
adc = lower->adc;
if (adc == NULL)
{
pwrerr("ERROR: Failed to get ADC lower level interface");
}
/* TODO: create current limit table */
UNUSED(priv);
return OK;
}
static int smps_start(FAR struct smps_dev_s *dev)
{
FAR struct smps_lower_dev_s *lower = dev->lower;
FAR struct stm32_adc_dev_s *stm32_adc =
(FAR struct stm32_adc_dev_s *) lower->adc->ad_priv;
FAR struct smps_s *smps = (FAR struct smps_s *)dev->priv;
FAR struct smps_priv_s *priv = (struct smps_priv_s *)smps->priv;
FAR struct hrtim_dev_s *hrtim = lower->hrtim;
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);
#endif
/* Get TIMA period value for given frequency */
fclk = HRTIM_FCLK_GET(hrtim, HRTIM_TIMER_TIMA);
per = fclk/TIMA_PWM_FREQ;
if (per > HRTIM_PER_MAX)
{
pwrerr("ERROR: Can not achieve tima pwm freq=%u if fclk=%llu\n",
(uint32_t)TIMA_PWM_FREQ, (uint64_t)fclk);
ret = -EINVAL;
goto errout;
}
/* Set TIMA period value */
HRTIM_PER_SET(hrtim, HRTIM_TIMER_TIMA, (uint16_t)per);
/* Get TIMB period value for given frequency */
fclk = HRTIM_FCLK_GET(hrtim, HRTIM_TIMER_TIMB);
per = fclk/TIMB_PWM_FREQ;
if (per > HRTIM_PER_MAX)
{
pwrerr("ERROR: Can not achieve timb pwm freq=%u if fclk=%llu\n",
(uint32_t)TIMB_PWM_FREQ, (uint64_t)fclk);
ret = -EINVAL;
goto errout;
}
/* Set TIMB period value */
HRTIM_PER_SET(hrtim, HRTIM_TIMER_TIMB, (uint16_t)per);
/* ADC trigger on TIMA CMP4 */
HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMA, HRTIM_CMP4, 10000);
/* Configure TIMER A and TIMER B 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_TIMA, HRTIM_DT_EDGE_RISING, DT_RISING);
HRTIM_DEADTIME_UPDATE(hrtim, HRTIM_TIMER_TIMA, HRTIM_DT_EDGE_FALLING, DT_FALLING);
HRTIM_DEADTIME_UPDATE(hrtim, HRTIM_TIMER_TIMB, HRTIM_DT_EDGE_RISING, DT_RISING);
HRTIM_DEADTIME_UPDATE(hrtim, HRTIM_TIMER_TIMB, HRTIM_DT_EDGE_FALLING, DT_FALLING);
/* Set T4 and T12 to a low state.
* Deadtime mode force T11 and T5 to a high state.
*/
HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMA_CH1, HRTIM_OUT_SET_NONE);
HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMA_CH1, HRTIM_OUT_RST_PER);
HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMB_CH1, HRTIM_OUT_SET_NONE);
HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMB_CH1, HRTIM_OUT_RST_PER);
/* Set running flag */
priv->running = true;
HRTIM_ALL_OUTPUTS_ENABLE(hrtim, true);
/* Enable ADC interrupts */
stm32_adc->ops->int_en(stm32_adc, ADC_INT_JEOS);
errout:
return ret;
}
static int smps_stop(FAR struct smps_dev_s *dev)
{
FAR struct smps_lower_dev_s *lower = dev->lower;
FAR struct smps_s *smps = (FAR struct smps_s *)dev->priv;
FAR struct smps_priv_s *priv = (struct smps_priv_s *)smps->priv;
FAR struct hrtim_dev_s *hrtim = lower->hrtim;
FAR struct stm32_adc_dev_s *stm32_adc =
(FAR struct stm32_adc_dev_s *) lower->adc->ad_priv;
/* Disable HRTIM outputs */
HRTIM_ALL_OUTPUTS_ENABLE(hrtim, false);
/* Disable ADC interrupts */
stm32_adc->ops->int_dis(stm32_adc, ADC_INT_JEOS);
/* Reset running flag */
priv->running = false;
return OK;
}
static int smps_params_set(FAR struct smps_dev_s *dev,
FAR struct smps_params_s *param)
{
FAR struct smps_s *smps = (FAR 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;
}
static int smps_mode_set(FAR struct smps_dev_s *dev, uint8_t mode)
{
FAR struct smps_s *smps = (FAR 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;
}
static int smps_limits_set(FAR struct smps_dev_s *dev,
FAR struct smps_limits_s *limits)
{
FAR struct smps_s *smps = (FAR 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 absoulute 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 absoulute 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 absoulute 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;
}
static int smps_state_get(FAR struct smps_dev_s *dev,
FAR struct smps_state_s *state)
{
FAR struct smps_s *smps = (FAR 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;
}
static int smps_fault_set(FAR struct smps_dev_s *dev, uint8_t fault)
{
return OK;
}
static int smps_fault_get(FAR struct smps_dev_s *dev, FAR uint8_t *fault)
{
return OK;
}
static int smps_fault_clean(FAR struct smps_dev_s *dev, uint8_t fault)
{
return OK;
}
static int smps_ioctl(FAR struct smps_dev_s *dev, int cmd, unsigned long arg)
{
return OK;
}
/****************************************************************************
* Name: smps_controller
****************************************************************************/
static float smps_controller(FAR 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
****************************************************************************/
static void smps_duty_set(struct smps_priv_s *priv, struct smps_lower_dev_s *lower,
float out)
{
FAR 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_TIMA);
cmp = (uint16_t)(per * duty);
if (cmp > per-30) cmp = per - 30;
/* Set T4 duty cycle. T11 is complementary to T4 */
HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMA, HRTIM_CMP1, cmp);
break;
}
case CONVERTER_MODE_BOOST:
{
per = HRTIM_PER_GET(hrtim, HRTIM_TIMER_TIMA);
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 T12 duty cycle. T5 is complementary to T12 */
HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMB, 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_TIMA);
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 T12 duty cycle. T5 is complementary to T12 */
HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMB, 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)
{
FAR 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 T12 low (T5 high) on the next PER */
HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMB_CH1, HRTIM_OUT_SET_NONE);
HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMB_CH1, HRTIM_OUT_RST_PER);
/* Set T4 to a high state on PER and reset on CMP1.
T11 is complementary to T4. */
HRTIM_OUTPUT_SET_SET(hrtim, HRTIM_OUT_TIMA_CH1, HRTIM_OUT_SET_PER);
HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMA_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_TIMA_CH1, HRTIM_OUT_SET_PER);
HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMA_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_TIMB_CH1, HRTIM_OUT_SET_PER);
HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMB_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_TIMA_CH1, HRTIM_OUT_SET_PER);
HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMA_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_TIMB_CH1, HRTIM_OUT_SET_PER);
HRTIM_OUTPUT_RST_SET(hrtim, HRTIM_OUT_TIMB_CH1, HRTIM_OUT_RST_CMP1);
/* Set fixed duty cycle (80%) on buck converter (T4 and T11) */
HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMA, HRTIM_CMP1,
0.8 * ((uint16_t)HRTIM_PER_GET(hrtim, HRTIM_TIMER_TIMA)));
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)
{
FAR struct smps_dev_s *dev = &g_smps_dev;
FAR struct smps_s *smps = (FAR struct smps_s *)dev->priv;
FAR struct smps_priv_s *priv = (struct smps_priv_s *)smps->priv;
FAR struct smps_lower_dev_s *lower = dev->lower;
FAR struct stm32_adc_dev_s *adc =
(FAR struct stm32_adc_dev_s*)lower->adc->ad_priv;
uint32_t pending;
float ref = ADC_REF_VOLTAGE;
float bit = ADC_VAL_MAX;
float err;
float out;
uint8_t mode;
pending = adc->ops->int_get(adc);
if (pending & ADC_INT_JEOC && priv->running == true)
{
/* Get raw ADC values */
priv->v_out_raw = adc->ops->inj_get(adc, V_OUT_ADC_INJ_CHANNEL);
priv->v_in_raw = adc->ops->inj_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 greather 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 regualtor 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 */
adc->ops->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)
{
FAR struct smps_lower_dev_s *lower = &g_smps_lower;
FAR struct smps_dev_s *smps = &g_smps_dev;
FAR struct hrtim_dev_s *hrtim = NULL;
FAR 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;
lower->comp = NULL;
lower->dac = NULL;
lower->opamp = NULL;
/* Attach ADC12 ram vector */
ret = up_ramvec_attach(STM32_IRQ_ADC12, adc12_handler);
if (ret < 0)
{
pwrerr("ERROR: up_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;
}
up_enable_irq(STM32_IRQ_ADC12);
/* 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*/