cembeddedstm32microcontrollerpwm

How to use LL drivers on an STM32 to generate PWM deadtime


I am trying to generate 3 channel center aligned PWM with complementary outputs and deadtime insertion on an STM32G474. To test functionality of the timers, I currently have the 6 outputs connected to 6 LEDS to hopefully see a visible time delay between the main outputs going low and the complementary outputs going high. This however isn't the case, and I can't see any delay between the toggling despite a calculated deadtime of around 98ms. There is no visible time where no LED is active

My timer initiation code is below

void TIM1_Init() {
    // Enable clocks for GPIOA, GPIOB and TIM1
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);

    // Set relevant GPIO pins to AF mode
    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_6;

    GPIO_InitStruct.Pin = LL_GPIO_PIN_8;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_10;
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_13;
    LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_14;
    LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
    GPIO_InitStruct.Pin = LL_GPIO_PIN_15;
    LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    LL_TIM_InitTypeDef TIM_InitStruct;
    LL_TIM_StructInit(&TIM_InitStruct);
    TIM_InitStruct.Prescaler = 0xFFFF;
    TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_CENTER_UP_DOWN;
    TIM_InitStruct.Autoreload = 244;
    TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;

    LL_TIM_Init(TIM1, &TIM_InitStruct);
    LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);

    LL_TIM_OC_InitTypeDef TIM_OC_InitStruct;
    LL_TIM_OC_StructInit(&TIM_OC_InitStruct);
    TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM2;
    TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
    TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE;
    TIM_OC_InitStruct.CompareValue = 244/2;
    //TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW;
    //TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_LOW;
    TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
    TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;

    LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
    LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct);
    LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH3, &TIM_OC_InitStruct);

    LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1);
    LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH2);
    LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3);

    LL_TIM_BDTR_InitTypeDef bdtr = {0};
    bdtr.OSSRState = LL_TIM_OSSR_ENABLE;
    bdtr.OSSIState = LL_TIM_OSSI_ENABLE;
    bdtr.LockLevel = LL_TIM_LOCKLEVEL_OFF;
    bdtr.DeadTime = 24;  // Try max for visible LED effect
    bdtr.BreakState = LL_TIM_BREAK_DISABLE;
    bdtr.BreakPolarity = LL_TIM_BREAK_POLARITY_HIGH;
    bdtr.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_ENABLE;
    LL_TIM_BDTR_Init(TIM1, &bdtr);

    LL_TIM_EnableAllOutputs(TIM1);
    LL_TIM_EnableCounter(TIM1);
    LL_TIM_GenerateEvent_UPDATE(TIM1);
}

In the wider code, the main function simply runs this function only then does a simple LED blinking program for a separate LED. Nothing else is changed, including clock speed at 16MHz

I have tried many setups and configuration options and nothing is working, I appreciate all help.


Solution

  • How did you calculate the dead time?

    As far as I can tell you cannot achieve such a long dead time.
    I assume you calculated it based on the timer clock frequency divided by the prescaler.

    The dead time generator used Tdts, which is not derived from the prescaled tim_psc_ck, it is based on the kernel timer, tim_ker_ck.

    The reference manual gives examples based on an 8MHz clock, and the maximum dead time that can be inserted with this method is 31750ns. There is a register to prescale the Tdts, but only with a maximum of 4. I don't think you will be able to see it with a camera. Maybe if you slow down the entire clock tree to some extreme values.