From b41929522c9d86d35cec9cf80665bfeab49750ee Mon Sep 17 00:00:00 2001 From: zouboan Date: Sun, 26 Jun 2022 20:00:38 +0800 Subject: [PATCH] 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(-) create mode 100644 drivers/timers/capture.c create mode 100644 include/nuttx/timers/capture.h 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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 +#include + +/**************************************************************************** + * 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 */