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/08/12 17:10:23 UTC

[incubator-nuttx] branch master updated: samv7/common: add support for GPIO driver based encoder

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


The following commit(s) were added to refs/heads/master by this push:
     new c7c78972d6 samv7/common: add support for GPIO driver based encoder
c7c78972d6 is described below

commit c7c78972d654d6adc100afea6e883e04930f1883
Author: Michal Lenc <mi...@seznam.cz>
AuthorDate: Fri Aug 12 12:43:01 2022 +0200

    samv7/common: add support for GPIO driver based encoder
    
    This commit adds support for GPIO driver based encoder for SAMv7 boards.
    This encoder do not use the microcontroller's dedicated driver but two
    GPIO pins with interrupts. The position is calculated based on those
    interrupts.
    
    This can be used for boards that do not have pins routed to the dedicated
    driver.
    
    Signed-off-by: Michal Lenc <mi...@seznam.cz>
---
 boards/arm/samv7/common/Kconfig                  |   9 +
 boards/arm/samv7/common/include/board_gpio_enc.h |  71 +++++
 boards/arm/samv7/common/src/Make.defs            |   4 +
 boards/arm/samv7/common/src/sam_gpio_enc.c       | 363 +++++++++++++++++++++++
 4 files changed, 447 insertions(+)

diff --git a/boards/arm/samv7/common/Kconfig b/boards/arm/samv7/common/Kconfig
index 8fa68fc64c..d779af2fdd 100644
--- a/boards/arm/samv7/common/Kconfig
+++ b/boards/arm/samv7/common/Kconfig
@@ -3,6 +3,15 @@
 # see the file kconfiglanguage.txt in the NuttX tools repository.
 #
 
+config SAMV7_GPIO_ENC
+	bool "GPIO driver based encoder"
+	default n
+	depends on SENSORS_QENCODER
+	---help---
+		This options allows the usage of an encoder based on a GPIO driver.
+		The encoder is initialized by sam_gpio_enc_init() function call
+		from board specific bringup code.
+
 config SAMV7_HSMCI0_AUTOMOUNT
 	bool "HSMCI0 automounter"
 	default n
diff --git a/boards/arm/samv7/common/include/board_gpio_enc.h b/boards/arm/samv7/common/include/board_gpio_enc.h
new file mode 100644
index 0000000000..2988f3e9b9
--- /dev/null
+++ b/boards/arm/samv7/common/include/board_gpio_enc.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+ * boards/arm/samv7/common/include/board_gpio_enc.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 __BOARDS_ARM_SAMV7_COMMON_INCLUDE_BOARD_GPIO_ENC_H
+#define __BOARDS_ARM_SAMV7_COMMON_INCLUDE_BOARD_GPIO_ENC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "sam_gpio.h"
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Functions Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sam_gpio_enc_init
+ *
+ * Description:
+ *   Initialize and register the ENC driver.
+ *
+ * Input Parameters:
+ *   enca_cfg - ENC_A pin
+ *   encb_cfg - ENC_B pin
+ *   enca_irq - ENC_A interrupt
+ *   encb_irq - ENC_B interrupt
+ *
+ ****************************************************************************/
+
+int sam_gpio_enc_init(gpio_pinset_t enca_cfg, gpio_pinset_t encb_cfg,
+                      int enca_irq, int encb_irq);
+
+#endif /* __ASSEMBLY__ */
+#endif /* __BOARDS_ARM_SAMV7_COMMON_INCLUDE_BOARD_GPIO_ENC_H */
diff --git a/boards/arm/samv7/common/src/Make.defs b/boards/arm/samv7/common/src/Make.defs
index 9b060b8952..0b5cbf78ad 100644
--- a/boards/arm/samv7/common/src/Make.defs
+++ b/boards/arm/samv7/common/src/Make.defs
@@ -42,6 +42,10 @@ ifeq ($(CONFIG_FS_AUTOMOUNTER),y)
 CSRCS += sam_automount.c
 endif
 
