diff --git a/arch/arm/src/kinetis/Kconfig b/arch/arm/src/kinetis/Kconfig index fdf54e1941..5710c2d32c 100644 --- a/arch/arm/src/kinetis/Kconfig +++ b/arch/arm/src/kinetis/Kconfig @@ -383,6 +383,69 @@ config KINETIS_PIT endmenu +config KINETIS_FTM0_PWM + bool "FTM0 PWM" + default n + depends on KINETIS_FTM0 + ---help--- + Reserve timer 0 for use by PWM + + Timer devices may be used for different purposes. One special purpose is + to generate modulated outputs for such things as motor control. If KINETIS_FTM0 + is defined then THIS following may also be defined to indicate that + the timer is intended to be used for pulsed output modulation. + +config KINETIS_FTM0_CHANNEL + int "FTM0 PWM Output Channel" + default 0 + range 0 7 + depends on KINETIS_FTM0_PWM + ---help--- + If FTM0 is enabled for PWM usage, you also need specifies the timer output + channel {0,..,7} + +config KINETIS_FTM1_PWM + bool "FTM1 PWM" + default n + depends on KINETIS_FTM1 + ---help--- + Reserve timer 1 for use by PWM + + Timer devices may be used for different purposes. One special purpose is + to generate modulated outputs for such things as motor control. If KINETIS_FTM1 + is defined then THIS following may also be defined to indicate that + the timer is intended to be used for pulsed output modulation. + +config KINETIS_FTM1_CHANNEL + int "FTM1 PWM Output Channel" + default 0 + range 0 1 + depends on KINETIS_FTM1_PWM + ---help--- + If FTM1 is enabled for PWM usage, you also need specifies the timer output + channel {0,..,1} + +config KINETIS_FTM2_PWM + bool "FTM2 PWM" + default n + depends on KINETIS_FTM2 + ---help--- + Reserve timer 2 for use by PWM + + Timer devices may be used for different purposes. One special purpose is + to generate modulated outputs for such things as motor control. If KINETIS_FTM2 + is defined then THIS following may also be defined to indicate that + the timer is intended to be used for pulsed output modulation. + +config KINETIS_FTM2_CHANNEL + int "FTM2 PWM Output Channel" + default 0 + range 0 1 + depends on KINETIS_FTM2_PWM + ---help--- + If FTM2 is enabled for PWM usage, you also need specifies the timer output + channel {0,..,1} + comment "Kinetis GPIO Interrupt Configuration" config GPIO_IRQ diff --git a/arch/arm/src/kinetis/Make.defs b/arch/arm/src/kinetis/Make.defs index c9c5ec6ca8..ed4311a721 100644 --- a/arch/arm/src/kinetis/Make.defs +++ b/arch/arm/src/kinetis/Make.defs @@ -108,7 +108,7 @@ CHIP_CSRCS += kinetis_pinirq.c endif ifeq ($(CONFIG_DEBUG_GPIO),y) -CHIP_CSRCS += kinetis_pindbg.c +CHIP_CSRCS += kinetis_pindump.c endif ifeq ($(CONFIG_KINETIS_SDHC),y) @@ -127,6 +127,10 @@ ifeq ($(CONFIG_KINETIS_DMA),y) CHIP_CSRCS += kinetis_dma.c kinetis_pindma.c endif +ifeq ($(CONFIG_PWM),y) +CHIP_CSRCS += kinetis_pwm.c +endif + ifeq ($(CONFIG_NET),y) ifeq ($(CONFIG_KINETIS_ENET),y) CHIP_CSRCS += kinetis_enet.c diff --git a/arch/arm/src/kinetis/kinetis.h b/arch/arm/src/kinetis/kinetis.h index b0c016d22a..325b88d697 100644 --- a/arch/arm/src/kinetis/kinetis.h +++ b/arch/arm/src/kinetis/kinetis.h @@ -567,7 +567,7 @@ void kinetis_pindmadisable(uint32_t pinset); ************************************************************************************/ #ifdef CONFIG_DEBUG_GPIO -int kinetis_pindump(uint32_t pinset, const char *msg); +void kinetis_pindump(uint32_t pinset, const char *msg); #else # define kinetis_pindump(p,m) #endif diff --git a/arch/arm/src/kinetis/kinetis_pindump.c b/arch/arm/src/kinetis/kinetis_pindump.c new file mode 100644 index 0000000000..354fa3fcb5 --- /dev/null +++ b/arch/arm/src/kinetis/kinetis_pindump.c @@ -0,0 +1,135 @@ +/**************************************************************************** + * arch/arm/src/kinetis/kinetis_pindump.c + * + * Copyright (C) 2013, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include + +#include +#include "up_arch.h" + +#include "kinetis.h" +#include "kinetis_gpio.h" +#include "kinetis_port.h" + +#ifdef CONFIG_DEBUG_GPIO + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* Port letters for prettier debug output */ + +static const char g_portchar[KINETIS_NPORTS] = +{ +#if KINETIS_NPORTS > 9 +# error "Additional support required for this number of GPIOs" +#elif KINETIS_NPORTS > 8 + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I' +#elif KINETIS_NPORTS > 7 + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' +#elif KINETIS_NPORTS > 6 + 'A', 'B', 'C', 'D', 'E', 'F', 'G' +#elif KINETIS_NPORTS > 5 + 'A', 'B', 'C', 'D', 'E', 'F' +#elif KINETIS_NPORTS > 4 + 'A', 'B', 'C', 'D', 'E' +#elif KINETIS_NPORTS > 3 + 'A', 'B', 'C', 'D' +#elif KINETIS_NPORTS > 2 + 'A', 'B', 'C' +#elif KINETIS_NPORTS > 1 + 'A', 'B' +#elif KINETIS_NPORTS > 0 + 'A' +#else +# error "Bad number of GPIOs" +#endif +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: kinetis_pindump + * + * Description: + * Dump all GPIO registers associated with the provided pin description + * along with a descriptive messasge. + * + ****************************************************************************/ + +void kinetis_pindump(uint32_t pinset, const char *msg) +{ + irqstate_t flags; + uintptr_t base; + int port; + + /* Decode the port and pin. Use the port number to get the GPIO base + * address. + */ + + port = (pinset & _PIN_PORT_MASK) >> _PIN_PORT_SHIFT; + DEBUGASSERT((unsigned)port < KINETIS_NPORTS); + base = KINETIS_GPIO_BASE(port); + + /* The following requires exclusive access to the GPIO registers */ + + flags = enter_critical_section(); + + lldbg("GPIO%c pinset: %08x base: %08x -- %s\n", + g_portchar[port], pinset, base, msg); + lldbg(" PDOR: %08x PDIR: %08x PDDR: %08x\n", + getreg32(base + KINETIS_GPIO_PDOR_OFFSET), + getreg32(base + KINETIS_GPIO_PDIR_OFFSET), + getreg32(base + KINETIS_GPIO_PDDR_OFFSET)); + + leave_critical_section(flags); +} + +#endif /* CONFIG_DEBUG_GPIO */ diff --git a/arch/arm/src/kinetis/kinetis_pwm.c b/arch/arm/src/kinetis/kinetis_pwm.c new file mode 100644 index 0000000000..0cf31bbd90 --- /dev/null +++ b/arch/arm/src/kinetis/kinetis_pwm.c @@ -0,0 +1,804 @@ +/**************************************************************************** + * arch/arm/src/kinetis/kinetis_pwm.c + * + * Copyright (C) 2013, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Alan Carvalho de Assis + * Ken Fazzone + * + * 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" + +#include "kinetis.h" +#include "kinetis_pwm.h" +#include "kinetis_gpio.h" +#include "kinetis_ftm.h" +#include "kinetis_sim.h" + +/* This module then only compiles if there is at least one enabled timer + * intended for use with the PWM upper half driver. + */ + +#if defined(CONFIG_KINETIS_FTM0_PWM) || defined(CONFIG_KINETIS_FTM1_PWM) || \ + defined(CONFIG_KINETIS_FTM2_PWM) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* PWM/Timer Definitions ****************************************************/ + +/* Debug ********************************************************************/ +/* Non-standard debug that may be enabled just for testing PWM */ + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_PWM +#endif + +#ifdef CONFIG_DEBUG_PWM +# define pwmdbg dbg +# define pwmlldbg lldbg +# ifdef CONFIG_DEBUG_VERBOSE +# define pwmvdbg vdbg +# define pwmllvdbg llvdbg +# define pwm_dumpgpio(p,m) kinetis_pindump(p,m) +# else +# define pwmlldbg(x...) +# define pwmllvdbg(x...) +# define pwm_dumpgpio(p,m) +# endif +#else +# define pwmdbg(x...) +# define pwmlldbg(x...) +# define pwmvdbg(x...) +# define pwmllvdbg(x...) +# define pwm_dumpgpio(p,m) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* This structure represents the state of one PWM timer */ + +struct kinetis_pwmtimer_s +{ + FAR const struct pwm_ops_s *ops; /* PWM operations */ + uint8_t tpmid; /* Timer/PWM Module ID {0,..,2} */ + uint8_t channel; /* Timer/PWM Module channel: {0,..5} */ + uint32_t base; /* The base address of the timer */ + uint32_t pincfg; /* Output pin configuration */ + uint32_t pclk; /* The frequency of the peripheral clock */ +}; + +/**************************************************************************** + * Static Function Prototypes + ****************************************************************************/ +/* Register access */ + +static uint32_t pwm_getreg(struct kinetis_pwmtimer_s *priv, int offset); +static void pwm_putreg(struct kinetis_pwmtimer_s *priv, int offset, uint32_t value); + +#if defined(CONFIG_DEBUG_PWM) && defined(CONFIG_DEBUG_VERBOSE) +static void pwm_dumpregs(struct kinetis_pwmtimer_s *priv, FAR const char *msg); +#else +# define pwm_dumpregs(priv,msg) +#endif + +/* Timer management */ + +static int pwm_timer(FAR struct kinetis_pwmtimer_s *priv, + FAR const struct pwm_info_s *info); + +/* PWM driver methods */ + +static int pwm_setup(FAR struct pwm_lowerhalf_s *dev); +static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev); + +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info); + +static int pwm_stop(FAR struct pwm_lowerhalf_s *dev); +static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, + int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* This is the list of lower half PWM driver methods used by the upper half driver */ + +static const struct pwm_ops_s g_pwmops = +{ + .setup = pwm_setup, + .shutdown = pwm_shutdown, + .start = pwm_start, + .stop = pwm_stop, + .ioctl = pwm_ioctl, +}; + +#ifdef CONFIG_KINETIS_FTM0_PWM +static struct kinetis_pwmtimer_s g_pwm0dev = +{ + .ops = &g_pwmops, + .tpmid = 0, + .channel = CONFIG_KINETIS_FTM0_CHANNEL, + .base = KINETIS_FTM0_BASE, + .pincfg = PWM_FTM0_PINCFG, + .pclk = BOARD_CORECLK_FREQ, +}; +#endif + +#ifdef CONFIG_KINETIS_FTM1_PWM +static struct kinetis_pwmtimer_s g_pwm1dev = +{ + .ops = &g_pwmops, + .tpmid = 1, + .channel = CONFIG_KINETIS_FTM1_CHANNEL, + .base = KINETIS_FTM1_BASE, + .pincfg = PWM_FTM1_PINCFG, + .pclk = BOARD_CORECLK_FREQ, +}; +#endif + +#ifdef CONFIG_KINETIS_FTM2_PWM +static struct kinetis_pwmtimer_s g_pwm2dev = +{ + .ops = &g_pwmops, + .tpmid = 2, + .channel = CONFIG_KINETIS_FTM2_CHANNEL, + .base = KINETIS_FTM2_BASE, + .pincfg = PWM_FTM2_PINCFG, + .pclk = BOARD_CORECLK_FREQ, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pwm_getreg + * + * Description: + * Read the value of an PWM timer register. + * + * Input Parameters: + * priv - A reference to the PWM block status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +static uint32_t pwm_getreg(struct kinetis_pwmtimer_s *priv, int offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: pwm_putreg + * + * Description: + * Read the value of an PWM timer register. + * + * Input Parameters: + * priv - A reference to the PWM block status + * offset - The offset to the register to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void pwm_putreg(struct kinetis_pwmtimer_s *priv, int offset, uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: pwm_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input parameters: + * priv - A reference to the PWM block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_DEBUG_PWM) && defined(CONFIG_DEBUG_VERBOSE) +static void pwm_dumpregs(struct kinetis_pwmtimer_s *priv, FAR const char *msg) +{ + int nchannels = (priv->tpmid == 0) ? 8 : 2; + + pwmvdbg("%s:\n", msg); + pwmvdbg(" FTM%d_SC: %04x FTM%d_CNT: %04x FTM%d_MOD: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_SC_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_CNT_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_MOD_OFFSET)); + pwmvdbg(" FTM%d_STATUS: %04x FTM%d_CONF: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_STATUS_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_CONF_OFFSET)); + pwmvdbg(" FTM%d_C0SC: %04x FTM%d_C0V: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C0SC_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C0V_OFFSET)); + pwmvdbg(" FTM%d_C1SC: %04x FTM%d_C1V: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C1SC_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C1V_OFFSET)); + + if (nchannels >= 3) + { + pwmvdbg(" FTM%d_C2SC: %04x FTM%d_C2V: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C2SC_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C2V_OFFSET)); + } + + if (nchannels >= 4) + { + pwmvdbg(" FTM%d_C3SC: %04x FTM%d_C3V: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C3SC_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C3V_OFFSET)); + } + + if (nchannels >= 5) + { + pwmvdbg(" FTM%d_C4SC: %04x FTM%d_C4V: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C4SC_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C4V_OFFSET)); + } + + if (nchannels >= 6) + { + pwmvdbg(" FTM%d_C5SC: %04x FTM%d_C5V: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C5SC_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C5V_OFFSET)); + } + if (nchannels >= 7) + { + pwmvdbg(" FTM%d_C6SC: %04x FTM%d_C6V: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C6SC_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C6V_OFFSET)); + } + if (nchannels >= 8) + { + pwmvdbg(" FTM%d_C7SC: %04x FTM%d_C7V: %04x\n", + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C7SC_OFFSET), + priv->tpmid, + pwm_getreg(priv, KINETIS_FTM_C7V_OFFSET)); + } +} +#endif + +/**************************************************************************** + * Name: pwm_timer + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input parameters: + * priv - A reference to the lower half PWM driver state structure + * info - A reference to the characteristics of the pulsed output + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_timer(FAR struct kinetis_pwmtimer_s *priv, + FAR const struct pwm_info_s *info) +{ + /* Calculated values */ + + uint32_t prescaler; + uint32_t tpmclk; + uint32_t modulo; + uint32_t regval; + uint32_t cv; + uint8_t i; + + static const uint8_t presc_values[8] = {1, 2, 4, 8, 16, 32, 64, 128}; + + /* Register contents */ + + DEBUGASSERT(priv != NULL && info != NULL); + + pwmvdbg("FTM%d channel: %d frequency: %d duty: %08x\n", + priv->tpmid, priv->channel, info->frequency, info->duty); + + DEBUGASSERT(info->frequency > 0 && info->duty > 0 && + info->duty < uitoub16(100)); + + /* Calculate optimal values for the timer prescaler and for the timer modulo + * register. If' frequency' is the desired frequency, then + * + * modulo = tpmclk / frequency + * tpmclk = pclk / presc + * + * Or, + * + * modulo = pclk / presc / frequency + * + * There are many solutions to do this, but the best solution will be the + * one that has the largest modulo value and the smallest prescaler value. + * That is the solution that should give us the most accuracy in the timer + * control. Subject to: + * + * 1 <= presc <= 128 (need to be 1, 2, 4, 8, 16, 32, 64, 128) + * 1 <= modulo <= 65535 + * + * So presc = pclk / 65535 / frequency would be optimal. + * + * Example: + * + * pclk = 24 MHz + * frequency = 100 Hz + * + * prescaler = 24,000,000 / 65,535 / 100 + * = 3.6 (or 4 -- taking the ceiling always) + * timclk = 24,000,000 / 4 + * = 6,000,000 + * modulo = 6,000,000 / 100 + * = 60,000 + */ + + prescaler = (priv->pclk / info->frequency + 65534) / 65535; + + for (i = 0; i < 7; i++) + { + if (prescaler <= presc_values[i]) + { + break; + } + } + + prescaler = i; + + tpmclk = priv->pclk / presc_values[prescaler]; + + modulo = tpmclk / info->frequency; + if (modulo < 1) + { + modulo = 1; + } + else if (modulo > 65535) + { + modulo = 65535; + } + + /* Duty cycle: + * + * duty cycle = cv / modulo (fractional value) + */ + + cv = b16toi(info->duty * modulo + b16HALF); + + pwmvdbg("FTM%d PCLK: %d frequency: %d FTMCLK: %d prescaler: %d modulo: %d c0v: %d\n", + priv->tpmid, priv->pclk, info->frequency, tpmclk, + presc_values[prescaler], modulo, cv); + + /* Disable FTM and reset CNT before writing MOD and PS */ + + pwm_putreg(priv, KINETIS_FTM_SC_OFFSET, FTM_SC_CLKS_NONE); + pwm_putreg(priv, KINETIS_FTM_CNT_OFFSET, 0); + + /* Set the modulo value */ + + pwm_putreg(priv, KINETIS_FTM_MOD_OFFSET, (uint16_t)modulo); + + /* Set the duty cycle for channel specific */ + + switch (priv->channel) + { + case 0: /* PWM Mode configuration: Channel 0 */ + { + pwm_putreg(priv, KINETIS_FTM_C0SC_OFFSET, FTM_CSC_MSB | FTM_CSC_ELSB); + pwm_putreg(priv, KINETIS_FTM_C0V_OFFSET, (uint16_t) cv); + } + break; + + case 1: /* PWM Mode configuration: Channel 1 */ + { + pwm_putreg(priv, KINETIS_FTM_C1SC_OFFSET, FTM_CSC_MSB | FTM_CSC_ELSB); + pwm_putreg(priv, KINETIS_FTM_C1V_OFFSET, (uint16_t) cv); + } + break; + + case 2: /* PWM Mode configuration: Channel 2 */ + { + pwm_putreg(priv, KINETIS_FTM_C2SC_OFFSET, FTM_CSC_MSB | FTM_CSC_ELSB); + pwm_putreg(priv, KINETIS_FTM_C2V_OFFSET, (uint16_t) cv); + } + break; + + case 3: /* PWM Mode configuration: Channel 3 */ + { + pwm_putreg(priv, KINETIS_FTM_C3SC_OFFSET, FTM_CSC_MSB | FTM_CSC_ELSB); + pwm_putreg(priv, KINETIS_FTM_C3V_OFFSET, (uint16_t) cv); + } + break; + + case 4: /* PWM Mode configuration: Channel 4 */ + { + pwm_putreg(priv, KINETIS_FTM_C4SC_OFFSET, FTM_CSC_MSB | FTM_CSC_ELSB); + pwm_putreg(priv, KINETIS_FTM_C4V_OFFSET, (uint16_t) cv); + } + break; + + case 5: /* PWM Mode configuration: Channel 5 */ + { + pwm_putreg(priv, KINETIS_FTM_C5SC_OFFSET, FTM_CSC_MSB | FTM_CSC_ELSB); + pwm_putreg(priv, KINETIS_FTM_C5V_OFFSET, (uint16_t) cv); + } + break; + + case 6: /* PWM Mode configuration: Channel 6 */ + { + pwm_putreg(priv, KINETIS_FTM_C6SC_OFFSET, FTM_CSC_MSB | FTM_CSC_ELSB); + pwm_putreg(priv, KINETIS_FTM_C6V_OFFSET, (uint16_t) cv); + } + break; + case 7: /* PWM Mode configuration: Channel 7 */ + { + pwm_putreg(priv, KINETIS_FTM_C7SC_OFFSET, FTM_CSC_MSB | FTM_CSC_ELSB); + pwm_putreg(priv, KINETIS_FTM_C7V_OFFSET, (uint16_t) cv); + } + break; + + default: + pwmdbg("No such channel: %d\n", priv->channel); + return -EINVAL; + } + + /* Set prescaler and enable clock */ + + regval = pwm_getreg(priv, KINETIS_FTM_SC_OFFSET); + regval &= ~(FTM_SC_PS_MASK); + regval &= ~(FTM_SC_CLKS_MASK); + regval |= prescaler | FTM_SC_CLKS_SYSCLK; + pwm_putreg(priv, KINETIS_FTM_SC_OFFSET, (uint16_t)regval); + + pwm_dumpregs(priv, "After starting"); + return OK; +} + +/**************************************************************************** + * Name: pwm_setup + * + * Description: + * This method is called when the driver is opened. The lower half driver + * should configure and initialize the device so that it is ready for use. + * It should not, however, output pulses until the start method is called. + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + * Assumptions: + * AHB1 or 2 clocking for the GPIOs and timer has already been configured + * by the RCC logic at power up. + * + ****************************************************************************/ + +static int pwm_setup(FAR struct pwm_lowerhalf_s *dev) +{ + uint32_t regval; + FAR struct kinetis_pwmtimer_s *priv = (FAR struct kinetis_pwmtimer_s *)dev; + + /* Enable access to FTM modules */ + + regval = getreg32(KINETIS_SIM_SCGC6); + regval |= SIM_SCGC6_FTM0 | SIM_SCGC6_FTM1; + putreg32(regval, KINETIS_SIM_SCGC6); + + regval = getreg32(KINETIS_SIM_SCGC3); + regval |= SIM_SCGC3_FTM2; + putreg32(regval, KINETIS_SIM_SCGC3); + + pwmvdbg("FTM%d pincfg: %08x\n", priv->tpmid, priv->pincfg); + pwm_dumpregs(priv, "Initially"); + + /* Configure the PWM output pin, but do not start the timer yet */ + + kinetis_pinconfig(priv->pincfg); + pwm_dumpgpio(priv->pincfg, "PWM setup"); + return OK; +} + +/**************************************************************************** + * Name: pwm_shutdown + * + * Description: + * This method is called when the driver is closed. The lower half driver + * stop pulsed output, free any resources, disable the timer hardware, and + * put the system into the lowest possible power usage state + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct kinetis_pwmtimer_s *priv = (FAR struct kinetis_pwmtimer_s *)dev; + uint32_t pincfg; + + pwmvdbg("FTM%d pincfg: %08x\n", priv->tpmid, priv->pincfg); + + /* Make sure that the output has been stopped */ + + pwm_stop(dev); + + /* Then put the GPIO pin back to the default state */ + + pincfg = (priv->pincfg & ~(_PIN_MODE_MASK)); + pincfg |= GPIO_INPUT; + kinetis_pinconfig(pincfg); + return OK; +} + +/**************************************************************************** + * Name: pwm_start + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * info - A reference to the characteristics of the pulsed output + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info) +{ + FAR struct kinetis_pwmtimer_s *priv = (FAR struct kinetis_pwmtimer_s *)dev; + return pwm_timer(priv, info); +} + +/**************************************************************************** + * Name: pwm_stop + * + * Description: + * Stop the pulsed output and reset the timer resources + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + * Assumptions: + * This function is called to stop the pulsed output at anytime. This + * method is also called from the timer interrupt handler when a repetition + * count expires... automatically stopping the timer. + * + ****************************************************************************/ + +static int pwm_stop(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct kinetis_pwmtimer_s *priv = (FAR struct kinetis_pwmtimer_s *)dev; + irqstate_t flags; + + pwmvdbg("FTM%d\n", priv->tpmid); + + /* Disable interrupts momentary to stop any ongoing timer processing and + * to prevent any concurrent access to the reset register. + */ + + flags = enter_critical_section(); + + /* Disable further interrupts and stop the timer */ + + pwm_putreg(priv, KINETIS_FTM_SC_OFFSET, FTM_SC_CLKS_NONE); + pwm_putreg(priv, KINETIS_FTM_CNT_OFFSET, 0); + + /* Determine which timer channel to clear */ + + switch (priv->channel) + { + case 0: + pwm_putreg(priv, KINETIS_FTM_C0V_OFFSET, 0); + break; + + case 1: + pwm_putreg(priv, KINETIS_FTM_C1V_OFFSET, 0); + break; + + case 2: + pwm_putreg(priv, KINETIS_FTM_C2V_OFFSET, 0); + break; + + case 3: + pwm_putreg(priv, KINETIS_FTM_C3V_OFFSET, 0); + break; + + case 4: + pwm_putreg(priv, KINETIS_FTM_C4V_OFFSET, 0); + break; + + case 5: + pwm_putreg(priv, KINETIS_FTM_C5V_OFFSET, 0); + break; + + case 6: + pwm_putreg(priv, KINETIS_FTM_C6V_OFFSET, 0); + break; + + case 7: + pwm_putreg(priv, KINETIS_FTM_C7V_OFFSET, 0); + break; + + default: + pwmdbg("No such channel: %d\n", priv->channel); + return -EINVAL; + } + + leave_critical_section(flags); + + pwm_dumpregs(priv, "After stop"); + return OK; +} + +/**************************************************************************** + * Name: pwm_ioctl + * + * Description: + * Lower-half logic may support platform-specific ioctl commands + * + * Input parameters: + * dev - A reference to the lower half PWM driver state structure + * cmd - The ioctl command + * arg - The argument accompanying the ioctl command + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, int cmd, unsigned long arg) +{ +#ifdef CONFIG_DEBUG_PWM + FAR struct kinetis_pwmtimer_s *priv = (FAR struct kinetis_pwmtimer_s *)dev; + + /* There are no platform-specific ioctl commands */ + + pwmvdbg("FTM%d\n", priv->tpmid); +#endif + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: kinetis_pwminitialize + * + * Description: + * Initialize one timer for use with the upper_level PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer use. + * + * Returned Value: + * On success, a pointer to the kinetis lower half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct pwm_lowerhalf_s *kinetis_pwminitialize(int timer) +{ + FAR struct kinetis_pwmtimer_s *lower; + + pwmvdbg("FTM%d\n", timer); + + switch (timer) + { +#ifdef CONFIG_KINETIS_FTM0_PWM + case 0: + lower = &g_pwm0dev; + + break; +#endif + +#ifdef CONFIG_KINETIS_FTM1_PWM + case 1: + lower = &g_pwm1dev; + + break; +#endif + +#ifdef CONFIG_KINETIS_FTM2_PWM + case 2: + lower = &g_pwm2dev; + + break; +#endif + + default: + pwmdbg("No such timer configured\n"); + return NULL; + } + + return (FAR struct pwm_lowerhalf_s *)lower; +} + +#endif /* CONFIG_KINETIS_FTMn_PWM, n = 0,...,2 */ diff --git a/arch/arm/src/kinetis/kinetis_pwm.h b/arch/arm/src/kinetis/kinetis_pwm.h new file mode 100644 index 0000000000..e9db2c1d77 --- /dev/null +++ b/arch/arm/src/kinetis/kinetis_pwm.h @@ -0,0 +1,197 @@ +/************************************************************************************ + * arch/arm/src/kinetis/kinetis_pwm.h + * + * Copyright (C) 2013, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Alan Carvalho de Assis + * Ken Fazzone + * + * 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. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_KINETIS_KINETIS_PWM_H +#define __ARCH_ARM_SRC_KINETIS_KINETIS_PWM_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include "chip.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* 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_KINETIS_FTMn + * is defined then the CONFIG_KINETIS_FTMn_PWM must also be defined to indicate that + * timer "n" is intended to be used for pulsed output signal generation. + */ + +#ifndef CONFIG_KINETIS_FTM0 +# undef CONFIG_KINETIS_FTM0_PWM +#endif +#ifndef CONFIG_KINETIS_FTM1 +# undef CONFIG_KINETIS_FTM1_PWM +#endif +#ifndef CONFIG_KINETIS_FTM2 +# undef CONFIG_KINETIS_FTM2_PWM +#endif + +/* Check if PWM support for any channel is enabled. */ + +#if defined(CONFIG_KINETIS_FTM0_PWM) || defined(CONFIG_KINETIS_FTM1_PWM) || \ + defined(CONFIG_KINETIS_FTM2_PWM) + +#include +#include "kinetis_pinmux.h" + +/* For each timer that is enabled for PWM usage, we need the following additional + * configuration settings: + * + * CONFIG_KINETIS_FTMx_CHANNEL - Specifies the timer output channel {1,..,4} + * PWM_FTMx_CHn - One of the values defined in kinetis*_pinmap.h. In the case + * where there are multiple pin selections, the correct setting must be provided + * in the arch/board/board.h file. + */ + +#ifdef CONFIG_KINETIS_FTM0_PWM +# if !defined(CONFIG_KINETIS_FTM0_CHANNEL) +# error "CONFIG_KINETIS_FTM0_CHANNEL must be provided" +# elif CONFIG_KINETIS_FTM0_CHANNEL == 0 +# define PWM_FTM0_PINCFG GPIO_FTM0_CH0OUT +# elif CONFIG_KINETIS_FTM0_CHANNEL == 1 +# define PWM_FTM0_PINCFG GPIO_FTM0_CH1OUT +# elif CONFIG_KINETIS_FTM0_CHANNEL == 2 +# define PWM_FTM0_PINCFG GPIO_FTM0_CH2OUT +# elif CONFIG_KINETIS_FTM0_CHANNEL == 3 +# define PWM_FTM0_PINCFG GPIO_FTM0_CH3OUT +# elif CONFIG_KINETIS_FTM0_CHANNEL == 4 +# define PWM_FTM0_PINCFG GPIO_FTM0_CH4OUT +# elif CONFIG_KINETIS_FTM0_CHANNEL == 5 +# define PWM_FTM0_PINCFG GPIO_FTM0_CH5OUT +# elif CONFIG_KINETIS_FTM0_CHANNEL == 6 +# define PWM_FTM0_PINCFG GPIO_FTM0_CH6OUT +# elif CONFIG_KINETIS_FTM0_CHANNEL == 7 +# define PWM_FTM0_PINCFG GPIO_FTM0_CH7OUT +# else +# error "Unsupported value of CONFIG_KINETIS_FTM1_CHANNEL" +# endif +#endif + +#ifdef CONFIG_KINETIS_FTM1_PWM +# if !defined(CONFIG_KINETIS_FTM1_CHANNEL) +# error "CONFIG_KINETIS_FTM1_CHANNEL must be provided" +# elif CONFIG_KINETIS_FTM1_CHANNEL == 0 +# define PWM_FTM1_PINCFG GPIO_FTM1_CH0OUT +# elif CONFIG_KINETIS_FTM1_CHANNEL == 1 +# define PWM_FTM1_PINCFG GPIO_FTM1_CH1OUT +# elif CONFIG_KINETIS_FTM1_CHANNEL == 2 +# define PWM_FTM1_PINCFG GPIO_FTM1_CH2OUT +# elif CONFIG_KINETIS_FTM1_CHANNEL == 3 +# define PWM_FTM1_PINCFG GPIO_FTM1_CH3OUT +# elif CONFIG_KINETIS_FTM1_CHANNEL == 4 +# define PWM_FTM1_PINCFG GPIO_FTM1_CH4OUT +# elif CONFIG_KINETIS_FTM1_CHANNEL == 5 +# define PWM_FTM1_PINCFG GPIO_FTM1_CH5OUT +# else +# error "Unsupported value of CONFIG_KINETIS_FTM2_CHANNEL" +# endif +#endif + +#ifdef CONFIG_KINETIS_FTM2_PWM +# if !defined(CONFIG_KINETIS_FTM2_CHANNEL) +# error "CONFIG_KINETIS_FTM2_CHANNEL must be provided" +# elif CONFIG_KINETIS_FTM2_CHANNEL == 0 +# define PWM_FTM2_PINCFG GPIO_FTM2_CH0OUT +# elif CONFIG_KINETIS_FTM2_CHANNEL == 1 +# define PWM_FTM2_PINCFG GPIO_FTM2_CH1OUT +# elif CONFIG_KINETIS_FTM2_CHANNEL == 2 +# define PWM_FTM2_PINCFG GPIO_FTM2_CH2OUT +# elif CONFIG_KINETIS_FTM2_CHANNEL == 3 +# define PWM_FTM2_PINCFG GPIO_FTM2_CH3OUT +# elif CONFIG_KINETIS_FTM2_CHANNEL == 4 +# define PWM_FTM2_PINCFG GPIO_FTM2_CH4OUT +# elif CONFIG_KINETIS_FTM2_CHANNEL == 5 +# define PWM_FTM2_PINCFG GPIO_FTM2_CH5OUT +# else +# error "Unsupported value of CONFIG_KINETIS_FTM3_CHANNEL" +# endif +#endif + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: kinetis_pwminitialize + * + * Description: + * Initialize one timer for use with the upper_level PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer use. + * + * Returned Value: + * On success, a pointer to the kinetis lower half PWM driver is returned. + * NULL is returned on any failure. + * + ************************************************************************************/ + +FAR struct pwm_lowerhalf_s *kinetis_pwminitialize(int timer); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_KINETIS_FTMx_PWM */ +#endif /* __ARCH_ARM_SRC_KINETIS_KINETIS_PWM_H */ diff --git a/configs/teensy-3.x/include/board.h b/configs/teensy-3.x/include/board.h index 2fd7bb8368..e734268351 100644 --- a/configs/teensy-3.x/include/board.h +++ b/configs/teensy-3.x/include/board.h @@ -141,6 +141,28 @@ #define BOARD_FLEXBUS_FREQ (BOARD_MCG_FREQ / BOARD_OUTDIV3) #define BOARD_FLASHCLK_FREQ (BOARD_MCG_FREQ / BOARD_OUTDIV4) +/* PWM Configuration */ +/* FTM0 Channels */ + +#define GPIO_FTM0_CH0OUT PIN_FTM0_CH0_2 /* Pin 22: PTC1 */ +#define GPIO_FTM0_CH1OUT PIN_FTM0_CH1_2 /* Pin 23: PTC2 */ +#define GPIO_FTM0_CH2OUT PIN_FTM0_CH2_2 /* Pin 9: PTC3 */ +#define GPIO_FTM0_CH3OUT PIN_FTM0_CH3 /* Pin 10: PTC4 */ +#define GPIO_FTM0_CH4OUT PIN_FTM0_CH4 /* Pin 6: PTD4 */ +#define GPIO_FTM0_CH5OUT PIN_FTM0_CH5_2 /* Pin 20: PTD5 */ +#define GPIO_FTM0_CH6OUT PIN_FTM0_CH6_2 /* Pin 21: PTD6 */ +#define GPIO_FTM0_CH7OUT PIN_FTM0_CH7_2 /* Pin 5: PTD7 */ + +/* FTM1 Channels */ + +#define GPIO_FTM1_CH0OUT PIN_FTM1_CH0_1 /* Pin 3: PTA12 */ +#define GPIO_FTM1_CH1OUT PIN_FTM1_CH1_1 /* Pin 4: PTA13 */ + +/* FTM2 Channels */ + +#define GPIO_FTM2_CH0OUT PIN_FTM2_CH0 /* Pin 25: PTB18 */ +#define GPIO_FTM2_CH1OUT PIN_FTM2_CH1 /* Pin 32: PTB19 */ + /* LED definitions ******************************************************************/ /* A single LED is available driven by PTC5. The LED is grounded so bringing PTC5 * high will illuminate the LED. diff --git a/configs/teensy-3.x/src/Makefile b/configs/teensy-3.x/src/Makefile index ff73a6953a..87734c76c0 100644 --- a/configs/teensy-3.x/src/Makefile +++ b/configs/teensy-3.x/src/Makefile @@ -1,7 +1,7 @@ ############################################################################ # configs/teensy-3.x/src/Makefile # -# Copyright (C) 2015 Gregory Nutt. All rights reserved. +# Copyright (C) 2015,2016 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -51,4 +51,8 @@ ifeq ($(CONFIG_LIB_BOARDCTL),y) CSRCS += k20_appinit.c endif +ifeq ($(CONFIG_PWM),y) +CSRCS += k20_pwm.c +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/teensy-3.x/src/k20_pwm.c b/configs/teensy-3.x/src/k20_pwm.c new file mode 100644 index 0000000000..94af831308 --- /dev/null +++ b/configs/teensy-3.x/src/k20_pwm.c @@ -0,0 +1,160 @@ +/************************************************************************************ + * configs/teensy-3.x/src/k20_pwm.c + * + * Copyright (C) 2015,2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * Alan Carvalho de Assis + * + * 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 + +#include +#include +#include + +#include +#include + +#include + +#include "chip.h" +#include "up_arch.h" +#include "kinetis_pwm.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration *******************************************************************/ +/* PWM + * + * The Kinetis Freedom board provides a LED on GPIO. + */ + +#ifdef CONFIG_PWM + +extern struct pwm_lowerhalf_s *kinetis_pwminitialize(int timer); + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: board_pwm_setup + * + * Description: + * All Kinetis K20 architectures must provide the following interface to work with + * examples/pwm. + * + ************************************************************************************/ + +int board_pwm_setup(void) +{ + static bool initialized = false; + struct pwm_lowerhalf_s *pwm; + int ret; + + /* Have we already initialized? */ + + if (!initialized) + { + /* Call kinetis_pwminitialize() to get an instance of the PWM interface */ +#ifdef CONFIG_KINETIS_FTM0_PWM + pwm = kinetis_pwminitialize(0); + if (!pwm) + { + adbg("Failed to get the KL20 PWM lower half\n"); + return -ENODEV; + } + + /* Register the PWM driver at "/dev/pwm0" */ + + ret = pwm_register("/dev/pwm0", pwm); + if (ret < 0) + { + adbg("pwm_register failed: %d\n", ret); + return ret; + } + + /* Now we are initialized */ +#endif +#ifdef CONFIG_KINETIS_FTM1_PWM + pwm = kinetis_pwminitialize(1); + if (!pwm) + { + adbg("Failed to get the KL20 PWM lower half\n"); + return -ENODEV; + } + + /* Register the PWM driver at "/dev/pwm1" */ + + ret = pwm_register("/dev/pwm1", pwm); + if (ret < 0) + { + adbg("pwm_register failed: %d\n", ret); + return ret; + } + + /* Now we are initialized */ +#endif +#ifdef CONFIG_KINETIS_FTM2_PWM + pwm = kinetis_pwminitialize(2); + if (!pwm) + { + adbg("Failed to get the KL20 PWM lower half\n"); + return -ENODEV; + } + + /* Register the PWM driver at "/dev/pwm2" */ + + ret = pwm_register("/dev/pwm2", pwm); + if (ret < 0) + { + adbg("pwm_register failed: %d\n", ret); + return ret; + } + + /* Now we are initialized */ +#endif + initialized = true; + } + + return OK; +} + +#endif /* CONFIG_PWM */