Add SPWM example to test STM32L4 PWM driver low level operations

Author: Alan Carvalho de Assis <acassis@gmail.com>

    Run nxstyle again .c file and fix error message

Author: Daniel P. Carvalho <danieloak@gmail.com>

    Add SPWM example to test STM32L4 PWM driver low level operations.

    Fix BUGs.
This commit is contained in:
Daniel P. Carvalho 2020-01-14 11:53:26 -03:00 committed by Alan Carvalho de Assis
parent 21ea255ea4
commit bf5d48acac
9 changed files with 6729 additions and 1425 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/************************************************************************************
/****************************************************************************
* arch/arm/src/stm32l4/stm32l4_pwm.h
*
* Copyright (C) 2011, 2015 Gregory Nutt. All rights reserved.
@ -33,36 +33,38 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************************/
****************************************************************************/
#ifndef __ARCH_ARM_SRC_STM32L4_STM32L4_PWM_H
#define __ARCH_ARM_SRC_STM32L4_STM32L4_PWM_H
/* The STM32L4 does not have dedicated PWM hardware. Rather, pulsed output control
* is a capability of the STM32L4 timers. The logic in this file implements the
* lower half of the standard, NuttX PWM interface using the STM32L4 timers. That
* interface is described in include/nuttx/timers/pwm.h.
/* The STM32L4 does not have dedicated PWM hardware. Rather, pulsed output
* control is a capability of the STM32L4 timers. The logic in this file
* implements the lower half of the standard, NuttX PWM interface using the
* STM32L4 timers. That interface is described in include/nuttx/timers/pwm.h.
*/
/************************************************************************************
/****************************************************************************
* Included Files
************************************************************************************/
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/timers/pwm.h>
#include "chip.h"
/************************************************************************************
/****************************************************************************
* Pre-processor Definitions
************************************************************************************/
****************************************************************************/
/* Configuration ********************************************************************/
/* Configuration ************************************************************/
/* Timer devices may be used for different purposes. One special purpose is
* to generate modulated outputs for such things as motor control. If
* CONFIG_STM32L4_TIMn is defined then the CONFIG_STM32L4_TIMn_PWM must also be
* defined to indicate that timer "n" is intended to be used for pulsed output
* signal generation.
* CONFIG_STM32L4_TIMn is defined then the CONFIG_STM32L4_TIMn_PWM must also
* be defined to indicate that timer "n" is intended to be used for pulsed
* output signal generation.
*/
#ifndef CONFIG_STM32L4_TIM1
@ -99,7 +101,9 @@
# undef CONFIG_STM32L4_LPTIM2_PWM
#endif
/* The basic timers (timer 6 and 7) are not capable of generating output pulses */
/* The basic timers (timer 6 and 7) are not capable of generating output
* pulses.
*/
#undef CONFIG_STM32L4_TIM6_PWM
#undef CONFIG_STM32L4_TIM7_PWM
@ -117,7 +121,9 @@
#include "hardware/stm32l4_tim.h"
#include "hardware/stm32l4_lptim.h"
#ifdef CONFIG_PWM_MULTICHAN
/* PWM driver channels configuration */
#ifdef CONFIG_STM32L4_PWM_MULTICHAN
#ifdef CONFIG_STM32L4_TIM1_CHANNEL1
# ifdef CONFIG_STM32L4_TIM1_CH1OUT
@ -502,33 +508,19 @@
#endif
#define PWM_LPTIM2_NCHANNELS PWM_LPTIM2_CHANNEL1
#define PWM_MAX(a, b) ((a) > (b) ? (a) : (b))
#else /* !CONFIG_STM32L4_PWM_MULTICHAN */
#define PWM_NCHANNELS PWM_MAX(PWM_TIM1_NCHANNELS, \
PWM_MAX(PWM_TIM2_NCHANNELS, \
PWM_MAX(PWM_TIM3_NCHANNELS, \
PWM_MAX(PWM_TIM4_NCHANNELS, \
PWM_MAX(PWM_TIM5_NCHANNELS, \
PWM_MAX(PWM_TIM8_NCHANNELS, \
PWM_MAX(PWM_TIM15_NCHANNELS, \
PWM_MAX(PWM_TIM16_NCHANNELS, \
PWM_MAX(PWM_TIM17_NCHANNELS, \
PWM_MAX(PWM_LPTIM1_NCHANNELS, \
PWM_LPTIM2_NCHANNELS))))))))))
#else
/* For each timer that is enabled for PWM usage, we need the following additional
* configuration settings:
/* For each timer that is enabled for PWM usage, we need the following
* additional configuration settings:
*
* CONFIG_STM32L4_TIMx_CHANNEL - Specifies the timer output channel {1,..,4}
* PWM_TIMx_CHn - One of the values defined in chip/stm32*_pinmap.h. In the case
* where there are multiple pin selections, the correct setting must be provided
* in the arch/board/board.h file.
* PWM_TIMx_CHn - One of the values defined in chip/stm32*_pinmap.h. In the
* case where there are multiple pin selections, the correct setting must be
* provided in the arch/board/board.h file.
*
* NOTE: The STM32L4 timers are each capable of generating different signals on
* each of the four channels with different duty cycles. That capability is
* not supported by this driver: Only one output channel per timer.
* NOTE: The STM32L4 timers are each capable of generating different signals
* on each of the four channels with different duty cycles. That capability
* is not supported by this driver: Only one output channel per timer.
*/
#ifdef CONFIG_STM32L4_TIM1_PWM
@ -537,25 +529,46 @@
# elif CONFIG_STM32L4_TIM1_CHANNEL == 1
# define CONFIG_STM32L4_TIM1_CHANNEL1 1
# define CONFIG_STM32L4_TIM1_CH1MODE CONFIG_STM32L4_TIM1_CHMODE
# define PWM_TIM1_CH1CFG GPIO_TIM1_CH1OUT
# define PWM_TIM1_CH1NCFG 0
# ifdef CONFIG_STM32L4_TIM1_CH1OUT
# define PWM_TIM1_CH1CFG GPIO_TIM1_CH1OUT
# endif
# ifdef CONFIG_STM32L4_TIM1_CH1NOUT
# define PWM_TIM1_CH1NCFG GPIO_TIM1_CH1NOUT
# else
# define PWM_TIM1_CH1NCFG 0
# endif
# elif CONFIG_STM32L4_TIM1_CHANNEL == 2
# define CONFIG_STM32L4_TIM1_CHANNEL2 1
# define CONFIG_STM32L4_TIM1_CH2MODE CONFIG_STM32L4_TIM1_CHMODE
# define PWM_TIM1_CH2CFG GPIO_TIM1_CH2OUT
# define PWM_TIM1_CH2NCFG 0
# ifdef CONFIG_STM32L4_TIM1_CH2OUT
# define PWM_TIM1_CH2CFG GPIO_TIM1_CH2OUT
# endif
# ifdef CONFIG_STM32L4_TIM1_CH2NOUT
# define PWM_TIM1_CH2NCFG GPIO_TIM1_CH2NOUT
# else
# define PWM_TIM1_CH2NCFG 0
# endif
# elif CONFIG_STM32L4_TIM1_CHANNEL == 3
# define CONFIG_STM32L4_TIM1_CHANNEL3 1
# define CONFIG_STM32L4_TIM1_CH3MODE CONFIG_STM32L4_TIM1_CHMODE
# define PWM_TIM1_CH3CFG GPIO_TIM1_CH3OUT
# define PWM_TIM1_CH3NCFG 0
# ifdef CONFIG_STM32L4_TIM1_CH3OUT
# define PWM_TIM1_CH3CFG GPIO_TIM1_CH3OUT
# endif
# ifdef CONFIG_STM32L4_TIM1_CH3NOUT
# define PWM_TIM1_CH3NCFG GPIO_TIM1_CH3NOUT
# else
# define PWM_TIM1_CH3NCFG 0
# endif
# elif CONFIG_STM32L4_TIM1_CHANNEL == 4
# define CONFIG_STM32L4_TIM1_CHANNEL4 1
# define CONFIG_STM32L4_TIM1_CH4MODE CONFIG_STM32L4_TIM1_CHMODE
# define PWM_TIM1_CH4CFG GPIO_TIM1_CH4OUT
# ifdef CONFIG_STM32L4_TIM1_CH4OUT
# define PWM_TIM1_CH4CFG GPIO_TIM1_CH4OUT
# endif
# else
# error "Unsupported value of CONFIG_STM32L4_TIM1_CHANNEL"
# endif
# define PWM_TIM1_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_TIM2_PWM
@ -580,6 +593,7 @@
# else
# error "Unsupported value of CONFIG_STM32L4_TIM2_CHANNEL"
# endif
# define PWM_TIM2_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_TIM3_PWM
@ -604,6 +618,7 @@
# else
# error "Unsupported value of CONFIG_STM32L4_TIM3_CHANNEL"
# endif
# define PWM_TIM3_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_TIM4_PWM
@ -628,6 +643,7 @@
# else
# error "Unsupported value of CONFIG_STM32L4_TIM4_CHANNEL"
# endif
# define PWM_TIM4_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_TIM5_PWM
@ -652,6 +668,7 @@
# else
# error "Unsupported value of CONFIG_STM32L4_TIM5_CHANNEL"
# endif
# define PWM_TIM5_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_TIM8_PWM
@ -679,6 +696,7 @@
# else
# error "Unsupported value of CONFIG_STM32L4_TIM8_CHANNEL"
# endif
# define PWM_TIM8_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_TIM15_PWM
@ -696,6 +714,7 @@
# else
# error "Unsupported value of CONFIG_STM32L4_TIM15_CHANNEL"
# endif
# define PWM_TIM15_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_TIM16_PWM
@ -709,6 +728,7 @@
# else
# error "Unsupported value of CONFIG_STM32L4_TIM16_CHANNEL"
# endif
# define PWM_TIM16_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_TIM17_PWM
@ -722,6 +742,7 @@
# else
# error "Unsupported value of CONFIG_STM32L4_TIM17_CHANNEL"
# endif
# define PWM_TIM17_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_LPTIM1_PWM
@ -734,6 +755,7 @@
# else
# error "Unsupported value of CONFIG_STM32L4_LPTIM1_CHANNEL"
# endif
# define PWM_LPTIM1_NCHANNELS 1
#endif
#ifdef CONFIG_STM32L4_LPTIM2_PWM
@ -746,19 +768,258 @@
# else
# error "Unsupported value of CONFIG_STM32L4_LPTIM2_CHANNEL"
# endif
# define PWM_LPTIM2_NCHANNELS 1
#endif
#define PWM_NCHANNELS 1
#endif
/************************************************************************************
/* Complementary outputs support */
#if defined(CONFIG_STM32L4_TIM1_CH1NOUT) || defined(CONFIG_STM32L4_TIM1_CH2NOUT) || \
defined(CONFIG_STM32L4_TIM1_CH3NOUT)
# define HAVE_TIM1_COMPLEMENTARY
#endif
#if defined(CONFIG_STM32L4_TIM8_CH1NOUT) || defined(CONFIG_STM32L4_TIM8_CH2NOUT) || \
defined(CONFIG_STM32L4_TIM8_CH3NOUT)
# define HAVE_TIM8_COMPLEMENTARY
#endif
#if defined(CONFIG_STM32L4_TIM15_CH1NOUT)
# define HAVE_TIM15_COMPLEMENTARY
#endif
#if defined(CONFIG_STM32L4_TIM16_CH1NOUT)
# define HAVE_TIM16_COMPLEMENTARY
#endif
#if defined(CONFIG_STM32L4_TIM17_CH1NOUT)
# define HAVE_TIM17_COMPLEMENTARY
#endif
#if defined(CONFIG_STM32L4_LPTIM1_CH1NOUT)
# define HAVE_LPTIM1_COMPLEMENTARY
#endif
#if defined(CONFIG_STM32L4_LPTIM2_CH1NOUT)
# define HAVE_LPTIM2_COMPLEMENTARY
#endif
#if defined(HAVE_TIM1_COMPLEMENTARY) || defined(HAVE_TIM8_COMPLEMENTARY) || \
defined(HAVE_TIM15_COMPLEMENTARY) || defined(HAVE_TIM16_COMPLEMENTARY) || \
defined(HAVE_TIM17_COMPLEMENTARY) || defined(HAVE_LPTIM1_COMPLEMENTARY) || \
defined(HAVE_LPTIM2_COMPLEMENTARY)
# define HAVE_PWM_COMPLEMENTARY
#endif
/* Low-level ops helpers ************************************************************/
#ifdef CONFIG_STM32L4_PWM_LL_OPS
/* NOTE: low-level ops accept pwm_lowerhalf_s as first argument, but llops
* access can be found in stm32l4_pwm_dev_s
*/
#define PWM_SETUP(dev) \
(dev)->ops->setup((FAR struct pwm_lowerhalf_s *)dev)
#define PWM_SHUTDOWN(dev) \
(dev)->ops->shutdown((FAR struct pwm_lowerhalf_s *)dev)
#define PWM_CCR_UPDATE(dev, index, ccr) \
(dev)->llops->ccr_update((FAR struct pwm_lowerhalf_s *)dev, index, ccr)
#define PWM_MODE_UPDATE(dev, index, mode) \
(dev)->llops->mode_update((FAR struct pwm_lowerhalf_s *)dev, index, mode)
#define PWM_CCR_GET(dev, index) \
(dev)->llops->ccr_get((FAR struct pwm_lowerhalf_s *)dev, index)
#define PWM_ARR_UPDATE(dev, arr) \
(dev)->llops->arr_update((FAR struct pwm_lowerhalf_s *)dev, arr)
#define PWM_ARR_GET(dev) \
(dev)->llops->arr_get((FAR struct pwm_lowerhalf_s *)dev)
#define PWM_OUTPUTS_ENABLE(dev, out, state) \
(dev)->llops->outputs_enable((FAR struct pwm_lowerhalf_s *)dev, out, state)
#define PWM_SOFT_UPDATE(dev) \
(dev)->llops->soft_update((FAR struct pwm_lowerhalf_s *)dev)
#define PWM_CONFIGURE(dev) \
(dev)->llops->configure((FAR struct pwm_lowerhalf_s *)dev)
#define PWM_SOFT_BREAK(dev, state) \
(dev)->llops->soft_break((FAR struct pwm_lowerhalf_s *)dev, state)
#define PWM_FREQ_UPDATE(dev, freq) \
(dev)->llops->freq_update((FAR struct pwm_lowerhalf_s *)dev, freq)
#define PWM_TIM_ENABLE(dev, state) \
(dev)->llops->tim_enable((FAR struct pwm_lowerhalf_s *)dev, state)
#ifdef CONFIG_DEBUG_STM32L4_PWM_INFO
# define PWM_DUMP_REGS(dev, msg) \
(dev)->llops->dump_regs((FAR struct pwm_lowerhalf_s *)dev, msg)
#else
# define PWM_DUMP_REGS(dev, msg)
#endif
#define PWM_DT_UPDATE(dev, dt) \
(dev)->llops->dt_update((FAR struct pwm_lowerhalf_s *)dev, dt)
#endif
/****************************************************************************
* Public Types
************************************************************************************/
****************************************************************************/
/************************************************************************************
/* Timer mode */
enum stm32l4_timmode_e
{
STM32L4_TIMMODE_COUNTUP = 0,
STM32L4_TIMMODE_COUNTDOWN = 1,
STM32L4_TIMMODE_CENTER1 = 2,
STM32L4_TIMMODE_CENTER2 = 3,
STM32L4_TIMMODE_CENTER3 = 4,
};
/* Timer output polarity */
enum stm32l4_pwm_pol_e
{
STM32L4_POL_POS = 0,
STM32L4_POL_NEG = 1,
};
/* Timer output IDLE state */
enum stm32l4_pwm_idle_e
{
STM32L4_IDLE_INACTIVE = 0,
STM32L4_IDLE_ACTIVE = 1
};
/* PWM channel mode */
enum stm32l4_chanmode_e
{
STM32L4_CHANMODE_FRZN = 0, /* CCRx matches has no effects on outputs */
STM32L4_CHANMODE_CHACT = 1, /* OCxREF active on match */
STM32L4_CHANMODE_CHINACT = 2, /* OCxREF inactive on match */
STM32L4_CHANMODE_OCREFTOG = 3, /* OCxREF toggles when TIMy_CNT=TIMyCCRx */
STM32L4_CHANMODE_OCREFLO = 4, /* OCxREF is forced low */
STM32L4_CHANMODE_OCREFHI = 5, /* OCxREF is forced high */
STM32L4_CHANMODE_PWM1 = 6, /* PWM mode 1 */
STM32L4_CHANMODE_PWM2 = 7, /* PWM mode 2 */
STM32L4_CHANMODE_COMBINED1 = 8, /* Combined PWM mode 1 */
STM32L4_CHANMODE_COMBINED2 = 9, /* Combined PWM mode 2 */
STM32L4_CHANMODE_ASYMMETRIC1 = 10, /* Asymmetric PWM mode 1 */
STM32L4_CHANMODE_ASYMMETRIC2 = 11, /* Asymmetric PWM mode 2 */
};
/* PWM timer channel */
enum stm32l4_pwm_chan_e
{
STM32L4_PWM_CHAN1 = 1,
STM32L4_PWM_CHAN2 = 2,
STM32L4_PWM_CHAN3 = 3,
STM32L4_PWM_CHAN4 = 4,
STM32L4_PWM_CHAN5 = 5,
STM32L4_PWM_CHAN6 = 6,
};
/* PWM timer channel output */
enum stm32l4_pwm_output_e
{
STM32L4_PWM_OUT1 = (1 << 0),
STM32L4_PWM_OUT1N = (1 << 1),
STM32L4_PWM_OUT2 = (1 << 2),
STM32L4_PWM_OUT2N = (1 << 3),
STM32L4_PWM_OUT3 = (1 << 4),
STM32L4_PWM_OUT3N = (1 << 5),
STM32L4_PWM_OUT4 = (1 << 6),
/* 1 << 7 reserved - no complementary output for CH4 */
/* Only available inside micro */
STM32L4_PWM_OUT5 = (1 << 8),
/* 1 << 9 reserved - no complementary output for CH5 */
STM32L4_PWM_OUT6 = (1 << 10),
/* 1 << 11 reserved - no complementary output for CH6 */
};
#ifdef CONFIG_STM32L4_PWM_LL_OPS
/* This structure provides the publicly visable representation of the
* "lower-half" PWM driver structure.
*/
struct stm32l4_pwm_dev_s
{
/* The first field of this state structure must be a pointer to the PWM
* callback structure to be consistent with upper-half PWM driver.
*/
FAR const struct pwm_ops_s *ops;
/* Publicly visible portion of the "lower-half" PWM driver structure */
FAR const struct stm32l4_pwm_ops_s *llops;
/* Require cast-compatibility with private "lower-half" PWM strucutre */
};
/* Low-level operations for PWM */
struct pwm_lowerhalf_s;
struct stm32l4_pwm_ops_s
{
/* Update CCR register */
int (*ccr_update)(FAR struct pwm_lowerhalf_s *dev,
uint8_t index, uint32_t ccr);
/* Update PWM mode */
int (*mode_update)(FAR struct pwm_lowerhalf_s *dev,
uint8_t index, uint32_t mode);
/* Get CCR register */
uint32_t (*ccr_get)(FAR struct pwm_lowerhalf_s *dev, uint8_t index);
/* Update ARR register */
int (*arr_update)(FAR struct pwm_lowerhalf_s *dev, uint32_t arr);
/* Get ARR register */
uint32_t (*arr_get)(FAR struct pwm_lowerhalf_s *dev);
/* Enable outputs */
int (*outputs_enable)(FAR struct pwm_lowerhalf_s *dev,
uint16_t outputs, bool state);
/* Software update */
int (*soft_update)(FAR struct pwm_lowerhalf_s *dev);
/* PWM configure */
int (*configure)(FAR struct pwm_lowerhalf_s *dev);
/* Software break */
int (*soft_break)(FAR struct pwm_lowerhalf_s *dev, bool state);
/* Update frequency */
int (*freq_update)(FAR struct pwm_lowerhalf_s *dev, uint32_t frequency);
/* Enable timer counter */
int (*tim_enable)(FAR struct pwm_lowerhalf_s *dev, bool state);
#ifdef CONFIG_DEBUG_PWM_INFO
/* Dump timer registers */
void (*dump_regs)(FAR struct pwm_lowerhalf_s *dev, FAR const char *msg);
#endif
#ifdef HAVE_PWM_COMPLEMENTARY
/* Deadtime update */
int (*dt_update)(FAR struct pwm_lowerhalf_s *dev, uint8_t dt);
#endif
};
#endif /* CONFIG_STM32L4_PWM_LL_OPS */
/****************************************************************************
* Public Data
************************************************************************************/
****************************************************************************/
#ifndef __ASSEMBLY__
@ -771,11 +1032,11 @@ extern "C"
#define EXTERN extern
#endif
/************************************************************************************
/****************************************************************************
* Public Functions
************************************************************************************/
****************************************************************************/
/************************************************************************************
/****************************************************************************
* Name: stm32l4_pwminitialize
*
* Description:
@ -790,7 +1051,7 @@ extern "C"
* On success, a pointer to the STM32 lower half PWM driver is returned.
* NULL is returned on any failure.
*
************************************************************************************/
****************************************************************************/
FAR struct pwm_lowerhalf_s *stm32l4_pwminitialize(int timer);

