From f5dd77e43c4152e1580e6a6098dc091017276d63 Mon Sep 17 00:00:00 2001 From: Felipe Moura Date: Tue, 31 Mar 2026 08:44:16 -0300 Subject: [PATCH] arm/ht32f491x3: rework PWM lower-half like nrf53 Use static lower-half instances per timer and select the channel through Kconfig and board data instead of passing runtime PWM routing into the driver. This keeps the HT32 PWM support aligned with the existing NuttX pattern and documents the esk32 single-device TMR3 routing. Signed-off-by: Felipe Moura --- .../arm/ht32f491x3/boards/esk32/index.rst | 126 +++- arch/arm/src/ht32f491x3/CMakeLists.txt | 4 + arch/arm/src/ht32f491x3/Kconfig | 236 +++++++- arch/arm/src/ht32f491x3/Make.defs | 4 + .../src/ht32f491x3/hardware/ht32f491x3_crm.h | 20 +- .../src/ht32f491x3/hardware/ht32f491x3_tmr.h | 99 ++++ arch/arm/src/ht32f491x3/ht32f491x3_pwm.c | 560 ++++++++++++++++++ arch/arm/src/ht32f491x3/ht32f491x3_pwm.h | 55 ++ boards/arm/ht32f491x3/esk32/Kconfig | 2 +- .../ht32f491x3/esk32/configs/pwm/defconfig | 58 ++ boards/arm/ht32f491x3/esk32/include/board.h | 42 ++ .../arm/ht32f491x3/esk32/src/CMakeLists.txt | 4 + boards/arm/ht32f491x3/esk32/src/Makefile | 4 + .../arm/ht32f491x3/esk32/src/ht32_appinit.c | 9 + boards/arm/ht32f491x3/esk32/src/ht32_pwm.c | 84 +++ 15 files changed, 1292 insertions(+), 15 deletions(-) create mode 100644 arch/arm/src/ht32f491x3/hardware/ht32f491x3_tmr.h create mode 100644 arch/arm/src/ht32f491x3/ht32f491x3_pwm.c create mode 100644 arch/arm/src/ht32f491x3/ht32f491x3_pwm.h create mode 100644 boards/arm/ht32f491x3/esk32/configs/pwm/defconfig create mode 100644 boards/arm/ht32f491x3/esk32/src/ht32_pwm.c 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 +}