You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/06/28 02:35:48 UTC

[incubator-nuttx] branch master updated (4afd25b567 -> 00e8e4fa28)

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

xiaoxiang pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git


    from 4afd25b567 this flag is meaningless for the linker
     new fd8eaf4f42 arch/stm32_capture_lowerhalf.c: add lower half support of capture
     new b41929522c drivers/timers/capture.c: add support of pwm capture driver
     new 00e8e4fa28 boards/stm32f4discovery: add setup of pwm capture device

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 arch/arm/src/stm32/Kconfig                         | 232 +++++++++
 arch/arm/src/stm32/Make.defs                       |   4 +
 arch/arm/src/stm32/stm32_capture.c                 | 228 ++++++--
 arch/arm/src/stm32/stm32_capture.h                 |  68 ++-
 arch/arm/src/stm32/stm32_capture_lowerhalf.c       | 575 +++++++++++++++++++++
 arch/arm/src/stm32/stm32_tim.c                     |  36 +-
 boards/arm/stm32/stm32f4discovery/include/board.h  |   9 +
 boards/arm/stm32/stm32f4discovery/src/Make.defs    |   4 +
 .../arm/stm32/stm32f4discovery/src/stm32_bringup.c |  11 +
 .../src/stm32_capture.c}                           |  93 ++--
 .../stm32/stm32f4discovery/src/stm32f4discovery.h  |  21 +
 drivers/timers/Kconfig                             |   7 +
 drivers/timers/Make.defs                           |   6 +
 drivers/{sensors/qencoder.c => timers/capture.c}   | 181 +++----
 include/nuttx/fs/ioctl.h                           |   7 +-
 .../nuttx/{sensors/hall3ph.h => timers/capture.h}  |  78 ++-
 16 files changed, 1290 insertions(+), 270 deletions(-)
 create mode 100644 arch/arm/src/stm32/stm32_capture_lowerhalf.c
 copy boards/arm/stm32/{nucleo-g431kb/src/stm32_dac.c => stm32f4discovery/src/stm32_capture.c} (62%)
 copy drivers/{sensors/qencoder.c => timers/capture.c} (65%)
 copy include/nuttx/{sensors/hall3ph.h => timers/capture.h} (67%)


[incubator-nuttx] 03/03: boards/stm32f4discovery: add setup of pwm capture device

Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 00e8e4fa28d957a257c94784235887e282220e01
Author: zouboan <ff...@feedforward.com.cn>
AuthorDate: Sun Jun 26 20:54:57 2022 +0800

    boards/stm32f4discovery: add setup of pwm capture device
    
    drivers/timers/capture.c: add support of pwm capture driver
---
 boards/arm/stm32/stm32f4discovery/include/board.h  |   9 ++
 boards/arm/stm32/stm32f4discovery/src/Make.defs    |   4 +
 .../arm/stm32/stm32f4discovery/src/stm32_bringup.c |  11 +++
 .../arm/stm32/stm32f4discovery/src/stm32_capture.c | 109 +++++++++++++++++++++
 .../stm32/stm32f4discovery/src/stm32f4discovery.h  |  21 ++++
 drivers/timers/capture.c                           |   1 -
 6 files changed, 154 insertions(+), 1 deletion(-)

diff --git a/boards/arm/stm32/stm32f4discovery/include/board.h b/boards/arm/stm32/stm32f4discovery/include/board.h
index 6732108415..6f2698a1f2 100644
--- a/boards/arm/stm32/stm32f4discovery/include/board.h
+++ b/boards/arm/stm32/stm32f4discovery/include/board.h
@@ -314,6 +314,15 @@
 
 #define GPIO_TIM4_CH2OUT  GPIO_TIM4_CH2OUT_2
 
+/* Capture
+ *
+ * The STM32F4 Discovery has no real on-board pwm capture devices, but the
+ * board can be configured to capture pwm using TIM3 CH2 PB5.
+ */
+
+#define GPIO_TIM3_CH2IN  GPIO_TIM3_CH2IN_2
+#define GPIO_TIM3_CH1IN  GPIO_TIM3_CH2IN_2
+
 /* RGB LED
  *
  * R = TIM1 CH1 on PE9 | G = TIM2 CH2 on PA1 | B = TIM3 CH3 on PB0
diff --git a/boards/arm/stm32/stm32f4discovery/src/Make.defs b/boards/arm/stm32/stm32f4discovery/src/Make.defs
index 89fb34f154..1793630ca3 100644
--- a/boards/arm/stm32/stm32f4discovery/src/Make.defs
+++ b/boards/arm/stm32/stm32f4discovery/src/Make.defs
@@ -92,6 +92,10 @@ ifeq ($(CONFIG_PWM),y)
 CSRCS += stm32_pwm.c
 endif
 
+ifeq ($(CONFIG_CAPTURE),y)
+CSRCS += stm32_capture.c
+endif
+
 ifeq ($(CONFIG_BOARDCTL),y)
 CSRCS += stm32_appinit.c
 ifeq ($(CONFIG_BOARDCTL_RESET),y)
diff --git a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c
index 17f0e51383..9a4340b421 100644
--- a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c
+++ b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c
@@ -347,6 +347,17 @@ int stm32_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_CAPTURE
+  /* Initialize Capture and register the Capture driver. */
+
+  ret = stm32_capture_setup("/dev/capture0");
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: stm32_capture_setup failed: %d\n", ret);
+      return ret;
+    }
+#endif
+
 #ifdef CONFIG_STM32_CAN_CHARDRIVER
   /* Initialize CAN and register the CAN driver. */
 
diff --git a/boards/arm/stm32/stm32f4discovery/src/stm32_capture.c b/boards/arm/stm32/stm32f4discovery/src/stm32_capture.c
new file mode 100644
index 0000000000..811bdabbe0
--- /dev/null
+++ b/boards/arm/stm32/stm32f4discovery/src/stm32_capture.c
@@ -0,0 +1,109 @@
+/****************************************************************************
+ * boards/arm/stm32/stm32f4discovery/src/stm32_capture.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <debug.h>
+#include <nuttx/timers/capture.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+
+#include "stm32.h"
+#include "stm32_capture.h"
+#include "arm_internal.h"
+
+#include "stm32f4discovery.h"
+
+#if defined(CONFIG_CAPTURE)
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Capture
+ *
+ * The stm32f4discovery has no real on-board pwm capture devices, but the
+ * board can be configured to capture pwm using TIM3 CH2 PB5.
+ */
+
+#define HAVE_CAPTURE 1
+
+#ifndef CONFIG_CAPTURE
+#  undef HAVE_CAPTURE
+#endif
+
+#ifndef CONFIG_STM32_TIM3
+#  undef HAVE_CAPTURE
+#endif
+
+#ifndef CONFIG_STM32_TIM3_CAP
+#  undef HAVE_CAPTURE
+#endif
+
+#if !defined(CONFIG_STM32_TIM3_CHANNEL) || CONFIG_STM32_TIM3_CHANNEL != STM32F4DISCOVERY_CAPTURECHANNEL
+#  undef HAVE_CAPTURE
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_capture_setup
+ *
+ * Description:
+ *   Initialize and register the pwm capture driver.
+ *
+ * Input parameters:
+ *   devpath - The full path to the driver to register. E.g., "/dev/capture0"
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int stm32_capture_setup(FAR const char *devpath)
+{
+#ifdef HAVE_CAPTURE
+  struct cap_lowerhalf_s *capture;
+  int ret;
+
+  capture = stm32_cap_initialize(STM32F4DISCOVERY_CAPTURETIMER);
+
+  /* Then register the pwm capture sensor */
+
+  ret = cap_register(devpath, capture);
+  if (ret < 0)
+    {
+      mtrerr("ERROR: Error registering capture\n");
+    }
+
+  return ret;
+#else
+  return -ENODEV;
+#endif
+}
+
+#endif /* CONFIG_CAPTURE */
diff --git a/boards/arm/stm32/stm32f4discovery/src/stm32f4discovery.h b/boards/arm/stm32/stm32f4discovery/src/stm32f4discovery.h
index 8b8e8d308e..15953dd4a6 100644
--- a/boards/arm/stm32/stm32f4discovery/src/stm32f4discovery.h
+++ b/boards/arm/stm32/stm32f4discovery/src/stm32f4discovery.h
@@ -243,6 +243,15 @@
 #define STM32F4DISCOVERY_PWMTIMER   4
 #define STM32F4DISCOVERY_PWMCHANNEL 2
 