View File

@ -107,4 +107,38 @@ endchoice
endmenu
menuconfig NUCLEOL432KC_SPWM
bool "Sinusoidal PWM generator example"
default n
if NUCLEOL432KC_SPWM
choice
prompt "Sinusoidal PWM source"
default NUCLEOL432KC_SPWM_USE_TIM1
config NUCLEOL432KC_SPWM_USE_TIM1
bool "Use TIM1 as PWM source"
endchoice
config NUCLEOL432KC_SPWM_PWM_FREQ
int "PWM frequency in Hz"
default 100000
config NUCLEOL432KC_SPWM_SAMPLES
int "Sine samples"
default 100
config NUCLEOL432KC_SPWM_FREQ
int "Waveform frequency in Hz"
default 60
config NUCLEOL432KC_SPWM_PHASE_NUM
int "Number of phases"
default 1
range 1 4 if NUCLEOL432KC_SPWM_USE_TIM1
endif
endif # ARCH_BOARD_NUCLEO_L432KC

View File

@ -535,3 +535,14 @@ Configurations
Pin 33 PA10 USART1_TX some RS-232 converters
Pin 20 GND
Pin 8 U5V
spwm
----
Configures the sinusoidal PWM (SPWM) example which presents a simple use case
of the STM32L4 PWM lower-half driver without generic upper-half PWM logic.
It uses TIM1 to generate PWM and TIM6 to change waveform samples
At the moment, the waveform parameters are hardcoded, but it should be easy to
modify this example and make it more functional.

