This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 2bcc86e2550ae4309b34bcd0034e6ddfec3929af
Author: raiden00pl <[email protected]>
AuthorDate: Thu Oct 29 20:16:03 2026 +0100

    arch/arm/src/common/stm32: Add LPTIM timer lower-half.
    
    stm32_lptim_timer_initialize() registers LPTIMn as a timer device. The
    period picks the fastest prescaler that fits the 16-bit counter; the
    kernel clock is STM32_LPTIMn_CLKIN from board.h.
    
    Signed-off-by: raiden00pl <[email protected]>
    Assisted-by: Claude Code
---
 arch/arm/src/common/stm32/CMakeLists.txt          |   3 +
 arch/arm/src/common/stm32/Make.defs               |   3 +
 arch/arm/src/common/stm32/stm32_lptim.h           |   6 +
 arch/arm/src/common/stm32/stm32_lptim_lowerhalf.c | 401 ++++++++++++++++++++++
 4 files changed, 413 insertions(+)

diff --git a/arch/arm/src/common/stm32/CMakeLists.txt 
b/arch/arm/src/common/stm32/CMakeLists.txt
index caa856c1ac4..2453bc75b36 100644
--- a/arch/arm/src/common/stm32/CMakeLists.txt
+++ b/arch/arm/src/common/stm32/CMakeLists.txt
@@ -99,6 +99,9 @@ endif()
 
 if(CONFIG_STM32_LPTIM)
   list(APPEND SRCS stm32_lptim.c)
+  if(CONFIG_TIMER)
+    list(APPEND SRCS stm32_lptim_lowerhalf.c)
+  endif()
 endif()
 
 if(CONFIG_STM32_FREERUN)
diff --git a/arch/arm/src/common/stm32/Make.defs 
b/arch/arm/src/common/stm32/Make.defs
index e3c1821ddc8..7c2611b450d 100644
--- a/arch/arm/src/common/stm32/Make.defs
+++ b/arch/arm/src/common/stm32/Make.defs
@@ -109,6 +109,9 @@ CHIP_CSRCS += stm32_tim.c
 endif
 ifeq ($(CONFIG_STM32_LPTIM),y)
 CHIP_CSRCS += stm32_lptim.c
+ifeq ($(CONFIG_TIMER),y)
+CHIP_CSRCS += stm32_lptim_lowerhalf.c
+endif
 endif
 ifeq ($(CONFIG_STM32_FREERUN),y)
 CHIP_CSRCS += stm32_freerun.c
diff --git a/arch/arm/src/common/stm32/stm32_lptim.h 
b/arch/arm/src/common/stm32/stm32_lptim.h
index 1afe37be7f6..6f006d4d40d 100644
--- a/arch/arm/src/common/stm32/stm32_lptim.h
+++ b/arch/arm/src/common/stm32/stm32_lptim.h
@@ -168,6 +168,12 @@ struct stm32_lptim_dev_s *stm32_lptim_init(int lptimer);
 
 int stm32_lptim_deinit(struct stm32_lptim_dev_s *dev);
 
