/************************************************************************************ * arch/arm/src/tiva/common/tiva_qencoder.c * * Copyright (C) 2016 Young Mu. All rights reserved. * Author: Young Mu * * The basic structure of this driver derives in spirit (if nothing more) * from the NuttX STM32 QEI driver which has: * * Copyright (C) 2012 Gregory Nutt. All rights reserved. * Authors: Gregory Nutt * Diego Sanchez * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ************************************************************************************/ /************************************************************************************ * Included Files ************************************************************************************/ #include #include #include #include #include "arm_arch.h" #include "tiva_gpio.h" #include "tiva_qencoder.h" #include "tiva_enablepwr.h" #include "tiva_enableclks.h" #include "hardware/tiva_qencoder.h" #include "hardware/tiva_pinmap.h" #include "hardware/tiva_memorymap.h" #include /************************************************************************************ * Private Types ************************************************************************************/ struct tiva_qe_s { const struct qe_ops_s *ops; uint8_t id; uintptr_t base; uint32_t idx; uint32_t pha; uint32_t phb; uint32_t maxpos; }; /************************************************************************************ * Private Function Prototypes ************************************************************************************/ static inline void tiva_qe_putreg(struct tiva_qe_s *qe, unsigned int offset, uint32_t regval); static inline uint32_t tiva_qe_getreg(struct tiva_qe_s *qe, unsigned int offset); static int tiva_qe_setup(FAR struct qe_lowerhalf_s *lower); static int tiva_qe_shutdown(FAR struct qe_lowerhalf_s *lower); static int tiva_qe_position(FAR struct qe_lowerhalf_s *lower, FAR int32_t *pos); static int tiva_qe_reset(FAR struct qe_lowerhalf_s *lower); static int tiva_qe_ioctl(FAR struct qe_lowerhalf_s *lower, int cmd, unsigned long arg); static int tiva_qe_direction(struct tiva_qe_s *qe, unsigned long *dir); static int tiva_qe_velocity(struct tiva_qe_s *qe, unsigned long *vel); static int tiva_qe_resetatppr(struct tiva_qe_s *qe, unsigned long ppr); static int tiva_qe_resetatmaxpos(FAR struct tiva_qe_s *qe, unsigned long offs); static int tiva_qe_resetatindex(FAR struct tiva_qe_s *qe); /************************************************************************************ * Private Data ************************************************************************************/ static const struct qe_ops_s g_qe_ops = { .setup = tiva_qe_setup, .shutdown = tiva_qe_shutdown, .position = tiva_qe_position, .reset = tiva_qe_reset, .ioctl = tiva_qe_ioctl, }; #ifdef CONFIG_TIVA_QEI0 static struct tiva_qe_s g_qe0 = { .ops = &g_qe_ops, .id = 0, .base = TIVA_QEI0_BASE, .idx = GPIO_QEI0_IDX, .pha = GPIO_QEI0_PHA, .phb = GPIO_QEI0_PHB, .maxpos = 0, }; #endif #ifdef CONFIG_TIVA_QEI1 static struct tiva_qe_s g_qe1 = { .ops = &g_qe_ops, .id = 1, .base = TIVA_QEI1_BASE, .idx = GPIO_QEI1_IDX, .pha = GPIO_QEI1_PHA, .phb = GPIO_QEI1_PHB, .maxpos = 0, }; #endif /************************************************************************************ * Private Functions ************************************************************************************/ /************************************************************************************ * Name: tiva_qe_getreg * * Description: * Get a 32-bit register value by offset * ************************************************************************************/ static inline uint32_t tiva_qe_getreg(struct tiva_qe_s *qe, unsigned int offset) { uintptr_t regaddr = qe->base + offset; return getreg32(regaddr); } /************************************************************************************ * Name: tiva_qe_putreg * * Description: * Put a 32-bit register value by offset * ************************************************************************************/ static inline void tiva_qe_putreg(struct tiva_qe_s *qe, unsigned int offset, uint32_t regval) { uintptr_t regaddr = qe->base + offset; putreg32(regval, regaddr); } /**************************************************************************** * Name: qe_setup * * Description: * This method is called when the driver is opened. The lower half driver * will be configured and initialized the device so that it is ready for * use. It will not, however, output pulses until the start method is * called. * * Input Parameters: * lower - A reference to the lower half QEI driver state structure * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_setup(FAR struct qe_lowerhalf_s *lower) { uint32_t ctlreg = 0; FAR struct tiva_qe_s *qe = (FAR struct tiva_qe_s *)lower; int ret; sninfo("setup QEI %d\n", qe->id); /* Enable GPIO port, GPIO pin type and GPIO alternate function */ ret = tiva_configgpio(qe->idx); if (ret < 0) { snerr("ERROR: tiva_configgpio failed (%x)\n", qe->idx); return -1; } ret = tiva_configgpio(qe->pha); if (ret < 0) { snerr("ERROR: tiva_configgpio failed (%x)\n", qe->pha); return -1; } ret = tiva_configgpio(qe->phb); if (ret < 0) { snerr("ERROR: tiva_configgpio failed (%x)\n", qe->phb); return -1; } /* Get initial value of register */ ctlreg = tiva_qe_getreg(qe, TIVA_QEI_CTL_OFFSET); /* Turn off the bits we're about to control */ ctlreg &= ~(uint32_t)((1 << TIVA_QEI_CTL_RESMODE) | (1 << TIVA_QEI_CTL_CAPMODE) | (1 << TIVA_QEI_CTL_VELEN) | (1 << TIVA_QEI_CTL_SIGMODE) | (1 << TIVA_QEI_CTL_SWAP)); /* Set reset mode (default as MAXPOS) */ ctlreg |= RESMODE_BY_MAXPOS << TIVA_QEI_CTL_RESMODE; /* Set capture mode (default as PHA_AND_PHB) */ ctlreg |= CAPMODE_PHA_AND_PHB << TIVA_QEI_CTL_CAPMODE; /* Enable velocity capture */ ctlreg |= VELEN_ENABLE << TIVA_QEI_CTL_VELEN; /* Set signal mode (default as quadrature) */ ctlreg |= SIGMODE_QUADRATURE << TIVA_QEI_CTL_SIGMODE; /* Set swap mode (default as no swap) */ ctlreg |= SWAP_NO_SWAP << TIVA_QEI_CTL_SWAP; /* Program the register */ tiva_qe_putreg(qe, TIVA_QEI_CTL_OFFSET, ctlreg); /* Set default maxpos value to entire uint32_t range */ qe->maxpos = UINT32_MAX; tiva_qe_putreg(qe, TIVA_QEI_MAXPOS_OFFSET, qe->maxpos); /* Reset the position */ tiva_qe_putreg(qe, TIVA_QEI_POS_OFFSET, 0); /* Set prediv (1) */ ctlreg = tiva_qe_getreg(qe, TIVA_QEI_CTL_OFFSET); ctlreg |= VELDIV_1 << TIVA_QEI_CTL_VELDIV; tiva_qe_putreg(qe, TIVA_QEI_CTL_OFFSET, ctlreg); /* Set period load (10ms for TM4C1294NCPDT) */ tiva_qe_putreg(qe, TIVA_QEI_LOAD_OFFSET, 1200000); /* Enable the QEI */ ctlreg = tiva_qe_getreg(qe, TIVA_QEI_CTL_OFFSET); ctlreg |= QEI_ENABLE << TIVA_QEI_CTL_ENABLE; tiva_qe_putreg(qe, TIVA_QEI_CTL_OFFSET, ctlreg); return OK; } /**************************************************************************** * Name: qe_shutdown * * Description: * This method is called when the driver is closed. The lower half driver * stop data collection, free any resources, disable the timer hardware, and * put the system into the lowest possible power usage state * * Input Parameters: * lower - A reference to the lower half QEI driver state structure * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_shutdown(FAR struct qe_lowerhalf_s *lower) { FAR struct tiva_qe_s *qe = (FAR struct tiva_qe_s *)lower; sninfo("shutdown QEI %d\n", qe->id); /* Disable the QEI */ tiva_qe_putreg(qe, TIVA_SYSCON_SRQEI_OFFSET, SYSCON_SRQEI(qe->id)); return OK; } /**************************************************************************** * Name: tiva_qe_reset * * Description: * Reset the position measurement to zero. * * Input Parameters: * lower - A reference to the lower half QEI driver state structure * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_reset(FAR struct qe_lowerhalf_s *lower) { FAR struct tiva_qe_s *qe = (FAR struct tiva_qe_s *)lower; sninfo("reset QEI %d\n", qe->id); tiva_qe_putreg(qe, TIVA_QEI_POS_OFFSET, 0); return OK; } /**************************************************************************** * Name: tiva_qe_position * * Description: * Return the position mesaured by QEI. * * Input Parameters: * lower - A reference to the lower half QEI driver state structure * pos - pointer to the position returned * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_position(FAR struct qe_lowerhalf_s *lower, FAR int32_t *pos) { FAR struct tiva_qe_s *qe = (FAR struct tiva_qe_s *)lower; sninfo("get position of QEI %d\n", qe->id); *pos = (int32_t) tiva_qe_getreg(qe, TIVA_QEI_POS_OFFSET); return OK; } /**************************************************************************** * Name: tiva_qe_ioctl * * Description: * Lower-half logic may support platform-specific ioctl commands * * Input Parameters: * lower - A reference to the lower half QEI driver state structure * cmd - The ioctl command * arg - The argument accompanying the ioctl command * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_ioctl(FAR struct qe_lowerhalf_s *lower, int cmd, unsigned long arg) { FAR struct tiva_qe_s *qe = (FAR struct tiva_qe_s *)lower; sninfo("ioctl QEI %d\n", qe->id); switch (cmd) { case QEIOC_DIRECTION: tiva_qe_direction(qe, (unsigned long *)arg); break; case QEIOC_VELOCITY: tiva_qe_velocity(qe, (unsigned long *)arg); break; case QEIOC_RESETATPPR: tiva_qe_resetatppr(qe, arg); break; case QEIOC_RESETATMAXPOS: tiva_qe_resetatmaxpos(qe, arg); break; case QEIOC_RESETATINDEX: tiva_qe_resetatindex(qe); break; default: snerr("ERROR: invalid cmd %x\n", cmd); break; } return OK; } /**************************************************************************** * Name: tiva_qe_direction * * Description: * Return the direction mesaured by QEI. * * Input Parameters: * qe - A reference to the TIVA QEI structure * dir - pointer to the direction returned * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_direction(FAR struct tiva_qe_s *qe, unsigned long *dir) { sninfo("get direction of QEI %d\n", qe->id); uint32_t statreg; statreg = tiva_qe_getreg(qe, TIVA_QEI_STAT_OFFSET); int32_t dirbit; dirbit = (statreg & (1 << TIVA_QEI_STAT_DIRECTION)) == DIRECTION_FORWARD ? 1 : -1; *dir = dirbit; return OK; } /**************************************************************************** * Name: tiva_qe_direction * * Description: * Return the velocity (A/B pulses per second) mesaured by QEI. * * Input Parameters: * qe - A reference to the TIVA QEI structure * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_velocity(FAR struct tiva_qe_s *qe, unsigned long *vel) { sninfo("get direction of QEI %d\n", qe->id); *vel = (int32_t) tiva_qe_getreg(qe, TIVA_QEI_SPEED_OFFSET) * 100 / 4; return OK; } /**************************************************************************** * Name: tiva_qe_resetatppr * * Description: * Set reset mode as MAXPOS and set maxpos value to number of pulses * per round of encoder. Note that this function multiplies the given * ppr by 4 because capture mode is PHA_AND_PHB, meaning we count 4 * edges per "pulse." * * Input Parameters: * qe - A reference to the TIVA QEI structure * ppr - pulses per round of encoder * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_resetatppr(FAR struct tiva_qe_s *qe, unsigned long ppr) { /* maxpos is 4 times of ppr since we set capture mode as PHA_AND_PHB */ return tiva_qe_resetatmaxpos(qe, ppr * 4); } /**************************************************************************** * Name: tiva_qe_resetatmaxpos * * Description: * Set reset mode as MAXPOS and set maxpos value as given. * * Input Parameters: * qe - A reference to the TIVA QEI structure * maxpos - Maximum position count at which QEI resets. To get the full * range, give UINT32_MAX. * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_resetatmaxpos(FAR struct tiva_qe_s *qe, unsigned long maxpos) { sninfo("set maxpos reset mode and maxpos value of QEI %d\n", qe->id); FAR struct qe_lowerhalf_s *lower; uint32_t ctlreg = 0; /* Disable the QEI */ lower = (FAR struct qe_lowerhalf_s *)qe; tiva_qe_shutdown(lower); qe->maxpos = maxpos; /* Set reset mode as MAXPOS */ ctlreg = tiva_qe_getreg(qe, TIVA_QEI_CTL_OFFSET); ctlreg &= ~(uint32_t)(1 << TIVA_QEI_CTL_RESMODE); ctlreg |= RESMODE_BY_MAXPOS << TIVA_QEI_CTL_RESMODE; tiva_qe_putreg(qe, TIVA_QEI_CTL_OFFSET, ctlreg); /* Set maxpos value */ tiva_qe_putreg(qe, TIVA_QEI_MAXPOS_OFFSET, qe->maxpos); /* Enable the QEI */ ctlreg = tiva_qe_getreg(qe, TIVA_QEI_CTL_OFFSET); ctlreg |= QEI_ENABLE << TIVA_QEI_CTL_ENABLE; tiva_qe_putreg(qe, TIVA_QEI_CTL_OFFSET, ctlreg); return OK; } /**************************************************************************** * Name: tiva_qe_resetatindex * * Description: * Set reset mode as INDEX * * Input Parameters: * qe - A reference to the TIVA QEI structure * * Returned Value: * Zero on success; a negated errno value on failure * ****************************************************************************/ static int tiva_qe_resetatindex(FAR struct tiva_qe_s *qe) { sninfo("set index reset mode of QEI %d\n", qe->id); FAR struct qe_lowerhalf_s *lower; uint32_t ctlreg = 0; /* Disable the QEI */ lower = (FAR struct qe_lowerhalf_s *)qe; tiva_qe_shutdown(lower); /* Set reset mode as INDEX */ ctlreg = tiva_qe_getreg(qe, TIVA_QEI_CTL_OFFSET); ctlreg &= ~(uint32_t)(1 << TIVA_QEI_CTL_RESMODE); ctlreg |= RESMODE_BY_INDEX_PULSE << TIVA_QEI_CTL_RESMODE; tiva_qe_putreg(qe, TIVA_QEI_CTL_OFFSET, ctlreg); /* Enable the QEI */ ctlreg = tiva_qe_getreg(qe, TIVA_QEI_CTL_OFFSET); ctlreg |= QEI_ENABLE << TIVA_QEI_CTL_ENABLE; tiva_qe_putreg(qe, TIVA_QEI_CTL_OFFSET, ctlreg); return OK; } /************************************************************************************ * Name: tiva_qei_initialize * * Description: * Enable power and clock for quadrature encoder interface. This function * must be called from board-specific logic. * * Input Parameters: * id - A number identifying certain QEI. * * Returned Value: * On success, a pointer to the lower half QEI driver is returned. * NULL is returned on any failure. * ************************************************************************************/ FAR struct qe_lowerhalf_s *tiva_qei_initialize(int id) { FAR struct tiva_qe_s *qe; FAR struct qe_lowerhalf_s *lower; switch (id) { #ifdef CONFIG_TIVA_QEI0 case 0: qe = &g_qe0; break; #endif #ifdef CONFIG_TIVA_QEI1 case 1: qe = &g_qe1; break; #endif default: snerr("ERROR: invalid QEI %d\n", id); return NULL; } /* Enable QEI clock */ tiva_qei_enablepwr(qe->id); tiva_qei_enableclk(qe->id); /* Make sure that the QEI enable bit has been cleared */ lower = (FAR struct qe_lowerhalf_s *)qe; tiva_qe_shutdown(lower); return lower; }