File diff suppressed because it is too large Load Diff

View File

@ -68,7 +68,8 @@
* synced MSI.
*
* System Clock source : PLL (HSI)
* SYSCLK(Hz) : 80000000 Determined by PLL configuration
* SYSCLK(Hz) : 80000000 Determined by PLL
* configuration
* HCLK(Hz) : 80000000 (STM32L4_RCC_CFGR_HPRE)
* (Max 80 MHz)
* AHB Prescaler : 1 (STM32L4_RCC_CFGR_HPRE)
@ -233,9 +234,9 @@
/* 'main' PLL config; we use this to generate our system clock via the R
* output. We set it up as 16 MHz / 1 * 10 / 2 = 80 MHz
*
* XXX NOTE: currently the main PLL is implicitly turned on and is implicitly
* the system clock; this should be configurable since not all applications may
* want things done this way.
* XXX NOTE: currently the main PLL is implicitly turned on and is
* implicitly the system clock; this should be configurable since not all
* applications may want things done this way.
*/
#define STM32L4_PLLCFG_PLLN RCC_PLLCFG_PLLN(10)
@ -495,6 +496,8 @@
#define STM32L4_APB1_TIM2_CLKIN (STM32L4_PCLK1_FREQUENCY)
#define STM32L4_APB1_TIM6_CLKIN (STM32L4_PCLK1_FREQUENCY)
#define STM32L4_APB1_TIM7_CLKIN (STM32L4_PCLK1_FREQUENCY)
#define STM32L4_APB1_LPTIM1_CLKIN (STM32L4_PCLK1_FREQUENCY)
#define STM32L4_APB1_LPTIM2_CLKIN (STM32L4_PCLK1_FREQUENCY)
/* Configure the APB2 prescaler */
@ -507,8 +510,8 @@
#endif
/* The timer clock frequencies are automatically defined by hardware.
* If the APB prescaler equals 1, the timer clock frequencies are set to the same
/* The timer clock frequencies are automatically defined by hardware. If the
* APB prescaler equals 1, the timer clock frequencies are set to the same
* frequency as that of the APB domain. Otherwise they are set to twice.
* Note: TIM1,15,16 are on APB2, others on APB1
*/

