PWM 1 generation - owntech-foundation/Tutorials GitHub Wiki
Objective
The goal of this tutorial is to use the SPIN to generate a PWM signal and tweak all of its parameters. We will control it via the keyboard.
Context
The PWM signal is at the core of the control of power electronics systems. Generating and modulating the PWM is highly dependent on the application, which may require specific tweaks to the signal's properties.
At its very basic, a PWM signal is composed of a TimeON, TimeOFF and period 1(https://microchipdeveloper.com/pwr3101:pwm-edge-center-aligned-modes).
Generally speaking, the TimeON and TimeOFF are created from a threshold that is compared to a counter. Once the threshold value is reached, the PWM is brought to zero.
In this tutorial, you will generate different types of PWM signals and play around with their characteristics.
Required hardware
- SPIN
- STLinkV3
- PC 64-bits (windows or linux)
- Oscilloscope
Required software
- Git
- Visual Studio Code with PlatformIO (see Blinky tutorial)
Create the project
- We will create the project by copying the Serial Plot tutorial in a new branch, that will be called pwm_1. In the bottom menu, click on the New Terminal icon . This will open a new terminal into which you can write the following commands to create a new git branch.
git status
git add --all
git commit -m "Current work on the serial plot branch"
git branch pwm_1
git checkout pwm_1
- In the bottom menu, check that you are now in the
pwm_1
branch.
Step-by-step implementation
- Define the variables
In src/main.cpp
, in the section USER VARIABLE DECLARATIONS
, add the variables below. The initial duty cycle is 0.5. The duty cycle steps are set to 0.05. A boolean indicates the state of the PWM (ON or OFF). A variable will also define the period of the control task.
//--------------USER VARIABLES DECLARATIONS----------------------
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; //[bool] state of the PWM (ctrl task)
static uint32_t control_task_period = 50; //[us] period of the control task
- Configure the hardware peripherals and the software scheduling
In src/main.cpp
, in the setup_routine()
function, paste the code below. It initializes the pwm for PWMA and PWMC. Their pinout will be shown below. It also starts the critical task, the communication task and the application task. Due to its time criticity, the control task has a precise period and not a priority as the others.
void setup_routine(){
console_init();
// Setup the hardware first
spin.version.setBoardVersion(SPIN_v_1_0);
// PWM initialization
spin.pwm.setFrequency(200000); // frequency initializiation
spin.pwm.initUnit(PWMA); // PWMA init
spin.pwm.initUnit(PWMB); // PWMA init
spin.pwm.setDeadTime(PWMA, 50, 25); // sets the dead time of leg 1 as 50 and 25 ns respectively
spin.pwm.setDeadTime(PWMB, 25, 50); // sets the dead time of leg 2 as 50 and 25 ns respectively
spin.pwm.setPhaseShift(PWMB, 90); // sets the phase shift of leg 2 in relation to leg 1 in degrees
// Then declare tasks
uint32_t app_task_number = task.createBackground(loop_application_task);
uint32_t comm_task_number = task.createBackground(loop_communication_task);
task.createCritical(loop_critical_task, 500); // Uncomment if you use the critical task
// Finally, start tasks
task.startBackground(app_task_number);
task.startBackground(comm_task_number);
task.startCritical(); // Uncomment if you use the critical task
}
- Loop communication task
In the communication task, in comparison to the SerialPlot tutorial, we change the commands to update the duty cycle
instead of the counter
variable. We also add a new mode, called POWERMODE
.
In src/main.cpp
, in the list enum serial_interface_menu_mode
, declare a new mode called POWERMODE.
enum serial_interface_menu_mode //LIST OF POSSIBLE MODES FOR THE OWNTECH CONVERTER
{
IDLEMODE =0,
SERIALMODE,
POWERMODE
};
In src/main.cpp
, in the function loop_communication_task()
, paste the following code. This code listens to the serial port. Depending on the character it has received, it switches between IDLEMODE, SERIAL and POWERMODE. In POWERMODE, the PWM is enabled thus activating the power conversion.
void loop_communication_task()
{
while(1) {
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("|________________________________________|\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;
default:
break;
}
}
}
- Loop application task
The application task will print the value of duty cycle
.
In src/main.cpp
, in the function loop_application_task()
, paste 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.
void loop_application_task()
{
if(mode==IDLEMODE) {
spin.led.turnOff();
}else if(mode==SERIALMODE || mode==POWERMODE) {
spin.led.turnOn();
printk("%f \n", duty_cycle);
}
task.suspendBackgroundMs(100);
}
- Loop critical task
The control task will turn on/off the power conversion and send the duty cycle to the power converter.
In src/main.cpp
, in the function loop_control_task()
, paste the following code. It states that if either the IDLEMODE
or SERIALMODE
are on, the power conversion stops. If POWERMODE
is on, then the power conversion resumes and the duty cycle is sent to the drivers and to the power hardware.
void loop_critical_task()
{
if(mode==IDLEMODE || mode==SERIALMODE)
{
pwm_enable = false;
spin.pwm.stopDualOutput(PWMA);
spin.pwm.stopDualOutput(PWMB);
}
else if(mode==POWERMODE)
{
if(!pwm_enable) {
pwm_enable = true;
spin.pwm.startDualOutput(PWMA);
spin.pwm.startDualOutput(PWMB);
}
//Sends the PWM to the switches
spin.pwm.setDutyCycle(PWMA, duty_cycle);
spin.pwm.setDutyCycle(PWMB, duty_cycle);
}
}
- Connect hardware
Now we will connect the SPIN board to the PC.
- Connect the USB power supply cable. The LED1 of the SPIN should be ON.
- Connect the micro-JTAG connector of the O2 to the PC thanks to the STLinkV3.
- Build and Upload (+ ).
- In the bottom toolbar, click on the Serial Monitor icon . Select it and press the
h
key. Press thep
key, you should see the current value ofduty cycle
on the Serial Monitor. Remember to click on theopen
button in SerialPlot to liberate the serial interface.
Expected outputs
- Press the
i
key to switch to IDLEMODE: disables the power conversion, stops printingduty_cycle
and turns the LED1 OFF. - Press the
s
key to switch to SERIALMODE: disables the power conversion, prints theduty_cycle
and turns the LED1 ON. - Press the
p
key to switch to POWERMODE: enables the power conversion, printsduty_cycle
and turns the LED1 ON. - Press the
u
key to increase theduty_cycle
. - Press the
d
key to decrease theduty_cycle
.
We will observe the results through the oscilloscope, for the output 1 and 2 from channel A and C. Connect to PA8 and PA11 to watch the outputs 1 and 2 from channel A, and to PB12 and PB13 for the outputs 1 and 2 from the channel C.
The image below shows an overview of the used pins.
The image below shows the board connections.
Results from the oscilloscope
First, let's observe all the PWM signal.
We can measure the dead-time for the channel A and C.
- Dead-time in channel A
Check the dead-time in the rising and falling edge is 50ns and 25ns.
- Dead-time in Channel C
You can also check if the dead-time in the rising and falling edge is 25ns and 50ns
Finally, let's see if the phase shift between the channel A and C is 90° :
That’s it!
To go a little further
- How would you implement a code to change the deadtime from the keyboard, as with the duty cycle?
- How would you implement a code to change the phase shift from the keyboard, as with the duty cycle?
References
[1] PWM Edge and Center Aligned Modes, accessed in July 19th 2022 (https://microchipdeveloper.com/pwr3101:pwm-edge-center-aligned-modes)
Contributors
- 2021.11.04: Romain Delpoux, Loïc Quéval, Adrien Prévost
- 2021.11.07: Luiz Villa, Antoine Boche
- 2022.01.24: Luiz Villa, Loïc Quéval
- 2022.02.01: Luiz Villa
- 2022.03.13: Luiz Villa
- 2022.06.23: Loïc Quéval
- 2022.07.19: Luiz Villa
- 2022.07.23: Ayoub Farah