Jump to content
Rhab

Problems Using QEI with EK-TM4C123GXL

Recommended Posts

Hi,

 

i want'to use QEI to detect velocity and position of an electric motor. But it doesn't work as suggested. So I hope that anyone has an idea how to solve the problem.

 

The main problem could be that my PhA and PhB Signals are inverted, that means standard-state is 3.3V and pulse-state is 0V.

 

If I understood the TM4C123GH6PM Microcontroller datasheet it is possible to set PhA and PhB on inverted, but i don't understand how this works (Pages 1312-1314).

 

The second thing which i gues that could be wrong is unlocking of PD7 (if i use QEI0), but i have the same problem if i use PC5 and PC6 (QEI1).

 

This is my code:

#include <stdint.h>
#include <stdbool.h>

#include "inc/hw_gpio.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"

#include "driverlib/gpio.h"
#define PART_TM4C123GH6PM
#include "driverlib/pin_map.h"
#include "driverlib/qei.h"
#include "driverlib/sysctl.h"


void config_QEI()
{
         
  
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
        
        //Unlock GPIOD7
        HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
        HWREG(GPIO_PORTD_BASE + GPIO_O_CR) |= 0x80;
        HWREG(GPIO_PORTD_BASE + GPIO_O_AFSEL) &= ~0x80;   
        HWREG(GPIO_PORTD_BASE + GPIO_O_DEN) |= 0x80;
        HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = 0;

        
        // Enable QEI Peripherals
        SysCtlPeripheralEnable(SYSCTL_PERIPH_QEI0);
        
        //Set Pins to be PHA0 and PHB0
	GPIOPinConfigure(GPIO_PD6_PHA0); //GPIOPinConfigure(0x00031806);  //0x00031806 =>GPIO_PD6_PHA0
	GPIOPinConfigure(GPIO_PD7_PHB0); //GPIOPinConfigure(0x00031C06);  // 0x00031C06 => GPIO_PD7_PHB0
        
        //Set GPIO pins for QEI
	GPIOPinTypeQEI(GPIO_PORTD_BASE, (GPIO_PIN_6 | GPIO_PIN_7));
        
        //HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_M; 
         
        // Configure quadrature encoder, use an arbitrary top limit of 2000 and enable QEI 
	QEIConfigure(QEI0_BASE,(QEI_CONFIG_CAPTURE_A | QEI_CONFIG_NO_RESET | QEI_CONFIG_QUADRATURE | QEI_CONFIG_NO_SWAP), 2000);
        QEIEnable(QEI0_BASE);
        
        //Set position to a middle value
	QEIPositionSet(QEI0_BASE, 1000);

        //Configure and enable velocity
	QEIVelocityConfigure(QEI0_BASE, QEI_VELDIV_1, SysCtlClockGet()); // Divide by clock speed to get counts/sec
	QEIVelocityEnable(QEI0_BASE);

}
void setup()
{
  config_QEI();
  Serial.begin(9600);
  Serial.println("Start:");
  Serial.println("--------");
}

void loop()
{
  uint32_t velocity, position;
  int32_t rotatingdirection;
  
  position = QEIPositionGet(QEI0_BASE); 
  velocity = QEIVelocityGet(QEI0_BASE);
  rotatingdirection = QEIDirectionGet(QEI0_BASE);
  
  Serial.println(position);
  Serial.println(velocity);
  Serial.println(rotatingdirection);
  Serial.println("--------");
  delay(1000);
  
}

any ideas?

 

Edit: i forgot to tell you what doesn't work.

 

the position isn't chainging and the direction doesn't work correct.

i can't measure if the velocity is correct at the moment, but it seems to be correct...

 

Edit2: 

is there any possibility to test my unlock-code?

Share this post


Link to post
Share on other sites

Use QEI_CONFIG_SWAP instead of QEI_CONFIG_NO_SWAP for QEIConfigure to swap inputs, to invert inputs you have to add the constants yourself as they are not defined in the qei.h for some reason, I think the following defines are correct:

 

#define QEI_INVA 0x00000100

#define QEI_INVB 0x00000200

 

