Jump to content
Sign in to follow this  
captainkent

PID Controlled DC Motor

Recommended Posts

Hey all,

I've got a project going where I'm making a PID controlled DC motor using the TM4C1294NCPDT LaunchPad. The idea is to send a set point to the DC motor, whether it be a certain velocity (rpm) or a certain position (in degrees), and the motor will attempt to follow this profile with the implementation of a PID controller.

So far, I've completed the code which will drive my motor, which is shown below. This also allows a potentio meter to change the duty cycle, simply to show it is working, and will not be used in the final code.

 

To interpret the velocity and direction of the DC motor, I thought I would use QEI module and its APIs, but I'm a bit stuck. So far, it doesn't seem to interpret anything, so I'm lost as to what I've done wrong. Anybody here have any experiece with this module? If it isn't well supported, I want to move on and use an interrupt instead. The code is shown below.

 

Motor driving code.

/*Includes & definitions.*/
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/debug.h"
#include "driverlib/pwm.h"
#include "driverlib/pin_map.h"
#include "driverlib/adc.h"
//**********************************************************
#define PWM0_FREQUENCY 24000
//**********************************************************
volatile uint32_t ui32PWM0Load; //PWM Period.
volatile uint32_t ui32PWM0DutcyCycle; //PWM Duty cycle
volatile uint32_t ui32PWM0ClockFreq; //PWM clock frequency
volatile uint32_t ui32SysClkFreq; //System clock frequency
volatile uint32_t ui32PWM0DutyCycleADC[2];
volatile uint32_t ui32PWM0DutyCycleADCStored;

/*Functions.*/
void Init(void);
void InitPWM(void);
void InitADC(void);


/*
This code will take the input from a pot meter, convert it
to an ADC value, which will manipulate the duty cycle of
the PWM signal.

    pot meter
        _________
        |    |    |
        |    |    |
       +5V PE0 GND

   PWM signal

       PWM0         - PF0
       PWM1(/PWM0)    - PF1
*/

int main(void)
{
    Init();
    InitADC();
    InitPWM();

    while(1)
    {
        ADCIntClear(ADC0_BASE, 1);
        ADCProcessorTrigger(ADC0_BASE, 1);
        while(!ADCIntStatus(ADC0_BASE, 1, false))
        {
        }
        ADCSequenceDataGet(ADC0_BASE, 1, ui32PWM0DutyCycleADC);
        ui32PWM0DutyCycleADCStored = ui32PWM0DutyCycleADC[0];
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, ui32PWM0DutyCycleADCStored/13);
    }

}

void Init()
{
/*    Initialize the clock for 120MHz.*/
    ui32SysClkFreq = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);

/*    Enable the peripherals.*/
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);

}

void InitADC()
{
/*    Iniatialize the ADC.*/
    ADCHardwareOversampleConfigure(ADC0_BASE, 64);//Sample the read value, to smooth it out.

    //ADC configure.
    GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0);//Set the GPIO pin PE0 as an ADC pin.

    ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PROCESSOR, 0);
    ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_CH3|ADC_CTL_IE|ADC_CTL_END);

    ADCSequenceEnable(ADC0_BASE, 1);

}

void InitPWM()
{
        PWMClockSet(PWM0_BASE,PWM_SYSCLK_DIV_16);

        GPIOPinConfigure(GPIO_PF0_M0PWM0);
        GPIOPinConfigure(GPIO_PF1_M0PWM1);
        GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_1);

        ui32PWM0ClockFreq = (ui32SysClkFreq / 16); //(120000000 / 64 = 1875000)
        ui32PWM0Load = ((ui32PWM0ClockFreq / PWM0_FREQUENCY) -1); //1875000 / 24000 =

        //Configure PWM.
        PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN);
        PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, ui32PWM0Load);
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, ui32PWM0Load/2);

        PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT, true);
        PWMOutputState(PWM0_BASE, PWM_OUT_1_BIT, true);
        PWMGenEnable(PWM0_BASE, PWM_GEN_0);
        PWMDeadBandEnable(PWM0_BASE, PWM_GEN_0, 8, 8);
}

