From a2b00fd348c7dd7ae518568c7d6cc7fb0c2f791a Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Sun, 13 Sep 2020 12:29:19 +0200 Subject: [PATCH] nrf52: add PWM support --- arch/arm/Kconfig | 1 + arch/arm/src/nrf52/Kconfig | 177 ++++++ arch/arm/src/nrf52/Make.defs | 4 + arch/arm/src/nrf52/hardware/nrf52_pwm.h | 176 ++++++ arch/arm/src/nrf52/nrf52_pwm.c | 719 ++++++++++++++++++++++++ arch/arm/src/nrf52/nrf52_pwm.h | 131 +++++ 6 files changed, 1208 insertions(+) create mode 100644 arch/arm/src/nrf52/hardware/nrf52_pwm.h create mode 100644 arch/arm/src/nrf52/nrf52_pwm.c create mode 100644 arch/arm/src/nrf52/nrf52_pwm.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e1ea1ba67e..8250309a95 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -212,6 +212,7 @@ config ARCH_CHIP_NRF52 #select ARCH_HAVE_MPU #select ARM_HAVE_MPU_UNIFIED select ARCH_HAVE_FPU + select ARCH_HAVE_PWM_MULTICHAN ---help--- Nordic NRF52 architectures (ARM Cortex-M4). diff --git a/arch/arm/src/nrf52/Kconfig b/arch/arm/src/nrf52/Kconfig index 8c07cf1fc2..ff5413381e 100644 --- a/arch/arm/src/nrf52/Kconfig +++ b/arch/arm/src/nrf52/Kconfig @@ -101,6 +101,10 @@ config NRF52_TIMER bool default n +config NRF52_PWM + bool + default n + config NRF52_RTC bool default n @@ -219,6 +223,27 @@ config NRF52_TIMER4 select NRF52_TIMER default n +config NRF52_PWM0 + bool "PWM0" + select NRF52_PWM + default n + +config NRF52_PWM1 + bool "PWM1" + select NRF52_PWM + default n + +config NRF52_PWM2 + bool "PWM2" + select NRF52_PWM + default n + +config NRF52_PWM3 + bool "PWM3" + depends on NRF52_HAVE_PWM3 + select NRF52_PWM + default n + config NRF52_PPI bool "PPI" default n @@ -358,3 +383,155 @@ config NRF52_PROGMEM menu "GPIO Interrupt Configuration" endmenu # GPIO Interrupt Configuration + +menu "PWM configuration" + +config NRF52_PWM_MULTICHAN + bool "PWM Multiple Output Channels" + default n + +if NRF52_PWM_MULTICHAN + +if NRF52_PWM0 + +config NRF52_PWM0_CH0 + bool "PWM0 Channel 0 Output" + default n + ---help--- + Enables channel 0 output. + +config NRF52_PWM0_CH1 + bool "PWM0 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +config NRF52_PWM0_CH2 + bool "PWM0 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +config NRF52_PWM0_CH3 + bool "PWM0 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # NRF52_PWM0 + +if NRF52_PWM1 + +config NRF52_PWM1_CH0 + bool "PWM1 Channel 0 Output" + default n + ---help--- + Enables channel 0 output. + +config NRF52_PWM1_CH1 + bool "PWM1 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +config NRF52_PWM1_CH2 + bool "PWM1 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +config NRF52_PWM1_CH3 + bool "PWM1 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # NRF52_PWM1 + +if NRF52_PWM2 + +config NRF52_PWM2_CH0 + bool "PWM2 Channel 0 Output" + default n + ---help--- + Enables channel 0 output. + +config NRF52_PWM2_CH1 + bool "PWM2 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +config NRF52_PWM2_CH2 + bool "PWM2 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +config NRF52_PWM2_CH3 + bool "PWM2 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # NRF52_PWM2 + +if NRF52_PWM3 + +config NRF52_PWM3_CH0 + bool "PWM3 Channel 0 Output" + default n + ---help--- + Enables channel 0 output. + +config NRF52_PWM3_CH1 + bool "PWM3 Channel 1 Output" + default n + ---help--- + Enables channel 1 output. + +config NRF52_PWM3_CH2 + bool "PWM3 Channel 2 Output" + default n + ---help--- + Enables channel 2 output. + +config NRF52_PWM3_CH3 + bool "PWM3 Channel 3 Output" + default n + ---help--- + Enables channel 3 output. + +endif # NRF52_PWM3 + +endif # !NRF52_PWM_MULTICHAN + +if !NRF52_PWM_MULTICHAN + +config NRF52_PWM0_CHANNEL + int "PWM0 Output Channel" + depends on NRF52_PWM0 + default 0 + range 0 3 + +config NRF52_PWM1_CHANNEL + int "PWM1 Output Channel" + depends on NRF52_PWM1 + default 0 + range 0 3 + +config NRF52_PWM2_CHANNEL + int "PWM2 Output Channel" + depends on NRF52_PWM2 + default 0 + range 0 3 + +config NRF52_PWM3_CHANNEL + int "PWM3 Output Channel" + depends on NRF52_PWM3 + default 0 + range 0 3 + +endif # !NRF52_PWM_MULTICHAN + +endmenu # PWM configuration diff --git a/arch/arm/src/nrf52/Make.defs b/arch/arm/src/nrf52/Make.defs index ffce6a9df3..d69903d7bf 100644 --- a/arch/arm/src/nrf52/Make.defs +++ b/arch/arm/src/nrf52/Make.defs @@ -147,3 +147,7 @@ endif ifeq ($(CONFIG_NRF52_RTC),y) CHIP_CSRCS += nrf52_rtc.c endif + +ifeq ($(CONFIG_NRF52_PWM),y) +CHIP_CSRCS += nrf52_pwm.c +endif diff --git a/arch/arm/src/nrf52/hardware/nrf52_pwm.h b/arch/arm/src/nrf52/hardware/nrf52_pwm.h new file mode 100644 index 0000000000..91645ed08d --- /dev/null +++ b/arch/arm/src/nrf52/hardware/nrf52_pwm.h @@ -0,0 +1,176 @@ +/**************************************************************************** + * arch/arm/src/nrf52/hardware/nrf52_pwm.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_PWM_H +#define __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_PWM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "hardware/nrf52_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Register offsets *********************************************************/ + +#define NRF52_PWM_TASKS_STOP_OFFSET 0x0004 /* Stop PWM */ +#define NRF52_PWM_TASKS_SEQSTART0_OFFSET 0x0008 /* Sequence 0 start */ +#define NRF52_PWM_TASKS_SEQSTART1_OFFSET 0x000c /* Sequence 1 start */ +#define NRF52_PWM_TASKS_NEXTSTEP_OFFSET 0x0010 /* Steps by one value in the current sequence */ +#define NRF52_PWM_EVENTS_STOPPED_OFFSET 0x0104 /* STOP event */ +#define NRF52_PWM_EVENTS_SEQSTARTED0_OFFSET 0x0108 /* Sequence 0 started event */ +#define NRF52_PWM_EVENTS_SEQSTARTED1_OFFSET 0x010c /* Sequence 1 started event */ +#define NRF52_PWM_EVENTS_SEQEND0_OFFSET 0x0110 /* Sequence 0 end event */ +#define NRF52_PWM_EVENTS_SEQEND1_OFFSET 0x0114 /* Sequence 1 end event */ +#define NRF52_PWM_EVENTS_PWMPERIODEN_OFFSET 0x0118 /* PWM period end event */ +#define NRF52_PWM_EVENTS_LOOPSDONE_OFFSET 0x011c /* Loop done event */ +#define NRF52_PWM_SHORTS_OFFSET 0x0200 /* Shourtcut register */ +#define NRF52_PWM_INTEN_OFFSET 0x0300 /* Enable or disable interrupt */ +#define NRF52_PWM_INTENSET_OFFSET 0x0304 /* Enable interrupt */ +#define NRF52_PWM_INTENCLR_OFFSET 0x0308 /* Disable interrupt */ +#define NRF52_PWM_ENABLE_OFFSET 0x0500 /* PWM enable */ +#define NRF52_PWM_MODE_OFFSET 0x0504 /* Wave counter mode */ +#define NRF52_PWM_COUNTERTOP_OFFSET 0x0508 /* Counter max value */ +#define NRF52_PWM_PRESCALER_OFFSET 0x050c /* Prescaler configuration */ +#define NRF52_PWM_DECODER_OFFSET 0x0510 /* Configuration of the decoder */ +#define NRF52_PWM_LOOP_OFFSET 0x0514 /* Amount of playback of a loop */ +#define NRF52_PWM_SEQ0PTR_OFFSET 0x0520 /* Sequence 0 beginning address */ +#define NRF52_PWM_SEQ0CNT_OFFSET 0x0524 /* Sequence 0 length */ +#define NRF52_PWM_SEQ0REFRESH_OFFSET 0x0528 /* Sequence 0 additional periods */ +#define NRF52_PWM_SEQ0ENDDELAY_OFFSET 0x052c /* Time added after sequence 0 */ +#define NRF52_PWM_SEQ1PTR_OFFSET 0x0540 /* Sequence 1 beginning address */ +#define NRF52_PWM_SEQ1CNT_OFFSET 0x0544 /* Sequence 1 length */ +#define NRF52_PWM_SEQ1REFRESH_OFFSET 0x0548 /* Sequence 0 additional periods */ +#define NRF52_PWM_SEQ1ENDDELAY_OFFSET 0x054c /* Time added after sequence 1 */ +#define NRF52_PWM_PSEL0_OFFSET 0x0560 /* Output pin select for PWM chan 0 */ +#define NRF52_PWM_PSEL1_OFFSET 0x0564 /* Output pin select for PWM chan 1 */ +#define NRF52_PWM_PSEL2_OFFSET 0x0568 /* Output pin select for PWM chan 2 */ +#define NRF52_PWM_PSEL3_OFFSET 0x056c /* Output pin select for PWM chan 3 */ + +/* Register Bitfield Definitions ********************************************/ + +/* TASKS_STOP Register */ + +#define PWM_TASKS_STOP (1 << 0) /* Bit 0: Stop PWM */ + +/* TASKS_SEQSTART[n] Register */ + +#define PWM_TASKS_SEQSTART (1 << 0) /* Bit 0: Start sequence */ + +/* TASKS_NEXTSTEP Register */ + +#define PWM_TASKS_NEXTSTEP (1 << 0) /* Bit 0: Next step */ + +/* SHORTS Register */ + +#define PWM_SHORTS_SEQEND0_STOP (1 << 0) /* Bit 0: Shortcut between event SEQEND[0] and task STOP */ +#define PWM_SHORTS_SEQEND1_STOP (1 << 1) /* Bit 1: Shortcut between event SEQEND[1] and task STOP */ +#define PWM_SHORTS_LOOPSDONE_SEQSTART0 (1 << 2) /* Bit 2: Shortcut between event LOOPSDONE and task SEQSTART[0] */ +#define PWM_SHORTS_LOOPSDONE_SEQSTART1 (1 << 3) /* Bit 3: Shortcut between event LOOPSDONE and task SEQSTART[1] */ +#define PWM_SHORTS_LOOPSDONE_STOP (1 << 4) /* Bit 4: Shortcut between event LOOPSDONE and task STOP */ + +/* INTEN/INTENSET/INTENCLR Register */ + +#define PWM_INT_STOPPED (1 << 0) /* Bit 0: Interrupt for event STOPPED */ +#define PWM_INT_SEQSTARTED0 (1 << 1) /* Bit 1: Interrupt for event SEQSTARTED0 */ +#define PWM_INT_SEQSTARTED1 (1 << 2) /* Bit 2: Interrupt for event SEQSTARTED2 */ +#define PWM_INT_SEQEND0 (1 << 3) /* Bit 3: Interrupt for event SEQEND0 */ +#define PWM_INT_SEQEND1 (1 << 4) /* Bit 4: Interrupt for event SEQEND1 */ +#define PWM_INT_PWMPERIODEND (1 << 5) /* Bit 5: Interrupt for event PWMPERIODEND */ +#define PWM_INT_LOOPSDONE (1 << 6) /* Bit 6: Interrupt for event LOOPSDONE */ + +/* ENABLE Register */ + +#define PWM_ENABLE_ENABLE (1 << 0) /* Bit 0: Enable PWM module */ +#define PWM_ENABLE_DISABLE (0 << 0) /* Bit 0: Disable PWM module */ + +/* MODE Register */ + +#define PWM_MODE_UP (0 << 0) /* Bit 0: Up counter, edge-aligned PWM */ +#define PWM_MODE_UPDOWN (1 << 0) /* Bit 0: Up and down counter, center-aligned PWM */ + +/* COUNTERTOP Register */ + +#define PWM_COUNTERTOP_MASK (0x7fff) + +/* PRESCALER Register */ + +#define PWM_PRESCALER_SHIFT (0) +#define PWM_PRESCALER_MASK (7 << PWM_PRESCALER_SHIFT) +# define PWM_PRESCALER_16MHZ (0 << PWM_PRESCALER_SHIFT) +# define PWM_PRESCALER_8MHZ (1 << PWM_PRESCALER_SHIFT) +# define PWM_PRESCALER_4MHZ (2 << PWM_PRESCALER_SHIFT) +# define PWM_PRESCALER_2MHZ (3 << PWM_PRESCALER_SHIFT) +# define PWM_PRESCALER_1MHZ (4 << PWM_PRESCALER_SHIFT) +# define PWM_PRESCALER_500KHZ (5 << PWM_PRESCALER_SHIFT) +# define PWM_PRESCALER_250KHZ (6 << PWM_PRESCALER_SHIFT) +# define PWM_PRESCALER_125KHZ (7 << PWM_PRESCALER_SHIFT) + +/* DECODER Register */ + +#define PWM_DECODER_LOAD_SHIFT (0) /* Bits 0-1: How a sequence is read from RAM */ +#define PWM_DECODER_LOAD_MASK (3 << PWM_DECODER_LOAD_SHIFT) +# define PWM_DECODER_LOAD_COMMON (0 << PWM_DECODER_LOAD_SHIFT) +# define PWM_DECODER_LOAD_GROUPED (1 << PWM_DECODER_LOAD_SHIFT) +# define PWM_DECODER_LOAD_INDIVIDUAL (2 << PWM_DECODER_LOAD_SHIFT) +# define PWM_DECODER_LOAD_WAVEFORM (3 << PWM_DECODER_LOAD_SHIFT) + +#define PWM_DECODER_MODE_REFRESH (8 << 0) /* Bit 8: */ +#define PWM_DECODER_MODE_NEXTSTEP (8 << 1) /* Bit 8: */ + +/* LOOP Register */ + +#define PWM_LOOP_MASK (0xffff) + +/* SEQ[n]CNT Register */ + +#define PWM_SEQCNT_MASK (0x7fff) + +/* SEQ[n]REFRESH Register */ + +#define PWM_SEQREFRESH_MASK (0xffffff) + +/* SEQ[n]ENDDELAY Register */ + +#define PWM_SEQENDDELAY_MASK (0xffffff) + +/* PSEL[x] Register */ + +#define PWM_PSEL_PIN_SHIFT (0) /* Bits 0-4: OUT pin number */ +#define PWM_PSEL_PIN_MASK (0x1f << PWM_PSELSDA_PIN_SHIFT) +#define PWM_PSEL_PORT_SHIFT (5) /* Bit 5: PUT port number */ +#define PWM_PSEL_PORT_MASK (0x1 << PWM_PSELSDA_PORT_SHIFT) +#define PWM_PSEL_CONNECTED (1 << 31) /* Bit 31: Connection */ +#define PWM_PSEL_RESET (0xffffffff) + +/* Decoder data */ + +#define PWM_DECODER_COMPARE_SHIFT (0) +#define PWM_DECODER_COMPARE_MASK (0x7fff) +#define PWM_DECODER_POL_RISING (0 << 15) +#define PWM_DECODER_POL_FALLING (1 << 15) + +#endif /* __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_PWM_H */ diff --git a/arch/arm/src/nrf52/nrf52_pwm.c b/arch/arm/src/nrf52/nrf52_pwm.c new file mode 100644 index 0000000000..94a2dc40cf --- /dev/null +++ b/arch/arm/src/nrf52/nrf52_pwm.c @@ -0,0 +1,719 @@ +/**************************************************************************** + * arch/arm/src/nrf52/nrf52_pwm.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "arm_internal.h" +#include "arm_arch.h" + +#include "nrf52_gpio.h" +#include "nrf52_pwm.h" + +#include "hardware/nrf52_pwm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Default PWM polarity */ + +#define PWM_POLARITY_DEFAULT (PWM_DECODER_POL_FALLING) + +/* Sequence 0 length */ + +#define PWM_SEQ0_LEN (4) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf52_pwm_s +{ + FAR const struct pwm_ops_s *ops; /* PWM operations */ + uint32_t base; /* Base address of PWM register */ + uint32_t frequency; /* Current frequency setting */ + uint32_t cntrtop; /* Counter top */ + uint32_t ch0_pin; /* Channel 1 pin */ + uint32_t ch1_pin; /* Channel 2 pin */ + uint32_t ch2_pin; /* Channel 3 pin */ + uint32_t ch3_pin; /* Channel 4 pin */ + + /* Sequence 0 */ + + uint16_t seq0[PWM_SEQ0_LEN]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* PWM Register access */ + +static inline void nrf52_pwm_putreg(FAR struct nrf52_pwm_s *priv, + uint32_t offset, + uint32_t value); +static inline uint32_t nrf52_pwm_getreg(FAR struct nrf52_pwm_s *priv, + uint32_t offset); + +/* PWM helpers */ + +static int nrf52_pwm_configure(FAR struct nrf52_pwm_s *priv); +static int nrf52_pwm_duty(FAR struct nrf52_pwm_s *priv, uint8_t chan, + ub16_t duty); +static int nrf52_pwm_freq(FAR struct nrf52_pwm_s *priv, uint32_t freq); + +/* PWM driver methods */ + +static int nrf52_pwm_setup(FAR struct pwm_lowerhalf_s *dev); +static int nrf52_pwm_shutdown(FAR struct pwm_lowerhalf_s *dev); +#ifdef CONFIG_PWM_PULSECOUNT +static int nrf52_pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info, + FAR void *handle); +#else +static int nrf52_pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info); +#endif +static int nrf52_pwm_stop(FAR struct pwm_lowerhalf_s *dev); +static int nrf52_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_nrf52_pwmops = +{ + .setup = nrf52_pwm_setup, + .shutdown = nrf52_pwm_shutdown, + .start = nrf52_pwm_start, + .stop = nrf52_pwm_stop, + .ioctl = nrf52_pwm_ioctl, +}; + +#ifdef CONFIG_NRF52_PWM0 +/* PWM 0 */ + +struct nrf52_pwm_s g_nrf52_pwm0 = +{ + .ops = &g_nrf52_pwmops, + .base = NRF52_PWM0_BASE, +#ifdef CONFIG_NRF52_PWM0_CH0 + .ch0_pin = NRF52_PWM0_CH0_PIN, +#endif +#ifdef CONFIG_NRF52_PWM0_CH1 + .ch1_pin = NRF52_PWM0_CH1_PIN, +#endif +#ifdef CONFIG_NRF52_PWM0_CH2 + .ch2_pin = NRF52_PWM0_CH2_PIN, +#endif +#ifdef CONFIG_NRF52_PWM0_CH3 + .ch3_pin = NRF52_PWM0_CH3_PIN, +#endif +}; +#endif + +#ifdef CONFIG_NRF52_PWM1 +/* PWM 1 */ + +struct nrf52_pwm_s g_nrf52_pwm1 = +{ + .ops = &g_nrf52_pwmops, + .base = NRF52_PWM1_BASE +#ifdef CONFIG_NRF52_PWM1_CH0 + .ch0_pin = NRF52_PWM1_CH0_PIN, +#endif +#ifdef CONFIG_NRF52_PWM1_CH1 + .ch1_pin = NRF52_PWM1_CH1_PIN, +#endif +#ifdef CONFIG_NRF52_PWM1_CH2 + .ch2_pin = NRF52_PWM1_CH2_PIN, +#endif +#ifdef CONFIG_NRF52_PWM1_CH3 + .ch3_pin = NRF52_PWM1_CH3_PIN, +#endif +}; +#endif + +#ifdef CONFIG_NRF52_PWM2 +/* PWM 2 */ + +struct nrf52_pwm_s g_nrf52_pwm2 = +{ + .ops = &g_nrf52_pwmops, + .base = NRF52_PWM2_BASE, +#ifdef CONFIG_NRF52_PWM2_CH0 + .ch0_pin = NRF52_PWM2_CH0_PIN, +#endif +#ifdef CONFIG_NRF52_PWM2_CH1 + .ch1_pin = NRF52_PWM2_CH1_PIN, +#endif +#ifdef CONFIG_NRF52_PWM2_CH2 + .ch2_pin = NRF52_PWM2_CH2_PIN, +#endif +#ifdef CONFIG_NRF52_PWM2_CH3 + .ch3_pin = NRF52_PWM2_CH3_PIN, +#endif +}; +#endif + +#ifdef CONFIG_NRF52_PWM3 +/* PWM 3 */ + +struct nrf52_pwm_s g_nrf52_pwm3 = +{ + .ops = &g_nrf52_pwmops, + .base = NRF52_PWM3_BASE, +#ifdef CONFIG_NRF52_PWM3_CH0 + .ch0_pin = NRF52_PWM3_CH0_PIN, +#endif +#ifdef CONFIG_NRF52_PWM3_CH1 + .ch1_pin = NRF52_PWM3_CH1_PIN, +#endif +#ifdef CONFIG_NRF52_PWM3_CH2 + .ch2_pin = NRF52_PWM3_CH2_PIN, +#endif +#ifdef CONFIG_NRF52_PWM3_CH3 + .ch3_pin = NRF52_PWM3_CH3_PIN, +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf52_pwm_putreg + * + * Description: + * Put a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void nrf52_pwm_putreg(FAR struct nrf52_pwm_s *priv, + uint32_t offset, + uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: nrf52_pwm_getreg + * + * Description: + * Get a 32-bit register value by offset + * + ****************************************************************************/ + +static inline uint32_t nrf52_pwm_getreg(FAR struct nrf52_pwm_s *priv, + uint32_t offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: nrf52_pwm_configure + * + * Description: + * Configure PWM + * + ****************************************************************************/ + +static int nrf52_pwm_configure(FAR struct nrf52_pwm_s *priv) +{ + uint32_t regval = 0; + int ret = OK; + + DEBUGASSERT(priv); + + /* Configure PWM mode */ + + nrf52_pwm_putreg(priv, NRF52_PWM_MODE_OFFSET, PWM_MODE_UP); + + /* Configure decoder */ + + regval = PWM_DECODER_LOAD_INDIVIDUAL | PWM_DECODER_MODE_REFRESH; + nrf52_pwm_putreg(priv, NRF52_PWM_DECODER_OFFSET, regval); + + /* Configure sequence 0 */ + + regval = (uint32_t)priv->seq0; + nrf52_pwm_putreg(priv, NRF52_PWM_SEQ0PTR_OFFSET, regval); + + regval = PWM_SEQ0_LEN; + nrf52_pwm_putreg(priv, NRF52_PWM_SEQ0CNT_OFFSET, regval); + + regval = 0; + nrf52_pwm_putreg(priv, NRF52_PWM_SEQ0REFRESH_OFFSET, regval); + + return ret; +} + +/**************************************************************************** + * Name: nrf52_pwm_duty + * + * Description: + * Configure PWM duty + * + ****************************************************************************/ + +static int nrf52_pwm_duty(FAR struct nrf52_pwm_s *priv, uint8_t chan, + ub16_t duty) +{ + uint16_t compare = 0; + + DEBUGASSERT(priv); + + pwminfo("PWM channel: %d duty: %d\n", chan, duty); + + /* Get compare + * + * duty cycle = compare / reload (fractional value) + */ + + compare = b16toi(duty * priv->cntrtop + b16HALF); + + /* Configure channel sequence */ + + priv->seq0[chan] = PWM_POLARITY_DEFAULT | compare; + + pwminfo("seq0[%d]: %d %d\n", chan, compare, priv->seq0[chan]); + + return OK; +} + +/**************************************************************************** + * Name: nrf52_pwm_freq + * + * Description: + * Configure PWM frequency + * + ****************************************************************************/ + +static int nrf52_pwm_freq(FAR struct nrf52_pwm_s *priv, uint32_t freq) +{ + uint32_t regval = 0; + uint32_t pwm_clk = 0; + uint32_t top = 0; + uint32_t prescaler = 0; + uint64_t tmp = 0; + int ret = OK; + + DEBUGASSERT(priv); + + /* Get best prescaler */ + + tmp = PWM_COUNTERTOP_MASK * freq; + + if (tmp >= 16000000) + { + pwm_clk = 16000000; + prescaler = PWM_PRESCALER_16MHZ; + } + else if (tmp >= 8000000) + { + pwm_clk = 8000000; + prescaler = PWM_PRESCALER_8MHZ; + } + else if (tmp >= 4000000) + { + pwm_clk = 4000000; + prescaler = PWM_PRESCALER_4MHZ; + } + else if (tmp >= 2000000) + { + pwm_clk = 2000000; + prescaler = PWM_PRESCALER_2MHZ; + } + else if (tmp >= 1000000) + { + pwm_clk = 1000000; + prescaler = PWM_PRESCALER_1MHZ; + } + else if (tmp >= 500000) + { + pwm_clk = 500000; + prescaler = PWM_PRESCALER_500KHZ; + } + else if (tmp >= 250000) + { + pwm_clk = 250000; + prescaler = PWM_PRESCALER_250KHZ; + } + else + { + pwm_clk = 125000; + prescaler = PWM_PRESCALER_125KHZ; + } + + /* Configure prescaler */ + + nrf52_pwm_putreg(priv, NRF52_PWM_PRESCALER_OFFSET, prescaler); + + /* Get counter max */ + + top = pwm_clk / freq; + if (top < 2) + { + top = 1; + } + else if (top > PWM_COUNTERTOP_MASK) + { + top = PWM_COUNTERTOP_MASK; + } + else + { + top = top - 1; + } + + /* Configure counter max */ + + regval = top; + nrf52_pwm_putreg(priv, NRF52_PWM_COUNTERTOP_OFFSET, regval); + + priv->cntrtop = top; + + pwminfo("PWM frequency: %d pwm_clk: %d pwm_prescaler: %d top: %d\n", + freq, pwm_clk, prescaler, top); + + return ret; +} + +/**************************************************************************** + * Name: nrf52_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. + * + ****************************************************************************/ + +static int nrf52_pwm_setup(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct nrf52_pwm_s *priv = (FAR struct nrf52_pwm_s *)dev; + int ret = OK; + uint32_t regval = 0; + uint32_t pin = 0; + uint32_t port = 0; + + DEBUGASSERT(dev); + + /* Configure channels */ + + if (priv->ch0_pin != 0) + { + nrf52_gpio_config(priv->ch0_pin); + + pin = GPIO_PIN_DECODE(priv->ch0_pin); + port = GPIO_PORT_DECODE(priv->ch0_pin); + + regval = (port << PWM_PSEL_PORT_SHIFT); + regval |= (pin << PWM_PSEL_PIN_SHIFT); + + nrf52_pwm_putreg(priv, NRF52_PWM_PSEL0_OFFSET, regval); + } + + if (priv->ch1_pin != 0) + { + nrf52_gpio_config(priv->ch1_pin); + + pin = GPIO_PIN_DECODE(priv->ch1_pin); + port = GPIO_PORT_DECODE(priv->ch1_pin); + + regval = (port << PWM_PSEL_PORT_SHIFT); + regval |= (pin << PWM_PSEL_PIN_SHIFT); + + nrf52_pwm_putreg(priv, NRF52_PWM_PSEL1_OFFSET, regval); + } + + if (priv->ch2_pin != 0) + { + nrf52_gpio_config(priv->ch2_pin); + + pin = GPIO_PIN_DECODE(priv->ch2_pin); + port = GPIO_PORT_DECODE(priv->ch2_pin); + + regval = (port << PWM_PSEL_PORT_SHIFT); + regval |= (pin << PWM_PSEL_PIN_SHIFT); + + nrf52_pwm_putreg(priv, NRF52_PWM_PSEL2_OFFSET, regval); + } + + if (priv->ch3_pin != 0) + { + nrf52_gpio_config(priv->ch3_pin); + + pin = GPIO_PIN_DECODE(priv->ch3_pin); + port = GPIO_PORT_DECODE(priv->ch3_pin); + + regval = (port << PWM_PSEL_PORT_SHIFT); + regval |= (pin << PWM_PSEL_PIN_SHIFT); + + nrf52_pwm_putreg(priv, NRF52_PWM_PSEL3_OFFSET, regval); + } + + /* Configure PWM */ + + ret = nrf52_pwm_configure(priv); + if (ret < 0) + { + pwmerr("ERROR: nrf52_pwm_configure failed %d\n", ret); + goto errout; + } + + /* Enable PWM */ + + nrf52_pwm_putreg(priv, NRF52_PWM_ENABLE_OFFSET, 1); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf52_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 + * + ****************************************************************************/ + +static int nrf52_pwm_shutdown(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct nrf52_pwm_s *priv = (FAR struct nrf52_pwm_s *)dev; + int ret = OK; + + DEBUGASSERT(dev); + + /* Disable PWM */ + + nrf52_pwm_putreg(priv, NRF52_PWM_ENABLE_OFFSET, 0); + + return ret; +} + +/**************************************************************************** + * Name: nrf52_pwm_start + * + * Description: + * (Re-)initialize the PWM and start the pulsed output + * + ****************************************************************************/ + +#ifdef CONFIG_PWM_PULSECOUNT +static int nrf52_pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info, + FAR void *handle) +{ +#error Not supported +} +#else +static int nrf52_pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info) +{ + FAR struct nrf52_pwm_s *priv = (FAR struct nrf52_pwm_s *)dev; + int ret = OK; +#ifdef CONFIG_PWM_MULTICHAN + int i = 0; +#endif + + DEBUGASSERT(dev); + + /* If frequency has not changed we just update duty */ + + if (info->frequency != priv->frequency) + { + /* Update frequency */ + + ret = nrf52_pwm_freq(priv, info->frequency); + + if (ret == OK) + { + priv->frequency = info->frequency; + } + } + +#ifdef CONFIG_PWM_MULTICHAN + for (i = 0; ret == OK && i < CONFIG_PWM_NCHANNELS; i++) + { + /* Set output if channel configured */ + + if (info->channels[i].channel != 0) + { + ret = nrf52_pwm_duty(priv, + (info->channels[i].channel - 1), + info->channels[i].duty); + } + } + +#else + ret = nrf52_pwm_duty(dev, + (priv->channels[0].channel - 1), + info->duty); +#endif /* CONFIG_PWM_MULTICHAN */ + + /* Start sequence 0 */ + + nrf52_pwm_putreg(priv, NRF52_PWM_TASKS_SEQSTART0_OFFSET, 1); + + /* Wait for sequence started */ + + while (nrf52_pwm_getreg(priv, NRF52_PWM_EVENTS_SEQSTARTED0_OFFSET) != 1); + + return ret; +} +#endif + +/**************************************************************************** + * Name: nrf52_pwm_stop + * + * Description: + * Stop the PWM + * + ****************************************************************************/ + +static int nrf52_pwm_stop(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct nrf52_pwm_s *priv = (FAR struct nrf52_pwm_s *)dev; + + DEBUGASSERT(dev); + + /* Stop PWM */ + + nrf52_pwm_putreg(priv, NRF52_PWM_TASKS_STOP_OFFSET, 1); + + /* Wait for PWM stopped */ + + while (nrf52_pwm_getreg(priv, NRF52_PWM_EVENTS_STOPPED_OFFSET) != 1); + + return OK; +} + +/**************************************************************************** + * Name: nrf52_pwm_ioctl + * + * Description: + * Lower-half logic may support platform-specific ioctl commands + * + ****************************************************************************/ + +static int nrf52_pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, + int cmd, unsigned long arg) +{ + FAR struct nrf52_pwm_s *priv = (FAR struct nrf52_pwm_s *)dev; + + DEBUGASSERT(dev); + + /* There are no platform-specific ioctl commands */ + + UNUSED(priv); + + return -ENOTTY; +} + +/**************************************************************************** + * Public Function + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf52_pwminitialize + * + * Description: + * Initialize one timer for use with the upper_level PWM driver. + * + * Input Parameters: + * pwm - A number identifying the pwm instance. + * + * Returned Value: + * On success, a pointer to the NRF52 lower half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct pwm_lowerhalf_s *nrf52_pwminitialize(int pwm) +{ + struct nrf52_pwm_s *lower = NULL; + + pwminfo("Initialize PWM%u\n", pwm); + + switch (pwm) + { +#ifdef CONFIG_NRF52_PWM0 + case 0: + { + lower = &g_nrf52_pwm0; + break; + } +#endif + +#ifdef CONFIG_NRF52_PWM1 + case 1: + { + lower = &g_nrf52_pwm1; + break; + } +#endif + +#ifdef CONFIG_NRF52_PWM2 + case 2: + { + lower = &g_nrf52_pwm; + break; + } +#endif + +#ifdef CONFIG_NRF52_PWM3 + case 3: + { + lower = &g_nrf52_pwm; + break; + } +#endif + + default: + { + pwmerr("ERROR: No such PWM device %d\n", pwm); + lower = NULL; + goto errout; + } + } + +errout: + return (struct pwm_lowerhalf_s *)lower; +} diff --git a/arch/arm/src/nrf52/nrf52_pwm.h b/arch/arm/src/nrf52/nrf52_pwm.h new file mode 100644 index 0000000000..90755da8eb --- /dev/null +++ b/arch/arm/src/nrf52/nrf52_pwm.h @@ -0,0 +1,131 @@ +/**************************************************************************** + * arch/arm/src/nrf52/nrf52_pwm.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF52_NRF52_PWM_H +#define __ARCH_ARM_SRC_NRF52_NRF52_PWM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Enable the specified PWM channel if multichannel PWM is disabled */ + +#ifndef CONFIG_NRF52_PWM_MULTICHAN + +# ifdef CONFIG_NRF52_PWM0 +# if !defined(CONFIG_NRF52_PWM0_CHANNEL) +# error "CONFIG_NRF52_PWM0_CHANNEL must be provided" +# elif CONFIG_NRF52_PWM0_CHANNEL == 0 +# define CONFIG_NRF52_PWM0_CH0 1 +# elif CONFIG_NRF52_PWM0_CHANNEL == 1 +# define CONFIG_NRF52_PWM0_CH1 1 +# elif CONFIG_NRF52_PWM0_CHANNEL == 2 +# define CONFIG_NRF52_PWM0_CH2 1 +# elif CONFIG_NRF52_PWM0_CHANNEL == 3 +# define CONFIG_NRF52_PWM0_CH3 1 +# else +# error "Unsupported value of CONFIG_NRF52_PWM0_CHANNEL" +# endif +# endif + +# ifdef CONFIG_NRF52_PWM1 +# if !defined(CONFIG_NRF52_PWM1_CHANNEL) +# error "CONFIG_NRF52_PWM1_CHANNEL must be provided" +# elif CONFIG_NRF52_PWM1_CHANNEL == 0 +# define CONFIG_NRF52_PWM1_CH0 1 +# elif CONFIG_NRF52_PWM1_CHANNEL == 1 +# define CONFIG_NRF52_PWM1_CH1 1 +# elif CONFIG_NRF52_PWM1_CHANNEL == 2 +# define CONFIG_NRF52_PWM1_CH2 1 +# elif CONFIG_NRF52_PWM1_CHANNEL == 3 +# define CONFIG_NRF52_PWM1_CH3 1 +# else +# error "Unsupported value of CONFIG_NRF52_PWM1_CHANNEL" +# endif +# endif + +# ifdef CONFIG_NRF52_PWM2 +# if !defined(CONFIG_NRF52_PWM2_CHANNEL) +# error "CONFIG_NRF52_PWM2_CHANNEL must be provided" +# elif CONFIG_NRF52_PWM2_CHANNEL == 0 +# define CONFIG_NRF52_PWM2_CH0 1 +# elif CONFIG_NRF52_PWM2_CHANNEL == 1 +# define CONFIG_NRF52_PWM2_CH1 1 +# elif CONFIG_NRF52_PWM2_CHANNEL == 2 +# define CONFIG_NRF52_PWM2_CH2 1 +# elif CONFIG_NRF52_PWM2_CHANNEL == 3 +# define CONFIG_NRF52_PWM2_CH3 1 +# else +# error "Unsupported value of CONFIG_NRF52_PWM2_CHANNEL" +# endif +# endif + +# ifdef CONFIG_NRF52_PWM3 +# if !defined(CONFIG_NRF52_PWM3_CHANNEL) +# error "CONFIG_NRF52_PWM3_CHANNEL must be provided" +# elif CONFIG_NRF52_PWM3_CHANNEL == 0 +# define CONFIG_NRF52_PWM3_CH0 1 +# elif CONFIG_NRF52_PWM3_CHANNEL == 1 +# define CONFIG_NRF52_PWM3_CH1 1 +# elif CONFIG_NRF52_PWM3_CHANNEL == 2 +# define CONFIG_NRF52_PWM3_CH2 1 +# elif CONFIG_NRF52_PWM3_CHANNEL == 3 +# define CONFIG_NRF52_PWM3_CH3 1 +# else +# error "Unsupported value of CONFIG_NRF52_PWM3_CHANNEL" +# endif +# endif + +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf52_pwminitialize + * + * Description: + * Initialize one timer for use with the upper_level PWM driver. + * + * Input Parameters: + * pwm - A number identifying the pwm instance. + * + * Returned Value: + * On success, a pointer to the NRF52 lower half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct pwm_lowerhalf_s *nrf52_pwminitialize(int pwm); + +#endif /* __ARCH_ARM_SRC_NRF52_NRF52_PWM_H */