+/* Capture
+ *
+ * The STM32F4 Discovery has no real on-board pwm capture devices, but the
+ * board can be configured to capture pwm using TIM3 CH2 PB5.
+ */
+
+#define STM32F4DISCOVERY_CAPTURETIMER   3
+#define STM32F4DISCOVERY_CAPTURECHANNEL 2
+
 /* SPI chip selects */
 
 #define GPIO_CS_MEMS      (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
@@ -555,6 +564,18 @@ int stm32_usbhost_initialize(void);
 int stm32_pwm_setup(void);
 #endif
 
+/****************************************************************************
+ * Name: stm32_capture_setup
+ *
+ * Description:
+ *  Initialize pwm capture support
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_CAPTURE
+int stm32_capture_setup(FAR const char *devpath);
+#endif
+
 /****************************************************************************
  * Name: stm32_can_setup
  *
diff --git a/drivers/timers/capture.c b/drivers/timers/capture.c
index 9537f72a06..379a5eaed9 100644
--- a/drivers/timers/capture.c
+++ b/drivers/timers/capture.c
@@ -358,7 +358,6 @@ int cap_register(FAR const char *devpath, FAR struct cap_lowerhalf_s *lower)
            kmm_zalloc(sizeof(struct cap_upperhalf_s));
   if (!upper)
     {
-      snprintf("ERROR: Allocation failed\n");
       return -ENOMEM;
     }
 


[incubator-nuttx] 01/03: arch/stm32_capture_lowerhalf.c: add lower half support of capture

Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit fd8eaf4f42a1a230fb2af1de4502093b414ceeed
Author: zouboan <ff...@feedforward.com.cn>
AuthorDate: Sun Jun 26 19:52:36 2022 +0800

    arch/stm32_capture_lowerhalf.c: add lower half support of capture
---
 arch/arm/src/stm32/Kconfig                   | 232 +++++++++++
 arch/arm/src/stm32/Make.defs                 |   4 +
 arch/arm/src/stm32/stm32_capture.c           | 228 +++++++++--
 arch/arm/src/stm32/stm32_capture.h           |  68 +++-
 arch/arm/src/stm32/stm32_capture_lowerhalf.c | 575 +++++++++++++++++++++++++++
 arch/arm/src/stm32/stm32_tim.c               |  36 +-
 6 files changed, 1079 insertions(+), 64 deletions(-)

diff --git a/arch/arm/src/stm32/Kconfig b/arch/arm/src/stm32/Kconfig
index 28d17d3b78..df1e139865 100644
--- a/arch/arm/src/stm32/Kconfig
+++ b/arch/arm/src/stm32/Kconfig
@@ -3413,6 +3413,10 @@ config STM32_PWM
 	bool
 	default n
 
+config STM32_CAP
+	bool
+	default n
+
 config STM32_COMP
 	bool
 	default n
@@ -6765,122 +6769,350 @@ config STM32_TIM1_CAP
 	bool "TIM1 Capture"
 	default n
 	depends on STM32_TIM1
+	select STM32_CAP
 	---help---
 		Reserve timer 1 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM1_CAP
+
+config STM32_TIM1_CHANNEL
+	int "TIM1 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM1 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM1_CLOCK
+	int "TIM1 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM1_CAP
+
 config STM32_TIM2_CAP
 	bool "TIM2 Capture"
 	default n
 	depends on STM32_TIM2
+	select STM32_CAP
 	---help---
 		Reserve timer 2 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM2_CAP
+
+config STM32_TIM2_CHANNEL
+	int "TIM2 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM2 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM2_CLOCK
+	int "TIM2 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM2_CAP
+
 config STM32_TIM3_CAP
 	bool "TIM3 Capture"
 	default n
 	depends on STM32_TIM3
+	select STM32_CAP
 	---help---
 		Reserve timer 3 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM3_CAP
+
+config STM32_TIM3_CHANNEL
+	int "TIM3 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM3 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM3_CLOCK
+	int "TIM3 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM3_CAP
+
 config STM32_TIM4_CAP
 	bool "TIM4 Capture"
 	default n
 	depends on STM32_TIM4
+	select STM32_CAP
 	---help---
 		Reserve timer 4 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM4_CAP
+
+config STM32_TIM4_CHANNEL
+	int "TIM4 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM4 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM4_CLOCK
+	int "TIM4 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM4_CAP
+
 config STM32_TIM5_CAP
 	bool "TIM5 Capture"
 	default n
 	depends on STM32_TIM5
+	select STM32_CAP
 	---help---
 		Reserve timer 5 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM5_CAP
+
+config STM32_TIM5_CHANNEL
+	int "TIM5 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM5 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM5_CLOCK
+	int "TIM5 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM5_CAP
+
 config STM32_TIM8_CAP
 	bool "TIM8 Capture"
 	default n
 	depends on STM32_TIM8
+	select STM32_CAP
 	---help---
 		Reserve timer 8 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM8_CAP
+
+config STM32_TIM8_CHANNEL
+	int "TIM8 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM8 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM8_CLOCK
+	int "TIM8 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM8_CAP
+
 config STM32_TIM9_CAP
 	bool "TIM9 Capture"
 	default n
 	depends on STM32_TIM9
+	select STM32_CAP
 	---help---
 		Reserve timer 9 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM9_CAP
+
+config STM32_TIM9_CHANNEL
+	int "TIM9 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM9 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM9_CLOCK
+	int "TIM9 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM9_CAP
+
 config STM32_TIM10_CAP
 	bool "TIM10 Capture"
 	default n
 	depends on STM32_TIM10
+	select STM32_CAP
 	---help---
 		Reserve timer 10 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM10_CAP
+
+config STM32_TIM10_CHANNEL
+	int "TIM10 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM10 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM10_CLOCK
+	int "TIM10 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM10_CAP
+
 config STM32_TIM11_CAP
 	bool "TIM11 Capture"
 	default n
 	depends on STM32_TIM11
+	select STM32_CAP
 	---help---
 		Reserve timer 11 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM11_CAP
+
+config STM32_TIM11_CHANNEL
+	int "TIM11 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM11 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM11_CLOCK
+	int "TIM11 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM11_CAP
+
 config STM32_TIM12_CAP
 	bool "TIM12 Capture"
 	default n
 	depends on STM32_TIM12
+	select STM32_CAP
 	---help---
 		Reserve timer 12 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM12_CAP
+
+config STM32_TIM12_CHANNEL
+	int "TIM12 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM12 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM12_CLOCK
+	int "TIM12 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM12_CAP
+
 config STM32_TIM13_CAP
 	bool "TIM13 Capture"
 	default n
 	depends on STM32_TIM13
+	select STM32_CAP
 	---help---
 		Reserve timer 13 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM13_CAP
+
+config STM32_TIM13_CHANNEL
+	int "TIM13 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM13 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM13_CLOCK
+	int "TIM13 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM13_CAP
+
 config STM32_TIM14_CAP
 	bool "TIM14 Capture"
 	default n
 	depends on STM32_TIM14
+	select STM32_CAP
 	---help---
 		Reserve timer 14 for use by Capture
 
 		Timer devices may be used for different purposes.  One special purpose is
 		to capture input.
 
+if STM32_TIM14_CAP
+
+config STM32_TIM14_CHANNEL
+	int "TIM14 Capture Input Channel"
+	default 1
+	range 1 4
+	---help---
+		If TIM14 is enabled for capture usage, you also need specifies the timer input
+		channel {1,..,4}
+
+config STM32_TIM14_CLOCK
+	int "TIM14 work frequence for capture"
+	default 1000000
+	---help---
+		This clock frequence limiting the count rate at the expense of resolution.
+
+endif # STM32_TIM14_CAP
+
 menu "STM32 TIMx Outputs Configuration"
 
 config STM32_TIM1_CH1POL
diff --git a/arch/arm/src/stm32/Make.defs b/arch/arm/src/stm32/Make.defs
index 2413ecb014..4f7bf86e04 100644
--- a/arch/arm/src/stm32/Make.defs
+++ b/arch/arm/src/stm32/Make.defs
@@ -175,6 +175,10 @@ ifeq ($(CONFIG_STM32_PWM),y)
 CHIP_CSRCS += stm32_pwm.c
 endif
 
+ifeq ($(CONFIG_STM32_CAP),y)
+CHIP_CSRCS += stm32_capture_lowerhalf.c
+endif
+
 ifeq ($(CONFIG_SENSORS_QENCODER),y)
 CHIP_CSRCS += stm32_qencoder.c
 endif
diff --git a/arch/arm/src/stm32/stm32_capture.c b/arch/arm/src/stm32/stm32_capture.c
index 57050a83ed..20ed6a109f 100644
--- a/arch/arm/src/stm32/stm32_capture.c
+++ b/arch/arm/src/stm32/stm32_capture.c
@@ -617,13 +617,15 @@ static inline int stm32_cap_set_rcc(const struct stm32_cap_priv_s *priv,
  ****************************************************************************/
 
 static int stm32_cap_setclock(struct stm32_cap_dev_s *dev,
-                              stm32_cap_clk_t clk,
-                              uint32_t prescaler, uint32_t max)
+                              uint32_t freq, uint32_t max)
 {
   const struct stm32_cap_priv_s *priv = (const struct stm32_cap_priv_s *)dev;
-  uint16_t regval = 0;
+  uint32_t freqin;
+  int prescaler;
+
+  /* Disable Timer? */
 
-  if (prescaler == 0)
+  if (freq == 0)
     {
       /* Disable Timer */
 
@@ -631,6 +633,85 @@ static int stm32_cap_setclock(struct stm32_cap_dev_s *dev,
       return 0;
     }
 
+  /* Get the input clock frequency for this timer.  These vary with
+   * different timer clock sources, MCU-specific timer configuration, and
+   * board-specific clock configuration.  The correct input clock frequency
+   * must be defined in the board.h header file.
+   */
+
+  switch (priv->base)
+    {
+#ifdef CONFIG_STM32_TIM1
+      case STM32_TIM1_BASE:
+        freqin = STM32_APB2_TIM1_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM2
+      case STM32_TIM2_BASE:
+        freqin = STM32_APB1_TIM2_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM3
+      case STM32_TIM3_BASE:
+        freqin = STM32_APB1_TIM3_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM4
+      case STM32_TIM4_BASE:
+        freqin = STM32_APB1_TIM4_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM5
+      case STM32_TIM5_BASE:
+        freqin = STM32_APB1_TIM5_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM8
+      case STM32_TIM8_BASE:
+        freqin = STM32_APB2_TIM8_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM9
+      case STM32_TIM9_BASE:
+        freqin = STM32_APB2_TIM9_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM10
+      case STM32_TIM10_BASE:
+        freqin = STM32_APB2_TIM10_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM11
+      case STM32_TIM11_BASE:
+        freqin = STM32_APB2_TIM11_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM12
+      case STM32_TIM12_BASE:
+        freqin = STM32_APB1_TIM12_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM13
+      case STM32_TIM13_BASE:
+        freqin = STM32_APB1_TIM13_CLKIN;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM14
+      case STM32_TIM14_BASE:
+        freqin = STM32_APB1_TIM14_CLKIN;
+        break;
+#endif
+
+      default:
+        return -EINVAL;
+    }
+
+  /* Select a pre-scaler value for this timer using the input clock
+   * frequency.
+   */
+
+  prescaler = freqin / freq;
+
   /* We need to decrement value for '1', but only, if we are allowed to
    * not to cause underflow. Check for overflow.
    */
@@ -645,73 +726,141 @@ static int stm32_cap_setclock(struct stm32_cap_dev_s *dev,
       prescaler = 0xffff;
     }
 
-  switch (clk)
+  /* Set Maximum */
+
+  stm32_putreg32(priv, STM32_BTIM_ARR_OFFSET, max);
+
+  /* Set prescaler */
+
+  stm32_putreg16(priv, STM32_BTIM_PSC_OFFSET, prescaler);
+
+  /* Reset counter timer */
+
+  stm32_modifyreg16(priv, STM32_BTIM_EGR_OFFSET, 0, BTIM_EGR_UG);
+
+  /* Enable timer */
+
+  stm32_modifyreg16(priv, STM32_BTIM_CR1_OFFSET, 0, BTIM_CR1_CEN);
+
+#ifdef USE_ADVENCED_TIM
+  /* Advanced registers require Main Output Enable */
+
+  if (priv->base == STM32_TIM1_BASE || priv->base == STM32_TIM8_BASE)
+    {
+      stm32_modifyreg16(priv, STM32_ATIM_BDTR_OFFSET, 0, ATIM_BDTR_MOE);
+    }
+#endif
+
+  return prescaler;
+}
+
+/****************************************************************************
+ * Name: stm32_cap_setsmc
+ *
+ * Description:
+ *   set slave mode control register
+ *
+ * Input Parameters:
+ *   dev - A pointer of the stm32 capture device structure.
+ *   cfg - Slave mode control register configure of timer.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_cap_setsmc(struct stm32_cap_dev_s *dev,
+                            stm32_cap_smc_cfg_t cfg)
+{
+  const struct stm32_cap_priv_s *priv = (const struct stm32_cap_priv_s *)dev;
+  uint16_t regval = 0;
+  uint16_t mask = 0;
+
+  switch (cfg & STM32_CAP_SMS_MASK)
     {
-      case STM32_CAP_CLK_INT:
-          regval = GTIM_SMCR_DISAB;
+      case STM32_CAP_SMS_INT:
+          regval |= GTIM_SMCR_DISAB;
           break;
 
-      case STM32_CAP_CLK_ENC1:
-          regval = GTIM_SMCR_ENCMD1;
+      case STM32_CAP_SMS_ENC1:
+          regval |= GTIM_SMCR_ENCMD1;
           break;
 
-      case STM32_CAP_CLK_ENC2:
-          regval = GTIM_SMCR_ENCMD2;
+      case STM32_CAP_SMS_ENC2:
+          regval |= GTIM_SMCR_ENCMD2;
           break;
 
-      case STM32_CAP_CLK_ENC3:
-          regval = GTIM_SMCR_ENCMD3;
+      case STM32_CAP_SMS_ENC3:
+          regval |= GTIM_SMCR_ENCMD3;
           break;
 
-      case STM32_CAP_CLK_RST:
-          regval = GTIM_SMCR_RESET;
+      case STM32_CAP_SMS_RST:
+          regval |= GTIM_SMCR_RESET;
           break;
 
-      case STM32_CAP_CLK_GAT:
-          regval = GTIM_SMCR_GATED;
+      case STM32_CAP_SMS_GAT:
+          regval |= GTIM_SMCR_GATED;
           break;
 
-      case STM32_CAP_CLK_TRG:
-          regval = GTIM_SMCR_TRIGGER;
+      case STM32_CAP_SMS_TRG:
+          regval |= GTIM_SMCR_TRIGGER;
           break;
 
-      case STM32_CAP_CLK_EXT:
-          regval = GTIM_SMCR_EXTCLK1;
+      case STM32_CAP_SMS_EXT:
+          regval |= GTIM_SMCR_EXTCLK1;
           break;
 
       default:
-        return ERROR;
+          break;
     }
 
-  stm32_modifyreg16(priv, STM32_GTIM_SMCR_OFFSET,
-                    GTIM_SMCR_SMS_MASK, regval);
-
-  /* Set Maximum */
+  switch (cfg & STM32_CAP_TS_MASK)
+    {
+      case STM32_CAP_TS_ITR0:
+          regval |= GTIM_SMCR_ITR0;
+          break;
 
-  stm32_putreg32(priv, STM32_BTIM_ARR_OFFSET, max);
+      case STM32_CAP_TS_ITR1:
+          regval |= GTIM_SMCR_ITR1;
+          break;
 
-  /* Set prescaler */
+      case STM32_CAP_TS_ITR2:
+          regval |= GTIM_SMCR_ITR2;
+          break;
 
-  stm32_putreg16(priv, STM32_BTIM_PSC_OFFSET, prescaler);
+      case STM32_CAP_TS_ITR3:
+          regval |= GTIM_SMCR_ITR3;
+          break;
 
-  /* Reset counter timer */
+      case STM32_CAP_TS_TI1FED:
+          regval |= GTIM_SMCR_TI1FED;
+          break;
 
-  stm32_modifyreg16(priv, STM32_BTIM_EGR_OFFSET, 0, BTIM_EGR_UG);
+      case STM32_CAP_TS_TI1FP1:
+          regval |= GTIM_SMCR_TI1FP1;
+          break;
 
-  /* Enable timer */
+      case STM32_CAP_TS_TI2FP2:
+          regval |= GTIM_SMCR_TI2FP2;
+          break;
 
-  stm32_modifyreg16(priv, STM32_BTIM_CR1_OFFSET, 0, BTIM_CR1_CEN);
+      case STM32_CAP_TS_ETRF:
+          regval |= GTIM_SMCR_ETRF;
+          break;
 
-#ifdef USE_ADVENCED_TIM
-  /* Advanced registers require Main Output Enable */
+      default:
+          break;
+    }
 
-  if (priv->base == STM32_TIM1_BASE || priv->base == STM32_TIM8_BASE)
+  if (cfg & STM32_CAP_MSM_MASK)
     {
-      stm32_modifyreg16(priv, STM32_ATIM_BDTR_OFFSET, 0, ATIM_BDTR_MOE);
+      regval |= STM32_CAP_MSM_MASK;
     }
-#endif
 
-  return prescaler;
+  mask = (STM32_CAP_SMS_MASK | STM32_CAP_TS_MASK | STM32_CAP_MSM_MASK);
+  stm32_modifyreg16(priv, STM32_GTIM_SMCR_OFFSET, mask, regval);
+
+  return OK;
 }
 
 static int stm32_cap_setisr(struct stm32_cap_dev_s *dev, xcpt_t handler,
@@ -1088,6 +1237,7 @@ static uint32_t stm32_cap_getcapture(struct stm32_cap_dev_s *dev,
 
 struct stm32_cap_ops_s stm32_cap_ops =
 {
+  .setsmc       = &stm32_cap_setsmc,
   .setclock     = &stm32_cap_setclock,
   .setchannel   = &stm32_cap_setchannel,
   .getcapture   = &stm32_cap_getcapture,
diff --git a/arch/arm/src/stm32/stm32_capture.h b/arch/arm/src/stm32/stm32_capture.h
index 347545fc31..ae73cb1989 100644
--- a/arch/arm/src/stm32/stm32_capture.h
+++ b/arch/arm/src/stm32/stm32_capture.h
@@ -37,7 +37,8 @@
 
 /* Helpers ******************************************************************/
 
-#define STM32_CAP_SETCLOCK(d,clk_src,psc,max)   ((d)->ops->setclock(d,clk_src,psc,max))
+#define STM32_CAP_SETSMC(d,cfg)                 ((d)->ops->setsmc(d,cfg))
+#define STM32_CAP_SETCLOCK(d,clk,max)           ((d)->ops->setclock(d,clk,max))
 #define STM32_CAP_SETCHANNEL(d,ch,cfg)          ((d)->ops->setchannel(d,ch,cfg))
 #define STM32_CAP_GETCAPTURE(d,ch)              ((d)->ops->getcapture(d,ch))
 #define STM32_CAP_SETISR(d,hnd,arg)             ((d)->ops->setisr(d,hnd,arg))
@@ -122,19 +123,38 @@ typedef enum
   STM32_CAP_EDGE_BOTH           = (3 << 8),
 } stm32_cap_ch_cfg_t;
 
-/* Capture clock sources */
+/* Slave mode control configure */
 
 typedef enum
 {
-  STM32_CAP_CLK_INT = 0,
-  STM32_CAP_CLK_ENC1,
-  STM32_CAP_CLK_ENC2,
-  STM32_CAP_CLK_ENC3,
-  STM32_CAP_CLK_RST,
-  STM32_CAP_CLK_GAT,
-  STM32_CAP_CLK_TRG,
-  STM32_CAP_CLK_EXT,
-} stm32_cap_clk_t;
+  /* Slave mode selection */
+
+  STM32_CAP_SMS_MASK            = (7 << GTIM_SMCR_SMS_SHIFT),
+  STM32_CAP_SMS_INT             = (0 << GTIM_SMCR_SMS_SHIFT),
+  STM32_CAP_SMS_ENC1            = (1 << GTIM_SMCR_SMS_SHIFT),
+  STM32_CAP_SMS_ENC2            = (2 << GTIM_SMCR_SMS_SHIFT),
+  STM32_CAP_SMS_ENC3            = (3 << GTIM_SMCR_SMS_SHIFT),
+  STM32_CAP_SMS_RST             = (4 << GTIM_SMCR_SMS_SHIFT),
+  STM32_CAP_SMS_GAT             = (5 << GTIM_SMCR_SMS_SHIFT),
+  STM32_CAP_SMS_TRG             = (6 << GTIM_SMCR_SMS_SHIFT),
+  STM32_CAP_SMS_EXT             = (7 << GTIM_SMCR_SMS_SHIFT),
+
+  /* Trigger selection */
+
+  STM32_CAP_TS_MASK             = (7 << GTIM_SMCR_TS_SHIFT),
+  STM32_CAP_TS_ITR0             = (0 << GTIM_SMCR_TS_SHIFT),
+  STM32_CAP_TS_ITR1             = (1 << GTIM_SMCR_TS_SHIFT),
+  STM32_CAP_TS_ITR2             = (2 << GTIM_SMCR_TS_SHIFT),
+  STM32_CAP_TS_ITR3             = (3 << GTIM_SMCR_TS_SHIFT),
+  STM32_CAP_TS_TI1FED           = (4 << GTIM_SMCR_TS_SHIFT),
+  STM32_CAP_TS_TI1FP1           = (5 << GTIM_SMCR_TS_SHIFT),
+  STM32_CAP_TS_TI2FP2           = (6 << GTIM_SMCR_TS_SHIFT),
+  STM32_CAP_TS_ETRF             = (7 << GTIM_SMCR_TS_SHIFT),
+
+  /* Master/Slave mode seting */
+
+  STM32_CAP_MSM_MASK            = (1 << 7)
+} stm32_cap_smc_cfg_t;
 
 /* Capture flags */
 
@@ -163,8 +183,9 @@ typedef enum
 
 struct stm32_cap_ops_s
 {
-  int  (*setclock)(struct stm32_cap_dev_s *dev, stm32_cap_clk_t clk,
-                   uint32_t prescaler, uint32_t max);
+  int  (*setsmc)(struct stm32_cap_dev_s *dev, stm32_cap_smc_cfg_t cfg);
+  int  (*setclock)(struct stm32_cap_dev_s *dev, uint32_t freq,
+                   uint32_t max);
   int  (*setchannel)(struct stm32_cap_dev_s *dev, uint8_t channel,
                      stm32_cap_ch_cfg_t cfg);
   uint32_t (*getcapture)(struct stm32_cap_dev_s *dev, uint8_t channel);
@@ -187,6 +208,27 @@ struct stm32_cap_dev_s *stm32_cap_init(int timer);
 
 int stm32_cap_deinit(struct stm32_cap_dev_s *dev);
 
+/****************************************************************************
+ * Name: stm32_cap_initialize
+ *
+ * Description:
+ *   Initialize one timer for use with the upper_level capture driver.
+ *
+ * Input Parameters:
+ *   timer - A number identifying the timer use.  The number of valid timer
+ *     IDs varies with the STM32 MCU and MCU family but is somewhere in
+ *     the range of {1,..,5 8,...,14}.
+ *
+ * Returned Value:
+ *   On success, a pointer to the STM32 lower half capture driver returned.
+ *   NULL is returned on any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_CAPTURE
+struct cap_lowerhalf_s *stm32_cap_initialize(int timer);
+#endif
+
 #undef EXTERN
 #if defined(__cplusplus)
 }
diff --git a/arch/arm/src/stm32/stm32_capture_lowerhalf.c b/arch/arm/src/stm32/stm32_capture_lowerhalf.c
new file mode 100644
index 0000000000..bc27927818
--- /dev/null
+++ b/arch/arm/src/stm32/stm32_capture_lowerhalf.c
@@ -0,0 +1,575 @@
+/****************************************************************************
+ * arch/arm/src/stm32/stm32_capture_lowerhalf.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/timers/capture.h>
+
+#include <arch/board/board.h>
+
+#include "stm32_capture.h"
+
+#if defined(CONFIG_STM32_CAP)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define STM32_TIM1_RES   16
+#if defined(CONFIG_STM32_STM32L15XX) || defined(CONFIG_STM32_STM32F10XX)
+#  define STM32_TIM2_RES 16
+#else
+#  define STM32_TIM2_RES 32
+#endif
+#define STM32_TIM3_RES   16
+#define STM32_TIM4_RES   16
+#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX)
+#  define STM32_TIM5_RES 16
+#else
+#  define STM32_TIM5_RES 32
+#endif
+#define STM32_TIM8_RES   16
+#define STM32_TIM9_RES   16
+#define STM32_TIM10_RES  16
+#define STM32_TIM11_RES  16
+#define STM32_TIM12_RES  16
+#define STM32_TIM13_RES  16
+#define STM32_TIM14_RES  16
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure provides the private representation of the "lower-half"
+ * driver state structure.  This structure must be cast-compatible with the
+ * cap_lowerhalf_s structure.
+ */
+
+struct stm32_lowerhalf_s
+{
+  const struct cap_ops_s *ops;       /* Lower half operations */
+  struct stm32_cap_dev_s *cap;       /* stm32 capture driver */
+  bool                   started;    /* True: Timer has been started */
+  const uint8_t          resolution; /* Number of bits in the timer */
+  uint8_t                channel;    /* pwm input channel */
+  uint32_t               clock;      /* Timer clock frequence */
+  uint8_t                duty;       /* Result pwm frequence */
+  uint32_t               freq;       /* Result pwm frequence */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int stm32_cap_handler(int irq, void * context, void * arg);
+
+/* "Lower half" driver methods **********************************************/
+
+static int stm32_start(struct cap_lowerhalf_s *lower);
+static int stm32_stop(struct cap_lowerhalf_s *lower);
+static int stm32_getduty(struct cap_lowerhalf_s *lower, uint8_t *duty);
+static int stm32_getfreq(struct cap_lowerhalf_s *lower, uint32_t *freq);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* "Lower half" driver methods */
+
+static const struct cap_ops_s g_cap_ops =
+{
+  .start       = stm32_start,
+  .stop        = stm32_stop,
+  .getduty     = stm32_getduty,
+  .getfreq     = stm32_getfreq,
+};
+
+#ifdef CONFIG_STM32_TIM1_CAP
+static struct stm32_lowerhalf_s g_cap1_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM1_RES,
+  .channel     = CONFIG_STM32_TIM1_CHANNEL,
+  .clock       = CONFIG_STM32_TIM1_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM2_CAP
+static struct stm32_lowerhalf_s g_cap2_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM2_RES,
+  .channel     = CONFIG_STM32_TIM2_CHANNEL,
+  .clock       = CONFIG_STM32_TIM2_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM3_CAP
+static struct stm32_lowerhalf_s g_cap3_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM3_RES,
+  .channel     = CONFIG_STM32_TIM3_CHANNEL,
+  .clock       = CONFIG_STM32_TIM3_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM4_CAP
+static struct stm32_lowerhalf_s g_cap4_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM4_RES,
+  .channel     = CONFIG_STM32_TIM4_CHANNEL,
+  .clock       = CONFIG_STM32_TIM4_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM5_CAP
+static struct stm32_lowerhalf_s g_cap5_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM5_RES,
+  .channel     = CONFIG_STM32_TIM5_CHANNEL,
+  .clock       = CONFIG_STM32_TIM5_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM8_CAP
+static struct stm32_lowerhalf_s g_cap8_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM8_RES,
+  .channel     = CONFIG_STM32_TIM8_CHANNEL,
+  .clock       = CONFIG_STM32_TIM8_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM9_CAP
+static struct stm32_lowerhalf_s g_cap9_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM9_RES,
+  .channel     = CONFIG_STM32_TIM9_CHANNEL,
+  .clock       = CONFIG_STM32_TIM9_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM10_CAP
+static struct stm32_lowerhalf_s g_cap10_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM10_RES,
+  .channel     = CONFIG_STM32_TIM10_CHANNEL,
+  .clock       = CONFIG_STM32_TIM10_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM11_CAP
+static struct stm32_lowerhalf_s g_cap11_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM11_RES,
+  .channel     = CONFIG_STM32_TIM11_CHANNEL,
+  .clock       = CONFIG_STM32_TIM11_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM12_CAP
+static struct stm32_lowerhalf_s g_cap12_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM12_RES,
+  .channel     = CONFIG_STM32_TIM12_CHANNEL,
+  .clock       = CONFIG_STM32_TIM12_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM13_CAP
+static struct stm32_lowerhalf_s g_cap13_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM13_RES,
+  .channel     = CONFIG_STM32_TIM13_CHANNEL,
+  .clock       = CONFIG_STM32_TIM13_CLOCK,
+};
+#endif
+
+#ifdef CONFIG_STM32_TIM14_CAP
+static struct stm32_lowerhalf_s g_cap14_lowerhalf =
+{
+  .ops         = &g_cap_ops,
+  .resolution  = STM32_TIM14_RES,
+  .channel     = CONFIG_STM32_TIM14_CHANNEL,
+  .clock       = CONFIG_STM32_TIM14_CLOCK,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_cap_handler
+ *
+ * Description:
+ *   timer interrupt handler
+ *
+ * Input Parameters:
+ *
+ * Returned Value:
+ *
+ ****************************************************************************/
+
+static int stm32_cap_handler(int irq, void * context, void * arg)
+{
+  struct stm32_lowerhalf_s *lower = (struct stm32_lowerhalf_s *) arg;
+  uint8_t ch = 0x3 & lower->channel;
+  int period = 0;
+  int flags = 0;
+
+  flags = (int)STM32_CAP_GETFLAGS(lower->cap) ;
+
+  STM32_CAP_ACKFLAGS(lower->cap, flags);
+
+  period = STM32_CAP_GETCAPTURE(lower->cap, ch);
+
+  if (period != 0)
+    {
+      lower->duty = (100 * STM32_CAP_GETCAPTURE(lower->cap, 0x3 & (~ch))) /
+                    period;
+    }
+  else
+    {
+      lower->duty = 0;
+    }
+
+  lower->freq = lower->clock / period;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_start
+ *
+ * Description:
+ *   Start the timer, resetting the time to the current timeout,
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the
+ *           "lower-half" driver state structure.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_start(struct cap_lowerhalf_s *lower)
+{
+  struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
+  int flags = 0;
+  uint32_t maxtimeout = (1 << priv->resolution) - 1;
+
+  if (priv->started)
+    {
+      /* Return EBUSY to indicate that the timer was already running */
+
+      return -EBUSY;
+    }
+
+  switch (priv->channel)
+    {
+      case 1:
+        STM32_CAP_SETSMC(priv->cap, STM32_CAP_SMS_RST |
+                         STM32_CAP_TS_TI1FP1 |
+                         STM32_CAP_MSM_MASK);
+
+        STM32_CAP_SETCLOCK(priv->cap, priv->clock, maxtimeout);
+
+        STM32_CAP_SETCHANNEL(priv->cap, 1,
+                             STM32_CAP_EDGE_RISING |
+                             STM32_CAP_MAPPED_TI1);
+        STM32_CAP_SETCHANNEL(priv->cap, 2,
+                             STM32_CAP_EDGE_FALLING |
+                             STM32_CAP_MAPPED_TI2);
+
+        flags = (int)STM32_CAP_GETFLAGS(priv->cap);
+        STM32_CAP_ACKFLAGS(priv->cap, flags);
+
+        STM32_CAP_SETISR(priv->cap, stm32_cap_handler, priv);
+        STM32_CAP_ENABLEINT(priv->cap, STM32_CAP_FLAG_IRQ_CH_1, true);
+
+        priv->started = true;
+        break;
+
+      case 2:
+        STM32_CAP_SETSMC(priv->cap, STM32_CAP_SMS_RST |
+                         STM32_CAP_TS_TI2FP2 |
+                         STM32_CAP_MSM_MASK);
+
+        STM32_CAP_SETCLOCK(priv->cap, priv->clock, maxtimeout);
+
+        STM32_CAP_SETCHANNEL(priv->cap, 2,
+                             STM32_CAP_EDGE_RISING |
+                             STM32_CAP_MAPPED_TI1);
+        STM32_CAP_SETCHANNEL(priv->cap, 1,
+                             STM32_CAP_EDGE_FALLING |
+                             STM32_CAP_MAPPED_TI2);
+
+        flags = (int)STM32_CAP_GETFLAGS(priv->cap);
+        STM32_CAP_ACKFLAGS(priv->cap, flags);
+
+        STM32_CAP_SETISR(priv->cap, stm32_cap_handler, priv);
+        STM32_CAP_ENABLEINT(priv->cap, STM32_CAP_FLAG_IRQ_CH_2, true);
+
+        priv->started = true;
+        break;
+
+      default:
+        return ERROR;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_stop
+ *
+ * Description:
+ *   Stop the capture
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the
+ *           "lower-half" driver state structure.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_stop(struct cap_lowerhalf_s *lower)
+{
+  struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
+
+  if (priv->started)
+    {
+      STM32_CAP_SETCHANNEL(priv->cap, STM32_CAP_FLAG_IRQ_COUNTER,
+                           STM32_CAP_EDGE_DISABLED);
+      switch (priv->channel)
+        {
+          case 1:
+            STM32_CAP_ENABLEINT(priv->cap, STM32_CAP_FLAG_IRQ_CH_1, false);
+            break;
+
+          case 2:
+            STM32_CAP_ENABLEINT(priv->cap, STM32_CAP_FLAG_IRQ_CH_2, false);
+            break;
+
+          default:
+            return ERROR;
+        }
+
+      STM32_CAP_SETISR(priv->cap, NULL, NULL);
+      priv->started = false;
+      return OK;
+    }
+
+  /* Return ENODEV to indicate that the timer was not running */
+
+  return -ENODEV;
+}
+
+/****************************************************************************
+ * Name: stm32_getduty
+ *
+ * Description:
+ *   get result duty
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the
+ *             "lower-half" driver state structure.
+ *   duty  - DutyCycle * 100.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_getduty(struct cap_lowerhalf_s *lower, uint8_t *duty)
+{
+  struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
+
+  irqstate_t flags = enter_critical_section();
+
+  *duty = priv->duty;
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_getfreq
+ *
+ * Description:
+ *   get result freq
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the
+ *             "lower-half" driver state structure.
+ *   freq  - Frequence in Hz .
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_getfreq(struct cap_lowerhalf_s *lower, uint32_t *freq)
+{
+  struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
+
+  irqstate_t flags = enter_critical_section();
+
+  *freq = priv->freq;
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_cap_initialize
+ *
+ * Description:
+ *   Initialize one timer for use with the upper_level capture driver.
+ *
+ * Input Parameters:
+ *   timer - A number identifying the timer use.  The number of valid timer
+ *     IDs varies with the STM32 MCU and MCU family but is somewhere in
+ *     the range of {1,..,5 8,...,14}.
+ *
+ * Returned Value:
+ *   On success, a pointer to the STM32 lower half capture driver returned.
+ *   NULL is returned on any failure.
+ *
+ ****************************************************************************/
+
+struct cap_lowerhalf_s *stm32_cap_initialize(int timer)
+{
+  struct stm32_lowerhalf_s *lower = NULL;
+
+  switch (timer)
+    {
+#ifdef CONFIG_STM32_TIM1_CAP
+      case 1:
+        lower = &g_cap1_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM2_CAP
+      case 2:
+        lower = &g_cap2_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM3_CAP
+      case 3:
+        lower = &g_cap3_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM4_CAP
+      case 4:
+        lower = &g_cap4_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM5_CAP
+      case 5:
+        lower = &g_cap5_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM6_CAP
+      case 6:
+        lower = &g_cap6_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM9_CAP
+      case 9:
+        lower = &g_cap9_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM10_CAP
+      case 10:
+        lower = &g_cap10_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM11_CAP
+      case 11:
+        lower = &g_cap11_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM12_CAP
+      case 12:
+        lower = &g_cap12_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM13_CAP
+      case 13:
+        lower = &g_cap13_lowerhalf;
+        break;
+#endif
+#ifdef CONFIG_STM32_TIM14_CAP
+      case 14:
+        lower = &g_cap14_lowerhalf;
+        break;
+#endif
+      default:
+        {
+          lower = NULL;
+          goto errout;
+        }
+    }
+
+  /* Initialize the elements of lower half state structure */
+
+  lower->started  = false;
+  lower->cap      = stm32_cap_init(timer);
+
+  if (lower->cap == NULL)
+    {
+      lower = NULL;
+    }
+
+errout:
+  return (struct cap_lowerhalf_s *)lower;
+}
+
+#endif /* CONFIG_STM32_CAP */
diff --git a/arch/arm/src/stm32/stm32_tim.c b/arch/arm/src/stm32/stm32_tim.c
index 7046e1f212..0367f6015e 100644
--- a/arch/arm/src/stm32/stm32_tim.c
+++ b/arch/arm/src/stm32/stm32_tim.c
@@ -71,23 +71,28 @@
  */
 
 #if defined(CONFIG_STM32_TIM1_PWM) || defined (CONFIG_STM32_TIM1_ADC) || \
-    defined(CONFIG_STM32_TIM1_DAC) || defined(CONFIG_STM32_TIM1_QE)
+    defined(CONFIG_STM32_TIM1_DAC) || defined(CONFIG_STM32_TIM1_QE) || \
+    defined(CONFIG_STM32_TIM1_CAP)
 #  undef CONFIG_STM32_TIM1
 #endif
 #if defined(CONFIG_STM32_TIM2_PWM) || defined (CONFIG_STM32_TIM2_ADC) || \
-    defined(CONFIG_STM32_TIM2_DAC) || defined(CONFIG_STM32_TIM2_QE)
+    defined(CONFIG_STM32_TIM2_DAC) || defined(CONFIG_STM32_TIM2_QE) || \
+    defined(CONFIG_STM32_TIM2_CAP)
 #  undef CONFIG_STM32_TIM2
 #endif
 #if defined(CONFIG_STM32_TIM3_PWM) || defined (CONFIG_STM32_TIM3_ADC) || \
-    defined(CONFIG_STM32_TIM3_DAC) || defined(CONFIG_STM32_TIM3_QE)
+    defined(CONFIG_STM32_TIM3_DAC) || defined(CONFIG_STM32_TIM3_QE) || \
+    defined(CONFIG_STM32_TIM3_CAP)
 #  undef CONFIG_STM32_TIM3
 #endif
 #if defined(CONFIG_STM32_TIM4_PWM) || defined (CONFIG_STM32_TIM4_ADC) || \
-    defined(CONFIG_STM32_TIM4_DAC) || defined(CONFIG_STM32_TIM4_QE)
+    defined(CONFIG_STM32_TIM4_DAC) || defined(CONFIG_STM32_TIM4_QE) || \
+    defined(CONFIG_STM32_TIM4_CAP)
 #  undef CONFIG_STM32_TIM4
 #endif
 #if defined(CONFIG_STM32_TIM5_PWM) || defined (CONFIG_STM32_TIM5_ADC) || \
-    defined(CONFIG_STM32_TIM5_DAC) || defined(CONFIG_STM32_TIM5_QE)
+    defined(CONFIG_STM32_TIM5_DAC) || defined(CONFIG_STM32_TIM5_QE) || \
+    defined(CONFIG_STM32_TIM5_CAP)
 #  undef CONFIG_STM32_TIM5
 #endif
 #if defined(CONFIG_STM32_TIM6_PWM) || defined (CONFIG_STM32_TIM6_ADC) || \
@@ -99,31 +104,38 @@
 #  undef CONFIG_STM32_TIM7
 #endif
 #if defined(CONFIG_STM32_TIM8_PWM) || defined (CONFIG_STM32_TIM8_ADC) || \
-    defined(CONFIG_STM32_TIM8_DAC) || defined(CONFIG_STM32_TIM8_QE)
+    defined(CONFIG_STM32_TIM8_DAC) || defined(CONFIG_STM32_TIM8_QE) || \
+    defined(CONFIG_STM32_TIM8_CAP)
 #  undef CONFIG_STM32_TIM8
 #endif
 #if defined(CONFIG_STM32_TIM9_PWM) || defined (CONFIG_STM32_TIM9_ADC) || \
-    defined(CONFIG_STM32_TIM9_DAC) || defined(CONFIG_STM32_TIM9_QE)
+    defined(CONFIG_STM32_TIM9_DAC) || defined(CONFIG_STM32_TIM9_QE) || \
+    defined(CONFIG_STM32_TIM9_CAP)
 #  undef CONFIG_STM32_TIM9
 #endif
 #if defined(CONFIG_STM32_TIM10_PWM) || defined (CONFIG_STM32_TIM10_ADC) || \
-    defined(CONFIG_STM32_TIM10_DAC) || defined(CONFIG_STM32_TIM10_QE)
+    defined(CONFIG_STM32_TIM10_DAC) || defined(CONFIG_STM32_TIM10_QE) || \
+    defined(CONFIG_STM32_TIM10_CAP)
 #  undef CONFIG_STM32_TIM10
 #endif
 #if defined(CONFIG_STM32_TIM11_PWM) || defined (CONFIG_STM32_TIM11_ADC) || \
-    defined(CONFIG_STM32_TIM11_DAC) || defined(CONFIG_STM32_TIM11_QE)
+    defined(CONFIG_STM32_TIM11_DAC) || defined(CONFIG_STM32_TIM11_QE) || \
+    defined(CONFIG_STM32_TIM11_CAP)
 #  undef CONFIG_STM32_TIM11
 #endif
 #if defined(CONFIG_STM32_TIM12_PWM) || defined (CONFIG_STM32_TIM12_ADC) || \
-    defined(CONFIG_STM32_TIM12_DAC) || defined(CONFIG_STM32_TIM12_QE)
+    defined(CONFIG_STM32_TIM12_DAC) || defined(CONFIG_STM32_TIM12_QE) || \
+    defined(CONFIG_STM32_TIM12_CAP)
 #  undef CONFIG_STM32_TIM12
 #endif
 #if defined(CONFIG_STM32_TIM13_PWM) || defined (CONFIG_STM32_TIM13_ADC) || \
-    defined(CONFIG_STM32_TIM13_DAC) || defined(CONFIG_STM32_TIM13_QE)
+    defined(CONFIG_STM32_TIM13_DAC) || defined(CONFIG_STM32_TIM13_QE) || \
+    defined(CONFIG_STM32_TIM13_CAP)
 #  undef CONFIG_STM32_TIM13
 #endif
 #if defined(CONFIG_STM32_TIM14_PWM) || defined (CONFIG_STM32_TIM14_ADC) || \
-    defined(CONFIG_STM32_TIM14_DAC) || defined(CONFIG_STM32_TIM14_QE)
+    defined(CONFIG_STM32_TIM14_DAC) || defined(CONFIG_STM32_TIM14_QE) || \
+    defined(CONFIG_STM32_TIM14_CAP)
 #  undef CONFIG_STM32_TIM14
 #endif
 #if defined(CONFIG_STM32_TIM15_PWM) || defined (CONFIG_STM32_TIM15_ADC) || \


[incubator-nuttx] 02/03: drivers/timers/capture.c: add support of pwm capture driver

Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b41929522c9d86d35cec9cf80665bfeab49750ee
Author: zouboan <ff...@feedforward.com.cn>
AuthorDate: Sun Jun 26 20:00:38 2022 +0800

    drivers/timers/capture.c: add support of pwm capture driver
---
 drivers/timers/Kconfig         |   7 +
 drivers/timers/Make.defs       |   6 +
 drivers/timers/capture.c       | 377 +++++++++++++++++++++++++++++++++++++++++
 include/nuttx/fs/ioctl.h       |   7 +-
 include/nuttx/timers/capture.h | 128 ++++++++++++++
 5 files changed, 524 insertions(+), 1 deletion(-)

diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig
index 8579a7a190..5160ae910e 100644
--- a/drivers/timers/Kconfig
+++ b/drivers/timers/Kconfig
@@ -53,6 +53,13 @@ config PWM_NCHANNELS
 endif # PWM_MULTICHAN
 endif # PWM
 
+config CAPTURE
+	bool "Capture Driver Support"
+	default n
+	---help---
+		This selection enables building of the "upper-half" Capture driver.
+		See include/nuttx/timers/capture.h for further Capture driver information.
+
 config TIMER
 	bool "Timer Support"
 	default n
diff --git a/drivers/timers/Make.defs b/drivers/timers/Make.defs
index 902391a466..44e268b5ba 100644
--- a/drivers/timers/Make.defs
+++ b/drivers/timers/Make.defs
@@ -104,6 +104,12 @@ ifeq ($(CONFIG_PWM),y)
   TMRVPATH = :timers
 endif
 
+ifeq ($(CONFIG_CAPTURE),y)
+  CSRCS += capture.c
+  TMRDEPPATH = --dep-path timers
+  TMRVPATH = :timers
+endif
+
 # Include timer build support (if any were selected)
 
 DEPPATH += $(TMRDEPPATH)
diff --git a/drivers/timers/capture.c b/drivers/timers/capture.c
new file mode 100644
index 0000000000..9537f72a06
--- /dev/null
+++ b/drivers/timers/capture.c
@@ -0,0 +1,377 @@
+/****************************************************************************
+ * drivers/timers/capture.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/timers/capture.h>
+
+#include <arch/irq.h>
+
+#ifdef CONFIG_CAPTURE
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Debug ********************************************************************/
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+/* This structure describes the state of the upper half driver */
+
+struct cap_upperhalf_s
+{
+  uint8_t                    crefs;    /* The number of times the device has been opened */
+  sem_t                      exclsem;  /* Supports mutual exclusion */
+  FAR struct cap_lowerhalf_s *lower;   /* lower-half state */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int     cap_open(FAR struct file *filep);
+static int     cap_close(FAR struct file *filep);
+static ssize_t cap_read(FAR struct file *filep, FAR char *buffer,
+                       size_t buflen);
+static ssize_t cap_write(FAR struct file *filep, FAR const char *buffer,
+                        size_t buflen);
+static int cap_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_capops =
+{
+  cap_open,  /* open */
+  cap_close, /* close */
+  cap_read,  /* read */
+  cap_write, /* write */
+  NULL,      /* seek */
+  cap_ioctl, /* ioctl */
+  NULL,      /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  NULL       /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cap_open
+ *
+ * Description:
+ *   This function is called whenever the PWM Capture device is opened.
+ *
+ ****************************************************************************/
+
+static int cap_open(FAR struct file *filep)
+{
+  FAR struct inode           *inode = filep->f_inode;
+  FAR struct cap_upperhalf_s *upper = inode->i_private;
+  uint8_t                     tmp;
+  int                         ret;
+
+  /* Get exclusive access to the device structures */
+
+  ret = nxsem_wait(&upper->exclsem);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  /* Increment the count of references to the device.  If this is the first
+   * time that the driver has been opened for this device, then initialize
+   * the device.
+   */
+
+  tmp = upper->crefs + 1;
+  if (tmp == 0)
+    {
+      /* More than 255 opens; uint8_t overflows to zero */
+
+      ret = -EMFILE;
+      goto errout_with_sem;
+    }
+
+  /* Check if this is the first time that the driver has been opened. */
+
+  if (tmp == 1)
+    {
+      FAR struct cap_lowerhalf_s *lower = upper->lower;
+
+      /* Yes.. perform one time hardware initialization. */
+
+      DEBUGASSERT(lower->ops->start != NULL);
+
+      ret = lower->ops->start(lower);
+      if (ret < 0)
+        {
+          goto errout_with_sem;
+        }
+    }
+
+  /* Save the new open count on success */
+
+  upper->crefs = tmp;
+  ret = OK;
+
+errout_with_sem:
+  nxsem_post(&upper->exclsem);
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: cap_close
+ *
+ * Description:
+ *   This function is called when the PWM Capture device is closed.
+ *
+ ****************************************************************************/
+
+static int cap_close(FAR struct file *filep)
+{
+  FAR struct inode           *inode = filep->f_inode;
+  FAR struct cap_upperhalf_s *upper = inode->i_private;
+  int                         ret;
+
+  /* Get exclusive access to the device structures */
+
+  ret = nxsem_wait(&upper->exclsem);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  /* Decrement the references to the driver.  If the reference count will
+   * decrement to 0, then uninitialize the driver.
+   */
+
+  if (upper->crefs > 1)
+    {
+      upper->crefs--;
+    }
+  else
+    {
+      FAR struct cap_lowerhalf_s *lower = upper->lower;
+
+      /* There are no more references to the port */
+
+      upper->crefs = 0;
+
+      /* Disable the PWM Capture device */
+
+      DEBUGASSERT(lower->ops->stop != NULL);
+
+      lower->ops->stop(lower);
+    }
+
+  nxsem_post(&upper->exclsem);
+  ret = OK;
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: cap_read
+ *
+ * Description:
+ *   A dummy read method.  This is provided only to satisfy the VFS layer.
+ *
+ ****************************************************************************/
+
+static ssize_t cap_read(FAR struct file *filep,
+                       FAR char *buffer,
+                       size_t buflen)
+{
+  /* Return zero -- usually meaning end-of-file */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: cap_write
+ *
+ * Description:
+ *   A dummy write method.  This is provided only to satisfy the VFS layer.
+ *
+ ****************************************************************************/
+
+static ssize_t cap_write(FAR struct file *filep,
+                        FAR const char *buffer,
+                        size_t buflen)
+{
+  /* Return a failure */
+
+  return -EPERM;
+}
+
+/****************************************************************************
+ * Name: cap_ioctl
+ *
+ * Description:
+ *   The standard ioctl method.
+ *   This is where ALL of the PWM Capture work is done.
+ *
+ ****************************************************************************/
+
+static int cap_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+  FAR struct inode          *inode = filep->f_inode;
+  FAR struct cap_upperhalf_s *upper;
+  FAR struct cap_lowerhalf_s *lower;
+  int                        ret;
+
+  snprintf("cmd: %d arg: %ld\n", cmd, arg);
+  upper = inode->i_private;
+  DEBUGASSERT(upper != NULL);
+  lower = upper->lower;
+  DEBUGASSERT(lower != NULL);
+
+  /* Get exclusive access to the device structures */
+
+  ret = nxsem_wait(&upper->exclsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Handle built-in ioctl commands */
+
+  switch (cmd)
+    {
+      /* CAPIOC_DUTYCYCLE - Get the pwm duty from the capture.
+       * Argument: int8_t pointer to the location to return the duty.
+       */
+
+      case CAPIOC_DUTYCYCLE:
+        {
+          FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg);
+          DEBUGASSERT(lower->ops->getduty != NULL && ptr);
+          ret = lower->ops->getduty(lower, ptr);
+        }
+        break;
+
+      /* CAPIOC_FREQUENCE - Get the pulse frequence from the capture.
+       * Argument: int32_t pointer to the location to return the frequence.
+       */
+
+      case CAPIOC_FREQUENCE:
+        {
+          FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
+          DEBUGASSERT(lower->ops->getfreq != NULL && ptr);
+          ret = lower->ops->getfreq(lower, ptr);
+        }
+        break;
+
+      /* Any unrecognized IOCTL commands might be platform-specific ioctl
+       * commands
+       */
+
+      default:
+        {
+          snprintf("Forwarding unrecognized cmd: %d arg: %ld\n", cmd, arg);
+        }
+        break;
+    }
+
+  nxsem_post(&upper->exclsem);
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cap_register
+ *
+ * Description:
+ *   Register the PWM Capture lower half device as 'devpath'
+ *
+ * Input Parameters:
+ *   devpath - The full path to the driver to register. E.g., "/dev/cap0"
+ *   lower - An instance of the lower half interface
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.  The following
+ *   possible error values may be returned (most are returned by
+ *   register_driver()):
+ *
+ *   EINVAL - 'path' is invalid for this operation
+ *   EEXIST - An inode already exists at 'path'
+ *   ENOMEM - Failed to allocate in-memory resources for the operation
+ *
+ ****************************************************************************/
+
+int cap_register(FAR const char *devpath, FAR struct cap_lowerhalf_s *lower)
+{
+  FAR struct cap_upperhalf_s *upper;
+
+  /* Allocate the upper-half data structure */
+
+  upper = (FAR struct cap_upperhalf_s *)
+           kmm_zalloc(sizeof(struct cap_upperhalf_s));
+  if (!upper)
+    {
+      snprintf("ERROR: Allocation failed\n");
+      return -ENOMEM;
+    }
+
+  /* Initialize the PWM Capture device structure
+   * (it was already zeroed by kmm_zalloc())
+   */
+
+  nxsem_init(&upper->exclsem, 0, 1);
+  upper->lower = lower;
+
+  /* Register the PWM Capture device */
+
+  return register_driver(devpath, &g_capops, 0666, upper);
+}
+
+#endif /* CONFIG_CAPTURE */
diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h
index 469b0c615a..e81847002e 100644
--- a/include/nuttx/fs/ioctl.h
+++ b/include/nuttx/fs/ioctl.h
@@ -56,7 +56,7 @@
 #define _AUDIOIOCBASE   (0x1000) /* Audio ioctl commands */
 #define _LCDIOCBASE     (0x1100) /* LCD character driver ioctl commands */
 #define _SLCDIOCBASE    (0x1200) /* Segment LCD ioctl commands */
-                                 /* 0x1300: Not used */
+#define _CAPIOCBASE     (0x1300) /* Capture ioctl commands */
 #define _WLCIOCBASE     (0x1400) /* Wireless modules ioctl character driver commands */
 #define _CFGDIOCBASE    (0x1500) /* Config Data device (app config) ioctl commands */
 #define _TCIOCBASE      (0x1600) /* Timer ioctl commands */
@@ -325,6 +325,11 @@
 #define _PWMIOCVALID(c)   (_IOC_TYPE(c)==_PWMIOCBASE)
 #define _PWMIOC(nr)       _IOC(_PWMIOCBASE,nr)
 
+/* NuttX Capture ioctl definitions (see nuttx/timers/capture.h) *************/
+
+#define _CAPIOCVALID(c)   (_IOC_TYPE(c)==_CAPIOCBASE)
+#define _CAPIOC(nr)       _IOC(_CAPIOCBASE,nr)
+
 /* NuttX USB CDC/ACM serial driver ioctl definitions ************************/
 
 /* (see nuttx/usb/cdcacm.h) */
diff --git a/include/nuttx/timers/capture.h b/include/nuttx/timers/capture.h
new file mode 100644
index 0000000000..594d72d2af
--- /dev/null
+++ b/include/nuttx/timers/capture.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+ * include/nuttx/timers/capture.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_TIMERS_CAPTURE_H
+#define __INCLUDE_NUTTX_TIMERS_CAPTURE_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/fs/ioctl.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* IOCTL Commands ***********************************************************/
+
+#define CAPIOC_DUTYCYCLE _CAPIOC(1)
+#define CAPIOC_FREQUENCE _CAPIOC(2)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This structure provides the "lower-half" driver operations available to
+ * the "upper-half" driver.
+ */
+
+struct cap_lowerhalf_s;
+struct cap_ops_s
+{
+  /* Required methods *******************************************************/
+
+  /* Start the capture, resetting the configure of timers */
+
+  CODE int (*start)(FAR struct cap_lowerhalf_s *lower);
+
+  /* Stop the capture */
+
+  CODE int (*stop)(FAR struct cap_lowerhalf_s *lower);
+
+  /* Get the result pwm capture duty value */
+
+  CODE int (*getduty)(FAR struct cap_lowerhalf_s *lower,
+                      FAR uint8_t *duty);
+
+  /* Get the result pwm capture frequence value */
+
+  CODE int (*getfreq)(FAR struct cap_lowerhalf_s *lower,
+                      FAR uint32_t *freq);
+};
+
+/* This structure provides the publicly visible representation of the
+ * "lower-half" driver state structure.  "lower half" drivers will have an
+ * internal structure definition that will be cast-compatible with this
+ * structure definitions.
+ */
+
+struct cap_lowerhalf_s
+{
+  FAR const struct cap_ops_s *ops;
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cap_register
+ *
+ * Description:
+ *   Register the pulse capture lower half device as 'devpath'
+ *
+ * Input Parameters:
+ *   devpath - The full path to the driver to register. E.g., "/dev/cap0"
+ *   lower - An instance of the lower half interface
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.  The following
+ *   possible error values may be returned (most are returned by
+ *   register_driver()):
+ *
+ *   EINVAL - 'path' is invalid for this operation
+ *   EEXIST - An inode already exists at 'path'
+ *   ENOMEM - Failed to allocate in-memory resources for the operation
+ *
+ ****************************************************************************/
+
+int cap_register(FAR const char *devpath,
+                 FAR struct cap_lowerhalf_s *lower);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_TIMERS_CAPTURE_H */