Changing the PWM frequency for pedagogical purpose

Hello OwnTech,

I’d like to know if anybody has experience with changing the PWM frequency of the SPIN microcontroller. In a teaching context (introduction to power electronics), the idea would be to witness the effect changing the PWM frequency, in particular classical effect on the current ripple through an inductor (ΔI proportional to 1/f).

If I remember correctly previous discussions with @luiz_villa or @jalinei, I think the difficulty lays in the interplay between PWM signal generation and the synchronization of ADC sampling instants. However, in my target application, I can run open loop and measure voltages and current with probes, so it’s fine if the ADC measurements are broken :wink: !

Is there a code example for this?

Pierre

Hello Pierre,

Actually it will affect measurements if the modulation is left aligned. In center aligned mode, measurements should still be fine.

In your case you don’t want to measure at a precise instant on the PWM period, you can actually sweep through the period (in left aligned mode), and get an image of the ripple current with the onboard instruments. This is implemented in the Ripple Current example.

To show the effect of a frequency change on the inductor current, I suggest you to use in your setup_routine the function initVariableFrequency.

Once initialized, you can change the frequency at your will, using setFrequency(for instance when pressing a button).

This way you should be able to retrieve multiple plots showing the ripple current for different frequencies.

Let me know if it does what you want :slight_smile:

Thanks for the feedback.

It seems there some amount of shady magic going on with initVariableFrequency, in the sense that a min frequency must be declared and also with the warning “this may compromise the resolution of the PWM.”. But OK I’ll experiment with it.

Quick question about setFrequency: doc says “The new frequency can’t be inferior to the the one set in the initialization step.” → is it “can’t be inferior to the the minimum one” ?

[EDIT]

Another question: @jalinei, in your answer you assume I want to vary the frequency during operation. But if I just want to have a fixed PWM frequency, only different from the 200 kHz default, do I also need to call initVariableFrequency ?

Hehehe - Short answer, yes, setFrequency is enough to set any fixed frequency you want. This function computes the prescaler is order that you get the best time resolution available for that frequency. Now, as this prescaler affects the minimal frequency that can be achieved, at runtime, we can not ensure that the user will be able to switch to lower frequencies.

This is why initVariableFrequency has an under the hood logic that compute the maximal time resolution that matches both the initial frequency and the minimal frequency that will be used during runtime.

@jalinei, I tried to add either setFrequency and/or initFixedFrequency in my GitHub - pierre-haessig/ownverter-dcdc: Microcontroller code for using the OwnTech OwnVerter board as a DC/DC converter code, but in the end I didn’t find a combination which works. I got either:

  • frequency is still 200 kHz (while I tried setFrequency(50000))
  • pwm doesn’t work (the system isn’t crashed, but the power on action has no effect)

So it’s not clear to me how they should be used. In particular, within the setup_routine() there are several other intialization-style calls (shield.power.initBuck(ALL); , shield.sensors.enableDefaultOwnverterSensors();…).

Is there a constraint as to when these functions should be called?

Hello @pierre-haessig

Your question is very interesting and has multiple elements to it.

Why it does not work

The frequency of the Twist and OwnVerter boards is currently set on the dts file:

	powershield: power-shield{
        compatible = "power-leg";
        default-frequency = <200000>;
        min-frequency = <50000>;

These values are automatically retrieved by the power_init.cpp file:

uint32_t timer_frequency = DT_PROP(POWER_SHIELD_ID, default_frequency);

uint32_t timer_min_frequency = DT_PROP(POWER_SHIELD_ID, min_frequency);

These variables are used in turn in the PowerAPI::initMode function:

        /* Configure PWM frequency */
        spin.pwm.initVariableFrequency(timer_frequency, timer_min_frequency);

which is called at the setup_routine()function by:

    shield.power.initBuck(ALL);

When we call the spin.pwm.initFixedFrequency(value)function, we update values within a structure of the spin that is found within the hrtim.c file:

void hrtim_frequency_set(uint32_t frequency_set, uint32_t frequency_min)
{
    HRTIM_MINIM_FREQUENCY = frequency_min;

    timerMaster.pwm_conf.frequency = frequency_set;
    tu_channel[PWMA]->pwm_conf.frequency = frequency_set;
    tu_channel[PWMB]->pwm_conf.frequency = frequency_set;
    tu_channel[PWMC]->pwm_conf.frequency = frequency_set;
    tu_channel[PWMD]->pwm_conf.frequency = frequency_set;
    tu_channel[PWME]->pwm_conf.frequency = frequency_set;
    tu_channel[PWMF]->pwm_conf.frequency = frequency_set;

    timerMaster.pwm_conf.min_frequency = frequency_min;
    tu_channel[PWMA]->pwm_conf.min_frequency = frequency_min;
    tu_channel[PWMB]->pwm_conf.min_frequency = frequency_min;
    tu_channel[PWMC]->pwm_conf.min_frequency = frequency_min;
    tu_channel[PWMD]->pwm_conf.min_frequency = frequency_min;
    tu_channel[PWME]->pwm_conf.min_frequency = frequency_min;
    tu_channel[PWMF]->pwm_conf.min_frequency = frequency_min;
}

However, this change is indeed overwritten by the values that are present in the device tree of the twist board.

Thus, if I write on the setup_routine() of the voltage mode buck example:

void setup_routine()
{
    /* Buck voltage mode */
    spin.pwm.initFixedFrequency(50000); :
    shield.power.initBuck(ALL);

    shield.sensors.enableDefaultTwistSensors();

    pid.init(pid_params);

    /* Then declare tasks */
    uint32_t app_task_number = task.createBackground(loop_application_task);
    uint32_t com_task_number = task.createBackground(loop_communication_task);
    task.createCritical(loop_critical_task, 100);

    /* Finally, start tasks */
    task.startBackground(app_task_number);
    task.startBackground(com_task_number);
    task.startCritical();
}

I get a frequency of 200kHz regardless:

How to get it working (variable frequency)

However, if you change your communication function to:

void loop_communication_task()
{
    received_serial_char = console_getchar();
    switch (received_serial_char)
    {
    case 'h':
        /*----------SERIAL INTERFACE MENU----------------------- */
        printk(" ________________________________________ \n"
               "|     ---- MENU buck voltage mode ----   |\n"
               "|     press i : idle mode                |\n"
               "|     press p : power mode               |\n"
               "|     press u : voltage reference UP     |\n"
               "|     press d : voltage reference DOWN   |\n"
               "|________________________________________|\n\n");
        /*------------------------------------------------------ */
        break;
    case 'i':
        printk("idle mode\n");
        mode = IDLEMODE;
        break;
    case 'p':
        printk("power mode\n");
        mode = POWERMODE;
        break;
    case 'u':
        frequency += 500;
        spin.pwm.setFrequency(frequency);
        
        break;
    case 'd':
        frequency -= 500;
        spin.pwm.setFrequency(frequency);
        break;
    default:
        break;
    }
}

You will indeed vary your frequency natively between 200kHz and 50kHz, as it is 144kHzbelow.

So, as a conclusion:

  • The variable frequency is supported by default.
  • For now, it is defined on the device tree of your power shield
  • If you wish to make a different frequency, you will have to change the device tree (as of now)
  • If you want us to change this and create a function that overwrites the frequency, please create an issue on the Core and we’ll take care of it.