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 Not behaving as expected

Part: Tm4c123BH6ZRB

Quad encoder connected to PF0/PF1 (correctly unlocked and configured)

I get the QEI_INTDIR irq when changing direction on the quad encoder (basic unbounded knob).

When enabling the TIMER/Velocity feature, I can track the position accurately, but I don't really want to be handling irq's that often b/c the knob isn't the primary input.

What I would like/expect is an irq for each 'click' on the knob.  I believe that is what my user would expect as well.

How does one get an IRQ for each turn of the knob?  Note that each turn of the knob does indeed produce a PHA/PHB signal, so it seems like  this should be a reasonable request.

Here's the relevant code.  I have a ton of extraneous lines here to help with learning how the QEI is behaving. I feel like this should be possible, I just don't have the right config to make it work this way.

static void knobIsr()
{
	uint32_t status  = ROM_QEIIntStatus(QEI0_BASE, false);
	uint8_t button = KNOB_ROTATE_RIGHT_CHANNEL;
	ROM_QEIIntDisable(QEI0_BASE, QEI_INTDIR | QEI_INTINDEX | QEI_INTTIMER | QEI_INTERROR);
	ROM_QEIIntClear(QEI0_BASE, status);
	cnt++;
	if ( status & QEI_INTDIR ) {
		// change in direction?
		utilLog("change dir "); utilLogByte(cnt); utilLog("\r\n");
	}

	utilLog("Index "); utilLogByte(ROM_QEIPositionGet(QEI0_BASE));utilLog(" "); utilLogByte(status); utilLog("\r\n");

	if ( status & QEI_INTINDEX ) {
		utilLog("change idx "); utilLogByte(cnt); utilLog("\r\n");
		// index was updated ,so we detected a turn
		int32_t dir = ROM_QEIDirectionGet(QEI0_BASE);
		if ( dir < 0 ) {
			button = KNOB_ROTATE_LEFT_CHANNEL;
			toggleRight = !toggleRight;
			keypadSet(1, toggleRight);
		} else {
			toggleLeft = !toggleLeft;
			keypadSet(2, toggleLeft);
		}
		RingBufWrite(&g_quadeRingBuf, &button, 1);
	}
	ROM_QEIIntEnable(QEI0_BASE, QEI_INTDIR | QEI_INTINDEX | QEI_INTTIMER | QEI_INTERROR);

}
static void knobInit()
{
	// not directly related, but close enough
	levelLedInit();

#define USE_QUAD_ENCODER
#ifdef USE_QUAD_ENCODER
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_QEI0);
	ROM_GPIOPinTypeGPIOInput(GPIO_PORTE_BASE,  GPIO_PIN_4|GPIO_PIN_5); // just in case pins

#ifdef USE_TIVAWARE
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
#else
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY_DD;
#endif
    // Allow the register AFSEL, PUR, PDR and DEN to be written.
    HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= GPIO_PIN_0;
    // Clear the AFSEL register per the workaround.
    HWREG(GPIO_PORTF_BASE + GPIO_O_AFSEL) &= 0xfe;
    // Clear the DEN register per the workaround.
    HWREG(GPIO_PORTF_BASE + GPIO_O_DEN) &= 0xfe;
	ROM_GPIOPinTypeQEI(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	ROM_GPIOPinConfigure(GPIO_PF0_PHA0);
	ROM_GPIOPinConfigure(GPIO_PF1_PHB0);
    // Lock the GPIO.
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;

	RingBufInit(&g_quadeRingBuf, g_quadeBuffer, sizeof(g_quadeBuffer));

	ROM_QEIConfigure(QEI0_BASE,QEI_CONFIG_CAPTURE_A | QEI_CONFIG_RESET_IDX | QEI_CONFIG_QUADRATURE, 16  );

	// enable the filter to prevent noise from making extra ticks
	HWREG(QEI0_BASE + QEI_O_CTL ) |= QEI_CTL_FILTEN;

	ROM_QEIPositionSet(QEI0_BASE, 0);
	ROM_QEIVelocityEnable(QEI0_BASE);
	ROM_QEIVelocityConfigure(QEI0_BASE, QEI_VELDIV_1, ROM_SysCtlClockGet());

	QEIIntRegister(QEI0_BASE, knobIsr );
	ROM_QEIIntClear(QEI0_BASE, QEI_INTDIR | QEI_INTINDEX | QEI_INTTIMER | QEI_INTERROR);
	ROM_QEIIntEnable(QEI0_BASE, QEI_INTDIR | QEI_INTINDEX | QEI_INTTIMER | QEI_INTERROR);
	ROM_QEIEnable(QEI0_BASE);
#else
	ROM_GPIOPinTypeGPIOInput(GPIO_PORTE_BASE,  GPIO_PIN_4|GPIO_PIN_5); // just in case pins

	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	ROM_GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,  GPIO_PIN_0|GPIO_PIN_1);
	GPIOIntClear(GPIO_PORTE_BASE, GPIO_PIN_0);

	ROM_GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_0|GPIO_PIN_1, GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD);
	ROM_GPIOIntTypeSet(GPIO_PORTF_BASE, GPIO_PIN_0, GPIO_BOTH_EDGES);
	GPIOIntRegister(GPIO_PORTF_BASE, quadEhandler);
	GPIOIntEnable(GPIO_PORTF_BASE, GPIO_PIN_0);
#endif

	// watch the IRQ line from both lines
	ROM_GPIOPinTypeGPIOInput(GPIO_PORTE_BASE,  GPIO_PIN_6); // just in case pins
	ROM_GPIOPadConfigSet(GPIO_PORTE_BASE,GPIO_PIN_6, GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD);
	ROM_GPIOIntTypeSet(GPIO_PORTE_BASE, GPIO_PIN_6, GPIO_FALLING_EDGE);
//	GPIOIntRegister(GPIO_PORTE_BASE, quadIrqHandler);
//	GPIOIntEnable(GPIO_PORTE_BASE, GPIO_PIN_6);

	utilLog("knob init\r\n");
}

TIA.

  • @Jason,

    Love the crafting, detailing and presentation of your post.  Very well done...

    And - you're exactly right - single click interrupt would be a great aid.  (i.e. interrupt as needed)  Now while we've past used QEI for similar panel encoder projects (suspect much like yours) we use ARM MCUs from multiple vendors - and I cannot recall how ones here perform.  (to your precise question)

    Should it turn out that such, "individual encoder clicks" are "silent" - might you route each encoder to a separate GPIO pin - which is programmed to interrupt upon, "both signal edges?"  (sometimes - you may employ a, "diode And" circuit - and employ just a single GPIO in this manner)  I realize this "eats" a pin - but functionality should trump "pin-saving."  (says me/clients...)

    Suspect the Peripheral Driver Library's list of QEI functions and Registers may best reveal the MCU's capability - although that may take some digging to fully/properly extract...

    [edit] 10:34 CST 25 July: Dawns that - like us - you may be using the encoder as the means for a user to "navigate" thru a "drop down" list/menu (or similar).  (note that our encoders always include a, "Push switch" which enables, "Menu Selection!")   Now - in this case - interrupting upon each/every encoder "click" may not be ideal as your need really is the "end click total/result!"  And - would not this be handled by a straight-forward Timer interrupt @/about 100Hz?  (normal keypad/keyboard scan rate)  Simple read of QEI registers upon that interrupt should satisfy your requirement.  (one hopes)