Control your output voltage - owntech-foundation/Tutorials GitHub Wiki

Objectives

The goal of this tutorial is to use the TWIST as a buck converter in closed-loop. We will implement a PI controller that tracks the reference voltage. We will start from the previous tutorial.

To better represent what will be done, the image below shows the control of one single leg. It is possible to see that the output low-side voltage measurement is used by the power converter to drive its PWM value.

image

Simplified representation of a power converter showing its power switches, filter and control loop. This control will be implemented in this tutorial.

In the TWIST converter the legs are interleaved, meaning they are controlled in parallel. You can find the TWIST current implementation here.

Required hardware

  • TWIST v_1_1_2 or later
  • STLinkV3
  • PC 64-bits (windows or linux)
  • DC power supply (48 V, 2 A)
  • Resistive load
  • Oscilloscope or multimeter

Required software

  • Git
  • Visual Studio Code with PlatformIO (see Blinky tutorial)
  • Visual Studio Code with Source Control extension (see OwnPlot tutorial)
  • OwnPlot (see SerialPlot tutorial)

Create the project

  1. We will create the project by copying the buck-2 tutorial in a new branch, that will be called buck_3.
  2. In the side menu, click on the Source Control button, Which will open a new menu source_control_button. You can see that the only changes on your code are located at the main.cpp file. We are going to commit these changes as to keep track of our progress.
  3. Click on the + button on the right side of the main.cpp file to "stage" the changes. You can add a comment to explain what these changes are useful for. It is good practice to describe your changes when you commit them in git. Click on the + Commit button.

staged_changes_buck_2_tutorial

Staged changes for the buck-2 tutorial

  1. Click on the buck-2 branch on the bottom left of the screen and create a new branch called "buck-3".

buck-3_new_branch

Enter the name of your new branch buck-3

  1. In the bottom menu, check that you are now in the buck-3 branch.

buck-3_new_branch_bottom

Check the bottom of your screen to verify in which branch you are

Step-by-step implementation

  1. Configure the libraries and Define the variables

In src/main.cpp, in the section OWNTECH DRIVERS, include the following library. It will activate the code for PID control which will be used for the voltage mode.

//-------------OWNTECH DRIVERS-------------------
 # include "opalib_control_pid.h"

Go to the owntech.ini file under the main.cpp file and decomment line 18. As shown in the image below. It will automatically download the pid library that will be used in this tutorial.

The owntech.ini file

In src/main.cpp, in the section USER VARIABLE DECLARATIONS, add the voltage reference, a counter for the step applying a step on the reference and the PID parameteres. Keep all the previous variables.

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

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

//static float32_t kp = 0.000215;
//static float32_t ki = 2.86;
//static float32_t kd = 0.0;


  1. Configure the hardware peripherals

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

  1. Configure the software scheduling

In src/main.cpp, in the function setup_software(), initialize the pid controller, the data acquisition and all the scheduling functions. The pid control will now be available through a series of function calls.

void setup_software()
{
    // opalib_control_init_interleaved_pid(kp, ki, kd, control_task_period);

    application_task_number =  scheduling.defineAsynchronousTask(loop_application_task);
    communication_task_number = scheduling.defineAsynchronousTask(loop_communication_task);
    scheduling.defineUninterruptibleSynchronousTask(&loop_control_task, control_task_period);

    scheduling.startAsynchronousTask(application_task_number);
    scheduling.startAsynchronousTask(communication_task_number);
    scheduling.startUninterruptibleSynchronousTask();
}
  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,
};

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()
{
    received_serial_char = console_getchar();
    switch (received_serial_char) {
        case 'h':
            //----------SERIAL INTERFACE MENU-----------------------
	        printk(" ________________________________________\n");
            printk("|     ------- MENU ---------             |\n");
            printk("|     press i : idle mode                |\n");
            printk("|     press s : serial mode              |\n");
            printk("|     press p : power mode               |\n");
            printk("|     press u : duty cycle UP            |\n");
            printk("|     press d : duty cycle DOWN          |\n");
            printk("|     press b : closed-loop buck mode    |\n");
            printk("|________________________________________|\n\n");
            //------------------------------------------------------

            break;
        case 'i':
            printk("idle mode\n");
            mode = IDLEMODE;
            break;
        case 's':
            printk("serial mode\n");
            mode = SERIALMODE;
            break;
        case 'p':
            printk("power mode\n");
            mode = POWERMODE;
            pwm_enable = true;
            break;
        case 'u':
            printk("duty cycle UP: %f\n", duty_cycle);
            duty_cycle = duty_cycle + duty_cycle_step;
            if(duty_cycle>1.0) duty_cycle = 1.0;
            break;
        case 'd':
            printk("duty cycle DOWN: %f\n", duty_cycle); 
            duty_cycle = duty_cycle - duty_cycle_step;
            if(duty_cycle<0.0) duty_cycle = 0.0;
            break;
        case 'b':
            printk("closed-loop buck mode\n");
            mode = BUCKMODE;
            break;        
        default:
            break;
    }
}
  1. Define the application task