You may also check if SIGMODE is set correctly for your type of input.

                 

I am using similar code as yours with a stepper motor providing the phase inputs and it works as it should.

Share this post


Link to post
Share on other sites

thanks for your hint... i found out that there is a hw_qei.h with:

#define QEI_CTL_INVB            0x00000400  // Invert PhB
#define QEI_CTL_INVA            0x00000200  // Invert PhA

so i included it ( #include "inc/hw_qei.h")

 

i think i have to set this with:

HWREG(QEI0_BASE + QEI_O_CTL) |= QEI_CTL_INVA;
HWREG(QEI0_BASE + QEI_O_CTL) |= QEI_CTL_INVB;     

but i'm not sure about it and i'm not sure where to place it... all in all it doesn't work right now

 

i think my sigmode is set correct, but i am not 100% sure, i have two sensors (A and B ) that are placed in an 90

Share this post


Link to post
Share on other sites

Do you have access to a oscilloscope or a multimeter? Then you can check if you have clock/direction signals or not, if so one output will only change with change of direction.

 

You may add QEI_CTL_INVA/QEI_CTL_INVB to the ui32Config parameter for QEIConfigure

QEIConfigure(QEI0_BASE,(QEI_CONFIG_CAPTURE_A | QEI_CONFIG_NO_RESET | QEI_CONFIG_QUADRATURE | QEI_CONFIG_NO_SWAP | QEI_CTL_INVA | QEI_CTL_INVB), 2000);

setting them via HWREG is also correct.

 

Posting a photo of your motor may also be helpful if you want more ideas.

Share this post


Link to post
Share on other sites

Thanks, after you told me what the clock/direction-signal should be, i am sure that i have to use quadrature... i have two signals following each other...

 

 

the picture shows the QE-sensors and the engine shaft with a magnet....

 

 

post-46848-0-06411000-1441392814_thumb.png

Share this post


Link to post
Share on other sites

It sure must be quadrature output, seems like Hall sensors to me.

I can only post the code I am using then, it is not yet finished and is made as a grlib driver - anyway the QEI handling is working.

 

Good luck!


#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 "grlib/grlib.h"
#include "grlib/widget.h"

static volatile uint32_t xPos = 0, yPos = 0, b1 = 0;
static uint32_t xMax = 320, yMax = 240;
static int32_t xChange = 0;
static int32_t (*g_pfnTSHandler)(uint32_t ulMessage, int32_t lX, int32_t lY) = 0;
static void (*pfnBtn1Handler)(bool btn1Pressed) = 0;
static bool wrapAround = false;

void NavigatorIntHandler (void) {

	uint32_t pos, bchg;

	QEIIntClear(QEI0_BASE, QEI_INTTIMER);

	if(GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4)) {
		bchg = b1 == 3;
		b1 = 0;
	} else {
		bchg = b1 == 2;
		b1 = b1 < 3 ? b1 + 1 : b1;
	}

	if(bchg) {

		if(pfnBtn1Handler)
			pfnBtn1Handler(b1 == 3);

		QEIPositionSet(QEI0_BASE, b1 ? yPos : xPos);

		if(g_pfnTSHandler)
			g_pfnTSHandler(b1 ? WIDGET_MSG_PTR_DOWN : WIDGET_MSG_PTR_UP, (int32_t)xPos, (int32_t)yPos);

	} else if(b1 == 3) {

		if((pos = QEIPositionGet(QEI0_BASE)) != yPos) {

			if(pos > yMax) {
				if(!wrapAround || yPos > 10) {
					QEIPositionSet(QEI0_BASE, yMax);
					pos = yMax;
				} else {
					QEIPositionSet(QEI0_BASE, 0);
					pos = 0;
				}
			}

			yPos = pos;

			if(g_pfnTSHandler) {
				g_pfnTSHandler(WIDGET_MSG_PTR_MOVE, (int32_t)xPos, (int32_t)yPos);
			}

		}

	} else if((pos = QEIPositionGet(QEI0_BASE)) != xPos) {

		xChange = pos - xPos;

		if(pos > xMax) {
			if(xPos > 10) {
				QEIPositionSet(QEI0_BASE, xMax);
				pos = xMax;
			} else {
				QEIPositionSet(QEI0_BASE, 0);
				pos = 0;
			}
		}

		xPos = pos;

		if(g_pfnTSHandler) {
			g_pfnTSHandler(WIDGET_MSG_PTR_MOVE, (int32_t)xPos, (int32_t)yPos);
		}

	}

}

