DC motor 1 current control - owntech-foundation/Tutorials GitHub Wiki

Objectives

The goal of this tutorial is get the data from an encoder while running an average current PID. The encoder data will not be used to control the current in this example, but it can be used as a basis for creating DC motor control.

For this tutorial we will build on your knowledge of the previous tutorials while adding the encoder related elements. We will start from the Buck 3 Tutorial project.

Required hardware

  • O2 v_1_1_2
  • STLinkV3
  • PC (windows or linux)
  • power supply (40 V, 2 A)
  • An encoder

Required software

  • serialPlot

Create the project

  1. We will create the project by copying the communication project in a new branch, that will be called dc_motor_1. In the terminal,
git status
git add --all
git commit -m "work done on the previous branch (you can replace this message)"
git branch dc_motor_1
git checkout dc_motor_1
  1. On the bottom menu, check that you are now in the dc_motor_1 branch.

![step1]image

  1. Define the variables

In src/main.cpp, add the following code to define the variables that will be used to determine if the converter is in buck mode.

//--------------USER VARIABLES DECLARATIONS----------------------

static uint32_t counter = 0; //counter variable

static float32_t duty_cycle = 0.5; //duty cycle (comm task)
static float32_t duty_cycle_step = 0.05; //duty cycle step (comm task)
static bool pwm_enable = false; //state of the PWM (ctrl task)

static float32_t V1_low_value; //store value of V1_low (app task)
static float32_t V2_low_value; //store value of V2_low (app task)
static float32_t Vhigh_value; //store value of Vhigh (app task)

static float32_t i1_low_value; //store value of i1_low (app task)
static float32_t i2_low_value; //store value of i2_low (app task)
static float32_t ihigh_value; //store value of ihigh (app task)

static float32_t meas_data; //temp storage meas value (ctrl task)

static float32_t voltage_reference = 10; //voltage reference (app task)
static int cpt_step = 0; //counter for voltage reference (app task)

static float32_t current_reference = 1; //current reference (app task)

static uint32_t encoder_value; //store value from the encoder (ctrl task)
static uint32_t encoder_count = 0; //counts the number of control tasks interruptions 
static uint32_t encoder_counter_acquisition_period = 10; //number of interruptions needed prior to next encoder value calculation
static uint32_t encoder_value_old; //stores old encoder value
static uint32_t encoder_speed; //stores the new speed in rad/sec
static uint32_t encoder_gain = 6; //stores the gain to be multiplied into the encoder values to calculate rad/sec angular speed
  
  1. Configure the hardware peripherals

In src/main.cpp, in the setup_hardware() function, paste the code below. It sets the version of the board, initializes all the measurements in default mode and initializes the buck interleaved mode. It also sets the encoder for usage with the motor. This code also activates the console for serial communication.

void setup_hardware(){
    hwConfig.setBoardVersion(v_1_1_2); 
    hwConfig.configureAdcDefaultAllMeasurements();
    hwConfig.initInterleavedBuckMode();
    hwConfig.startLoggingIncrementalEncoder();  
    console_init();
}
  1. Configure the software scheduling

In src/main.cpp, in the function setup_software() function, do not modify anything.

  1. Loop communication task

In the communication task you will change the commands to enable the closed-loop buck mode. To do so you will create a new mode called POWERMODE.

Find the enum serial_interface_menu_mode. Add the following code to declare a new mode called BUCKMODE.

enum serial_interface_menu_mode //LIST OF POSSIBLE MODES FOR THE OWNTECH CONVERTER
{
    IDLEMODE =0,
    SERIALMODE,
    POWERMODE,
    BUCKMODE,
    MOTORMODE,
};

In src/main.cpp, in the function loop_communication_task(), add the following code. This code listens to the Serial port. Depending on the character it has received, it switches between IDLEMODE, POWERMODE and BUCKMODE.

void loop_communication_task()
{
    while(1) {
        received_serial_char = console_getchar();
        switch (received_serial_char) {
            case 'h':
                //----------SERIAL INTERFACE MENU-----------------------
	        printk(" ________________________________________\n");
                printk("|     Communication task Tutorial        |\n");
                printk("|     ------- MENU ---------             |\n");
                printk("|     press i : idle mode                |\n");
                printk("|     press s : serial mode              |\n");
                printk("|     press p : power mode on            |\n");
                printk("|     press u : duty cycle up by 0.05    |\n");
                printk("|     press d : duty cycle down by 0.05  |\n");
                printk("|     press b : closed-loop buck mode on |\n");
                printk("|________________________________________|\n\n");
                //------------------------------------------------------
                break;
            case 'i':
                printk("idle mode\n");
                mode = IDLEMODE;
                break;
            case 's':
                printk("serial mode on!\n");
                mode = SERIALMODE;
                break;
            case 'p':
                printk("power mode on!\n");
                mode = POWERMODE;
                break;
            case 'u':
                printk("Duty Cycle UP: %f\n", duty_cycle);
                duty_cycle = duty_cycle + duty_cycle_step;
                break;
            case 'd':
                printk("Duty Cycle DOWN: %f\n", duty_cycle);
                duty_cycle = duty_cycle - duty_cycle_step;
                break;
            case 'b':
                printk("closed-loop buck mode on!\n");
                mode = BUCKMODE;
                break;
            case 'm':
                printk("motor mode on!\n");
                mode = MOTORMODE;
                break;
            default:
                break;
        }
    }
}
  1. Loop application task

