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.

TM4C123gh6pm QEI error interfacing incremental encoder

Other Parts Discussed in Thread: TM4C123GH6PM

Hi

I am working on a cnc project based on Tiva c series launchpad board (TM4C123GH6PM) and i want to control a stepper motor in closed loop using an optical incremental rotary encoder 600ppr feedback. I am facing an issue that while motor is running motor, error bit in QEISTAT goes high and motor also does not moves upto desired distance. I want to make it immune to missing steps. Encoder shaft is driven via a timing belt.

here is my code

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_gpio.h"
#include "inc/hw_types.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/pin_map.h"
#include "driverlib/gpio.h"
#include "driverlib/qei.h"
#include "tm4c123gh6pm.h"

volatile int qeiPosition;

void forward(volatile int steps);
void reverse(volatile int steps);
void portfinit(void);
int main(void) {

// Set the clocking to run directly from the crystal.
SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

// Enable QEI Peripherals
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
SysCtlPeripheralEnable(SYSCTL_PERIPH_QEI0);


HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
HWREG(GPIO_PORTD_BASE + GPIO_O_CR) |= 0x80;
HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = 0;

//Set Pins to be PHA0 and PHB0
GPIOPinConfigure(GPIO_PD6_PHA0);
GPIOPinConfigure(GPIO_PD7_PHB0);

//Set GPIO pins for QEI. PhA0 -> PD6, PhB0 ->PD7.
GPIOPinTypeQEI(GPIO_PORTD_BASE, GPIO_PIN_6 | GPIO_PIN_7);

//DISable peripheral and int before configuration
QEIDisable(QEI0_BASE);
QEIIntDisable(QEI0_BASE,QEI_INTERROR | QEI_INTDIR | QEI_INTTIMER | QEI_INTINDEX);

//QEIConfigure(QEI0_BASE, (QEI_CONFIG_CAPTURE_A | QEI_CONFIG_NO_RESET | QEI_CONFIG_QUADRATURE | QEI_CONFIG_NO_SWAP), 9000);

QEI0_MAXPOS_R = 0X0000195F;

QEIEnable(QEI0_BASE); // Enable the quadrature encoder.

QEIPositionSet(QEI0_BASE, 00);

portfinit();


while (1) //This is the main loop of the program
{

unsigned long in;
unsigned long out;
in=GPIO_PORTF_DATA_R & 0X01; // checking switch to start motor
if(in == 0x00)
{
forward (1000); // calling function to rotate motor in forward direction
SysCtlDelay (900000);
reverse (1000); // calling function to rotate motor in reverse direction
SysCtlDelay (60000);
}
}

void forward (volatile int steps)
{

QEIPositionSet(QEI0_BASE, steps);
//QEIDisable(QEI0_BASE);
QEI0_CTL_R |= 0X00000000;
QEI0_CTL_R &= 0Xfffffffd;
QEIEnable(QEI0_BASE);
while(QEI0_POS_R>50)
{
GPIO_PORTF_DATA_R = 0x02; // PF2 direction, PF1 step for stepper motor
SysCtlDelay (15000);
GPIO_PORTF_DATA_R = 0x00;
SysCtlDelay (15000);
}


}
void reverse (volatile int steps)
{

QEIPositionSet(QEI0_BASE, steps);
//QEIDisable(QEI0_BASE);
QEI0_CTL_R |=0X00000002;
QEIEnable(QEI0_BASE);
while(QEI0_POS_R>50)
{
GPIO_PORTF_DATA_R = 0x06; // PF2 directiion, PF1 step ,for stepper motor
SysCtlDelay (15000);
GPIO_PORTF_DATA_R = 0x04;
SysCtlDelay (15000);
}

}

void portfinit(void)
{ volatile unsigned long delay;
GPIO_PORTF_LOCK_R |= 0X4C4F434B;
GPIO_PORTF_CR_R = 0X1F;
GPIO_PORTF_AMSEL_R =0X00;
GPIO_PORTF_DIR_R = 0X0E;
GPIO_PORTF_PUR_R = 0X11;
GPIO_PORTF_DEN_R = 0X1F;
}

  • Hello Dilraj

    First of all please use TivaWare API instead of the DRM style of coding. In the last 24 hrs another DRM code has caused a blunder for both the OP and me.
  • Hello Amit

    Here is code after modification , now used Tivaware API to configure QEI module.

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_gpio.h"
    #include "inc/hw_types.h"
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/gpio.h"
    #include "driverlib/qei.h"
    #include "tm4c123gh6pm.h"

    volatile int qeiPosition;

    void forward(volatile int steps);
    void reverse(volatile int steps);
    void portfinit(void);
    int main(void) {

    // Set the clocking to run directly from the crystal.
    SysCtlClockSet(SYSCTL_SYSDIV_2|SYSCTL_USE_PLL|SYSCTL_XTAL_25MHZ|SYSCTL_OSC_MAIN);

    // Enable QEI Peripherals
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_QEI0);


    HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
    HWREG(GPIO_PORTD_BASE + GPIO_O_CR) |= 0x80;
    HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = 0;

    //Set Pins to be PHA0 and PHB0
    GPIOPinConfigure(GPIO_PD6_PHA0);
    GPIOPinConfigure(GPIO_PD7_PHB0);

    //Set GPIO pins for QEI. PhA0 -> PD6, PhB0 ->PD7.
    GPIOPinTypeQEI(GPIO_PORTD_BASE, GPIO_PIN_6 | GPIO_PIN_7);

    //DISable peripheral and int before configuration
    QEIDisable(QEI0_BASE);
    QEIIntDisable(QEI0_BASE,QEI_INTERROR | QEI_INTDIR | QEI_INTTIMER | QEI_INTINDEX);


    QEIEnable(QEI0_BASE); // Enable the quadrature encoder.

    QEIPositionSet(QEI0_BASE, 00);

    portfinit();


    while (1) //This is the main loop of the program
    {

    unsigned long in;
    in=GPIO_PORTF_DATA_R & 0X01;
    if(in == 0x00) //checking Launchpad on-board switch status
    {
    forward (1000); //number of steps motor gas to move in forward direction
    SysCtlDelay (900000);
    SysCtlDelay (900000);
    SysCtlDelay (900000);
    reverse (1000); // number of steps motor has to take in reverse direction
    SysCtlDelay (60000);
    }
    }
    }

    void forward (volatile int steps)
    {

    QEIPositionSet(QEI0_BASE, steps);
    //QEIDisable(QEI0_BASE);
    QEIConfigure(QEI0_BASE, (QEI_CONFIG_CAPTURE_A_B| QEI_CONFIG_NO_RESET |
    QEI_CONFIG_QUADRATURE | QEI_CONFIG_NO_SWAP), 2399);

    QEIEnable(QEI0_BASE);
    while(QEI0_POS_R>50) //run motor until QEI0_POS_R is greater than 50
    {
    GPIO_PORTF_DATA_R = 0x02; //P2 direction, PF1 step for stepper motor
    SysCtlDelay (35000);
    GPIO_PORTF_DATA_R = 0x00;
    SysCtlDelay (35000);
    }


    }
    void reverse (volatile int steps)
    {

    QEIPositionSet(QEI0_BASE, steps);
    QEIConfigure(QEI0_BASE, (QEI_CONFIG_CAPTURE_A_B | QEI_CONFIG_NO_RESET |
    QEI_CONFIG_QUADRATURE | QEI_CONFIG_SWAP), 2399);
    QEIEnable(QEI0_BASE);
    while(QEI0_POS_R>50) //run motor until QEI0_POS_R is greater than 50
    {
    GPIO_PORTF_DATA_R = 0x06; // P2 direction, PF1 step for stepper motor
    SysCtlDelay (35000);
    GPIO_PORTF_DATA_R = 0x04;
    SysCtlDelay (35000);
    }

    }

    void portfinit(void) // configuring port F
    { volatile unsigned long delay;
    GPIO_PORTF_LOCK_R |= 0X4C4F434B;
    GPIO_PORTF_CR_R = 0X1F;
    GPIO_PORTF_AMSEL_R =0X00;
    GPIO_PORTF_DIR_R = 0X0E;
    GPIO_PORTF_PUR_R = 0X11;
    GPIO_PORTF_DEN_R = 0X1F;
    }
  • Hello Dilraj

    Thanks. As per the code the QEI is being configured multiple times. You do not need to do that. Configure it once and then enable the QEI module. Also did you check the PD6 and PD7 pin and confirm if the Quadrature signals are being generated as expected.
  • Hello Amit

    As far PD6 and PD7 are concerned, encoder is sending desirable outputs with internal pull-up.

    I have configured QEI once in "void forward(volatile int steps)" function and then in "void reverse(volatile int steps)" function to swap INVA and INVB.

    So do i have to disable QEI each time before configuring it then enable it ?

    Since i am using encoder to sense rotation of a stepper motor and stepper motors tend to vibrate and oscillate at mean position, could this be a reason for error signal.

    Regards
    Dilraj Singh
  • Hello Dilraj

    You do not need to swap. The QEI encoder will be able to figure out the change of direction based on the signal.
  • In reply to Amit Ashara:

    Hello Amit

    ThankYou ,But error is still there indication invalid greycode generated.

    Regards
    Dilraj Singh
  • Hello Dilraj

    Can you please share the scope snapshot of the encoder output to the TM4C device and QEI configuration?
  • In reply to Amit Ashara:

    Hello Amit

    Actually I just hooked up LED's on phase A and phase B to see output, but i don't have scope to check output.

    Regards
    Dilraj Singh
  • Hello Dilraj

    I would like to see scope snapshot and the QEI register configuration as given in Register Window of the IDE.
  • dilraj singh95 said:
    As far PD6 and PD7 are concerned, encoder is sending desirable outputs with internal pull-up.

    Your writing is unclear as to the location of those, "internal pull-ups."   If you speak to the MCU - those resistance values are high - it is likely that both rise & fall times land outside of specification.

    While your "Led Monitor" may serve some (initial/preliminary) purpose - they too may overchallenge the encoder's output signals.  

    Can you provide any insight into the "source or description" of the "QEI Error?"   Such report - w/out "any" detail - provides little (i.e. NO) value.

    In summary - if employing the MCU's internal pull-ups replace them w/10K or less external ones - placed as close as possible to the MCU.   Remove your Led Monitors.  Repeat your test...

  • HI Amit

    Thank You, As pointed by you, I was configuring QEI multiple times so I modified code to configure it only once and and it seems to resolve the problem. Here is modified code.

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_gpio.h"
    #include "inc/hw_types.h"
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/gpio.h"
    #include "driverlib/qei.h"
    #include "tm4c123gh6pm.h"

    volatile int qeiPosition;

    void forward(volatile int steps);
    void reverse(volatile int steps);
    void portfinit(void);
    int main(void) {

    // Set the clocking to run directly from the crystal.
    SysCtlClockSet(SYSCTL_SYSDIV_2|SYSCTL_USE_PLL|SYSCTL_XTAL_25MHZ|SYSCTL_OSC_MAIN);

    // Enable QEI Peripherals
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_QEI0);


    HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
    HWREG(GPIO_PORTD_BASE + GPIO_O_CR) |= 0x80;
    HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = 0;

    //Set Pins to be PHA0 and PHB0
    GPIOPinConfigure(GPIO_PD6_PHA0);
    GPIOPinConfigure(GPIO_PD7_PHB0);

    //Set GPIO pins for QEI. PhA0 -> PD6, PhB0 ->PD7.
    GPIOPinTypeQEI(GPIO_PORTD_BASE, GPIO_PIN_6 | GPIO_PIN_7);

    //DISable peripheral and int before configuration
    QEIDisable(QEI0_BASE);
    QEIIntDisable(QEI0_BASE,QEI_INTERROR | QEI_INTDIR | QEI_INTTIMER | QEI_INTINDEX);

    QEIConfigure(QEI0_BASE, (QEI_CONFIG_CAPTURE_A_B| QEI_CONFIG_NO_RESET |
    QEI_CONFIG_QUADRATURE | QEI_CONFIG_SWAP), 3999);
    QEIPositionSet(QEI0_BASE, 50);
    QEIEnable(QEI0_BASE);

    portfinit();
    unsigned long in;
    unsigned long out;
    unsigned long load;


    while (1) //This is the main loop of the program
    {

    in=GPIO_PORTF_DATA_R & 0X01;
    if(in == 0x00)
    {



    forward (1000);
    SysCtlDelay (900000);
    reverse (1000);
    SysCtlDelay (900000);
    }
    }
    }

    void forward (volatile int steps)
    {

    // QEIPositionSet(QEI0_BASE, steps);
    //QEIDisable(QEI0_BASE);

    while(QEI0_POS_R<2500)
    {
    GPIO_PORTF_DATA_R = 0x02; // pf2 dir, p1 step
    SysCtlDelay (25000);
    GPIO_PORTF_DATA_R = 0x00;
    SysCtlDelay (25000);
    }


    }
    void reverse (volatile int steps)
    {

    // QEIPositionSet(QEI0_BASE, steps);
    while(QEI0_POS_R>50)
    {
    GPIO_PORTF_DATA_R = 0x06; // pf2 dir, p1 step
    SysCtlDelay (25000);
    GPIO_PORTF_DATA_R = 0x04;
    SysCtlDelay (25000);
    }

    }

    void portfinit(void)
    { volatile unsigned long delay;
    GPIO_PORTF_LOCK_R |= 0X4C4F434B;
    GPIO_PORTF_CR_R = 0X1F;
    GPIO_PORTF_AMSEL_R =0X00;
    GPIO_PORTF_DIR_R = 0X0E;
    GPIO_PORTF_PUR_R = 0X11;
    GPIO_PORTF_DEN_R = 0X1F;
    }
  • Hello Dilraj

    Glad the issue is fixed. A good guideline for reconfiguration (normally not required), Is to disable the function, reconfigure and then enable it.