+ifeq ($(CONFIG_SAMV7_GPIO_ENC),y)
+CSRCS += sam_gpio_enc.c
+endif
+
 DEPPATH += --dep-path src
 VPATH += :src
 CFLAGS += $(shell $(INCDIR) "$(CC)" $(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src)
diff --git a/boards/arm/samv7/common/src/sam_gpio_enc.c b/boards/arm/samv7/common/src/sam_gpio_enc.c
new file mode 100644
index 0000000000..86f794eade
--- /dev/null
+++ b/boards/arm/samv7/common/src/sam_gpio_enc.c
@@ -0,0 +1,363 @@
+/****************************************************************************
+ * boards/arm/samv7/common/src/sam_gpio_enc.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 <stdio.h>
+
+#include <nuttx/board.h>
+#include <nuttx/sensors/qencoder.h>
+#include <arch/board/board.h>
+#include <nuttx/irq.h>
+
+#include "chip.h"
+#include "arm_internal.h"
+
+#include "board_gpio_enc.h"
+
+#if defined(CONFIG_SENSORS_QENCODER) && defined(CONFIG_SAMV7_GPIO_ENC)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct sam_qeconfig_s
+{
+  gpio_pinset_t enca;         /* ENC_A pin */
+  gpio_pinset_t encb;         /* ENC_B pin */
+  int enca_irq;               /* ENC_A irq */
+  int encb_irq;               /* ENC_B irq */
+  uint32_t  position;         /* Current position */
+  uint32_t  position_base;    /* Base position */
+  uint32_t  error;            /* Error count */
+};
+
+struct sam_gpio_enc_lowerhalf_s
+{
+  /* The first field of this state structure must be a pointer to the lower-
+   * half callback structure:
+   */
+
+  FAR const struct qe_ops_s *ops;           /* Lower half callback structure */
+  FAR struct sam_qeconfig_s *config;        /* static configuration */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int board_gpio_enc_irqx(gpio_pinset_t pinset, int irq,
+                               xcpt_t irqhandler, void *arg);
+static int sam_gpio_enc_interrupt(int irq, FAR void *context,
+                                     FAR void *arg);
+static int sam_gpio_enc_position(FAR struct qe_lowerhalf_s *lower,
+                                 FAR int32_t *pos);
+static int sam_gpio_enc_setup(FAR struct qe_lowerhalf_s *lower);
+static int sam_gpio_enc_shutdown(FAR struct qe_lowerhalf_s *lower);
+static int sam_gpio_enc_reset(FAR struct qe_lowerhalf_s *lower);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct qe_ops_s g_qecallbacks =
+{
+  .setup     = sam_gpio_enc_setup,
+  .shutdown  = sam_gpio_enc_shutdown,
+  .position  = sam_gpio_enc_position,
+  .setposmax = NULL,
+  .reset     = sam_gpio_enc_reset,
+  .ioctl     = NULL,
+};
+
+static struct sam_qeconfig_s sam_gpio_enc_config =
+{
+  .position = 0,
+  .position_base = 0,
+  .error = 0,
+};
+
+static struct sam_gpio_enc_lowerhalf_s sam_gpio_enc_priv =
+{
+  .ops = &g_qecallbacks,
+  .config = &sam_gpio_enc_config,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_gpio_enc_irqx
+ *
+ * Description:
+ *   This function implements the core of the board_button_irq() logic.
+ *
+ ****************************************************************************/
+
+static int board_gpio_enc_irqx(gpio_pinset_t pinset, int irq,
+                             xcpt_t irqhandler, void *arg)
+{
+  irqstate_t flags;
+
+  /* Disable interrupts until we are done. This guarantees that the
+   * following operations are atomic.
+   */
+
+  flags = enter_critical_section();
+
+  /* Are we attaching or detaching? */
+
+  if (irqhandler != NULL)
+    {
+      /* Configure the interrupt */
+
+      sam_gpioirq(pinset);
+      irq_attach(irq, irqhandler, arg);
+      sam_gpioirqenable(irq);
+    }
+  else
+    {
+      /* Detach and disable the interrupt */
+
+      irq_detach(irq);
+      sam_gpioirqdisable(irq);
+    }
+
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sam_gpio_enc_interrupt
+ *
+ * Description:
+ *   This function implements the core of the board_button_irq() logic.
+ *
+ ****************************************************************************/
+
+static int sam_gpio_enc_interrupt(int irq, FAR void *context,
+                                     FAR void *arg)
+{
+  FAR struct sam_gpio_enc_lowerhalf_s *dev =
+    (FAR struct sam_gpio_enc_lowerhalf_s *)arg;
+  FAR struct sam_qeconfig_s *priv = (struct sam_qeconfig_s *)dev->config;
+
+  unsigned int state_a;
+  unsigned int state_b;
+  int32_t incr;
+  uint32_t new;
+  uint32_t incr_mask;
+
+  /* Read the status of encoder pins */
+
+  state_a = sam_gpioread(priv->enca);
+  state_b = sam_gpioread(priv->encb);
+
+  new = (state_b << 1 | (state_a ^ state_b));
+  incr = ((new - priv->position + 1) & 3) - 1;
+  incr_mask = (int32_t)(1 - incr) >> 31;
+
+  /* Increment position */
+
+  priv->position += incr & ~incr_mask;
+
+  /* Count error */
+
+  priv->error -= incr_mask;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sam_gpio_enc_position
+ *
+ * Description:
+ *   Return the current position measurement.
+ *
+ ****************************************************************************/
+
+static int sam_gpio_enc_position(FAR struct qe_lowerhalf_s *lower,
+                                 FAR int32_t *pos)
+{
+  FAR struct sam_gpio_enc_lowerhalf_s *priv =
+    (FAR struct sam_gpio_enc_lowerhalf_s *)lower;
+  FAR struct sam_qeconfig_s *config = priv->config;
+
+  *pos = config->position - config->position_base;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sam_gpio_enc_setup
+ *
+ * Description:
+ *   This method is called when the driver is opened.  The lower half driver
+ *   should configure and initialize the device so that it is ready for use.
+ *   The initial position value should be zero.
+ *
+ ****************************************************************************/
+
+static int sam_gpio_enc_setup(FAR struct qe_lowerhalf_s *lower)
+{
+  FAR struct sam_gpio_enc_lowerhalf_s *priv =
+    (FAR struct sam_gpio_enc_lowerhalf_s *)lower;
+  FAR struct sam_qeconfig_s *config = priv->config;
+  int ret;
+  unsigned int state_a;
+  unsigned int state_b;
+  uint32_t new;
+
+  /* Configure GPIOs */
+
+  sam_configgpio(config->encb);
+  sam_configgpio(config->enca);
+
+  /* Reset the encoder position */
+
+  state_a = sam_gpioread(config->enca);
+  state_b = sam_gpioread(config->encb);
+  new = (state_b << 1 | (state_a ^ state_b));
+  config->position_base = config->position = new;
+
+  /* Setup interrups for ENC_A and ENC_B pins. */
+
+  ret = board_gpio_enc_irqx(config->enca, config->enca_irq,
+                            sam_gpio_enc_interrupt, priv);
+  if (ret != OK)
+    {
+      snerr("ERROR: board_gpio_enc_irqx for ENC_A failed %d\n", ret);
+      return -ERROR;
+    }
+
+  ret = board_gpio_enc_irqx(config->encb, config->encb_irq,
+                            sam_gpio_enc_interrupt, priv);
+  if (ret != OK)
+    {
+      snerr("ERROR: board_gpio_enc_irqx for ENC_B failed %d\n", ret);
+      return -ERROR;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: sam_gpio_enc_shutdown
+ *
+ * Description:
+ *   This method is called when the driver is closed.  The lower half driver
+ *   should stop data collection, free any resources, disable timer hardware,
+ *   and put the system into the lowest possible power usage state
+ *
+ ****************************************************************************/
+
+static int sam_gpio_enc_shutdown(FAR struct qe_lowerhalf_s *lower)
+{
+  FAR struct sam_gpio_enc_lowerhalf_s *priv =
+    (FAR struct sam_gpio_enc_lowerhalf_s *)lower;
+  FAR struct sam_qeconfig_s *config = priv->config;
+  int ret;
+
+  /* Disable GPIO interrupts. */
+
+  ret = board_gpio_enc_irqx(config->enca, config->enca_irq,
+                            NULL, priv);
+  if (ret != OK)
+    {
+      snerr("ERROR: board_gpio_enc_irqx disable for ENC_A failed %d\n",
+            ret);
+      return -ERROR;
+    }
+
+  ret = board_gpio_enc_irqx(config->encb, config->encb_irq,
+                            NULL, priv);
+  if (ret != OK)
+    {
+      snerr("ERROR: board_gpio_enc_irqx disable for ENC_B failed %d\n",
+            ret);
+      return -ERROR;
+    }
+
+  return OK;
+}
+
+static int sam_gpio_enc_reset(FAR struct qe_lowerhalf_s *lower)
+{
+  FAR struct sam_gpio_enc_lowerhalf_s *priv =
+    (FAR struct sam_gpio_enc_lowerhalf_s *)lower;
+  FAR struct sam_qeconfig_s *config =
+    (FAR struct sam_qeconfig_s *)priv->config;
+
+  config->position = config->position_base;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sam_gpio_enc_init
+ *
+ * Description:
+ *   Initialize and register the ENC driver.
+ *
+ * Input Parameters:
+ *   enca_cfg - ENC_A pin
+ *   encb_cfg - ENC_B pin
+ *   enca_irq - ENC_A interrupt
+ *   encb_irq - ENC_B interrupt
+ *
+ ****************************************************************************/
+
+int sam_gpio_enc_init(gpio_pinset_t enca_cfg, gpio_pinset_t encb_cfg,
+                      int enca_irq, int encb_irq)
+{
+  int ret;
+
+  FAR struct sam_gpio_enc_lowerhalf_s *dev =
+    (struct sam_gpio_enc_lowerhalf_s *)&sam_gpio_enc_priv;
+  FAR struct sam_qeconfig_s *priv = (struct sam_qeconfig_s *)dev->config;
+
+  /* Register the device as "dev/gpio_enc". */
+
+  ret = qe_register("/dev/gpio_enc", (FAR struct qe_lowerhalf_s *)dev);
+  if (ret < 0)
+    {
+      snerr("ERROR: qe_register failed: %d\n", ret);
+      return -ERROR;
+    }
+
+  priv->enca = enca_cfg;
+  priv->encb = encb_cfg;
+  priv->enca_irq = enca_irq;
+  priv->encb_irq = encb_irq;
+
+  return OK;
+}
+
+#endif /* CONFIG_SENSORS_QENCODER && CONFIG_SAMV7_GPIO_ENC */