View File

@ -92,4 +92,8 @@ ifeq ($(CONFIG_LIB_BOARDCTL),y)
CSRCS += stm32_appinit.c
endif
ifeq ($(CONFIG_NUCLEOL432KC_SPWM),y)
CSRCS += stm32_spwm.c
endif
include $(TOPDIR)/boards/Board.mk

View File

@ -0,0 +1,664 @@
/****************************************************************************
* boards/arm/stm32/nucleo-l432kc/src/stm32_spwm.c
*
* Copyright (C) 2018, 2019 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 <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <math.h>
#include <nuttx/arch.h>
#include <nuttx/signal.h>
#include <arch/irq.h>
#include <arch/chip/chip.h>
#include <arch/board/board.h>
#include "up_internal.h"
#include "ram_vectors.h"
#include "stm32l4_pwm.h"
#include "stm32l4_tim.h"
#ifdef CONFIG_NUCLEOL432KC_SPWM
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Assertions ***************************************************************/
#ifndef CONFIG_ARCH_CHIP_STM32L432KC
# warning "This only have been verified with CONFIG_ARCH_CHIP_STM32L432KC"
#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
/* Check the configuration for TIM1 */
#ifdef CONFIG_NUCLEOL432KC_SPWM_USE_TIM1
/* Phase 1 is TIM1 CH1 */
# if CONFIG_NUCLEOL432KC_SPWM_PHASE_NUM > 0
# ifndef CONFIG_STM32L4_TIM1_CH1OUT
# error
# endif
# ifndef CONFIG_STM32L4_TIM6
# error
# endif
# endif
/* Phase 2 is TIM1 CH2 */
# if CONFIG_NUCLEOL432KC_SPWM_PHASE_NUM > 1
# ifndef CONFIG_STM32L4_TIM1_CH2OUT
# error
# endif
# endif
/* Phase 3 is TIM1 CH3 */
# if CONFIG_NUCLEOL432KC_SPWM_PHASE_NUM > 2
# ifndef CONFIG_STM32L4_TIM1_CH3OUT
# error
# endif
# endif
/* Phase 4 is TIM1 CH4 */
# if CONFIG_NUCLEOL432KC_SPWM_PHASE_NUM > 3
# ifndef CONFIG_STM32L4_TIM1_CH4OUT
# error
# endif
# endif
# if CONFIG_NUCLEOL432KC_SPWM_PHASE_NUM != PWM_TIM1_NCHANNELS
# error
# endif
#endif /* CONFIG_NUCLEOL432KC_SPWM_USE_TIM1 */
/* Configuration ************************************************************/
#ifdef CONFIG_NUCLEOL432KC_SPWM_USE_TIM1
# define PWM_TIMERS_IN_USE 1
#endif
#define SPWM_PHASE_SHIFT ((360.0f/CONFIG_NUCLEOL432KC_SPWM_PHASE_NUM))
#define SAMPLES_NUM CONFIG_NUCLEOL432KC_SPWM_SAMPLES
#define PHASES_NUM CONFIG_NUCLEOL432KC_SPWM_PHASE_NUM
/****************************************************************************
* Private Types
****************************************************************************/
/* SPWM private data */
struct spwm_s
{
FAR struct stm32l4_pwm_dev_s *pwm;
#ifdef CONFIG_NUCLEOL432KC_SPWM_USE_TIM1
FAR struct stm32l4_tim_dev_s *tim;
#endif
float waveform[SAMPLES_NUM]; /* Waveform samples */
float phase_step; /* Waveform phase step */
float waveform_freq; /* Waveform frequency */
uint16_t cmp[SAMPLES_NUM]; /* PWM TIM compare table */
uint16_t per; /* PWM TIM period */
uint16_t samples; /* Modulation waveform samples num */
uint16_t phase_shift[PHASES_NUM]; /* Phase offset */
volatile uint16_t sample_now[PHASES_NUM]; /* Current sample number for
* phase */
uint8_t phases; /* Number of PWM phases */
};
/****************************************************************************
* Private Data
****************************************************************************/
static struct spwm_s g_spwm =
{
.waveform_freq = ((float)CONFIG_NUCLEOL432KC_SPWM_FREQ),
.phases = PHASES_NUM,
.samples = SAMPLES_NUM,
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static float waveform_func(float x);
static int waveform_init(FAR struct spwm_s *spwm, float (*f)(float));
static int spwm_start(FAR struct spwm_s *spwm);
static int spwm_start(FAR struct spwm_s *spwm);
static int spwm_stop(FAR struct spwm_s *spwm);
#ifdef CONFIG_NUCLEOL432KC_SPWM_USE_TIM1
static int spwm_tim1_setup(FAR struct spwm_s *spwm);
static int spwm_tim6_setup(FAR struct spwm_s *spwm);
static int spwm_tim1_start(FAR struct spwm_s *spwm);
static int spwm_tim6_start(FAR struct spwm_s *spwm);
static int spwm_tim1_stop(FAR struct spwm_s *spwm);
static int spwm_tim6_stop(FAR struct spwm_s *spwm);
#endif /* CONFIG_NUCLEOL432KC_SPWM_USE_TIM1 */
static int spwm_setup(FAR struct spwm_s *spwm);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: waveform_func
*
* Description:
* Modulation function. This function must return values from <0.0, 1.0>!
*
****************************************************************************/
static float waveform_func(float x)
{
DEBUGASSERT(x >= 0 && x <= 2 * M_PI);
/* Sine modulation */
return (sinf(x) + 1.0f) / 2.0f;
}
/****************************************************************************
* Name: waveform_init
*
* Description:
* Initialize modulation waveform
*
****************************************************************************/
static int waveform_init(FAR struct spwm_s *spwm, float (*f)(float))
{
uint16_t i = 0;
int ret = 0;
printf("Initialize waveform\n");
/* Get phase step to achieve one sine waveform period */
spwm->phase_step = (float)(2 * M_PI / spwm->samples);
/* Initialize sine and PWM compare tables */
for (i = 0; i < spwm->samples; i += 1)
{
/* We need sine in range from 0 to 1.0 */
spwm->waveform[i] = f(spwm->phase_step * i);
DEBUGASSERT(spwm->waveform[i] >= 0.0 && spwm->waveform[i] <= 2 * M_PI);
spwm->cmp[i] = (uint16_t)(spwm->waveform[i] * spwm->per);
}
/* Configure phase shift TODO: this should be configurable */
for (i = 0; i < spwm->phases; i += 1)
{
spwm->phase_shift[i] =
(spwm->samples / CONFIG_NUCLEOL432KC_SPWM_PHASE_NUM) * i;
}
/* Initialize offstes */
for (i = 0; i < spwm->phases; i += 1)
{
spwm->sample_now[i] = spwm->phase_shift[i];
}
printf("\tsamples = %d\n", spwm->samples);
printf("\tper = %d\n", spwm->per);
printf("\tphase = %d\n", spwm->phases);
for (i = 0; i < spwm->phases; i += 1)
{
printf("\tsnow%d = %d\n", i, spwm->sample_now[i]);
}
return ret;
}
/****************************************************************************
* Name: spwm_start
*
* Description:
* Start SPWM
*
****************************************************************************/
static int spwm_start(FAR struct spwm_s *spwm)
{
/* Start TIM1 */
spwm_tim1_start(spwm);
/* Start TIM6 */
spwm_tim6_start(spwm);
return OK;
}
/****************************************************************************
* Name: spwm_stop
*
* Description:
* Stop SPWM
*
****************************************************************************/
static int spwm_stop(FAR struct spwm_s *spwm)
{
/* Stop TIM1 */
spwm_tim1_stop(spwm);
/* Stop TIM6 */
spwm_tim6_stop(spwm);
return OK;
}
#ifdef CONFIG_NUCLEOL432KC_SPWM_USE_TIM1
/****************************************************************************
* Name: tim6_handler
****************************************************************************/
static void tim6_handler(void)
{
FAR struct spwm_s *spwm = &g_spwm;
FAR struct stm32l4_pwm_dev_s *pwm = spwm->pwm;
FAR struct stm32l4_tim_dev_s *tim = spwm->tim;
uint8_t i = 0;
for (i = 0; i < spwm->phases; i += 1)
{
/* Set new CMP for timers */
PWM_CCR_UPDATE(pwm, i + 1, spwm->cmp[spwm->sample_now[i]]);
/* Increase sample pointer */
spwm->sample_now[i] += 1;
if (spwm->sample_now[i] > spwm->samples)
{
spwm->sample_now[i] = 0;
}
}
/* TODO: Software update */
STM32L4_TIM_ACKINT(tim, ATIM_SR_UIF);
}
/****************************************************************************
* Name: spwm_tim6_setup
****************************************************************************/
static int spwm_tim6_setup(FAR struct spwm_s *spwm)
{
FAR struct stm32l4_tim_dev_s *tim = NULL;
uint64_t freq = 0;
uint32_t per = 0;
int ret = OK;
/* Get TIM6 interface */
tim = stm32l4_tim_init(6);
if (tim == NULL)
{
printf("ERROR: Failed to get TIM6 interface\n");
ret = -1;
goto errout;
}
spwm->tim = tim;
/* Frequency with which we will change samples.
*
* tim6_freq = samples_num * waveform_freq.
*/
freq = spwm->samples * spwm->waveform_freq;
per = BOARD_TIM6_FREQUENCY / freq;
if (per > 0xffff)
{
printf("ERROR: can not achieve TIM6 frequency\n");
ret = -1;
goto errout;
}
/* TODO: TIM_SETFREQ */
STM32L4_TIM_SETCLOCK(tim, BOARD_TIM6_FREQUENCY);
STM32L4_TIM_SETPERIOD(tim, per);
/* Attach TIM6 ram vector */
ret = up_ramvec_attach(STM32L4_IRQ_TIM6, tim6_handler);
if (ret < 0)
{
printf("ERROR: up_ramvec_attach failed: %d\n", ret);
ret = -1;
goto errout;
}
/* Set the priority of the TIM6 interrupt vector */
ret = up_prioritize_irq(STM32L4_IRQ_TIM6, NVIC_SYSH_HIGH_PRIORITY);
if (ret < 0)
{
printf("ERROR: up_prioritize_irq failed: %d\n", ret);
ret = -1;
goto errout;
}
spwm_tim6_stop(spwm);
errout:
return ret;
}
/****************************************************************************
* Name: spwm_tim6_start
****************************************************************************/
static int spwm_tim6_start(FAR struct spwm_s *spwm)
{
FAR struct stm32l4_tim_dev_s *tim = spwm->tim;
/* Enable the timer interrupt at the NVIC and at TIM6 */
up_enable_irq(STM32L4_IRQ_TIM6);
STM32L4_TIM_ENABLEINT(tim, BTIM_DIER_UIE);
return OK;
}
/****************************************************************************
* Name: spwm_tim6_stop
****************************************************************************/
static int spwm_tim6_stop(FAR struct spwm_s *spwm)
{
FAR struct stm32l4_tim_dev_s *tim = spwm->tim;
/* Disable the timer interrupt at the NVIC and at TIM6 */
up_disable_irq(STM32L4_IRQ_TIM6);
STM32L4_TIM_DISABLEINT(tim, BTIM_DIER_UIE);
return OK;
}
/****************************************************************************
* Name: spwm_tim1_setup
****************************************************************************/
static int spwm_tim1_setup(FAR struct spwm_s *spwm)
{
FAR struct stm32l4_pwm_dev_s *pwm = NULL;
int ret = OK;
/* Get TIM1 PWM interface */
pwm = (FAR struct stm32l4_pwm_dev_s *)stm32l4_pwminitialize(1);
if (pwm == NULL)
{
printf("ERROR: Failed to get TIM1 PWM interface\n");
ret = -1;
goto errout;
}
spwm->pwm = pwm;
/* Initial PWM1 setup */
ret = PWM_SETUP(pwm);
if (ret < 0)
{
printf("ERROR: Failed to get setup TIM1 PWM\n");
ret = -1;
goto errout;
}
/* Configure TIM1 PWM frequency */
ret = PWM_FREQ_UPDATE(pwm, CONFIG_NUCLEOL432KC_SPWM_PWM_FREQ);
if (ret < 0)
{
printf("ERROR: Failed to set TIM1 PWM frequency\n");
ret = -1;
goto errout;
}
/* Get TIM1 period (ARR) */
spwm->per = PWM_ARR_GET(pwm);
spwm_tim1_stop(spwm);
errout:
return ret;
}
/****************************************************************************
* Name: spwm_tim1_start
****************************************************************************/
static int spwm_tim1_start(FAR struct spwm_s *spwm)
{
FAR struct stm32l4_pwm_dev_s *pwm = spwm->pwm;
uint16_t outputs = 0;
int i = 0;
/* Get outputs */
for (i = 0; i < spwm->phases; i += 1)
{
outputs |= (1 << (i * 2));
}
/* Enable PWM outputs */
PWM_OUTPUTS_ENABLE(pwm, outputs, true);
/* Enable TIM1 */
PWM_TIM_ENABLE(pwm, true);
return OK;
}
/****************************************************************************
* Name: spwm_tim1_stop
****************************************************************************/
static int spwm_tim1_stop(FAR struct spwm_s *spwm)
{
FAR struct stm32l4_pwm_dev_s *pwm = spwm->pwm;
uint16_t outputs = 0;
int i = 0;
/* Get outputs */
for (i = 0; i < spwm->phases; i += 1)
{
outputs |= (1 << (i * 2));
}
/* Disable PWM outputs */
PWM_OUTPUTS_ENABLE(pwm, outputs, false);
/* Disable TIM1 */
PWM_TIM_ENABLE(pwm, false);
return OK;
}
#endif /* CONFIG_NUCLEOL432KC_SPWM_USE_TIM1 */
/****************************************************************************
* Name: spwm_setup
****************************************************************************/
static int spwm_setup(FAR struct spwm_s *spwm)
{
int ret = OK;
/* TIM1 setup - PWM */
printf("Setup TIM1 and TIM6\n");
ret = spwm_tim1_setup(spwm);
if (ret < 0)
{
goto errout;
}
/* TIM6 setup - IRQ */
ret = spwm_tim6_setup(spwm);
if (ret < 0)
{
goto errout;
}
errout:
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: spwm_main
*
* Description:
* Entrypoint for SPWM example.
*
****************************************************************************/
int spwm_main(int argc, char *argv[])
{
FAR struct spwm_s *spwm = NULL;
int ret = OK;
int i = 0;
spwm = &g_spwm;
printf("\nspwm_main: Started\n");
/* Setup SPWM example */
ret = spwm_setup(spwm);
if (ret < 0)
{
printf("ERROR: failed to setup SPWM %d!\n", ret);
goto errout;
}
/* Initialize modulation waveform */
ret = waveform_init(spwm, waveform_func);
if (ret < 0)
{
printf("ERROR: failed initialize modulation wavefrom %d!\n", ret);
goto errout;
}
/* Start SPWM */
ret = spwm_start(spwm);
if (ret < 0)
{
printf("ERROR: failed start SPWM %d!\n", ret);
goto errout;
}
/* Main loop */
while (1)
{
/* Print counter */
printf("%d\n", i);
/* Increase counter */
i += 1;
/* Sleep */
nxsig_sleep(1);
}
errout:
spwm_stop(spwm);
return 0;
}
#endif /* CONFIG_NUCLEOL432KC_SPWM */