void NavigatorInit (uint32_t xSize, uint32_t ySize) {

	xMax = xSize - 1;
	yMax = ySize - 1;

	// 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);

	//Unlock GPIOD7 - Like PF0 its used for NMI - Without this step it doesn't work
	HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY; //In Tiva include this is the same as "_DD" in older versions (0x4C4F434B)
	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. I believe this sets the pull up and makes them inputs
	GPIOPinTypeQEI(GPIO_PORTD_BASE, GPIO_PIN_6 | GPIO_PIN_7);

	GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_4);
	GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);

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

	// Configure quadrature encoder, use an arbitrary top limit of 1000
	QEIConfigure(QEI0_BASE, (QEI_CONFIG_CAPTURE_A_B  | QEI_CONFIG_NO_RESET 	| QEI_CONFIG_QUADRATURE | QEI_CONFIG_NO_SWAP), 1000);

	QEIVelocityConfigure(QEI0_BASE, QEI_VELDIV_1, SysCtlClockGet() / 100);

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

	//Set position to a middle value so we can see if things are working
	QEIPositionSet(QEI0_BASE, xPos);
	QEIIntRegister(QEI0_BASE, NavigatorIntHandler);
	QEIIntEnable(QEI0_BASE, QEI_INTTIMER);

}

int32_t NavigatorGetXChange (void) {
	return xChange;
}

uint32_t NavigatorGetXPosition (void) {
	return xPos;
}

uint32_t NavigatorGetYPosition (void) {
	return yMax - yPos;
}

uint32_t NavigatorSetXPosition (uint32_t pos, bool callback) {
	if(pos <= xMax) {
		xPos = pos;
		if(!b1)
			QEIPositionSet(QEI0_BASE, xPos);
		if(callback && g_pfnTSHandler)
			g_pfnTSHandler(WIDGET_MSG_PTR_MOVE, (int32_t)xPos, (int32_t)yPos);
	}
	return xPos;
}

uint32_t NavigatorSetYPosition (uint32_t pos, bool callback) {
	if(pos <= yMax) {
		if(b1)
			QEIPositionSet(QEI0_BASE, pos);
		yPos = pos;
		if(callback && g_pfnTSHandler)
			g_pfnTSHandler(WIDGET_MSG_PTR_MOVE, (int32_t)xPos, (int32_t)yPos);
	}
	return (int32_t)(yMax - yPos);
}

bool NavigatorSetPosition (uint32_t xPosNew, uint32_t yPosNew, bool callback) {

	xPos = xPosNew;
	yPos = yPosNew;

	QEIPositionSet(QEI0_BASE, b1 ? yPos : xPos);

	if(callback && g_pfnTSHandler)
		g_pfnTSHandler(WIDGET_MSG_PTR_MOVE, (int32_t)xPos, (int32_t)yPos);

	return true;
}

bool NavigatorGetSwitch1Pressed (void) {
	return b1;
}

bool NavigatorGetSwitch2Pressed (void) {
	return false;
}

void NavigatorCallbackSet(int32_t (*pfnCallback)(uint32_t ulMessage, int32_t lX, int32_t lY)) {
    g_pfnTSHandler = pfnCallback;
}

void NavigatorCallbackSet2(void (*pfnBtn1Callback)(bool btn1Pressed)) {
	pfnBtn1Handler = pfnBtn1Callback;
}


Share this post


Link to post
Share on other sites

yes, this are hall-sensors... 

 

thanks for your code

 

i can't see huge differences but the order is a bit different, so i'll try some things and maybe i'll have success... 

Share this post


Link to post
Share on other sites

i think i have a problem with my hardware, found out, that it is possible to change the velocity-value by shaking the cables... so maybe my code works

Share this post


Link to post
Share on other sites

