Rhab 1 Posted September 3, 2015 Share Posted September 3, 2015 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? Quote Link to post Share on other sites
terjeio 134 Posted September 3, 2015 Share Posted September 3, 2015 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. Rhab 1 Quote Link to post Share on other sites
Rhab 1 Posted September 4, 2015 Author Share Posted September 4, 2015 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 Quote Link to post Share on other sites
terjeio 134 Posted September 4, 2015 Share Posted September 4, 2015 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. Quote Link to post Share on other sites
Rhab 1 Posted September 4, 2015 Author Share Posted September 4, 2015 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.... Quote Link to post Share on other sites
terjeio 134 Posted September 4, 2015 Share Posted September 4, 2015 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; } Quote Link to post Share on other sites
Rhab 1 Posted September 5, 2015 Author Share Posted September 5, 2015 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... Quote Link to post Share on other sites
Rhab 1 Posted September 7, 2015 Author Share Posted September 7, 2015 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 Quote Link to post Share on other sites
Rhab 1 Posted September 9, 2015 Author Share Posted September 9, 2015 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. Quote Link to post Share on other sites
Rhab 1 Posted September 12, 2015 Author Share Posted September 12, 2015 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... Quote Link to post Share on other sites
altineller 4 Posted September 21, 2015 Share Posted September 21, 2015 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. Quote Link to post Share on other sites
Rhab 1 Posted September 21, 2015 Author Share Posted September 21, 2015 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 Quote Link to post Share on other sites
altineller 4 Posted September 28, 2015 Share Posted September 28, 2015 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 Quote Link to post Share on other sites
Rhab 1 Posted October 7, 2015 Author Share Posted October 7, 2015 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... altineller 1 Quote Link to post Share on other sites
altineller 4 Posted October 7, 2015 Share Posted October 7, 2015 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... Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.