In src/main.cpp, in the function loop_application_task(), add the following code to define the application task. In this example, the LED will be turned ON when the power is flowing. When the led is ON the O2 converter will print on the serial the value of:

  • duty cycle
  • VHigh
  • V1low
  • V2low
  • iHigh
  • i1low
  • i2low
  • speed
void loop_application_task()
{
    while(1){
        if(mode==IDLEMODE) {
            hwConfig.setLedOff();
        }else if(mode==SERIALMODE) {
            hwConfig.setLedOn();
        }else if(mode==POWERMODE) {
            hwConfig.setLedOn();
        }else if(mode==BUCKMODE) {
            hwConfig.setLedOn();
            if(cpt_step==10)
                voltage_reference = 15;
            if(cpt_step==20) {
                voltage_reference = 10;
                cpt_step=0;
            }
            cpt_step ++;
        }else if(mode==MOTORMODE) {
            hwConfig.setLedOn();
        }

        printk("%f:", duty_cycle);
        printk("%f:", Vhigh_value);
        printk("%f:", V1_low_value);
        printk("%f:", V2_low_value);
        printk("%f:", ihigh_value);
        printk("%f:", i1_low_value);
        printk("%f:", i2_low_value);
        printk("%f\n", (float32_t)encoder_speed); //casts the encoder speed as a float prior to print

        }        
        k_msleep(100);    
    }
}
  1. Loop control task

In src/main.cpp, in the function loop_control_task(), add the following code. In BUCKMODE, the PID tracks the voltage_reference by modifying the duty_cycle.

void loop_control_task()
{
    meas_data = dataAcquisition.getVHigh();
    if(meas_data!=-10000) Vhigh_value = meas_data;

    meas_data = dataAcquisition.getV1Low();
    if(meas_data!=-10000) V1_low_value = meas_data;

    meas_data = dataAcquisition.getV2Low();
    if(meas_data!=-10000) V2_low_value= meas_data;

    meas_data = dataAcquisition.getIHigh();
    if(meas_data!=-10000) ihigh_value = meas_data;

    meas_data = dataAcquisition.getI1Low();
    if(meas_data!=-10000) i1_low_value = meas_data;

    meas_data = dataAcquisition.getI2Low();
    if(meas_data!=-10000) i2_low_value = meas_data;

    if(mode==IDLEMODE || mode==SERIALMODE) {
         pwm_enable = false;
         hwConfig.setInterleavedOff();

    }else if(mode==POWERMODE || mode==BUCKMODE || mode==MOTORMODE)) {

        encoder_count++;

        if(encoder_count==encoder_counter_acquisition_period){
            encoder_value_old = encoder_value;
            encoder_value = hwConfig.getIncrementalEncoderValue();
            if((encoder_value-encoder_value_old)<200) encoder_speed =  (encoder_value-encoder_value_old)*encoder_gain; //Convert the measurement in rad/sec           
            encoder_count = 0;
        }

        if(!pwm_enable) {
            pwm_enable = true;
            hwConfig.setInterleavedOn();
        }

        if(mode==BUCKMODE) duty_cycle = opalib_control_interleaved_pid_calculation(voltage_reference, V1_low_value);

        if(mode==MOTORMODE) duty_cycle = opalib_control_interleaved_pid_calculation(current_reference, i1_low_value+i2_low_value);


        //Sends the PWM to the switches
	hwConfig.setInterleavedDutyCycle(duty_cycle);
    }
}
  1. Connect hardware

Now we will connect OwnTech’s O2 to the power supply and to the PC.

  • Connect the pins Vhigh and GND of the O2 to the 40 V power supply (set the current limitation at 1 A).
  • Connect the micro-JTAG connector of the O2 to the PC thanks to the STLinkV3. The leds PWR and COM should be ON.
  • Switch on the power supply. The led2 of the O2 should be ON.
  • Connect the encoder according to the image below

Encoder Connection

For the encoder provided by the OwnTech team, here is the connection to be made:

connector_view

  1. In the bottom menu, click on the Build icon build_icon. This will launch the compilation of the code. When the compilation is completed, you should see [SUCCESS] in the terminal.
  2. On the bottom menu, click on the Upload icon flash_icon. This will flash the compiled code on the microcontroller of the O2. When the process is completed, you should see [SUCCESS] in the terminal.
  3. On serialPlot, choose the STLinkV3 and set the baud to 115200. Click on Open button.

serialPlot Open connection

  1. When you press the send by the m command, data will start to flow in the screen, the LED will turn ON. Data from V1low, i1low, encoder and the duty cycle will appear on the screen.

Using serialPlot to explore the duty cycle

Expected outputs

  • The led1 of the O2 should start as off.
  • The led1 of the O2 should turn on when you press the p button.
  • The led1 of the O2 should turn off when you press the i button.
  • The voltage ratio between Vhigh and V1low changes when you press either u or d.
  • The voltage will converge to 10V if you press b.
  • The motor will start running when you press the m button.

That’s it!

Contributors

  • Romain Delpoux, 2021.11.04
  • Loic Queval, 2021.11.05
  • Adrien Prévost, 2021.11.05
  • Luiz Villa, 2021.11.07
  • Antoine Boche, 2021.11.08
  • Luiz Villa, 2022.01.24
  • Luiz Villa, 2022.02.22