In src/main.cpp, in the function loop_application_task(), add the following code. The IDLEMODE stops the power flow, the transmission of data and turns the LED1 off. The SERIALMODE only turns on the LED1. The POWERMODE turns the LED1 is ON and prints the values of duty_cycle on the Serial Monitor. In BUCKMODE, the value of the voltage_reference will change periodically creating a square wave. Now the data is constantly printed to the serial port.

void loop_application_task()
{
    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 ++;
    }

    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", voltage_reference);
    
    scheduling.suspendCurrentTaskMs(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.getLatest(V_HIGH);
    if(meas_data!=NO_VALUE) Vhigh_value = meas_data;

    meas_data = dataAcquisition.getLatest(V1_LOW);
    if(meas_data!=NO_VALUE) V1_low_value = meas_data;

    meas_data = dataAcquisition.getLatest(V2_LOW);
    if(meas_data!=NO_VALUE) V2_low_value= meas_data;

    meas_data = dataAcquisition.getLatest(I_HIGH);
    if(meas_data!=NO_VALUE) ihigh_value = meas_data;

    meas_data = dataAcquisition.getLatest(I1_LOW);
    if(meas_data!=NO_VALUE) i1_low_value = meas_data;

    meas_data = dataAcquisition.getLatest(I2_LOW);
    if(meas_data!=NO_VALUE) i2_low_value = meas_data;

    if(mode==IDLEMODE || mode==SERIALMODE) {
         pwm_enable = false;
         power.stopAll();


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


        if(!pwm_enable) {
            pwm_enable = true;
            power.startAll();
        }

        if(mode==BUCKMODE) {
           // this function compares the asked voltage to the output voltage in order to compute the duty cycle
            duty_cycle = opalib_control_interleaved_pid_calculation(voltage_reference, V1_low_value);
        }

        //Sends the PWM to the switches
	power.setAllDutyCycle(duty_cycle);
    }
}
  1. Connect hardware

Now we will connect your TWIST board to the power supply and to the PC via the STLink.

  • Connect the pins Vhigh and GND of the TWIST to the DC power supply (set its current limitation at 1 A).
  • Connect the pins VLow1, Vlow2 and GND of the TWIST to a resistive load.
  • Connect the micro-JTAG connector of the SPIN to the PC thanks to the STLinkV3.
  • Switch ON the DC power supply. Choose a voltage between 0 and 48 V.

Twist_setup_buck_mode

  1. Build and Upload (build_icon+ flash_icon).

  2. In the bottom toolbar, click on the Serial Monitor icon serial_icon. Select it and press the h key. Press the p button, you should see some data show on the terminal:

data_upload

Expected outputs

  • Press i to switch to IDLEMODE: stops the power and turns the LED1 OFF.
  • Press p to switch to POWERMODE: starts the power on open-loop and turns the LED1 ON.
  • Press u to increase the duty_cycle.
  • Press d to decrease the duty_cycle.
  • Press b to switch to BUCKMODE: tracks the reference voltage in closed loop and turns the LED1 ON.

OwnPlot Visualization

  1. First kill the Serial Monitor by clicking on the trash button on the right hand side of the window as shown in the image below.

Kill serial monitor

  1. Launch OwnPlot. In Port tab, choose the STLinkV3. Click on the Closed button to open the port.

:warning: The Port might have a different name depending on your operating system.

open_port_running

  1. In OwnPlot, Settings tab, set the # of channels to 8.

ownplot_settings_buck-3

  1. In OwnPlot, in the Chart tab, change the same of the datasets as shown below:

ownplot_chart_buck-3

  1. In OwnPlot, in the Send tab, add the commands below by typing the command name and its associated character.

ownplot_send_buck-3

  1. When you click on the Boost buttion the output should track the voltage reference that switches between 10 V and 15 V.

ownplot_counter_up_down

  1. In OwnPlot, click on the Idle button corresponding to the i command to switch to IDLEMODE and turn the LED1 OFF.

That’s it!

Contributors

  • 2021.11.04: Romain Delpoux, Loic Queval, Adrien Prévost
  • 2021.11.07: Luiz Villa, Antoine Boche
  • 2022.01.24: Luiz Villa, Loic Queval
  • 2022.02.01: Luiz Villa
  • 2022.02.22: Luiz Villa
  • 2022.03.13: Luiz Villa
  • 2023.01.17: Luiz Villa
  • 2023.07.10: Luiz Villa
  • 2023.10.14: Mathilde Longuet