You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32G474基于LL库配置3通道中心对齐互补PWM(带死区)无可见死区的问题求助

STM32G474基于LL库配置3通道中心对齐互补PWM(带死区)无可见死区的问题求助

我现在在STM32G474上用LL库做3通道中心对齐的互补PWM,还加了死区插入功能。为了测试效果,把6路输出分别接了6个LED,本来预期能看到主输出熄灭后,互补输出亮起中间有明显的延时(我之前算的死区大概98ms),但实际完全看不到这个间隙——从来没有6个LED同时熄灭的时间段。

系统时钟是16MHz,主函数里只跑了下面的TIM1初始化代码,之后就循环闪另一个独立的LED,没改其他配置。试了好几种参数都不行,求各位大佬帮忙看看问题出在哪!

我的TIM1初始化代码:

void TIM1_Init() {
    // 使能GPIOA、GPIOB和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);

    // 配置GPIO为复用功能
    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;

    // 配置CH1(PA8)、CH2(PA9)、CH3(PA10)
    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);

    // 配置CH1N(PB13)、CH2N(PB14)
    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);

    // 配置CH3N(PB15)
    GPIO_InitStruct.Alternate = LL_GPIO_AF_4; // 这里大概率错了!
    GPIO_InitStruct.Pin = LL_GPIO_PIN_15;
    LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    // 初始化TIM1基本参数
    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);

    // 初始化PWM输出比较参数
    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; // 50%占空比
    //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);

    // 使能PWM预装载
    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);

    // 初始化BDTR(刹车和死区寄存器)
    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; // 设了一个值想让LED能看到死区
    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);
}

我整理的几个重点排查方向:

  1. GPIO复用功能配置错误(最紧急!)
    你最后配置PB15(CH3N)的时候,把Alternate改成了LL_GPIO_AF_4,但查STM32G474的引脚手册,TIM1的CH3N对应的PB15复用功能是AF6,和其他互补通道一样!这个错误会导致PB15根本输出不了TIM1的互补PWM信号,自然看不到正确的死区效果,赶紧把PB15的AF改回6试试!

  2. 死区时间计算完全错误(核心问题!)
    你说计算的死区是98ms,但实际完全不是这么回事!STM32的死区时间计算是看BDTR寄存器的DTG位,规则是:

    • 当DTG值在0~127时:死区时间 = DTG × Tdts(Tdts是定时器的时钟周期,由TIM_CR1的CKD位决定)
    • 当DTG值在128~255时:死区时间 = (128 + DTG×2) × Tdts

    你现在的TIM1时钟是16MHz,CKD设的是DIV1,所以Tdts=1/16MHz=62.5ns。你设的DeadTime=24,那死区时间是24×62.5ns=1.5微秒!这和你预期的98ms差了6万多倍,人眼根本不可能看到!

    要得到可见的死区(比如10ms),你需要重新计算:
    10ms = 10^-2 s,Tdts=62.5ns=6.25×10^-8 s,需要的DTG值=10^-2 / 6.25×10^-8=160000,这远超DTG的8位最大值255。所以你得想办法降低Tdts的频率,比如通过TIM_CR1的CKD位设置分频(比如CKD=DIV4,Tdts=4×62.5ns=250ns),或者降低TIM1的时钟频率(比如把APB2的时钟分频)。或者换个思路,先设置DTG=255,看看死区时间是多少:255属于128~255区间,死区时间=(128+255×2)×62.5ns=(128+510)×62.5ns=638×62.5ns≈39.875μs,还是看不到。所以要得到可见的死区,你得把TIM1的时钟降得很低,比如降到1kHz,Tdts=1ms,那DTG=10的话就是10ms,这样就能看到了。

  3. PWM模式和极性的验证
    你用的是PWM2模式(计数器>CCR时输出有效),默认极性是主输出和互补输出都是高有效。加上你设置的空闲状态都是LOW,理论上死区时间内两路输出都是LOW,LED应该全灭。但因为实际死区太小看不到,你可以先把PWM的占空比调小(比如CompareValue=24,即10%占空比),这样主输出亮的时间短,灭的时间长,再配合调整死区参数,更容易看到效果。另外也可以手动设置极性,比如把主输出设为低有效,互补设为高有效,验证亮灭逻辑是否正确。

  4. 中心对齐模式的排除测试
    虽然中心对齐模式下死区应该正常工作,但你可以先把计数器模式改成边缘对齐(LL_TIM_COUNTERMODE_UP),测试死区是否能正常生效,排除模式带来的问题。

  5. LED连接方式确认
    确认你的LED是GPIO输出高点亮还是低点亮?如果某一路LED是低电平点亮,那死区时间内的LOW输出反而会点亮它,导致看起来没有全灭的情况。可以单独测试每个GPIO的输出高低对应的LED亮灭状态。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 11:43:01