This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

QEI - Reading Incorrect Position

Other Parts Discussed in Thread: TM4C123GH6PM

Hello,

I am having an issue using a TM4C123G to interface with an 4K resolution bi-directional incremental encoder. I am using CCS and put a watch/breakpoint on my variable position. The value comes back as 0,1, or 36000 at random, without any change in position. I have verified that the encoder is working via an oscilloscope. The index pulse also interrupts and runs the ISR correctly. 

Is there some sort of configuration that I'm missing or something in my code that would explain this behavior? I have gone through the search and looked for similar questions but didn't find a similar issue. 

EDIT: It appears I didn't unlock PF0 (NMI pin). I have edited my code below. After this edit, I consistently receive 0 as my position value even when the rotor of the encoder is moving. 

#include "stdint.h"
#include "stdbool.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_qei.h"

#include "driverlib/gpio.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/qei.h"
#include "driverlib/pin_map.h"
#include "driverlib/interrupt.h"


/* Encoder */
#define QEI_PHA_PORT            GPIO_PORTF_BASE		//Phase A
#define QEI_PHA_PIN             GPIO_PIN_0
#define QEI_PHA_INT             INT_GPIOF

#define QEI_PHB_PORT            GPIO_PORTF_BASE		//Phase B
#define QEI_PHB_PIN             GPIO_PIN_1

#define QEI_INDEX_PORT          GPIO_PORTJ_BASE		//Index Port
#define QEI_INDEX_PIN           GPIO_PIN_2
#define QEI_INDEX_INT           INT_GPIOJ

const uint32_t MaxPanEncResolution = 35999;
int32_t position;


void Encoder0IntHandler(void)
{
	uint32_t ulStatus;
	/* Get the interrrupt status and clear asserted interrupt. */
    ulStatus = QEIIntStatus(QEI0_BASE, true);
    QEIIntClear(QEI0_BASE, ulStatus);

	if(ulStatus == QEI_INTINDEX)
	{
		//position = 0;
	}
 }

void QEI_Init(void)
{
  	/* Enable the peripheral */
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_QEI0);
    MAP_SysCtlPeripheralPowerOn(SYSCTL_PERIPH_QEI0);
    	/* Configure the QEI pins. ENCODER 0 */
    GPIOPinConfigure(GPIO_PF0_PHA0);
    GPIOPinConfigure(GPIO_PF1_PHB0);
    GPIOPinConfigure(GPIO_PJ2_IDX0);

    MAP_GPIOPinTypeQEI(QEI_PHA_PORT, QEI_PHA_PIN);
    MAP_GPIOPinTypeQEI(QEI_PHB_PORT, QEI_PHB_PIN);
    MAP_GPIOPinTypeQEI(QEI_INDEX_PORT, QEI_INDEX_PIN);

	QEIConfigure(QEI0_BASE, (QEI_CONFIG_RESET_IDX | QEI_CONFIG_CAPTURE_A_B |	QEI_CONFIG_QUADRATURE | QEI_CONFIG_SWAP ), MaxPanEncResolution);

	MAP_QEIVelocityConfigure(QEI0_BASE, QEI_VELDIV_1, MAP_SysCtlClockGet()/100);	//Set to 10 ms
	MAP_QEIVelocityEnable(QEI0_BASE);

	/* Initialize the QEI position to zero. */
	QEIPositionSet(QEI0_BASE, 0);

	/* Enable the QEI module. */
	QEIEnable(QEI0_BASE);
	QEIIntEnable(QEI0_BASE, (QEI_INTERROR)| (QEI_INTINDEX) );
	//MAP_QEIIntEnable(QEI0_BASE, (QEI_INTERROR));
	/*Enable the QEI interrupt.*/
	IntEnable(INT_QEI0);

}

int ReadPositionFromEncoder(void)
{
	return ( (MAP_QEIPositionGet(QEI0_BASE) * 36000)/MaxPanEncResolution);
}

