Buck 3 Closed loop voltage mode - owntech-foundation/Tutorials GitHub Wiki
Objectives
The goal of this tutorial is to use the O2 as a buck converter in closed-loop. We will implement a PID controller that tracks the reference voltage. We will start from the Buck 2 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.
Simplified representation of a power converter showing its power switches, filter and control loop. This control will be implemented in this tutorial.
There is an important different between the previous image and the actual implementation. In the O2 converter, the legs are interleaved, meaning they are controlled in parallel. This is best represented by the image below, where both legs are driven by the control function.
The O2 converter with its both legs being driven by the control function
Required hardware
- O2
v_1_1_2
- STLinkV3
- PC 64-bits (windows or linux)
- DC power supply (48 V, 2 A)
- Oscilloscope or multimeter
Required software
- Git
- Visual Studio Code with PlatformIO (see Blinky tutorial)
- SerialPlot (see SerialPlot tutorial)
Create the project
-
We will save our progress by committing in git. In the side menu, click on the Source Control button, Which will open a new menu . 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.
-
Click on the
+
button on the right side of themain.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 for the
buck-2
tutorial
Step-by-step implementation
- 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.
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;
- Configure the hardware peripherals
In src/main.cpp
, in the setup_hardware()
function, do not modify anything.
- 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();
}
- 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':
duty_cycle = duty_cycle + duty_cycle_step;
if(duty_cycle>1.0) duty_cycle = 1.0;
break;
case 'd':
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;
}
}
- 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);
}
- 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;
hwConfig.setInterleavedOff();
}else if(mode==POWERMODE || mode==BUCKMODE) {
if(!pwm_enable) {
pwm_enable = true;
hwConfig.setInterleavedOn();
}
if(mode==BUCKMODE) duty_cycle = opalib_control_interleaved_pid_calculation(voltage_reference, V1_low_value);
//Sends the PWM to the switches
hwConfig.setInterleavedDutyCycle(duty_cycle);
}
}
- Connect hardware
Now we will connect OwnTech’s O2 to the power supply and to the PC.
- :warning: Make sure that the jumper JP4 is open.
- Connect the USB power supply cable. The LED2 of the O2 should be ON.
- 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 of the STLinkV3 should be ON.
- Switch ON the power supply.
- Build and Upload (+ ).
Expected outputs
- Press
i
to switch to IDLEMODE: stop printing measuremnent and turns the LED1 OFF. - Press
p
to switch to COMMMODE: print measurements and turns the LED1 ON. - Press
b
to switch to BUCKMODE: tracks the reference voltage. - Press
u
to increase theduty_cycle
. - Press
d
to decrease theduty_cycle
.
OwnPlot Visualization
- 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.
- 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.
- In OwnPlot, Settings tab, set the
# of channels
to 8.
- In OwnPlot, in the Chart tab, change the same of the datasets as shown below:
- In OwnPlot, in the Send tab, add the commands below by typing the command name and its associated character.
- When you click on the
Boost
buttion the output should track the voltage reference that switches between 10 V and 15 V.
- In OwnPlot, click on the
Idle
button corresponding to thei
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.12: Luiz Villa