edge lab LIVE! starts Tuesday in Munich. Grab a workshop seat and take home the board you build on. Code FRIEND-HACKSTER saves 20%. () Learn more!
Robert Fletcher
Published

Closed-Loop DC Motor Speed Controller Using TI TMS320F2812

Build a practical closed-loop DC motor controller around the TI TMS320F2812, using its high-speed ADC, PWM Event Manager, QEP interface, and

IntermediateWork in progress29
Closed-Loop DC Motor Speed Controller Using TI TMS320F2812

Things used in this project

Hardware components

TMS320F2812PGFA
Main 32-bit C28x controller
×1
12–24 V brushed DC motor
Motor under control
×1
N-channel MOSFET
Low-side motor switching
×1
MOSFET gate driver
Provides sufficient gate-drive current
×1
Incremental rotary encoder
Motor speed/position feedback
×1
Current-sense resistor
Measures motor current
×1
Current-sense amplifier
Conditions motor-current signal for ADC
×1
5 V regulator
Digital/control supply
×1
3.3 V regulator
Logic/ADC supply where required by the design
×1
Flyback diode
Suppresses inductive motor voltage
×1
DC input power supply
Motor power
×1
JTAG emulator
Programming and real-time debugging
×1
USB-to-UART adapter
Optional PC telemetry interface
×1
100 nF/1 Β΅F decoupling capacitors
Local supply bypassing
×1
Bulk electrolytic capacitor
Motor supply energy buffering
×1

Software apps and online services

Code Composer Studio (CCS)
TI C2000 development tools
TI TMS320F281x device support files
Tera Term
Oscilloscope software

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot-air rework station
Digital multimeter
Oscilloscope
Logic analyzer
Bench DC power supply
Wire cutter/stripper
Crimping tool
PCB assembly tools
PCB milling machine
PCB fabrication service

Story

Read more

Schematics

Motor Power Stage

Encoder Interface

Current Measurement

Firmware Architecture

Code

TMS320F2812 Motor Control Firmware Code

C/C++
#include "DSP281x_Device.h"
#include "DSP281x_Examples.h"

#define PWM_PERIOD      7500.0f
#define DUTY_MIN        0.05f
#define DUTY_MAX        0.95f

#define SPEED_REF_RPM   1500.0f

#define KP              0.0008f
#define KI              0.00002f

volatile float speed_ref = SPEED_REF_RPM;
volatile float speed_rpm = 0.0f;
volatile float motor_current = 0.0f;
volatile float pwm_duty = 0.10f;

volatile float speed_error = 0.0f;
volatile float integrator = 0.0f;

volatile Uint32 encoder_last = 0;
volatile Uint32 encoder_now = 0;

interrupt void ControlISR(void);


/*----------------------------------------------------------
 * PWM initialization
 *----------------------------------------------------------*/
void PWM_Init(void)
{
    EALLOW;

    /*
     * Configure Event Manager PWM period.
     *
     * The exact register configuration depends on
     * the selected EVA/EVB PWM output.
     */

    EvaRegs.T1PR = (Uint16)PWM_PERIOD;

    /*
     * Initial duty cycle = 10 %
     */
    EvaRegs.T1CMPR = (Uint16)(PWM_PERIOD * pwm_duty);

    /*
     * Enable Timer 1 operation.
     * Additional PWM polarity/dead-band configuration
     * should be added for the selected power stage.
     */

    EvaRegs.T1CON.all = 0x1042;

    EDIS;
}


/*----------------------------------------------------------
 * ADC initialization
 *----------------------------------------------------------*/
void ADC_Init(void)
{
    EALLOW;

    /*
     * ADC clock and sequencer configuration.
     *
     * The exact values should be selected according
     * to the ADC clock requirements in the F2812
     * datasheet.
     */

    AdcRegs.ADCTRL1.all = 0x0000;
    AdcRegs.ADCTRL2.all = 0x0000;
    AdcRegs.ADCTRL3.all = 0x0000;

    EDIS;
}


/*----------------------------------------------------------
 * Read motor current
 *----------------------------------------------------------*/
