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).

image

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

  1. 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 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
  1. In the bottom menu, check that you are now in the pwm_1 branch.

buck_1_branch_view

Step-by-step implementation

  1. 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
  1. 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
}
  1. 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;

        }
    }
}
  1. 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); 
}
  1. 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);
    }
}
  1. 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.

set_up

  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 key, you should see the current value of duty cycle on the Serial Monitor. Remember to click on the open button in SerialPlot to liberate the serial interface.

Expected outputs

  • Press the i key to switch to IDLEMODE: disables the power conversion, stops printing duty_cycle and turns the LED1 OFF.
  • Press the s key to switch to SERIALMODE: disables the power conversion, prints the duty_cycle and turns the LED1 ON.
  • Press the p key to switch to POWERMODE: enables the power conversion, prints duty_cycle and turns the LED1 ON.
  • Press the u key to increase the duty_cycle.
  • Press the d key to decrease the duty_cycle.

buck1_serial

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.

Pinout of the board

The image below shows the board connections.

board

Results from the oscilloscope

First, let's observe all the PWM signal.

PWM_output

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.

Chanel_1_DT_1

Channel_1_DT_2

  • Dead-time in Channel C

You can also check if the dead-time in the rising and falling edge is 25ns and 50ns

Channel_2_DT_2

Channel_2_DT1

Finally, let's see if the phase shift between the channel A and C is 90° :

phase_shift

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