mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 10:58:49 +08:00
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 <pressste@fel.cvut.cz>
This commit is contained in:
parent
d098c1dc87
commit
1a2e752ea7
2 changed files with 191 additions and 3 deletions
|
@ -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
|
||||
|
|
|
@ -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,11 +296,140 @@ 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)
|
||||
|
|
Loading…
Reference in a new issue