float Read_MotorCurrent(void)
{
    Uint16 adc_count;

    /*
     * ADCRESULT channel selection depends on the
     * actual hardware connection.
     */

    adc_count = AdcRegs.ADCRESULT0 >> 4;

    /*
     * Example conversion.
     *
     * Replace ADC_FULL_SCALE, SHUNT_RESISTOR and
     * CURRENT_AMP_GAIN with values from the actual
     * hardware.
     */

    return ((float)adc_count / 4095.0f);
}


/*----------------------------------------------------------
 * Encoder speed calculation
 *----------------------------------------------------------*/
float Calculate_RPM(void)
{
    Uint32 position;
    int32 delta;
    float rpm;

    /*
     * Replace this section with the actual QEP position
     * register used by the selected F2812 Event Manager.
     */

    position = EvaRegs.T2CNT;

    delta = (int32)position - (int32)encoder_last;

    encoder_last = position;

    /*
     * Example scaling only.
     *
     * The final conversion depends on:
     *   encoder counts/revolution
     *   control-loop frequency
     *   QEP counting mode
     */

    rpm = ((float)delta * 60.0f * 10000.0f) /
          4000.0f;

    return rpm;
}


/*----------------------------------------------------------
 * PI speed controller
 *----------------------------------------------------------*/
float PI_Controller(float reference, float feedback)
{
    float output;

    speed_error = reference - feedback;

    integrator += KI * speed_error;

    /*
     * Anti-windup
     */
    if (integrator > DUTY_MAX)
        integrator = DUTY_MAX;

    if (integrator < DUTY_MIN)
        integrator = DUTY_MIN;

    output = KP * speed_error + integrator;

    if (output > DUTY_MAX)
        output = DUTY_MAX;

    if (output < DUTY_MIN)
        output = DUTY_MIN;

    return output;
}


/*----------------------------------------------------------
 * 10 kHz control interrupt
 *----------------------------------------------------------*/
interrupt void ControlISR(void)
{
    float new_duty;

    /*
     * Read analog feedback
     */
    motor_current = Read_MotorCurrent();

    /*
     * Calculate speed from encoder feedback
     */
    speed_rpm = Calculate_RPM();

    /*
     * Closed-loop speed controller
     */
    new_duty = PI_Controller(speed_ref, speed_rpm);

    pwm_duty = new_duty;

    /*
     * Update PWM compare register
     */
    EvaRegs.T1CMPR =
        (Uint16)(PWM_PERIOD * pwm_duty);

    /*
     * Clear Event Manager interrupt flag
     */
    EvaRegs.EVAIFRA.bit.T1PINT = 1;

    /*
     * Acknowledge interrupt group
     */
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP2;
}


/*----------------------------------------------------------
 * Main
 *----------------------------------------------------------*/
void main(void)
{
    InitSysCtrl();

    DINT;

    InitPieCtrl();

    IER = 0x0000;
    IFR = 0x0000;

    InitPieVectTable();

    EALLOW;

    PieVectTable.T1PINT = &ControlISR;

    EDIS;

    PWM_Init();
    ADC_Init();

    /*
     * GPIO, QEP and SCI initialization should be
     * added according to the selected board.
     */

    /*
     * Enable Event Manager Timer 1 interrupt
     */
    EvaRegs.T1CON.bit.TENABLE = 1;

    PieCtrlRegs.PIEIER2.bit.INTx5 = 1;

    IER |= M_INT2;

    EINT;
    ERTM;

    while(1)
    {
        /*
         * Background tasks:
         *
         * - UART commands
         * - telemetry
         * - fault monitoring
         * - over-current detection
         * - status LED
         */

        if (motor_current > 5.0f)
        {
            /*
             * Example protection threshold.
             * Replace with actual motor/system limit.
             */

            pwm_duty = DUTY_MIN;

            EvaRegs.T1CMPR =
                (Uint16)(PWM_PERIOD * pwm_duty);
        }
    }
}

Credits

Robert Fletcher
20 projects β€’ 1 follower
Senior semiconductor engineer at AVAQ Semiconductor. With extensive experience in electronic components, supply chain solutions & sourcing.

Comments