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/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 1a2e752ea7 arch/arm/src/samv7/sam_qencoder.c: add support for GETINDEX
ioctl call
1a2e752ea7 is described below
commit 1a2e752ea77f6dd4afe3aff426e16e20b6f2e6f4
Author: Pressl, Štěpán <[email protected]>
AuthorDate: Wed Apr 3 21:39:53 2024 +0200
arch/arm/src/samv7/sam_qencoder.c: add support for GETINDEX ioctl call
The SAMV7's qencoder driver now supports the GETINDEX ioctl call
which does not reset the internal Timer/Counter and returns
the current position, position of the last index and the number
of captured indexes to a struct qe_index_s pointer. Because the
SAMV7's timers are 16bit, the extension to 32 bits must be done.
Select CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX in the Kconfig to
enable this functionality.
This driver does not obey the instructions given in the ATSAMV7
2023 datasheet because the recommended trigger resets the internal
counter which is not desired. Instead, a capture into capture A
and capture B registers is used. This way if an event happens
(the rising edge of the index signal), the current counter's value
is captured.
Signed-off-by: Stepan Pressl <[email protected]>
---
arch/arm/src/samv7/Kconfig | 13 +++
arch/arm/src/samv7/sam_qencoder.c | 183 +++++++++++++++++++++++++++++++++++++-
2 files changed, 192 insertions(+), 4 deletions(-)
diff --git a/arch/arm/src/samv7/Kconfig b/arch/arm/src/samv7/Kconfig
index f102d0b42a..f33b3a9018 100644
--- a/arch/arm/src/samv7/Kconfig
+++ b/arch/arm/src/samv7/Kconfig
@@ -3786,6 +3786,19 @@ config SAMV7_TC3_QE
---help---
Reserve TC3 for use by QEncoder.
+config SAMV7_QENCODER_ENABLE_GETINDEX
+ bool "Support ioctl GETINDEX call"
+ default n
+ ---help---
+ Index signal does not reset the internal counter.
+ However, each time index is hit, capture A and B registers
+ are used to save the counter's value instead of
+ resetting it.
+
+ This provides support for the GETINDEX ioctl call
+ with the qe_index_s struct. The 16 to 32 bit extension
+ of the driver's counter is also done.
+
config SAMV7_QENCODER_FILTER
bool "Enable filtering on SAMV7 QEncoder input"
default y
diff --git a/arch/arm/src/samv7/sam_qencoder.c
b/arch/arm/src/samv7/sam_qencoder.c
index ba28a73066..3539cd76e5 100644
--- a/arch/arm/src/samv7/sam_qencoder.c
+++ b/arch/arm/src/samv7/sam_qencoder.c
@@ -67,6 +67,19 @@ struct sam_lowerhalf_s
TC_HANDLE tch; /* Handle returned by sam_tc_initialize() */
bool inuse; /* True: The lower-half driver is in-use */
+
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+ /* qe_index_s IOCTL support:
+ * All variables are of an unsigned type, while the variables in the
+ * struct qe_index_s are of a signed type. The reason for using unsigned
+ * types is that the operations on unsigned types when extending is
+ * defined (overflow arithmetics).
+ */
+
+ uint32_t last_pos; /* The actual position */
+ uint32_t last_index; /* The actual position of the last index */
+ uint32_t index_cnt; /* The number of index hits */
+#endif
};
/****************************************************************************
@@ -85,6 +98,14 @@ static int sam_position(struct qe_lowerhalf_s *lower,
int32_t *pos);
static int sam_reset(struct qe_lowerhalf_s *lower);
static int sam_ioctl(struct qe_lowerhalf_s *lower, int cmd,
unsigned long arg);
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+static int sam_qeindex(struct qe_lowerhalf_s *lower,
+ struct qe_index_s *dest);
+static inline int32_t sam_qe_pos_16to32b(struct qe_lowerhalf_s *lower,
+ uint32_t current_pos);
+static inline int32_t sam_qe_indx_pos_16to32b(struct qe_lowerhalf_s *lower,
+ uint32_t current_indx_pos);
+#endif
/****************************************************************************
* Private Data
@@ -229,10 +250,15 @@ static int sam_shutdown(struct qe_lowerhalf_s *lower)
static int sam_position(struct qe_lowerhalf_s *lower, int32_t *pos)
{
struct sam_lowerhalf_s *priv = (struct sam_lowerhalf_s *)lower;
+ uint32_t new_pos;
+ new_pos = sam_tc_getcounter(priv->tch);
/* Return the counter value */
-
- *pos = (int32_t)sam_tc_getcounter(priv->tch);
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+ *pos = sam_qe_pos_16to32b(lower, new_pos);
+#else
+ *pos = (int32_t)new_pos;
+#endif
return OK;
}
@@ -270,10 +296,139 @@ static int sam_reset(struct qe_lowerhalf_s *lower)
static int sam_ioctl(struct qe_lowerhalf_s *lower, int cmd,
unsigned long arg)
{
- /* No ioctl commands supported */
-
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+ switch (cmd)
+ {
+ case QEIOC_GETINDEX:
+ {
+ /* Call the qeindex function */
+
+ sam_qeindex(lower, (struct qe_index_s *)arg);
+ return OK;
+ }
+
+ default:
+ {
+ return -ENOTTY;
+ }
+ }
+#else
return -ENOTTY;
+#endif
+}
+
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+/****************************************************************************
+ * Name: sam_qe_pos_16to32b
+ *
+ * Description:
+ * An inline function performing the extension of current position.
+ * Last reading is saved to priv->last_pos.
+ *
+ ****************************************************************************/
+
+static inline int32_t sam_qe_pos_16to32b(struct qe_lowerhalf_s *lower,
+ uint32_t current_pos)
+{
+ struct sam_lowerhalf_s *priv = (struct sam_lowerhalf_s *)lower;
+
+ uint32_t new_pos = *(volatile uint32_t *)&priv->last_pos;
+ new_pos += (int16_t)(current_pos - new_pos);
+ *(volatile uint32_t *)&priv->last_pos = new_pos;
+
+ return (int32_t)new_pos;
+}
+#endif
+
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+/****************************************************************************
+ * Name: sam_qe_indx_pos_16to32b
+ *
+ * Description:
+ * An inline function performing the extension of the last index position.
+ * Last reading is saved to priv->last_index.
+ *
+ ****************************************************************************/
+
+static inline int32_t sam_qe_indx_pos_16to32b(struct qe_lowerhalf_s *lower,
+ uint32_t current_indx_pos)
+{
+ struct sam_lowerhalf_s *priv = (struct sam_lowerhalf_s *)lower;
+
+ uint32_t new_index = *(volatile uint32_t *)&priv->last_pos;
+ new_index += (int16_t)(current_indx_pos - new_index);
+ *(volatile uint32_t *)&priv->last_index = new_index;
+
+ return (int32_t)new_index;
}
+#endif
+
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+/****************************************************************************
+ * Name: sam_qeindex
+ *
+ * Description:
+ * A function used for a GETINDEX ioctl call. Works with the internal
+ * variables needed for the 32 bit extension.
+ *
+ ****************************************************************************/
+
+static int sam_qeindex(struct qe_lowerhalf_s *lower, struct qe_index_s *dest)
+{
+ int32_t current_pos;
+ uint32_t status;
+ uint32_t current_indx_pos;
+ bool captured = false;
+ struct sam_lowerhalf_s *priv = (struct sam_lowerhalf_s *)lower;
+
+ /* Perform the current position retrieval everytime */
+
+ sam_position(lower, ¤t_pos);
+ dest->qenc_pos = current_pos;
+
+ /* Perform the capture logic */
+
+ TC_HANDLE handle = priv->tch;
+
+ /* Get the interrupt */
+
+ status = sam_tc_getpending(handle);
+
+ /* Check if something has been captured.
+ * The reason for using two capture registers is due to their exclusive
+ * access. So it requires reading switching.
+ */
+
+ if (status & TC_INT_LDRAS)
+ {
+ /* The new index pos is in the Capture A register */
+
+ current_indx_pos = sam_tc_getregister(handle, TC_REGA);
+ captured = true;
+ }
+ else if (status & TC_INT_LDRBS)
+ {
+ /* The new index pos is in the Capture B register */
+
+ current_indx_pos = sam_tc_getregister(handle, TC_REGB);
+ captured = true;
+ }
+
+ /* We've caught something. Increase the index hit count
+ * and extend the reading.
+ */
+
+ if (captured)
+ {
+ priv->index_cnt++;
+ sam_qe_indx_pos_16to32b(lower, current_indx_pos);
+ }
+
+ dest->indx_pos = priv->last_index;
+ dest->indx_cnt = priv->index_cnt;
+ return OK;
+}
+#endif
/****************************************************************************
* Public Functions
@@ -323,10 +478,30 @@ int sam_qeinitialize(const char *devpath, int tc)
/* Allocate the timer/counter and select its mode of operation */
+ /* When configuring the timer with no index reset, do not obey
+ * the datasheet's QDEC instructions. Do not set the TC_CMR_ABETRG and
+ * TC_CMR_ETRGEDG_RISING bits responsible for the counter reset,
+ * because triggers reset the internal counter.
+ * Instead, to get the position of the last index, use the ability
+ * to capture internal counter's value with an upcoming index.
+ *
+ * Due to the internal structure of the Timer/Counter, both Capture
+ * registers (A and B) must be used, because of the exclusive access
+ * to both Capture registers (refer to section 49-6 in the latest 2023
+ * ATSAMV7's datasheet).
+ */
+#ifdef CONFIG_SAMV7_QENCODER_ENABLE_GETINDEX
+ mode = TC_CMR_TCCLKS_XC0 | /* Use XC0 as an external TCCLKS value */
+ TC_CMR_CAPTURE | /* Select 'Capture mode' */
+ TC_CMR_LDRA_RISING | /* Select 'Rising edge' for the RA loading */
+ TC_CMR_LDRB_RISING | /* Select 'Rising edge' for the RB loading */
+ TC_CMR_SBSMPLR_ONE; /* Capture every upcoming edge */
+#else
mode = TC_CMR_TCCLKS_XC0 | /* Use XC0 as an external TCCLKS value */
TC_CMR_ETRGEDG_RISING | /* Select 'Rising edge' as the External
Trigger Edge */
TC_CMR_ABETRG | /* Select 'TIOAx' as the External Trigger */
TC_CMR_CAPTURE; /* Select 'Capture mode' */
+#endif
priv->tch = sam_tc_allocate(tc * SAM_TC_NCHANNELS, mode);
if (priv->tch == NULL)