My cable had a problem, but the code doesn't work... i tested the signals with an oscilloscope, i tested the inversion wir clock-dir-mode, i think the unlocking should work too...

 

In CLOCK_DIR mode i see that position is counted if the magnet passes (2 counts each time) it works with both signals (tested by swapping PhA an PhB).

In QUADRATURE mode, the position counter doesn't work. Sometimes it +1 or -1 around the base value but it doesn't count the rotations...

I have unlocked PD7 and i think this works... (see clock_dir mode), the inversion works because the direction in clock_dir mode changes if i don't invert.

 

:(

Share this post


Link to post
Share on other sites

found my problem signal doesn't fit to what quadrature, so i can't use qei to detect direction... but i can detect velocity and count position with clock_dir... 

Share this post


Link to post
Share on other sites

found my problem signal doesn't fit to what quadrature, so i can't use qei to detect direction... but i can detect velocity and count position with clock_dir... 

 

Hello,

 

I too want to use the QEI for a project, and I was wondering which launchpad you are using: The title says "EK-TM4C123GXL" which is the Tiva-C series connected launchpad? Is this correct?

 

Best regards,

C.A.

Share this post


Link to post
Share on other sites

Hello Rhab,

 

In the past I made a pid controller for pololu n20 motors with encoders. (tiny little motors) At the beginning I was using an optical encoder, which turned out to be problematic, (since they require external signal processing, and output not square but sine wave, and that sine wave can change in amplitude and offset depending on the position of the encoder disk to the sensor, they were problematic) Then I got the same encoders with hall effect sensors, which did indeed output pure square wave.

 

I then used interrupts to read encoder outputs, and driving two motors with the tiva-c board did indeed work, but for encoders that worked > 300 pulse per revolution, I had problems (due to interrupt traffic) - then I stopped working on the project all together.

 

Did you get your code to work using this QEI encoders? Can you assign any pin to read encoder input, and is it possible to drive two motors from one tiva-c board? I have read the thread carefully but I was not able to make a conclusion.

 

Best regards,

C.

 

 

 

 

This is correct. It is one of the Tiva C series connected launchpads. The cheapest one.

http://www.ti.com/ww/en/launchpad/launchpads-connected-ek-tm4c123gxl.html#tabs

 

best regars

rhab

Share this post


Link to post
Share on other sites

Did you get your code to work using this QEI encoders? Can you assign any pin to read encoder input, and is it possible to drive two motors from one tiva-c board? I have read the thread carefully but I was not able to make a conclusion.

 

yes and no. i decided to use the clock-direction mode with only one hallsensor, because the signal of both hall-sensors did not fit to what is needed for quadrature-mode (my sensors did not have the state both high, tell me if you need more information...).

i get the direction directly from my montor-controler and only need one hallsensor as "clock" because every flank (up and down) is counted, there are two counts per revolution. if you know this it should be no problem to use this mode if this is a good enough resolution...

 

sorry for this late answer

 

it should be possible to monitor two motors because the the EK-TM4C123 has two QEI-moduls. You should use the QEI-Pins to use QEI, so you have no big choice...

Share this post


Link to post
Share on other sites

Thanks, well I have multiple motors with different encoders, so I will try soon. It would be really exciting to get that board working with high res QEI.

 

Yes about sensors you mentioned `my sensors did not have the state both high` what does this mean? I have square wave sensor outputs, usually overlapping to a degree, and if on falling edge of A, B is high that is forward, and if the falling edge of A has B low, that is reverse. Would that be sufficient?

 

Best regards,

C.A.

 

 

yes and no. i decided to use the clock-direction mode with only one hallsensor, because the signal of both hall-sensors did not fit to what is needed for quadrature-mode (my sensors did not have the state both high, tell me if you need more information...).

i get the direction directly from my montor-controler and only need one hallsensor as "clock" because every flank (up and down) is counted, there are two counts per revolution. if you know this it should be no problem to use this mode if this is a good enough resolution...

 

sorry for this late answer

 

it should be possible to monitor two motors because the the EK-TM4C123 has two QEI-moduls. You should use the QEI-Pins to use QEI, so you have no big choice...

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...