+#ifdef CONFIG_TIMER
+/* Register LPTIMn as a timer device, e.g. "/dev/timer1" */
+
+int stm32_lptim_timer_initialize(const char *devpath, int lptim);
+#endif
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/arch/arm/src/common/stm32/stm32_lptim_lowerhalf.c 
b/arch/arm/src/common/stm32/stm32_lptim_lowerhalf.c
new file mode 100644
index 00000000000..311300e1054
--- /dev/null
+++ b/arch/arm/src/common/stm32/stm32_lptim_lowerhalf.c
@@ -0,0 +1,401 @@
+/****************************************************************************
+ * arch/arm/src/common/stm32/stm32_lptim_lowerhalf.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 <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <nuttx/debug.h>
+#include <nuttx/irq.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/timers/timer.h>
+
+#include "stm32_lptim.h"
+
+#if defined(CONFIG_TIMER) && defined(CONFIG_STM32_LPTIM)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Counts of the 16-bit counter in one period (ARR + 1) */
+
+#define LPTIM_PERIOD_MIN  2
+#define LPTIM_PERIOD_MAX  0x10000
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32_lptim_lowerhalf_s
+{
+  const struct timer_ops_s *ops;      /* Lower half operations */
+  struct stm32_lptim_dev_s *lptim;    /* LPTIM driver */
+  tccb_t                    callback; /* Upper half callback */
+  void                     *arg;      /* Argument of the callback */
+  bool                      started;  /* Timer is running */
+  uint32_t                  timeout;  /* Period in microseconds */
+  uint32_t                  fmin;     /* Slowest counter clock */
+  uint32_t                  freq;     /* Counter clock frequency */
+  uint32_t                  period;   /* Counts in one period */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int stm32_lptim_start(struct timer_lowerhalf_s *lower);
+static int stm32_lptim_stop(struct timer_lowerhalf_s *lower);
+static int stm32_lptim_getstatus(struct timer_lowerhalf_s *lower,
+                                 struct timer_status_s *status);
+static int stm32_lptim_settimeout(struct timer_lowerhalf_s *lower,
+                                  uint32_t timeout);
+static void stm32_lptim_setcallback(struct timer_lowerhalf_s *lower,
+                                    tccb_t callback, void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct timer_ops_s g_lptim_timer_ops =
+{
+  .start       = stm32_lptim_start,
+  .stop        = stm32_lptim_stop,
+  .getstatus   = stm32_lptim_getstatus,
+  .settimeout  = stm32_lptim_settimeout,
+  .setcallback = stm32_lptim_setcallback,
+  .ioctl       = NULL,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_lptim_run
+ *
+ * Description:
+ *   Start counting continuously with the configured period.
+ *
+ ****************************************************************************/
+
+static void stm32_lptim_run(struct stm32_lptim_lowerhalf_s *priv)
+{
+  STM32_LPTIM_ENABLE(priv->lptim, true);
+  STM32_LPTIM_SETPERIOD(priv->lptim, priv->period - 1);
+  STM32_LPTIM_START(priv->lptim, STM32_LPTIM_MODE_CONTINUOUS);
+}
+
+/****************************************************************************
+ * Name: stm32_lptim_handler
+ ****************************************************************************/
+
+static int stm32_lptim_handler(int irq, void *context, void *arg)
+{
+  struct stm32_lptim_lowerhalf_s *priv = arg;
+  uint32_t next = 0;
+  uint64_t period;
+
+  STM32_LPTIM_ACKINT(priv->lptim, LPTIM_ISR_ARRM);
+
+  if (priv->callback(&next, priv->arg))
+    {
+      if (next > 0 && next != priv->timeout)
+        {
+          period = (uint64_t)next * priv->freq / 1000000;
+          if (period >= LPTIM_PERIOD_MIN && period <= LPTIM_PERIOD_MAX)
+            {
+              STM32_LPTIM_SETPERIOD(priv->lptim, period - 1);
+              priv->timeout = next;
+              priv->period  = period;
+            }
+        }
+    }
+  else
+    {
+      stm32_lptim_stop((struct timer_lowerhalf_s *)priv);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_lptim_start
+ ****************************************************************************/
+
+static int stm32_lptim_start(struct timer_lowerhalf_s *lower)
+{
+  struct stm32_lptim_lowerhalf_s *priv =
+    (struct stm32_lptim_lowerhalf_s *)lower;
+
+  if (priv->started)
+    {
+      return -EBUSY;
+    }
+
+  if (priv->period == 0)
+    {
+      return -EPERM;
+    }
+
+  if (priv->callback != NULL)
+    {
+      STM32_LPTIM_SETISR(priv->lptim, stm32_lptim_handler, priv, 0);
+      STM32_LPTIM_ACKINT(priv->lptim, LPTIM_ISR_ARRM);
+      STM32_LPTIM_ENABLEINT(priv->lptim, LPTIM_ISR_ARRM);
+    }
+
+  stm32_lptim_run(priv);
+  priv->started = true;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_lptim_stop
+ ****************************************************************************/
+
+static int stm32_lptim_stop(struct timer_lowerhalf_s *lower)
+{
+  struct stm32_lptim_lowerhalf_s *priv =
+    (struct stm32_lptim_lowerhalf_s *)lower;
+
+  if (!priv->started)
+    {
+      return -ENODEV;
+    }
+
+  STM32_LPTIM_ENABLE(priv->lptim, false);
+  STM32_LPTIM_DISABLEINT(priv->lptim, LPTIM_ISR_ARRM);
+  STM32_LPTIM_SETISR(priv->lptim, NULL, NULL, 0);
+  priv->started = false;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_lptim_getstatus
+ ****************************************************************************/
+
+static int stm32_lptim_getstatus(struct timer_lowerhalf_s *lower,
+                                 struct timer_status_s *status)
+{
+  struct stm32_lptim_lowerhalf_s *priv =
+    (struct stm32_lptim_lowerhalf_s *)lower;
+  uint32_t count;
+
+  status->flags = 0;
+  if (priv->started)
+    {
+      status->flags |= TCFLAGS_ACTIVE;
+    }
+
+  if (priv->callback != NULL)
+    {
+      status->flags |= TCFLAGS_HANDLER;
+    }
+
+  status->timeout  = priv->timeout;
+  status->timeleft = 0;
+
+  if (priv->started && priv->freq != 0)
+    {
+      count = STM32_LPTIM_GETCOUNTER(priv->lptim);
+      if (count < priv->period)
+        {
+          status->timeleft = (uint64_t)(priv->period - count) * 1000000 /
+                             priv->freq;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_lptim_settimeout
+ ****************************************************************************/
+
+static int stm32_lptim_settimeout(struct timer_lowerhalf_s *lower,
+                                  uint32_t timeout)
+{
+  struct stm32_lptim_lowerhalf_s *priv =
+    (struct stm32_lptim_lowerhalf_s *)lower;
+  uint64_t freq;
+  uint64_t period;
+  int ret;
+
+  if (priv->started)
+    {
+      return -EPERM;
+    }
+
+  if (timeout == 0)
+    {
+      return -EINVAL;
+    }
+
+  /* The period must fit in the counter at the slowest counter clock */
+
+  if ((uint64_t)timeout * priv->fmin / 1000000 > LPTIM_PERIOD_MAX)
+    {
+      tmrerr("ERROR: timeout too long for the timer\n");
+      return -ERANGE;
+    }
+
+  /* The highest counter clock, set by the prescaler, that fits the period
+   * into the 16-bit counter.
+   */
+
+  freq = (uint64_t)LPTIM_PERIOD_MAX * 1000000 / timeout;
+  if (freq > UINT32_MAX)
+    {
+      freq = UINT32_MAX;
+    }
+
+  ret = STM32_LPTIM_SETCLOCK(priv->lptim, (uint32_t)freq);
+  if (ret <= 0)
+    {
+      return ret < 0 ? ret : -EINVAL;
+    }
+
+  period = (uint64_t)timeout * ret / 1000000;
+  if (period < LPTIM_PERIOD_MIN)
+    {
+      period = LPTIM_PERIOD_MIN;
+    }
+
+  priv->timeout = timeout;
+  priv->freq    = ret;
+  priv->period  = period;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_lptim_setcallback
+ ****************************************************************************/
+
+static void stm32_lptim_setcallback(struct timer_lowerhalf_s *lower,
+                                    tccb_t callback, void *arg)
+{
+  struct stm32_lptim_lowerhalf_s *priv =
+    (struct stm32_lptim_lowerhalf_s *)lower;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->callback = callback;
+  priv->arg      = arg;
+
+  if (priv->started)
+    {
+      /* The interrupt enable bits can only be changed while the timer is
+       * disabled.
+       */
+
+      STM32_LPTIM_ENABLE(priv->lptim, false);
+
+      if (callback != NULL)
+        {
+          STM32_LPTIM_SETISR(priv->lptim, stm32_lptim_handler, priv, 0);
+          STM32_LPTIM_ACKINT(priv->lptim, LPTIM_ISR_ARRM);
+          STM32_LPTIM_ENABLEINT(priv->lptim, LPTIM_ISR_ARRM);
+        }
+      else
+        {
+          STM32_LPTIM_DISABLEINT(priv->lptim, LPTIM_ISR_ARRM);
+          STM32_LPTIM_SETISR(priv->lptim, NULL, NULL, 0);
+        }
+
+      stm32_lptim_run(priv);
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_lptim_timer_initialize
+ *
+ * Description:
+ *   Register LPTIMn as a timer device.  The counter runs on the LPTIM
+ *   kernel clock, STM32_LPTIMn_CLKIN in board.h.
+ *
+ * Input Parameters:
+ *   devpath - The full path to the timer device, e.g. "/dev/timer1"
+ *   lptim   - The LPTIM instance number
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int stm32_lptim_timer_initialize(const char *devpath, int lptim)
+{
+  struct stm32_lptim_lowerhalf_s *priv;
+  int ret;
+
+  priv = kmm_zalloc(sizeof(struct stm32_lptim_lowerhalf_s));
+  if (priv == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  priv->lptim = stm32_lptim_init(lptim);
+  if (priv->lptim == NULL)
+    {
+      kmm_free(priv);
+      return -ENODEV;
+    }
+
+  /* The slowest counter clock gives the longest timeout */
+
+  ret = STM32_LPTIM_SETCLOCK(priv->lptim, 1);
+  if (ret <= 0)
+    {
+      tmrerr("ERROR: LPTIM%d kernel clock unknown\n", lptim);
+      stm32_lptim_deinit(priv->lptim);
+      kmm_free(priv);
+      return -EINVAL;
+    }
+
+  priv->fmin = ret;
+  priv->ops  = &g_lptim_timer_ops;
+
+  if (timer_register(devpath, (struct timer_lowerhalf_s *)priv) == NULL)
+    {
+      stm32_lptim_deinit(priv->lptim);
+      kmm_free(priv);
+      return -EEXIST;
+    }
+
+  return OK;
+}
+
+#endif /* CONFIG_TIMER && CONFIG_STM32_LPTIM */

Reply via email to