int main(void)
{
	SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
	MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
	MAP_SysCtlPeripheralPowerOn(SYSCTL_PERIPH_GPIOG);
	MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	MAP_SysCtlPeripheralPowerOn(SYSCTL_PERIPH_GPIOF);
	MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
	MAP_SysCtlPeripheralPowerOn(SYSCTL_PERIPH_GPIOJ);
	HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
	HWREG(GPIO_PORTF_BASE + GPIO_O_CR)   |= GPIO_PIN_0;;
	QEI_Init();
	MAP_IntMasterEnable();

	while(1)
	{
		SysCtlDelay(MAP_SysCtlClockGet()/3);
		position = ReadPositionFromEncoder();
	}
	return 0;
}

  • Do you mean w/o changing the encoder position?

    The 36000 may depend on how it handles rotation direction but certainly 0 and 1 aren't surprising. It's not unusual to have an encoder sitting on a transition edge and oscillate.

    Robert
  • It appears that I missed unlocking PF0 for use. Unfortunately, I now receive 0 consistently despite spinning the encoder's rotor around.
  • Hello CamK,

    Since you are using Index mode, has the index pulse been generated for the QEI pulse to reset the position counter?

    Also QEIPositionGet API does not need any processing as it will give the position of the wheel based on the number of positions already programmed.

    Regards

    Amit

  • Hi Amit & Robert,

    It appears that a "barrage" of such QEI posts have landed recently.   And - odds are (so) small that each user employs the same physical encoder - would not a simple program - employing just 2 (or 3) MCU GPIO outputs - offer a, "Safer Harbor?"   (i.e. all QEI users could test/verify w/this method - build familiarity & skill - prior to attaching to their (real) hardware.)

    The extreme variability invited by "unguided & unmodeled" QEI applications appears to "beg" for such a, "Learning Aid."   The alternative - endless time, effort & forum bloat - really - how can (avoidance) ever be good?

  • Such an app note might well be useful. So would skill with an oscilloscope

    Robert
  • While you "hedge" somewhat w/ "might well be useful" - can the cascade of "Un-App Noted, QEI misfortunes" be denied?   And each/every frustrated user lands here, wet/bruised, upon QEI Shipwreck isle.   May I ask - could ANYTHING prove as useful?   (as a proper QEI App Note - using TM4C GPIO as QEI Gen!)

    We note that poster reports, "Verified with scope." Yet there's no detail to support that claim - and the terrible results, "beg" to differ.

    A comprehensive MCU - such as those resident here - enables the creation of a powerful, "All in one test system" - ideal for such QEI test/verify/debug.   And this same test App could be used by many - assist many - save the endless "back-forth" to "tease out" the basics.  (which never/ever are properly presented)

    Is it not a pity that there's, "Never time to do the job right" - yet always time to do it, "Uniquely, in an endless spiral of variations - minus proper guidance?"   (some may consider the latter the "wrong way.")

  • Hello CamK.

    I do not use APIs. I go for direct register access programming. May the pseudocode below help you. RPM, Position and velocity all are working fine
    in the below code.

    void QEI_Init(void)
    {
    unsigned long delay;
    SYSCTL_RCGCQEI_R|=(1<<0); // enable QEI Module 0 clock
    SYSCTL_RCGC2_R |= 0x00000008; // 1) D clock
    GPIO_PORTD_LOCK_R = 0x4C4F434B ; //Unlock PD7
    GPIO_PORTD_CR_R = 0xF0; // allow changes to PD7-4
    GPIO_PORTD_AMSEL_R = 0x00; // 3) disable analog function
    GPIO_PORTD_PCTL_R = (GPIO_PORTD_PCTL_R&0x00FFFFFF)|0x66000000; // 4) GPIO clear bit PCTL
    GPIO_PORTD_DIR_R &= ~((1<<7)|(1<<6)); // 5) PD7,PD6 input
    GPIO_PORTD_AFSEL_R |= (1<<7)|(1<<6); // 6) no alternate function
    GPIO_PORTD_DEN_R = 0xF0; // 7) enable digital pins P7-4

    //pd6,7
    QEI0_CTL_R |=(1<<3)|(1<<4)|(1<<5) ; //enable capture mode, reset mode, velocity mode
    QEI0_MAXPOS_R = 0x00000F9F; // maximum position for my 2048 ppr encoder
    QEI0_LOAD_R = 20000000; // load value to calculate rpm
    QEI0_CTL_R |= (1<<0); // enable qei module 0.
    }
  • Amit,

    I connected an oscilloscope to the encoder and verified that an index pulse was generated when passing the index position. I have also connected the oscilloscope to the A and B pins and verified that pulses were being generated (separately on both pins) as I rotated the encoder. I have connected the encoder's Z (index) pin to PJ2, A pin to PF0, and B pin to PF1. These pins are the pins for QEI0. I have tried to write my QEI code following the functions described in the peripheral library but to no avail. I don't receive any fault ISR, which also leads me to believe I have an issue in the QEI software configuration.

    After taking a look at Ishan's pseudocode, I examined the registers during debug and noticed that  bit QEI0 in register SYSCTL_RCGC1 is set to 0 and not set to 1. According to page 467 of the datasheet, this means that "the module is unclocked and disabled." Am I missing something here?

    Also to note that since I changed 

    int ReadPositionFromEncoder(void)
    {
        return ( (MAP_QEIPositionGet(QEI0_BASE) * 36000)/MaxPanEncResolution);
    }

    to

    int ReadPositionFromEncoder(void)
    {
        return MAP_QEIPositionGet(QEI0_BASE);
    }

    as suggested by Amit, I always receive 35999 (my max encoder position) despite rotating the encoder and passing the index pulse.


    Cam

  • Hello Cam,

    The RCGCQEI register takes precedence over RCGCn register. So if the RCGCQEI bit is set to 1 and RCGC1 register not updated, it shall be OK and that is why no bus fault ISR.

    After configuration of QEI can you please take a register dump of QEI control registers and GPIO configuration registers and send the same across!

    Regards
    Amit
  • Amit,

    I have gone through the datasheet and examined the relevant registers. They look to be correct. Please verify.

    RCGCQEIO = 0x01
    RCGCGPIO = 10010000 = 0x90

    QEI0 registers:

    QEI_CTL = 0x3B
    QEI_STAT = 0x02
    QEI_POS = 0x8C9F
    QEI_MAXPOS = 0x8C9F
    QEI_LOAD = 49,999
    QEI_COUNT = 0
    QEI_SPEED = 0
    QEI_INTEN = 0x9
    QEI_RIS = 0x6
    QEI_ISC = 0

    PORT F registers:

    GPIO_DATA = 0x03
    GPIO_DIR = 0
    GPIO_IS = 0
    GPIO_IBE = 0
    GPIO_IEV = 0
    GPIO_IM = 0
    GPIO_RIS = 0
    GPIO_MIS = 0
    GPIO_ICR = 0
    GPIO_APSEL = 0x03
    GPIO_DR2R = 0xFF
    GPIO_ODR = 0
    GPIO_PUR = 0x03
    GPIO_PDR = 0
    GPIO_DEN = 0x03
    GPIO_LOCK = 0x01
    GPIO_CR = 0xFF
    GPIO_AMSEL = 0
    GPIO_PCTL = 0x66

    PORT J registers:

    GPIO_DATA = 0x34
    GPIO_DIR = 0
    GPIO_IS = 0
    GPIO_IBE = 0
    GPIO_IEV = 0
    GPIO_IM = 0
    GPIO_RIS = 0x30
    GPIO_MIS = 0
    GPIO_ICR = 0
    GPIO_APSEL = 0x04
    GPIO_DR2R = 0xFF
    GPIO_ODR = 0
    GPIO_PUR = 0x04
    GPIO_PDR = 0
    GPIO_DEN = 0x04
    GPIO_LOCK = 0x01
    GPIO_CR = 0xFF
    GPIO_AMSEL = 0
    GPIO_PCTL = 0x500

    Cam
  • Hello Cam

    Is the device a TM4C123GH6PM (64 pin part)? If not, then can you please give the correct part number?

    Regards
    Amit
  • Amit,

    The device is a tm4c123gh6pzt.  It has 100 pins. Datasheet can be found here.

    Thanks,

    Cam

  • Hello Cam,

    Thanks for the information. So when the encoder wheel is rotated and the Index pulse is generated, does the Index Interrupt bit in the QEIISC register get set?

    I also see that the Index pin reads as 1 in the GPIODATA registers. Is it a push pull from the encoder wheel and you see a 0->1->0 when the Index pulse is generated on the scope, in which case the pin must read as 0 for most of the time.

    Regards
    Amit
  • Hello Amit,

    I have discovered my issue.

    For further testing, I removed the connection with the index pulse (the Z line) and left just A and B. My encoder incremented the counter correctly, but (obviously) did call the ISR - there was no index pulse to trigger the interrupt. After further examining the registers related to the index pulse, I modified the INVI bit in the QEICTL register. This inverts the pulse.

    QEI0_CTL_R |= (1 << 11);

    This resolved the issue. The ISR is successfully called, indicating the pulse was received, when the index pulse is generated. From my understanding, the index pulse line has a weak pull-up resistor (according to the GPIO register) when attached to the microcontroller . This caused the line to be pulled high. Wouldn't this cause a high pulse to go by unnoticed? This was not visible on the oscilloscope when I disconnected the line from the microcontroller because I no longer had the pull-up resistor at that point. Inverting the pulse resolved this.

    I will try modifying the pull-up resistor and using a pull-down resistor with a non-inverted pulse tomorrow and confirm the results. If anything I said sounds incorrect, please correct me! Thank you for the help.

    Regards,
    Cam
  • Hello Cam,

    If the signal is already pulled up then a high pulse cannot be detected. Was the pull up you are referring to is the GPIOPUR?

    Regards
    Amit
  • Amit,

    By default, it seems that all pins set as inputs are configured with a weak pull-up resistor. I confirmed that my index signal line had a weak pull-up by looking at PORT J GPIOPUR (set as 0x40...meaning bit 3 for pin 2 was set as 1). I changed this to a weak pull-down resistor using the following line from the peripheral driver library:

    GPIOPadConfigSet(QEI_INDEX_PORT, QEI_INDEX_PIN, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPD);

    When I changed the index pulse back to non-inverting, it functions as expected. Thank you for the help along the way!

    Regards,

    Cam

  • Hello Cam,

    Amit Ashara said:
    I also see that the Index pin reads as 1 in the GPIODATA registers. Is it a push pull from the encoder wheel and you see a 0->1->0 when the Index pulse is generated on the scope, in which case the pin must read as 0 for most of the time.

    And that is what caught my attention when I responded based on the register values.

    Regards

    Amit