QEI code.

/*Global includes,  definitions and functions.*/
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/hw_qei.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/debug.h"
#include "driverlib/pwm.h"
#include "driverlib/pin_map.h"
#include "driverlib/qei.h"

volatile int32_t ui32QEIDirection;
volatile int32_t ui32QEIVelocity;
volatile uint32_t ui32QEIPosition;

int main(void)
{
    volatile uint32_t ui32SysClkFreq;
    uint32_t yolo = 0;


/*    Set the clock to run at 120MHz.*/
    ui32SysClkFreq = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);

/*    Enable peripherals.*/
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_QEI0);

/*    Configure GPIO pins.*/
    GPIOPinConfigure(GPIO_PL1_PHA0 | GPIO_PL2_PHB0);
    GPIOPinTypeQEI(GPIO_PORTL_BASE, GPIO_PIN_1 | GPIO_PIN_2);

/*    Configure QEI.*/
/*    Disable everything first.*/
    QEIDisable(QEI0_BASE);
    QEIVelocityDisable(QEI0_BASE);
    QEIIntDisable(QEI0_BASE, (QEI_INTERROR | QEI_INTDIR | QEI_INTTIMER | QEI_INTINDEX));

/*    Configure the QEI to capture on both A and B, not to reset when there is an index pulse, configure it as a quadrature encoder,
    and doesn't swap signals PHA0 and PHB0 and set the maxmimum position as 1999. (MAX_POS_CAPTURE_A_B = (ppr*4)-1.'*/
    QEIConfigure(QEI0_BASE, (QEI_CONFIG_CAPTURE_A_B | QEI_CONFIG_NO_RESET | QEI_CONFIG_QUADRATURE | QEI_CONFIG_NO_SWAP), 1999);
    QEIVelocityConfigure(QEI0_BASE, QEI_VELDIV_16, 40000);
    QEIPositionSet(QEI0_BASE, 999);

/*    Enable what needs to be enabled.*/
    QEIEnable(QEI0_BASE);
    QEIVelocityEnable(QEI0_BASE);

/*    Loop forever.*/
    while(1)
    {
/*        Get direction (1 = forward, -1 = backward).*/
        ui32QEIDirection = QEIDirectionGet(QEI0_BASE);
/*        Get velocity.*/
        ui32QEIVelocity = QEIVelocityGet(QEI0_BASE);
        if(ui32QEIDirection == 1)
        {
            yolo++;//This is done to see if my breakpoint is working.
        }
/*        Delay for certain amount of time.*/
        //SysCtlDelay(3000);

    }

}

Share this post


Link to post
Share on other sites

No experience in the QEI module, but are you hooking up a quad encoder? I don't see that in your description.

That's unfortunate. I am indeed hooking up a quad encoder (datasheet). In the meanwhile I thought it could be that the Launchpad wasn't seeing the logical high, as the quad encoder logical output was 5Vs, where the Launchpads logical high input is 2.145V - 4V. Therefore, I made a crude voltage divider to bring the quad encoders output down to 3.3V, but it still doesn't seem to work.

 

I'm gonna try and figure out how to make my own interrupts now, to see if I can just skip the QEI module entirely, but I'm getting a bit confused as to what I'm reading in the Tiva API guide vs. the examples provided.

Share this post


Link to post
Share on other sites

As an update, I got some feedback elsewhere on the QEI module problem. I had incorrectly configured the GPIO pins!

 

This line:

 GPIOPinConfigure(GPIO_PL1_PHA0 | GPIO_PL2_PHB0);

needs to be this:

GPIOPinConfigure(GPIO_PL1_PHA0);
GPIOPinConfigure(GPIO_PL2_PHB0);

Now to make the results from the QEI module more reliable!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×