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.

RTOS/TM4C123GH6PM: QEI multiplexing

Part Number: TM4C123GH6PM

Tool/software: TI-RTOS

Dear all,

I'm using TM4C123GH6PM,CCSv8.1.0,TI-RTOS - 2.16.00.08

As per my project requirement I have to use 8 QEI's. But TM4C123GH6PM is capable of 2 QEI . Is there any possibility of making normal GPIO pins as QEI capable for remaining 6 QEI. If not how can I over come this problem. Please suggest me a solution in this.

I have tried with multiplexing 4 motor's Hall sensors to QEI0 and other 4 Motor's Hall sensors to QEI1.
But for some reason my multiplex is not working.

I'm trying for last 10 days to solve this problem but i haven't got desired solution .(Now thinking that I'm going in a wrong way).
Really your help is required to solve this.

Thanks in Advance

Regards,

Yashwanth Kumar Gandeti

  • /*
     * Copyright (c) 2015, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /*
     *  ======== pwmled.c ========
     */
    /* XDCtools Header files */
    #include <xdc/std.h>
    #include <xdc/runtime/System.h>
    #include <xdc/runtime/Timestamp.h>// used for Timestamp() calls
    #include <ti/sysbios/knl/Clock.h> // used for clock calls
    #include <ti/sysbios/knl/Semaphore.h>
    
    /* TivaWare Header Files*/
    #include <stdint.h>
    #include <stdbool.h>
    #include "driverlib/sysctl.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/qei.h"
    
    /* BIOS Header files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Event.h>
    
    /* TI-RTOS Header files */
    #include <ti/drivers/GPIO.h>
    #include <ti/drivers/PWM.h>
    
    
    
    /* Example/Board Header files */
    #include "Board.h"
    
    
    #define TASKSTACKSIZE   512
    
    Task_Struct tsk0Struct;
    UInt8 tsk0Stack[TASKSTACKSIZE];
    Task_Handle task;
    Event_Handle myEvt;
    Semaphore_Struct sem0Struct, sem1Struct;
    Semaphore_Handle semHandle;
    Event_Struct evtStruct;
    
    volatile long enc_count = 0;
    long timeNew;
    long timeOld;
    long timeDelta;
    /*
     *  ======== pwmLEDFxn ========
     *  Task periodically increments the PWM duty for the on board LED.
     */
    Void pwmLEDFxn(UArg arg0, UArg arg1)
    {
        PWM_Handle pwm1;
        PWM_Handle pwm2 = NULL;
        PWM_Params params;
        uint32_t   pwmPeriod = 50;      // Period and duty in microseconds
        uint16_t   duty = 0;
        uint16_t   dutyInc = 1;
    
        PWM_Params_init(&params);
        params.period = pwmPeriod;
        pwm1 = PWM_open(Board_PWM0, &params);
        if (pwm1 == NULL) {
            System_abort("Board_PWM0 did not open");
        }
    
        if (Board_PWM1 != Board_PWM0) {
            params.polarity = PWM_POL_ACTIVE_LOW;
            pwm2 = PWM_open(Board_PWM1, &params);
            if (pwm2 == NULL) {
                System_abort("Board_PWM1 did not open");
            }
        }
    
        /* Loop forever incrementing the PWM duty */
        while (1) {
            PWM_setDuty(pwm1, duty);
            if (pwm2) {
                PWM_setDuty(pwm2, duty);
            }
    
            duty = (duty + dutyInc);
            if (duty == pwmPeriod || (!duty)) {
                dutyInc = - dutyInc;
            }
    
            Task_sleep((UInt) arg0);
        }
    }
    
    Void QEITaskFxn(UArg arg0, UArg arg1)
    {
        uint32_t qeiVelocity1 = 0;
        uint32_t qeiPosition1 = 0;
        int32_t qeiDirection1 = 0;
    
        uint32_t qeiVelocity2 = 0;
        uint32_t qeiPosition2 = 0;
        int32_t qeiDirection2 = 0;
    
        // Disable the quadrature encoder.
        QEIDisable(QEI0_BASE);
    
        QEIConfigure(QEI0_BASE, (QEI_CONFIG_CAPTURE_A_B | QEI_CONFIG_NO_RESET | QEI_CONFIG_QUADRATURE | QEI_CONFIG_NO_SWAP), 1000);
    
        // Configure the Velocity.
        QEIVelocityConfigure(QEI0_BASE,QEI_VELDIV_1,20000000);
    
        //  Note :For Calculating "ui32Period" , TM4c123GH6PM Supports 80 MHz Operation, So I Have to Take
        //  ui32Period = 1/4(80 MHz) = 20000000
    
        // setting the Position.
        QEIPositionSet(QEI0_BASE, 0);
    
        // Enable the quadrature encoder velocity.
        QEIVelocityEnable(QEI0_BASE);
    
        // Enable the quadrature encoder.
        QEIEnable(QEI0_BASE);
    
        // Delay for some time...
        Task_sleep(1000);
    
        while (1)
        {
            UInt posted;
            posted = Event_pend(myEvt,Event_Id_NONE,Event_Id_00+Event_Id_01+Event_Id_02,BIOS_WAIT_FOREVER);
            if(posted && Event_Id_00)
            { // Read the encoder position.
                qeiPosition1 = QEIPositionGet(QEI0_BASE);
                System_printf("\r\nqeiPosition1 = {%d} \r\n", qeiPosition);
                System_flush();
    
                // Read the encoder Direction.
                qeiDirection1 = QEIDirectionGet(QEI0_BASE);
                System_printf("\r\nqeiDirection1 = {%d} \r\n", qeiDirection);
                System_flush();
    
                // Read the encoder velocity.
                qeiVelocity1 = QEIVelocityGet(QEI0_BASE);
                System_printf("\r\nqeiVelocity1 = {%d} ******************\r\n",
                              qeiVelocity);
                System_flush();
            }
            if(posted && Event_Id_01)
            { // Read the encoder position.
                qeiPosition2 = QEIPositionGet(QEI0_BASE);
                System_printf("\r\nqeiPosition2 = {%d} \r\n", qeiPosition);
                System_flush();
    
                // Read the encoder Direction.
                qeiDirection2 = QEIDirectionGet(QEI0_BASE);
                System_printf("\r\nqeiDirection2 = {%d} \r\n", qeiDirection);
                System_flush();
    
                // Read the encoder velocity.
                qeiVelocity2 = QEIVelocityGet(QEI0_BASE);
                System_printf("\r\nqeiVelocity2 = {%d} ******************\r\n",
                              qeiVelocity);
                System_flush();
            }
    
    
            Task_sleep(100);
        }
    
    }
    
    void encoder_isr0(unsigned int index){
        GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_6, (GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_2) & GPIO_PIN_2));
        GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_7, (GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_3) & GPIO_PIN_3));
        Event_post(myEvt, Event_Id_00);
    }
    void encoder_isr1(unsigned int index){
        GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_6, (GPIOPinRead(GPIO_PORTB_BASE, GPIO_PIN_2) & GPIO_PIN_2));
        GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_7, (GPIOPinRead(GPIO_PORTB_BASE, GPIO_PIN_3) & GPIO_PIN_3));
        Semaphore_post(semHandle);
    }
    
    /*
     *  ======== main ========
     */
    int main(void)
    {
        Task_Params tskParams;
        Semaphore_Params semParams;
    
        /* Call board init functions. */
        Board_initGeneral();
        Board_initGPIO();
        Board_initPWM();
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    
        // QEI for first motor
        GPIOPinTypeGPIOInput(GPIO_PORTD_BASE, GPIO_PIN_2);
        GPIOPinTypeGPIOInput(GPIO_PORTD_BASE, GPIO_PIN_3);
    
        GPIO_setCallback(ENCODER_PHA0, encoder_isr0);  // ENCODER_PHA0 ==> GPIOTiva_PD_2 | GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_BOTH_EDGES
        GPIO_setCallback(ENCODER_PHB0, encoder_isr0);  // ENCODER_PHB0 ==> GPIOTiva_PD_3 | GPIO_CFG_IN_PU| GPIO_CFG_IN_INT_BOTH_EDGES
    
        //Enable interrupts
        GPIO_enableInt(ENCODER_PHA0);
        GPIO_enableInt(ENCODER_PHB0);
    
    
    
        // QEI for second motor
        GPIOPinTypeGPIOInput(GPIO_PORTB_BASE, GPIO_PIN_2);
        GPIOPinTypeGPIOInput(GPIO_PORTB_BASE, GPIO_PIN_3);
    
        GPIO_setCallback(ENCODER_PHA1, encoder_isr1);  // ENCODER_PHA1 ==> GPIOTiva_PB_2 | GPIO_CFG_IN_PU| GPIO_CFG_IN_INT_BOTH_EDGES
        GPIO_setCallback(ENCODER_PHB1, encoder_isr1);  // ENCODER_PHB1 ==> GPIOTiva_PB_3 | GPIO_CFG_IN_PU| GPIO_CFG_IN_INT_BOTH_EDGES
    
        //Enable interrupts
        GPIO_enableInt(ENCODER_PHA1);
        GPIO_enableInt(ENCODER_PHB1);
    
    
        // Enable QEI Peripherals
        SysCtlPeripheralEnable(SYSCTL_PERIPH_QEI0);
    
        //Set Pins to be PHA0 and PHB0
        GPIOPinConfigure(GPIO_PD6_PHA0);
        GPIOPinConfigure(GPIO_PD7_PHB0);
    
        //Set GPIO pins for QEI
        GPIOPinTypeQEI(GPIO_PORTD_BASE, (GPIO_PIN_6 | GPIO_PIN_7));
    
    
        /* Construct LED Task thread */
        Task_Params_init(&tskParams);
        tskParams.stackSize = TASKSTACKSIZE;
        tskParams.stack = &tsk0Stack;
        tskParams.arg0 = 50;
        tskParams.priority = 1;
        Task_construct(&tsk0Struct, (Task_FuncPtr)pwmLEDFxn, &tskParams, NULL);
    
        Event_construct(&evtStruct, NULL);
    
        /* Obtain event instance handle */
        myEvt = Event_handle(&evtStruct);
    
        Semaphore_Params_init(&semParams);
        semParams.mode = Semaphore_Mode_BINARY;
        semParams.event = myEvt;
        semParams.eventId = Event_Id_01;
        Semaphore_construct(&sem0Struct, 0, &semParams);
    
        semHandle = Semaphore_handle(&sem0Struct);
    
    
        /* Turn on user LED */
        GPIO_write(Board_LED0, Board_LED_ON);
    
        System_printf("Starting the example\nSystem provider is set to SysMin. "
                      "Halt the target to view any SysMin contents in ROV.\n");
    
        /* SysMin will only print to the console when you call flush or exit */
        System_flush();
    
        SysCtlClockFreqSet(SYSCTL_XTAL_12MHZ | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN, 8000000);
        /* Start BIOS */
        BIOS_start();
    
        return (0);
    }
    

  • I forgot to attach code in my post.

    In the above code I tried to multiplex 2 motor]s phA and phB channels.
    Step wise what i did:
    1. Motor 1 phA given to PD2 , phB given to PD3 and configured to raise an interrupt at both the edges.
    2. In encoder_isr0() - wrote PD2 value to PD6(QEI0 phA pin), PD3 value to PD7(QEI0 phB pin)
    3.Posted an Event_Id_00.
    4. Similarly for encoder_isr1() and posted an Event_Id_01.
    5.QEITaskFxn is made to wait for any one of these events and based on arrived event assigning required value to variables(velocity, direction, postion).
  • Hello Yashwanth,

    I don't see how you have added a multiplexer yet. When I made that suggestion, I meant an external piece of hardware that is controlled by the MCU to send the QEI signal down one of four channels. It would be 1 input (the QEI signal) and four outputs, and the output select is controlled by 2 GPIO's select pins.

    You would then have logic in your software to cycle through each multiplexer output one at a time to control each motor.

    I am not seeing that logic put in place so it doesn't surprise me that what you have been attempting to do is not working.

    There isn't anything else I can do to help with this issue as your problem is application specific and not device-related, though I hope my explanation of the multiplexer setup proves valuable.