nuttx/boards/arm/stm32/stm32f334-disco/src/stm32_powerled.c
Xiang Xiao c628529e2d arch/arm: Remove FAR and CODE from board folder(3)
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2022-05-03 16:50:52 +03:00

633 lines
17 KiB
C

/****************************************************************************
* boards/arm/stm32/stm32f334-disco/src/stm32_powerled.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 <sys/types.h>
#include <sys/ioctl.h>
#include <sys/boardctl.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <debug.h>
#include <nuttx/board.h>
#include <nuttx/fs/fs.h>
#include <nuttx/analog/comp.h>
#include <nuttx/analog/dac.h>
#include <nuttx/power/powerled.h>
#include "stm32_comp.h"
#include "stm32_hrtim.h"
#include "stm32_dac.h"
#if defined(CONFIG_EXAMPLES_POWERLED) && defined(CONFIG_DRIVERS_POWERLED)
#if !defined(CONFIG_STM32_HRTIM1) || !defined(CONFIG_STM32_HRTIM)
# error "Powerled example requires HRTIM1 support"
#endif
#if !defined(CONFIG_STM32_DAC1CH1) || !defined(CONFIG_DAC) || \
!defined(CONFIG_STM32_DAC1CH1_DMA)
# error "Powerled example requires DAC1 with DMA support"
#endif
#if !defined(CONFIG_STM32_COMP4) || !defined(CONFIG_COMP)
# error "Powerled example requires COMP4 support"
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define DAC_BUFFER_INIT(dac, buffer) \
dac->ad_ops->ao_ioctl(dac, IO_DMABUFFER_INIT, (unsigned long)buffer)
#define DAC_START(dac) dac->ad_ops->ao_send(dac, NULL);
#if CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE != 5
#error "This example requires DAC1CH1 DMA buffer size == 5"
#endif
/* Peripheral selection */
#define DAC_CURRENT_LIMIT 1
#define COMP_CURRENT_LIMIT 4
/* Maximum onboard LED current is 350mA */
#define LED_ABSOLUTE_CURRENT_LIMIT 250 /* in mA */
#if (CONFIG_EXAMPLES_POWERLED_CURRENT_LIMIT > LED_ABSOLUTE_CURRENT_LIMIT)
# error "Board LED maximum current is 250 mA"
#endif
/* Voltage reference for DAC (in mV) */
#define DAC_REV_VOLTAGE 3300
/* DAC resolution */
#define DAC_RESOLUTION 4096
/* Current sense resistance */
#define LED_CURRENT_SENSE_RESISTOR 1
/* There is 1 Ohm resistor in series with MOSFET which gives 1A/1V ratio */
#define CURRENT_TO_VOLTAGE_RATIO (1/(LED_CURRENT_SENSE_RESISTOR))
/****************************************************************************
* Private Type Definition
****************************************************************************/
/* Private data for powerled */
struct powerled_priv_s
{
bool running;
uint16_t dacbuffer[CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE];
uint16_t current_tab[CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE];
};
/****************************************************************************
* Private Function Protototypes
****************************************************************************/
static int powerled_setup(struct powerled_dev_s *dev);
static int powerled_shutdown(struct powerled_dev_s *dev);
static int powerled_start(struct powerled_dev_s *dev);
static int powerled_stop(struct powerled_dev_s *dev);
static int powerled_params_set(struct powerled_dev_s *dev,
struct powerled_params_s *param);
static int powerled_mode_set(struct powerled_dev_s *dev, uint8_t mode);
static int powerled_limits_set(struct powerled_dev_s *dev,
struct powerled_limits_s *limits);
static int powerled_state_get(struct powerled_dev_s *dev,
struct powerled_state_s *state);
static int powerled_fault_set(struct powerled_dev_s *dev, uint8_t fault);
static int powerled_fault_get(struct powerled_dev_s *dev,
uint8_t *fault);
static int powerled_fault_clean(struct powerled_dev_s *dev,
uint8_t fault);
static int powerled_ioctl(struct powerled_dev_s *dev, int cmd,
unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
struct powerled_priv_s powerled_priv;
struct powerled_s g_powerled;
struct powerled_ops_s g_powerled_ops =
{
.setup = powerled_setup,
.shutdown = powerled_shutdown,
.start = powerled_start,
.stop = powerled_stop,
.params_set = powerled_params_set,
.mode_set = powerled_mode_set,
.limits_set = powerled_limits_set,
.state_get = powerled_state_get,
.fault_set = powerled_fault_set,
.fault_get = powerled_fault_get,
.fault_clean = powerled_fault_clean,
.ioctl = powerled_ioctl
};
struct powerled_dev_s g_powerled_dev =
{
.ops = &g_powerled_ops,
.priv = &g_powerled
};
/* Lower part of the POWERLED driver gather 3 pheriperals:
* - HRTIM - responsible for PWM generation
* - COMP4 - acting as current limit
* - DAC1CH1 - provides threshold voltage to comparator inverting input
*/
struct powerled_lower_dev_s
{
struct hrtim_dev_s *hrtim; /* PWM generation */
struct comp_dev_s *comp; /* current limit control */
struct dac_dev_s *dac; /* current limit threshold */
struct adc_dev_s *adc; /* not used in this demo - only as reference */
struct opamp_dev_s *opamp; /* not used in this demo - only as reference */
};
struct powerled_lower_dev_s g_powerled_lower;
/****************************************************************************
* Private Functions
****************************************************************************/
static int powerled_shutdown(struct powerled_dev_s *dev)
{
struct powerled_s *powerled = (struct powerled_s *)dev->priv;
struct powerled_priv_s *priv =
(struct powerled_priv_s *)powerled->priv;
/* Stop powerled if running */
if (priv->running == true)
{
powerled_stop(dev);
}
/* Reset powerled structure */
memset(powerled, 0, sizeof(struct powerled_s));
return OK;
}
/****************************************************************************
* Name: powerled_setup
*
* Description:
*
*
* Returned Value:
* 0 on success, a negated errno value on failure
*
****************************************************************************/
static int powerled_setup(struct powerled_dev_s *dev)
{
struct powerled_lower_dev_s *lower = dev->lower;
struct powerled_s *powerled = (struct powerled_s *)dev->priv;
struct hrtim_dev_s *hrtim = NULL;
struct comp_dev_s *comp = NULL;
struct dac_dev_s *dac = NULL;
/* Initialize powerled structure */
powerled->opmode = POWERLED_OPMODE_INIT;
powerled->state.state = POWERLED_STATE_INIT;
powerled->priv = &powerled_priv;
/* Check lower half drivers */
hrtim = lower->hrtim;
if (hrtim == NULL)
{
pwrerr("ERROR: failed to get hrtim ");
}
comp = lower->comp;
if (comp == NULL)
{
pwrerr("ERROR: failed to get lower level interface");
}
dac = lower->dac;
if (dac == NULL)
{
pwrerr("ERROR: failed to get lower level interface");
}
/* Do nothing */
return OK;
}
static int powerled_start(struct powerled_dev_s *dev)
{
struct powerled_lower_dev_s *lower = dev->lower;
struct powerled_s *powerled =
(struct powerled_s *)dev->priv;
struct hrtim_dev_s *hrtim = lower->hrtim;
struct dac_dev_s *dac = lower->dac;
struct powerled_priv_s *priv =
(struct powerled_priv_s *)powerled->priv;
uint16_t burst_cmp = 0;
uint16_t burst_per = 0;
uint16_t burst_pre = 0;
int current_av = 0;
int current_max;
int i;
/* Set max current in mA */
current_max = (int)(powerled->limits.current * 1000);
/* Stop HRTIM PWM */
HRTIM_OUTPUTS_ENABLE(hrtim, HRTIM_OUT_TIMC_CH1, false);
HRTIM_BURST_ENABLE(hrtim, false);
/* Configure according to mode */
if (powerled->opmode == POWERLED_OPMODE_CONTINUOUS)
{
/* Average current set to max */
current_av = (uint16_t)(current_max);
/* Dimming through burst mode IDLE state */
burst_pre = HRTIM_BURST_PRESCALER_1;
burst_per = 1000;
burst_cmp = (uint16_t)(((float)burst_per) *
(100.0 - powerled->param.brightness) / 100.0);
}
else if (powerled->opmode == POWERLED_OPMODE_FLASH)
{
/* Average current - brightness */
/* Flashing through burst mode IDLE state */
/* Maximum brightness is achieved when average LED current is equalt to
* LED current limit, and there is no IDLE state
*/
current_av = (uint16_t)(powerled->param.brightness * current_max
/ POWERLED_BRIGHTNESS_MAX);
/* HRTIM clock = 144000000 Hz
* HRTIM burst prescaler = 32768,
* HRTIM burst clock = 4394 Hz
*/
burst_pre = HRTIM_BURST_PRESCALER_32768;
burst_per = (uint16_t)(((float)HRTIM_CLOCK / (1 << burst_pre)) /
powerled->param.frequency);
burst_cmp = (uint16_t)((float)burst_per *
((100 - powerled->param.duty) / 100.0));
}
/* Configure DAC buffer */
for (i = 0; i < CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE; i += 1)
{
/* TODO: add slope compensation */
priv->current_tab[i] = current_av ;
}
/* Convert current sense value thresholds for DAC */
for (i = 0; i < CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE; i += 1)
{
priv->dacbuffer[i] =
priv->current_tab[i] * DAC_RESOLUTION / DAC_REV_VOLTAGE;
}
/* Write DAC buffer */
DAC_BUFFER_INIT(dac, priv->dacbuffer);
/* Configure HRTIM PWM */
/* 1 period is 4us - 100% time */
HRTIM_PER_SET(hrtim, HRTIM_TIMER_TIMC, 18432);
/* 20% time */
HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMC, HRTIM_CMP1, 3686);
/* 40% time */
HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMC, HRTIM_CMP2, 7373);
/* 60% time */
HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMC, HRTIM_CMP3, 11059);
/* 80% time */
HRTIM_CMP_SET(hrtim, HRTIM_TIMER_TIMC, HRTIM_CMP4, 14746);
/* Start DAC */
DAC_START(dac);
/* Configure burst mode */
HRTIM_BURST_CMP_SET(hrtim, burst_cmp);
HRTIM_BURST_PER_SET(hrtim, burst_per);
HRTIM_BURST_PRE_SET(hrtim, burst_pre);
/* Enable burst mode */
HRTIM_BURST_ENABLE(hrtim, true);
/* Start HRTIM PWM */
HRTIM_OUTPUTS_ENABLE(hrtim, HRTIM_OUT_TIMC_CH1, true);
/* Set running flag */
if (priv->running == false)
{
priv->running = true;
}
return OK;
}
static int powerled_stop(struct powerled_dev_s *dev)
{
struct powerled_lower_dev_s *lower = dev->lower;
struct hrtim_dev_s *hrtim = lower->hrtim;
struct powerled_s *powerled =
(struct powerled_s *)dev->priv;
struct powerled_priv_s *priv =
(struct powerled_priv_s *)powerled->priv;
/* Disable output */
HRTIM_OUTPUTS_ENABLE(hrtim, HRTIM_OUT_TIMC_CH1, false);
/* Reset running flag */
priv->running = false;
return OK;
}
static int powerled_params_set(struct powerled_dev_s *dev,
struct powerled_params_s *param)
{
struct powerled_s *powerled = (struct powerled_s *)dev->priv;
int ret = OK;
/* Store params in pirvate struct. */
powerled->param.brightness = param->brightness;
powerled->param.frequency = param->frequency;
powerled->param.duty = param->duty;
return ret;
}
static int powerled_mode_set(struct powerled_dev_s *dev, uint8_t mode)
{
struct powerled_s *powerled = (struct powerled_s *)dev->priv;
int ret = OK;
switch (mode)
{
case POWERLED_OPMODE_CONTINUOUS:
{
powerled->opmode = mode;
break;
}
case POWERLED_OPMODE_FLASH:
{
powerled->opmode = mode;
break;
}
default:
{
pwrerr("ERROR: unsupported POWERLED mode %d!\n", mode);
ret = ERROR;
goto errout;
}
}
errout:
return ret;
}
static int powerled_limits_set(struct powerled_dev_s *dev,
struct powerled_limits_s *limits)
{
struct powerled_s *powerled = (struct powerled_s *)dev->priv;
int ret = OK;
/* Some assertions */
if (limits->current <= 0)
{
pwrerr("ERROR: Output current limit must be set!\n");
ret = ERROR;
goto errout;
}
if (limits->current * 1000 > LED_ABSOLUTE_CURRENT_LIMIT)
{
limits->current = (float)LED_ABSOLUTE_CURRENT_LIMIT / 1000.0;
pwrwarn("WARNING: "
"LED current limiit > LED absolute current limit."
" Set current limit to %lf.\n",
limits->current);
}
/* We need only output current */
powerled->limits.current = limits->current;
/* Lock limits */
powerled->limits.lock = true;
errout:
return ret;
}
static int powerled_state_get(struct powerled_dev_s *dev,
struct powerled_state_s *state)
{
struct powerled_s *powerled = (struct powerled_s *)dev->priv;
memcpy(state, &powerled->state, sizeof(struct powerled_state_s));
return OK;
}
static int powerled_fault_set(struct powerled_dev_s *dev, uint8_t fault)
{
/* Do nothing */
return -1;
}
static int powerled_fault_get(struct powerled_dev_s *dev,
uint8_t *fault)
{
/* Do nothing */
return -1;
}
static int powerled_fault_clean(struct powerled_dev_s *dev,
uint8_t fault)
{
/* Do nothing */
return -1;
}
static int powerled_ioctl(struct powerled_dev_s *dev, int cmd,
unsigned long arg)
{
/* Do nothing */
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: stm32_powerled_setup
*
* Description:
* Initialize POWERLED driver.
*
* This function should be call by board_app_initialize().
*
* Returned Value:
* 0 on success, a negated errno value on failure
*
****************************************************************************/
int stm32_powerled_setup(void)
{
struct powerled_lower_dev_s *lower = &g_powerled_lower;
struct powerled_dev_s *powerled = &g_powerled_dev;
struct hrtim_dev_s *hrtim = NULL;
struct comp_dev_s *comp = NULL;
struct dac_dev_s *dac = NULL;
static bool initialized = false;
int ret = OK;
/* 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;
}
/* Get the DAC interface */
dac = stm32_dacinitialize(DAC_CURRENT_LIMIT);
if (dac == NULL)
{
pwrerr("ERROR: Failed to get DAC %d interface\n",
DAC_CURRENT_LIMIT);
return -ENODEV;
}
/* Get the COMP interface */
comp = stm32_compinitialize(COMP_CURRENT_LIMIT);
if (comp == NULL)
{
pwrerr("ERROR: Failed to get COMP %d interface\n",
COMP_CURRENT_LIMIT);
return -ENODEV;
}
/* Initialize POWERLED lower driver interfaces */
lower->hrtim = hrtim;
lower->comp = comp;
lower->dac = dac;
lower->adc = NULL;
lower->opamp = NULL;
/* We do not need register character drivers for POWERLED lower
* peripherals.
* All control should be done via POWERLED character driver.
*/
ret = powerled_register("/dev/powerled0", powerled, (void *)lower);
if (ret < 0)
{
pwrerr("ERROR: powerled_register failed: %d\n", ret);
return ret;
}
initialized = true;
}
return ret;
}
#endif /* CONFIG_EXAMPLE_POWERLED && CONFIG_DRIVERS_POWERLED*/