diff --git a/Documentation/platforms/arm/ht32f491x3/boards/esk32/index.rst b/Documentation/platforms/arm/ht32f491x3/boards/esk32/index.rst index fbf7f2fa1f6cc..e48509635c052 100644 --- a/Documentation/platforms/arm/ht32f491x3/boards/esk32/index.rst +++ b/Documentation/platforms/arm/ht32f491x3/boards/esk32/index.rst @@ -7,7 +7,7 @@ ESK32 (HT32F49163) The ESK32 is a development board based on the Holtek HT32F49163 MCU. The current NuttX port targets the HT32F49163 device used on the HT32F49163 development kit and focuses on a working serial-console NSH -configuration with basic board bring-up. +configuration with basic board bring-up plus timer-based PWM validation. For additional hardware details, refer to Holtek's `HT32F491x3 Starter Kit User Guide `_. @@ -28,6 +28,7 @@ The current port provides: * Boot and clock initialization for the ESK32 8 MHz external crystal * System clock configured to 150 MHz * USART1 serial console at 115200 8N1 +* TMR3 PWM outputs exposed through ``/dev/pwm0`` .. ``/dev/pwm3`` * ``/bin`` mounted through ``binfs`` * ``/proc`` mounted through ``procfs`` * User LED registration through ``/dev/userleds`` @@ -41,6 +42,10 @@ applications: * ``dumpstack`` * ``leds`` +The ``esk32:pwm`` configuration keeps the same board bring-up and adds the +``pwm`` example application together with a board-level PWM device at +``/dev/pwm0`` .. ``/dev/pwm3``. + Buttons and LEDs ================ @@ -75,8 +80,12 @@ The current port uses the following MCU pins: ===== ========== ========== Pin Signal Notes ===== ========== ========== +PA6 TMR3_CH1 ``/dev/pwm0`` output in ``esk32:pwm`` +PA7 TMR3_CH2 ``/dev/pwm1`` output in ``esk32:pwm`` PA9 USART1_TX Default serial console TX PA10 USART1_RX Default serial console RX +PB0 TMR3_CH3 ``/dev/pwm2`` output in ``esk32:pwm`` +PB1 TMR3_CH4 ``/dev/pwm3`` output in ``esk32:pwm`` PD13 LED2 User LED, active-low PD14 LED3 User LED, active-low PD15 LED4 User LED, active-low @@ -124,16 +133,72 @@ The following commands are useful for validating the current port: When ``leds`` is executed, the example opens ``/dev/userleds`` and cycles through the LED bitmasks supported by the board. -Current Limitations -=================== +The ``esk32:pwm`` configuration also exposes ``/dev/pwm0`` through TMR3. The +default defconfig selects ``CONFIG_HT32F491X3_TMR3_CHANNEL=1``, so the PWM +signal is routed to ``PA6``. Probe ``PA6`` against board ``GND`` and run: + +.. code-block:: console + + nsh> pwm -f 1000 -d 50 -t 5 -The current port is still intentionally small. In particular: +This command starts a 1 kHz PWM waveform with a 50% duty cycle for 5 seconds. +On the default channel that corresponds to a 1 ms period with a 500 us high +pulse on ``PA6``. A typical console log is: -* only the ``nsh`` board configuration is maintained -* only USART1 routing is described by the board port -* LEDs are supported, but board buttons are not yet implemented -* internal GPIO helpers exist, but there is not yet a board-level ``/dev/gpio`` - test interface in this port +.. code-block:: console + + nsh> pwm -f 1000 -d 50 -t 5 + pwm_main: starting output with frequency: 1000 duty: 00007fff + pwm_main: stopping output + +Peripheral Support +================== + ++---------------------+---------+-------------------------------------+ +| Peripheral | Support | Notes | ++=====================+=========+=====================================+ +| Boot / Clock / IRQ | Yes | Board start-up, clock tree and tick | ++---------------------+---------+-------------------------------------+ +| UART | Yes | USART1 console | ++---------------------+---------+-------------------------------------+ +| GPIO | Partial | Internal helpers only | ++---------------------+---------+-------------------------------------+ +| LEDs | Yes | ``USERLED`` and ``/dev/userleds`` | ++---------------------+---------+-------------------------------------+ +| Buttons | No | | ++---------------------+---------+-------------------------------------+ +| PWM | Yes | TMR3 exposed as ``/dev/pwm0`` | ++---------------------+---------+-------------------------------------+ +| Pulse Counter | No | | ++---------------------+---------+-------------------------------------+ +| Timers | Partial | OS tick and TMR3 PWM | ++---------------------+---------+-------------------------------------+ +| SPI | No | | ++---------------------+---------+-------------------------------------+ +| I2C | No | | ++---------------------+---------+-------------------------------------+ +| ADC | No | | ++---------------------+---------+-------------------------------------+ +| DAC | No | | ++---------------------+---------+-------------------------------------+ +| CAN | No | | ++---------------------+---------+-------------------------------------+ +| DMA | No | | ++---------------------+---------+-------------------------------------+ +| RTC / ERTC | No | | ++---------------------+---------+-------------------------------------+ +| I2S | No | | ++---------------------+---------+-------------------------------------+ +| Watchdog | No | | ++---------------------+---------+-------------------------------------+ +| USB Device | No | | ++---------------------+---------+-------------------------------------+ +| USB Host | No | | ++---------------------+---------+-------------------------------------+ +| External Interrupts | No | | ++---------------------+---------+-------------------------------------+ +| Power Control | No | | ++---------------------+---------+-------------------------------------+ Configurations ============== @@ -175,3 +240,46 @@ And the built-in applications can be listed with: nsh ostest sh + +pwm +--- + +This configuration enables the generic PWM framework, registers the TMR3 +lower-half driver as ``/dev/pwm0`` .. ``/dev/pwm3``, and includes the ``pwm`` example +application for board-level validation. + +Configure and build it from the ``nuttx`` directory: + +.. code-block:: console + + $ ./tools/configure.sh -l esk32:pwm + $ make olddefconfig + $ make -j + +After flashing the image, the following command can be used from NSH to +validate PWM generation on ``/dev/pwm0``: + +.. code-block:: console + + nsh> pwm -f 1000 -d 50 -t 5 + +The expected result is a 1 kHz, 50% duty-cycle waveform on ``PA6`` for 5 +seconds, followed by the application stopping the output. A typical console +log is: + +.. code-block:: console + + nsh> pwm -f 1000 -d 50 -t 5 + pwm_main: starting output with frequency: 1000 duty: 00007fff + pwm_main: stopping output + +The additional TMR3 outputs are also registered: + +* ``/dev/pwm0``: ``PA6`` / TMR3_CH1 +* ``/dev/pwm1``: ``PA7`` / TMR3_CH2 +* ``/dev/pwm2``: ``PB0`` / TMR3_CH3 +* ``/dev/pwm3``: ``PB1`` / TMR3_CH4 + +All four device nodes share the same underlying TMR3 instance, so they must +run at the same PWM frequency. Starting one channel at a different frequency +while another channel is active returns ``-EBUSY``. diff --git a/arch/arm/src/ht32f491x3/CMakeLists.txt b/arch/arm/src/ht32f491x3/CMakeLists.txt index 1231e5546bdf4..266312b633cbf 100644 --- a/arch/arm/src/ht32f491x3/CMakeLists.txt +++ b/arch/arm/src/ht32f491x3/CMakeLists.txt @@ -30,6 +30,10 @@ list( ht32f491x3_lowputc.c ht32f491x3_serial.c) +if(CONFIG_PWM) + list(APPEND SRCS ht32f491x3_pwm.c) +endif() + if(CONFIG_ARCH_HAVE_CUSTOM_VECTORS) list(APPEND SRCS arm_vectors.c) endif() diff --git a/arch/arm/src/ht32f491x3/Kconfig b/arch/arm/src/ht32f491x3/Kconfig index 3a32d32146a23..edc081eff6cf7 100644 --- a/arch/arm/src/ht32f491x3/Kconfig +++ b/arch/arm/src/ht32f491x3/Kconfig @@ -54,6 +54,236 @@ config HT32F491X3_PCLK2_FREQUENCY menu "Peripheral Support" +config HT32F491X3_TMR1 + bool "TMR1" + default n + ---help--- + Enable support for the TMR1 peripheral. + +config HT32F491X3_TMR1_PWM + bool "TMR1 PWM output" + default n + depends on HT32F491X3_TMR1 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR1. + +config HT32F491X3_TMR1_CHANNEL + int "TMR1 PWM channel" + default 1 + range 1 4 + depends on HT32F491X3_TMR1_PWM + ---help--- + Select which TMR1 channel is exposed through the lower-half PWM + driver. + +config HT32F491X3_TMR2 + bool "TMR2" + default n + ---help--- + Enable support for the TMR2 peripheral. + +config HT32F491X3_TMR2_PWM + bool "TMR2 PWM output" + default n + depends on HT32F491X3_TMR2 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR2. + +config HT32F491X3_TMR2_CHANNEL + int "TMR2 PWM channel" + default 1 + range 1 4 + depends on HT32F491X3_TMR2_PWM + ---help--- + Select which TMR2 channel is exposed through the lower-half PWM + driver. + +config HT32F491X3_TMR3 + bool "TMR3" + default n + ---help--- + Enable support for the TMR3 peripheral. + +config HT32F491X3_TMR3_PWM + bool "TMR3 PWM output" + default n + depends on HT32F491X3_TMR3 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR3. + +config HT32F491X3_TMR3_CHANNEL + int "TMR3 PWM channel" + default 1 + range 1 4 + depends on HT32F491X3_TMR3_PWM + ---help--- + Select which TMR3 channel is exposed through the lower-half PWM + driver. + +config HT32F491X3_TMR4 + bool "TMR4" + default n + ---help--- + Enable support for the TMR4 peripheral. + +config HT32F491X3_TMR4_PWM + bool "TMR4 PWM output" + default n + depends on HT32F491X3_TMR4 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR4. + +config HT32F491X3_TMR4_CHANNEL + int "TMR4 PWM channel" + default 1 + range 1 4 + depends on HT32F491X3_TMR4_PWM + ---help--- + Select which TMR4 channel is exposed through the lower-half PWM + driver. + +config HT32F491X3_TMR6 + bool "TMR6" + default n + ---help--- + Enable support for the TMR6 peripheral. TMR6 is a basic timer and + does not provide PWM output channels. + +config HT32F491X3_TMR7 + bool "TMR7" + default n + ---help--- + Enable support for the TMR7 peripheral. TMR7 is a basic timer and + does not provide PWM output channels. + +config HT32F491X3_TMR9 + bool "TMR9" + default n + ---help--- + Enable support for the TMR9 peripheral. + +config HT32F491X3_TMR9_PWM + bool "TMR9 PWM output" + default n + depends on HT32F491X3_TMR9 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR9. + +config HT32F491X3_TMR9_CHANNEL + int "TMR9 PWM channel" + default 1 + range 1 2 + depends on HT32F491X3_TMR9_PWM + ---help--- + Select which TMR9 channel is exposed through the lower-half PWM + driver. + +config HT32F491X3_TMR10 + bool "TMR10" + default n + ---help--- + Enable support for the TMR10 peripheral. + +config HT32F491X3_TMR10_PWM + bool "TMR10 PWM output" + default n + depends on HT32F491X3_TMR10 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR10. + +config HT32F491X3_TMR10_CHANNEL + int "TMR10 PWM channel" + default 1 + range 1 1 + depends on HT32F491X3_TMR10_PWM + ---help--- + TMR10 exposes a single PWM channel. + +config HT32F491X3_TMR11 + bool "TMR11" + default n + ---help--- + Enable support for the TMR11 peripheral. + +config HT32F491X3_TMR11_PWM + bool "TMR11 PWM output" + default n + depends on HT32F491X3_TMR11 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR11. + +config HT32F491X3_TMR11_CHANNEL + int "TMR11 PWM channel" + default 1 + range 1 1 + depends on HT32F491X3_TMR11_PWM + ---help--- + TMR11 exposes a single PWM channel. + +config HT32F491X3_TMR12 + bool "TMR12" + default n + ---help--- + Enable support for the TMR12 peripheral. + +config HT32F491X3_TMR12_PWM + bool "TMR12 PWM output" + default n + depends on HT32F491X3_TMR12 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR12. + +config HT32F491X3_TMR12_CHANNEL + int "TMR12 PWM channel" + default 1 + range 1 2 + depends on HT32F491X3_TMR12_PWM + ---help--- + Select which TMR12 channel is exposed through the lower-half PWM + driver. + +config HT32F491X3_TMR13 + bool "TMR13" + default n + ---help--- + Enable support for the TMR13 peripheral. + +config HT32F491X3_TMR13_PWM + bool "TMR13 PWM output" + default n + depends on HT32F491X3_TMR13 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR13. + +config HT32F491X3_TMR13_CHANNEL + int "TMR13 PWM channel" + default 1 + range 1 1 + depends on HT32F491X3_TMR13_PWM + ---help--- + TMR13 exposes a single PWM channel. + +config HT32F491X3_TMR14 + bool "TMR14" + default n + ---help--- + Enable support for the TMR14 peripheral. + +config HT32F491X3_TMR14_PWM + bool "TMR14 PWM output" + default n + depends on HT32F491X3_TMR14 && PWM && !PWM_MULTICHAN + ---help--- + Enable lower-half PWM support on TMR14. + +config HT32F491X3_TMR14_CHANNEL + int "TMR14 PWM channel" + default 1 + range 1 1 + depends on HT32F491X3_TMR14_PWM + ---help--- + TMR14 exposes a single PWM channel. + config HT32F491X3_USART1_SERIALDRIVER bool "USART1" default y @@ -62,6 +292,10 @@ config HT32F491X3_USART1_SERIALDRIVER ---help--- Enable the standard lower-half driver for USART1. -comment "In this current version, only USART1 is exposed by the esk32 board support." +comment "In this current version, USART1 is the only serial peripheral exposed by the esk32 board support." + +comment "HT32F491x3 PWM-capable timers are TMR1-4 and TMR9-14; TMR6-7 are basic timers." + +comment "esk32 routes TMR3 channels PA6/PA7/PB0/PB1 to /dev/pwm0." endmenu # "Peripheral Support" diff --git a/arch/arm/src/ht32f491x3/Make.defs b/arch/arm/src/ht32f491x3/Make.defs index e8fee89b83f77..97186cee4c05d 100644 --- a/arch/arm/src/ht32f491x3/Make.defs +++ b/arch/arm/src/ht32f491x3/Make.defs @@ -29,6 +29,10 @@ CHIP_CSRCS += ht32f491x3_timerisr.c ht32f491x3_gpio.c CHIP_CSRCS += ht32f491x3_lowputc.c CHIP_CSRCS += ht32f491x3_serial.c +ifeq ($(CONFIG_PWM),y) +CHIP_CSRCS += ht32f491x3_pwm.c +endif + ifeq ($(CONFIG_ARCH_HAVE_CUSTOM_VECTORS),y) CHIP_CSRCS += arm_vectors.c endif diff --git a/arch/arm/src/ht32f491x3/hardware/ht32f491x3_crm.h b/arch/arm/src/ht32f491x3/hardware/ht32f491x3_crm.h index 2d0387e2778b5..010298860f627 100644 --- a/arch/arm/src/ht32f491x3/hardware/ht32f491x3_crm.h +++ b/arch/arm/src/ht32f491x3/hardware/ht32f491x3_crm.h @@ -189,10 +189,22 @@ /* Peripheral reset registers ***********************************************/ -#define HT32_CRM_APB1RST_USART2RST (1 << 17) -#define HT32_CRM_APB1RST_USART3RST (1 << 18) - -#define HT32_CRM_APB2RST_USART1RST (1 << 4) +#define HT32_CRM_APB1RST_TMR2RST (1 << 0) +#define HT32_CRM_APB1RST_TMR3RST (1 << 1) +#define HT32_CRM_APB1RST_TMR4RST (1 << 2) +#define HT32_CRM_APB1RST_TMR6RST (1 << 4) +#define HT32_CRM_APB1RST_TMR7RST (1 << 5) +#define HT32_CRM_APB1RST_TMR12RST (1 << 6) +#define HT32_CRM_APB1RST_TMR13RST (1 << 7) +#define HT32_CRM_APB1RST_TMR14RST (1 << 8) +#define HT32_CRM_APB1RST_USART2RST (1 << 17) +#define HT32_CRM_APB1RST_USART3RST (1 << 18) + +#define HT32_CRM_APB2RST_TMR1RST (1 << 0) +#define HT32_CRM_APB2RST_USART1RST (1 << 4) +#define HT32_CRM_APB2RST_TMR9RST (1 << 16) +#define HT32_CRM_APB2RST_TMR10RST (1 << 17) +#define HT32_CRM_APB2RST_TMR11RST (1 << 18) /* Clock enable registers ***************************************************/ diff --git a/arch/arm/src/ht32f491x3/hardware/ht32f491x3_tmr.h b/arch/arm/src/ht32f491x3/hardware/ht32f491x3_tmr.h new file mode 100644 index 0000000000000..451aee1392148 --- /dev/null +++ b/arch/arm/src/ht32f491x3/hardware/ht32f491x3_tmr.h @@ -0,0 +1,99 @@ +/**************************************************************************** + * arch/arm/src/ht32f491x3/hardware/ht32f491x3_tmr.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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_HT32F491X3_HARDWARE_HT32F491X3_TMR_H +#define __ARCH_ARM_SRC_HT32F491X3_HARDWARE_HT32F491X3_TMR_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define HT32_TMR_CTRL1_OFFSET 0x0000 +#define HT32_TMR_CTRL2_OFFSET 0x0004 +#define HT32_TMR_SWEVT_OFFSET 0x0014 +#define HT32_TMR_CM1_OFFSET 0x0018 +#define HT32_TMR_CM2_OFFSET 0x001c +#define HT32_TMR_CCTRL_OFFSET 0x0020 +#define HT32_TMR_CVAL_OFFSET 0x0024 +#define HT32_TMR_DIV_OFFSET 0x0028 +#define HT32_TMR_PR_OFFSET 0x002c +#define HT32_TMR_RPR_OFFSET 0x0030 +#define HT32_TMR_C1DT_OFFSET 0x0034 +#define HT32_TMR_C2DT_OFFSET 0x0038 +#define HT32_TMR_C3DT_OFFSET 0x003c +#define HT32_TMR_C4DT_OFFSET 0x0040 +#define HT32_TMR_BRK_OFFSET 0x0044 + +/* Control register 1 *******************************************************/ + +#define HT32_TMR_CTRL1_TMREN (1u << 0) +#define HT32_TMR_CTRL1_CNTDIR_SHIFT 4 +#define HT32_TMR_CTRL1_CNTDIR_MASK (7u << HT32_TMR_CTRL1_CNTDIR_SHIFT) +#define HT32_TMR_CTRL1_COUNTUP (0u << HT32_TMR_CTRL1_CNTDIR_SHIFT) +#define HT32_TMR_CTRL1_PRBEN (1u << 7) + +/* Event generation register ************************************************/ + +#define HT32_TMR_SWEVT_OVFSWTR (1u << 0) + +/* Compare mode register helpers ********************************************/ + +#define HT32_TMR_CM_CAPTURE_SEL_SHIFT(slot) ((slot) * 8) +#define HT32_TMR_CM_CAPTURE_SEL_MASK(slot) (3u << \ + HT32_TMR_CM_CAPTURE_SEL_SHIFT(slot)) +#define HT32_TMR_CM_OUTPUT_BUFFER_SHIFT(slot) ((slot) * 8 + 3) +#define HT32_TMR_CM_OUTPUT_BUFFER(slot) (1u << \ + HT32_TMR_CM_OUTPUT_BUFFER_SHIFT(slot)) +#define HT32_TMR_CM_OUTPUT_MODE_SHIFT(slot) ((slot) * 8 + 4) +#define HT32_TMR_CM_OUTPUT_MODE_MASK(slot) (7u << \ + HT32_TMR_CM_OUTPUT_MODE_SHIFT(slot)) +#define HT32_TMR_CM_OUTPUT_MODE(slot, mode) ((uint32_t)(mode) << \ + HT32_TMR_CM_OUTPUT_MODE_SHIFT(slot)) + +/* Capture compare control register helpers *********************************/ + +#define HT32_TMR_CCTRL_EN_SHIFT(ch) (((ch) - 1u) * 4u) +#define HT32_TMR_CCTRL_EN(ch) (1u << HT32_TMR_CCTRL_EN_SHIFT(ch)) +#define HT32_TMR_CCTRL_POL_SHIFT(ch) (HT32_TMR_CCTRL_EN_SHIFT(ch) + 1u) +#define HT32_TMR_CCTRL_POL(ch) (1u << HT32_TMR_CCTRL_POL_SHIFT(ch)) + +/* Brake register ***********************************************************/ + +#define HT32_TMR_BRK_OEN (1u << 15) + +/* Output compare mode values ***********************************************/ + +#define HT32_TMR_OUTPUT_CONTROL_PWM_A 6u + +/* Helper macros ************************************************************/ + +#define HT32_TMR_CCR_OFFSET(ch) (HT32_TMR_C1DT_OFFSET + (((ch) - 1u) * 4u)) + +#endif /* __ARCH_ARM_SRC_HT32F491X3_HARDWARE_HT32F491X3_TMR_H */ diff --git a/arch/arm/src/ht32f491x3/ht32f491x3_pwm.c b/arch/arm/src/ht32f491x3/ht32f491x3_pwm.c new file mode 100644 index 0000000000000..f87a0638174f6 --- /dev/null +++ b/arch/arm/src/ht32f491x3/ht32f491x3_pwm.c @@ -0,0 +1,560 @@ +/**************************************************************************** + * arch/arm/src/ht32f491x3/ht32f491x3_pwm.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 + +#include + +#include "arm_internal.h" +#include "chip.h" +#include "ht32f491x3_gpio.h" +#include "ht32f491x3_pwm.h" + +#include "hardware/ht32f491x3_crm.h" +#include "hardware/ht32f491x3_tmr.h" + +#if defined(CONFIG_HT32F491X3_TMR1_PWM) || defined(CONFIG_HT32F491X3_TMR2_PWM) || \ + defined(CONFIG_HT32F491X3_TMR3_PWM) || defined(CONFIG_HT32F491X3_TMR4_PWM) || \ + defined(CONFIG_HT32F491X3_TMR9_PWM) || defined(CONFIG_HT32F491X3_TMR10_PWM) || \ + defined(CONFIG_HT32F491X3_TMR11_PWM) || defined(CONFIG_HT32F491X3_TMR12_PWM) || \ + defined(CONFIG_HT32F491X3_TMR13_PWM) || defined(CONFIG_HT32F491X3_TMR14_PWM) +# define HAVE_HT32F491X3_PWM 1 +#endif + +#if defined(CONFIG_PWM) && defined(HAVE_HT32F491X3_PWM) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ht32f491x3_pwmcfg_s +{ + uint8_t timid; + uint8_t maxchannel; + bool advanced; + uintptr_t base; + uintptr_t clkreg; + uint32_t clkbit; + uintptr_t rstreg; + uint32_t rstbit; +}; + +struct ht32f491x3_pwmtimer_s +{ + const struct pwm_ops_s *ops; + FAR const struct ht32f491x3_pwmcfg_s *cfg; + uint32_t gpio_clken; + uintptr_t gpio_base; + uint8_t channel; + uint8_t pin; + uint8_t af; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int pwm_setup(struct pwm_lowerhalf_s *dev); +static int pwm_shutdown(struct pwm_lowerhalf_s *dev); +static int pwm_start(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info); +static int pwm_stop(struct pwm_lowerhalf_s *dev); +static int pwm_ioctl(struct pwm_lowerhalf_s *dev, + int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct pwm_ops_s g_pwmops = +{ + .setup = pwm_setup, + .shutdown = pwm_shutdown, + .start = pwm_start, + .stop = pwm_stop, + .ioctl = pwm_ioctl, +}; + +/* TMR6 and TMR7 are basic timers and do not provide PWM output channels. */ + +#define HT32_PWM_DEFINE(_timid, _maxchannel, _advanced, _base, _clkreg, \ + _clkbit, _rstreg, _rstbit) \ + static const struct ht32f491x3_pwmcfg_s g_pwm##_timid##cfg = \ + { \ + .timid = _timid, \ + .maxchannel = _maxchannel, \ + .advanced = _advanced, \ + .base = _base, \ + .clkreg = _clkreg, \ + .clkbit = _clkbit, \ + .rstreg = _rstreg, \ + .rstbit = _rstbit, \ + }; \ + \ + static struct ht32f491x3_pwmtimer_s g_pwm##_timid##dev = \ + { \ + .ops = &g_pwmops, \ + .cfg = &g_pwm##_timid##cfg, \ + .channel = CONFIG_HT32F491X3_TMR##_timid##_CHANNEL, \ + .gpio_clken = BOARD_TMR##_timid##_PWM_GPIO_CLKEN, \ + .gpio_base = BOARD_TMR##_timid##_PWM_GPIO_BASE, \ + .pin = BOARD_TMR##_timid##_PWM_GPIO_PIN, \ + .af = BOARD_TMR##_timid##_PWM_GPIO_AF, \ + } + +#ifdef CONFIG_HT32F491X3_TMR1_PWM +HT32_PWM_DEFINE(1, 4, true, HT32_TMR1_BASE, HT32_CRM_APB2EN, + HT32_CRM_APB2EN_TMR1EN, HT32_CRM_APB2RST, + HT32_CRM_APB2RST_TMR1RST); +#endif + +#ifdef CONFIG_HT32F491X3_TMR2_PWM +HT32_PWM_DEFINE(2, 4, false, HT32_TMR2_BASE, HT32_CRM_APB1EN, + HT32_CRM_APB1EN_TMR2EN, HT32_CRM_APB1RST, + HT32_CRM_APB1RST_TMR2RST); +#endif + +#ifdef CONFIG_HT32F491X3_TMR3_PWM +HT32_PWM_DEFINE(3, 4, false, HT32_TMR3_BASE, HT32_CRM_APB1EN, + HT32_CRM_APB1EN_TMR3EN, HT32_CRM_APB1RST, + HT32_CRM_APB1RST_TMR3RST); +#endif + +#ifdef CONFIG_HT32F491X3_TMR4_PWM +HT32_PWM_DEFINE(4, 4, false, HT32_TMR4_BASE, HT32_CRM_APB1EN, + HT32_CRM_APB1EN_TMR4EN, HT32_CRM_APB1RST, + HT32_CRM_APB1RST_TMR4RST); +#endif + +#ifdef CONFIG_HT32F491X3_TMR9_PWM +HT32_PWM_DEFINE(9, 2, false, HT32_TMR9_BASE, HT32_CRM_APB2EN, + HT32_CRM_APB2EN_TMR9EN, HT32_CRM_APB2RST, + HT32_CRM_APB2RST_TMR9RST); +#endif + +#ifdef CONFIG_HT32F491X3_TMR10_PWM +HT32_PWM_DEFINE(10, 1, false, HT32_TMR10_BASE, HT32_CRM_APB2EN, + HT32_CRM_APB2EN_TMR10EN, HT32_CRM_APB2RST, + HT32_CRM_APB2RST_TMR10RST); +#endif + +#ifdef CONFIG_HT32F491X3_TMR11_PWM +HT32_PWM_DEFINE(11, 1, false, HT32_TMR11_BASE, HT32_CRM_APB2EN, + HT32_CRM_APB2EN_TMR11EN, HT32_CRM_APB2RST, + HT32_CRM_APB2RST_TMR11RST); +#endif + +#ifdef CONFIG_HT32F491X3_TMR12_PWM +HT32_PWM_DEFINE(12, 2, false, HT32_TMR12_BASE, HT32_CRM_APB1EN, + HT32_CRM_APB1EN_TMR12EN, HT32_CRM_APB1RST, + HT32_CRM_APB1RST_TMR12RST); +#endif + +#ifdef CONFIG_HT32F491X3_TMR13_PWM +HT32_PWM_DEFINE(13, 1, false, HT32_TMR13_BASE, HT32_CRM_APB1EN, + HT32_CRM_APB1EN_TMR13EN, HT32_CRM_APB1RST, + HT32_CRM_APB1RST_TMR13RST); +#endif + +#ifdef CONFIG_HT32F491X3_TMR14_PWM +HT32_PWM_DEFINE(14, 1, false, HT32_TMR14_BASE, HT32_CRM_APB1EN, + HT32_CRM_APB1EN_TMR14EN, HT32_CRM_APB1RST, + HT32_CRM_APB1RST_TMR14RST); +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline uint32_t pwm_getreg(FAR struct ht32f491x3_pwmtimer_s *priv, + unsigned int offset) +{ + DEBUGASSERT(priv != NULL && priv->cfg != NULL); + return getreg32(priv->cfg->base + offset); +} + +static inline void pwm_putreg(FAR struct ht32f491x3_pwmtimer_s *priv, + unsigned int offset, uint32_t value) +{ + DEBUGASSERT(priv != NULL && priv->cfg != NULL); + putreg32(value, priv->cfg->base + offset); +} + +static inline unsigned int pwm_cm_offset(uint8_t channel) +{ + return channel <= 2 ? HT32_TMR_CM1_OFFSET : HT32_TMR_CM2_OFFSET; +} + +static inline unsigned int pwm_cm_slot(uint8_t channel) +{ + return (channel - 1u) & 1u; +} + +static inline uint32_t pwm_cm_mask(uint8_t channel) +{ + unsigned int slot = pwm_cm_slot(channel); + + return HT32_TMR_CM_CAPTURE_SEL_MASK(slot) | + HT32_TMR_CM_OUTPUT_BUFFER(slot) | + HT32_TMR_CM_OUTPUT_MODE_MASK(slot); +} + +static inline uint32_t pwm_cm_value(uint8_t channel) +{ + unsigned int slot = pwm_cm_slot(channel); + + return HT32_TMR_CM_OUTPUT_BUFFER(slot) | + HT32_TMR_CM_OUTPUT_MODE(slot, HT32_TMR_OUTPUT_CONTROL_PWM_A); +} + +static void ht32f491x3_pwm_enableclk(FAR struct ht32f491x3_pwmtimer_s *priv, + bool enable) +{ + DEBUGASSERT(priv != NULL && priv->cfg != NULL); + + modifyreg32(priv->cfg->clkreg, + priv->cfg->clkbit, + enable ? priv->cfg->clkbit : 0); +} + +static void ht32f491x3_pwm_reset(FAR struct ht32f491x3_pwmtimer_s *priv) +{ + DEBUGASSERT(priv != NULL && priv->cfg != NULL); + + modifyreg32(priv->cfg->rstreg, 0, priv->cfg->rstbit); + modifyreg32(priv->cfg->rstreg, priv->cfg->rstbit, 0); +} + +static void ht32f491x3_pwm_gpioconfig(FAR struct ht32f491x3_pwmtimer_s *priv, + bool enable) +{ + modifyreg32(HT32_CRM_AHBEN1, 0, priv->gpio_clken); + + ht32f491x3_gpioconfig(priv->gpio_base, priv->pin, + enable ? HT32_GPIO_MODE_ALTFN : + HT32_GPIO_MODE_INPUT, + false, + enable ? HT32_GPIO_DRIVE_HIGH : + HT32_GPIO_DRIVE_LOW, + HT32_GPIO_PULL_NONE, + enable ? priv->af : 0); +} + +static uint32_t ht32f491x3_pwm_timclk(FAR struct ht32f491x3_pwmtimer_s *priv) +{ + uint32_t regval = getreg32(HT32_CRM_CFG); + uint32_t pclk; + bool doubled; + + DEBUGASSERT(priv != NULL && priv->cfg != NULL); + + if (priv->cfg->clkreg == HT32_CRM_APB2EN) + { + pclk = CONFIG_HT32F491X3_PCLK2_FREQUENCY; + doubled = (regval & HT32_CRM_CFG_APB2DIV_MASK) != + HT32_CRM_CFG_APB2DIV_1; + } + else + { + pclk = CONFIG_HT32F491X3_PCLK1_FREQUENCY; + doubled = (regval & HT32_CRM_CFG_APB1DIV_MASK) != + HT32_CRM_CFG_APB1DIV_1; + } + + return doubled ? pclk * 2u : pclk; +} + +static int pwm_timer(FAR struct ht32f491x3_pwmtimer_s *priv, + FAR const struct pwm_info_s *info) +{ + uint64_t timclk; + uint64_t cycles; + uint32_t prescaler; + uint32_t reload; + uint32_t pulse; + uint32_t regval; + + DEBUGASSERT(priv != NULL && info != NULL); + + if (info->frequency == 0) + { + return -EINVAL; + } + + timclk = ht32f491x3_pwm_timclk(priv); + cycles = (timclk + (info->frequency / 2u)) / info->frequency; + + if (cycles == 0) + { + cycles = 1; + } + + prescaler = (cycles + 65535u) / 65536u; + if (prescaler == 0) + { + prescaler = 1; + } + + if (prescaler > 65536u) + { + pwmerr("ERROR: PWM frequency %" PRIu32 "Hz is out of range\n", + info->frequency); + return -ERANGE; + } + + reload = (uint32_t)((cycles + (prescaler / 2u)) / prescaler); + if (reload < 2u) + { + reload = 2u; + } + else if (reload > 65536u) + { + reload = 65536u; + } + + reload -= 1u; + pulse = (uint32_t)(((uint64_t)reload * info->duty + 0x8000ull) >> 16); + if (pulse > reload) + { + pulse = reload; + } + + ht32f491x3_pwm_enableclk(priv, true); + ht32f491x3_pwm_reset(priv); + ht32f491x3_pwm_gpioconfig(priv, true); + + pwm_putreg(priv, HT32_TMR_CTRL1_OFFSET, HT32_TMR_CTRL1_COUNTUP); + pwm_putreg(priv, HT32_TMR_CTRL2_OFFSET, 0); + pwm_putreg(priv, HT32_TMR_CCTRL_OFFSET, 0); + pwm_putreg(priv, HT32_TMR_CVAL_OFFSET, 0); + pwm_putreg(priv, HT32_TMR_DIV_OFFSET, prescaler - 1u); + pwm_putreg(priv, HT32_TMR_PR_OFFSET, reload); + pwm_putreg(priv, HT32_TMR_CCR_OFFSET(priv->channel), pulse); + pwm_putreg(priv, HT32_TMR_SWEVT_OFFSET, HT32_TMR_SWEVT_OVFSWTR); + + regval = pwm_getreg(priv, pwm_cm_offset(priv->channel)); + regval &= ~pwm_cm_mask(priv->channel); + regval |= pwm_cm_value(priv->channel); + pwm_putreg(priv, pwm_cm_offset(priv->channel), regval); + + regval = pwm_getreg(priv, HT32_TMR_CCTRL_OFFSET); + regval &= ~(HT32_TMR_CCTRL_EN(priv->channel) | + HT32_TMR_CCTRL_POL(priv->channel)); + regval |= HT32_TMR_CCTRL_EN(priv->channel); + pwm_putreg(priv, HT32_TMR_CCTRL_OFFSET, regval); + + if (priv->cfg->advanced) + { + modifyreg32(priv->cfg->base + HT32_TMR_BRK_OFFSET, 0, + HT32_TMR_BRK_OEN); + } + + modifyreg32(priv->cfg->base + HT32_TMR_CTRL1_OFFSET, 0, + HT32_TMR_CTRL1_PRBEN | HT32_TMR_CTRL1_TMREN); + + return OK; +} + +/**************************************************************************** + * PWM driver methods + ****************************************************************************/ + +static int pwm_setup(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct ht32f491x3_pwmtimer_s *priv = + (FAR struct ht32f491x3_pwmtimer_s *)dev; + + ht32f491x3_pwm_enableclk(priv, true); + ht32f491x3_pwm_gpioconfig(priv, true); + return OK; +} + +static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct ht32f491x3_pwmtimer_s *priv = + (FAR struct ht32f491x3_pwmtimer_s *)dev; + + pwm_stop(dev); + ht32f491x3_pwm_gpioconfig(priv, false); + ht32f491x3_pwm_enableclk(priv, false); + return OK; +} + +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info) +{ + FAR struct ht32f491x3_pwmtimer_s *priv = + (FAR struct ht32f491x3_pwmtimer_s *)dev; + + return pwm_timer(priv, info); +} + +static int pwm_stop(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct ht32f491x3_pwmtimer_s *priv = + (FAR struct ht32f491x3_pwmtimer_s *)dev; + irqstate_t flags; + + flags = enter_critical_section(); + modifyreg32(priv->cfg->base + HT32_TMR_CTRL1_OFFSET, + HT32_TMR_CTRL1_TMREN, 0); + modifyreg32(priv->cfg->base + HT32_TMR_CCTRL_OFFSET, + HT32_TMR_CCTRL_EN(priv->channel), 0); + leave_critical_section(flags); + + return OK; +} + +static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, + int cmd, unsigned long arg) +{ + (void)dev; + (void)cmd; + (void)arg; + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +struct pwm_lowerhalf_s *ht32f491x3_pwminitialize(int timer) +{ + FAR struct ht32f491x3_pwmtimer_s *lower = NULL; + + pwminfo("Initialize PWM timer %d\n", timer); + + switch (timer) + { +#ifdef CONFIG_HT32F491X3_TMR1_PWM + case 1: + { + lower = &g_pwm1dev; + break; + } +#endif + +#ifdef CONFIG_HT32F491X3_TMR2_PWM + case 2: + { + lower = &g_pwm2dev; + break; + } +#endif + +#ifdef CONFIG_HT32F491X3_TMR3_PWM + case 3: + { + lower = &g_pwm3dev; + break; + } +#endif + +#ifdef CONFIG_HT32F491X3_TMR4_PWM + case 4: + { + lower = &g_pwm4dev; + break; + } +#endif + +#ifdef CONFIG_HT32F491X3_TMR9_PWM + case 9: + { + lower = &g_pwm9dev; + break; + } +#endif + +#ifdef CONFIG_HT32F491X3_TMR10_PWM + case 10: + { + lower = &g_pwm10dev; + break; + } +#endif + +#ifdef CONFIG_HT32F491X3_TMR11_PWM + case 11: + { + lower = &g_pwm11dev; + break; + } +#endif + +#ifdef CONFIG_HT32F491X3_TMR12_PWM + case 12: + { + lower = &g_pwm12dev; + break; + } +#endif + +#ifdef CONFIG_HT32F491X3_TMR13_PWM + case 13: + { + lower = &g_pwm13dev; + break; + } +#endif + +#ifdef CONFIG_HT32F491X3_TMR14_PWM + case 14: + { + lower = &g_pwm14dev; + break; + } +#endif + + default: + { + pwmerr("ERROR: No such PWM timer %d\n", timer); + return NULL; + } + } + + DEBUGASSERT(lower != NULL && lower->cfg != NULL); + + if (lower->channel == 0 || lower->channel > lower->cfg->maxchannel) + { + pwmerr("ERROR: PWM timer %d does not support channel %" PRIu8 "\n", + timer, lower->channel); + return NULL; + } + + return (struct pwm_lowerhalf_s *)lower; +} + +#endif /* CONFIG_PWM && HAVE_HT32F491X3_PWM */ diff --git a/arch/arm/src/ht32f491x3/ht32f491x3_pwm.h b/arch/arm/src/ht32f491x3/ht32f491x3_pwm.h new file mode 100644 index 0000000000000..951b062a0b559 --- /dev/null +++ b/arch/arm/src/ht32f491x3/ht32f491x3_pwm.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * arch/arm/src/ht32f491x3/ht32f491x3_pwm.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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_HT32F491X3_HT32F491X3_PWM_H +#define __ARCH_ARM_SRC_HT32F491X3_HT32F491X3_PWM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ht32f491x3_pwminitialize + * + * Description: + * Initialize one timer for use with the upper-half PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer instance. + * + * Returned Value: + * On success, a pointer to the HT32 lower-half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ + +struct pwm_lowerhalf_s *ht32f491x3_pwminitialize(int timer); + +#endif /* __ARCH_ARM_SRC_HT32F491X3_HT32F491X3_PWM_H */ diff --git a/boards/arm/ht32f491x3/esk32/Kconfig b/boards/arm/ht32f491x3/esk32/Kconfig index 79ce98ccbc2ef..6c9a4b5f99a40 100644 --- a/boards/arm/ht32f491x3/esk32/Kconfig +++ b/boards/arm/ht32f491x3/esk32/Kconfig @@ -5,6 +5,6 @@ if ARCH_BOARD_ESK32 -comment "ESK32 note: only USART1 pin routing is available in the current board port." +comment "ESK32 note: USART1 is routed on the board, and PWM is exposed as /dev/pwm0 through TMR3." endif # ARCH_BOARD_ESK32 diff --git a/boards/arm/ht32f491x3/esk32/configs/pwm/defconfig b/boards/arm/ht32f491x3/esk32/configs/pwm/defconfig new file mode 100644 index 0000000000000..e336de43746b0 --- /dev/null +++ b/boards/arm/ht32f491x3/esk32/configs/pwm/defconfig @@ -0,0 +1,58 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_FPU is not set +# CONFIG_ARCH_LEDS is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="esk32" +CONFIG_ARCH_BOARD_ESK32=y +CONFIG_ARCH_CHIP="ht32f491x3" +CONFIG_ARCH_CHIP_HT32F49163=y +CONFIG_ARCH_CHIP_HT32F491X3=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARD_LOOPSPERMSEC=8499 +CONFIG_BUILTIN=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_EXAMPLES_PWM=y +CONFIG_EXAMPLES_PWM_FREQUENCY=1000 +CONFIG_FS_BINFS=y +CONFIG_FS_PROCFS=y +CONFIG_HT32F491X3_PCLK1_FREQUENCY=75000000 +CONFIG_HT32F491X3_SYSCLK_FREQUENCY=150000000 +CONFIG_HT32F491X3_TMR3=y +CONFIG_HT32F491X3_TMR3_PWM=y +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_LINE_MAX=64 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PWM=y +CONFIG_RAM_SIZE=49152 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_BACKTRACE=y +CONFIG_SCHED_WAITPID=y +CONFIG_START_DAY=29 +CONFIG_START_MONTH=3 +CONFIG_START_YEAR=2026 +CONFIG_SYSTEM_DUMPSTACK=y +CONFIG_SYSTEM_NSH=y +CONFIG_TASK_NAME_SIZE=0 +CONFIG_TESTING_OSTEST=y +CONFIG_TESTING_OSTEST_NBARRIER_THREADS=3 +CONFIG_TESTING_OSTEST_STACKSIZE=2048 +CONFIG_USART1_RXBUFSIZE=256 +CONFIG_USART1_SERIAL_CONSOLE=y +CONFIG_USART1_TXBUFSIZE=256 +CONFIG_USERLED=y +CONFIG_USERLED_LOWER=y diff --git a/boards/arm/ht32f491x3/esk32/include/board.h b/boards/arm/ht32f491x3/esk32/include/board.h index 3712e95600c85..1a5e99c76502c 100644 --- a/boards/arm/ht32f491x3/esk32/include/board.h +++ b/boards/arm/ht32f491x3/esk32/include/board.h @@ -48,6 +48,44 @@ #define BOARD_USART1_TX_AF 7u #define BOARD_USART1_RX_AF 7u +/**************************************************************************** + * Board PWM Pin Mapping + ****************************************************************************/ + +#if defined(CONFIG_HT32F491X3_TMR1_PWM) || defined(CONFIG_HT32F491X3_TMR2_PWM) || \ + defined(CONFIG_HT32F491X3_TMR4_PWM) || defined(CONFIG_HT32F491X3_TMR9_PWM) || \ + defined(CONFIG_HT32F491X3_TMR10_PWM) || defined(CONFIG_HT32F491X3_TMR11_PWM) || \ + defined(CONFIG_HT32F491X3_TMR12_PWM) || defined(CONFIG_HT32F491X3_TMR13_PWM) || \ + defined(CONFIG_HT32F491X3_TMR14_PWM) +# error "esk32 currently exposes PWM only through TMR3" +#endif + +#if defined(CONFIG_HT32F491X3_TMR3_PWM) +# if CONFIG_HT32F491X3_TMR3_CHANNEL == 1 +# define BOARD_TMR3_PWM_GPIO_CLKEN (1u << 0) +# define BOARD_TMR3_PWM_GPIO_BASE 0x40020000u +# define BOARD_TMR3_PWM_GPIO_PIN 6u +# define BOARD_TMR3_PWM_GPIO_AF 2u +# elif CONFIG_HT32F491X3_TMR3_CHANNEL == 2 +# define BOARD_TMR3_PWM_GPIO_CLKEN (1u << 0) +# define BOARD_TMR3_PWM_GPIO_BASE 0x40020000u +# define BOARD_TMR3_PWM_GPIO_PIN 7u +# define BOARD_TMR3_PWM_GPIO_AF 2u +# elif CONFIG_HT32F491X3_TMR3_CHANNEL == 3 +# define BOARD_TMR3_PWM_GPIO_CLKEN (1u << 1) +# define BOARD_TMR3_PWM_GPIO_BASE 0x40020400u +# define BOARD_TMR3_PWM_GPIO_PIN 0u +# define BOARD_TMR3_PWM_GPIO_AF 2u +# elif CONFIG_HT32F491X3_TMR3_CHANNEL == 4 +# define BOARD_TMR3_PWM_GPIO_CLKEN (1u << 1) +# define BOARD_TMR3_PWM_GPIO_BASE 0x40020400u +# define BOARD_TMR3_PWM_GPIO_PIN 1u +# define BOARD_TMR3_PWM_GPIO_AF 2u +# else +# error "Unsupported CONFIG_HT32F491X3_TMR3_CHANNEL value" +# endif +#endif + /**************************************************************************** * Board LED Pin Mapping ****************************************************************************/ @@ -82,6 +120,10 @@ void ht32f491x3_boardinitialize(void); int board_app_initialize(uintptr_t arg); +#ifdef CONFIG_PWM +int ht32_pwm_setup(void); +#endif + #if defined(CONFIG_USERLED) && !defined(CONFIG_ARCH_LEDS) uint32_t board_userled_initialize(void); void board_userled(int led, bool ledon); diff --git a/boards/arm/ht32f491x3/esk32/src/CMakeLists.txt b/boards/arm/ht32f491x3/esk32/src/CMakeLists.txt index 61475bb974c0e..a76fb93ce6460 100644 --- a/boards/arm/ht32f491x3/esk32/src/CMakeLists.txt +++ b/boards/arm/ht32f491x3/esk32/src/CMakeLists.txt @@ -30,6 +30,10 @@ if(CONFIG_USERLED) list(APPEND SRCS ht32_userleds.c) endif() +if(CONFIG_PWM) + list(APPEND SRCS ht32_pwm.c) +endif() + target_sources(board PRIVATE ${SRCS}) set_property(GLOBAL PROPERTY LD_SCRIPT "${NUTTX_BOARD_DIR}/scripts/ld.script") diff --git a/boards/arm/ht32f491x3/esk32/src/Makefile b/boards/arm/ht32f491x3/esk32/src/Makefile index 79c67f5ba5b25..2ff294bed3b49 100644 --- a/boards/arm/ht32f491x3/esk32/src/Makefile +++ b/boards/arm/ht32f491x3/esk32/src/Makefile @@ -34,4 +34,8 @@ ifeq ($(CONFIG_USERLED),y) CSRCS += ht32_userleds.c endif +ifeq ($(CONFIG_PWM),y) +CSRCS += ht32_pwm.c +endif + include $(TOPDIR)/boards/Board.mk diff --git a/boards/arm/ht32f491x3/esk32/src/ht32_appinit.c b/boards/arm/ht32f491x3/esk32/src/ht32_appinit.c index 80d92936023b8..3c1d276f0f7fa 100644 --- a/boards/arm/ht32f491x3/esk32/src/ht32_appinit.c +++ b/boards/arm/ht32f491x3/esk32/src/ht32_appinit.c @@ -57,6 +57,15 @@ static int ht32_bringup(void) # endif #endif +#ifdef CONFIG_PWM + tmp = ht32_pwm_setup(); + if (tmp < 0 && tmp != -EEXIST) + { + syslog(LOG_ERR, "ERROR: Failed to register /dev/pwm0: %d\n", tmp); + ret = tmp; + } +#endif + #ifdef CONFIG_FS_BINFS tmp = nx_mount(NULL, "/bin", "binfs", 0, NULL); if (tmp < 0 && tmp != -EBUSY) diff --git a/boards/arm/ht32f491x3/esk32/src/ht32_pwm.c b/boards/arm/ht32f491x3/esk32/src/ht32_pwm.c new file mode 100644 index 0000000000000..d90850360a6a7 --- /dev/null +++ b/boards/arm/ht32f491x3/esk32/src/ht32_pwm.c @@ -0,0 +1,84 @@ +/**************************************************************************** + * boards/arm/ht32f491x3/esk32/src/ht32_pwm.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "chip.h" +#include "arm_internal.h" +#include "ht32f491x3_pwm.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ht32_pwm_setup + * + * Description: + * Initialize PWM and register the PWM device. + * + * Return Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +int ht32_pwm_setup(void) +{ +#ifdef CONFIG_PWM + struct pwm_lowerhalf_s *pwm; + int ret; + +#if defined(CONFIG_HT32F491X3_TMR3_PWM) + pwm = ht32f491x3_pwminitialize(3); + if (!pwm) + { + pwmerr("ERROR: Failed to get the HT32 PWM lower half\n"); + return -ENODEV; + } + + ret = pwm_register("/dev/pwm0", pwm); + if (ret < 0) + { + pwmerr("ERROR: pwm_register failed: %d\n", ret); + return ret; + } + + return OK; +#else + return -ENODEV; +#endif +#else + return -ENODEV; +#endif +}