【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生

关于PWM产生原理,珠玉在前,这里就不再赘述,本文主要是整理和补充
使用:STM32F103ZET6
参考:PWM原理 PWM频率与占空比详解
STM32定时器----通用定时器输出带死区互补PWM
死区知识:

什么是死区时间? PWM是脉冲宽度调制,在电力电子中,最常用的就是整流和逆变。这就需要用到整流桥和逆变桥。

对三相电来说,就需要三个桥臂。以两电平为例,每个桥臂上有两个电力电子器件,比如IGBT
这两个IGBT不能同时导通,否则就会出现短路的情况,从而对系统造成损害。
这样就不会同时导通,从而避免功率元件烧毁;死区时间控制在通常的单片机所配备的PWM中都有这样的功能,下面会进一步介绍。
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生
相对于PWM来说,死区时间是在PWM输出的这个时间,上下管都不会有输出,当然会使波形输出中断,死区时间一般只占百分之几的周期。但是当PWM波本身占空比小时,空出的部分要比死区还大,所以死区会影响输出的纹波,但应该不是起到决定性作用的。

另外如果死区设置过小,但是仍然出现上下管同时导通,因为导通时间较短,电流较小,不足以烧毁,此时会导致开关元器件发热严重,所以选择合适的死区时间尤为重要;
————————————————
来自:STM32 TIM高级定时器死区时间的计算

一、CubeMX的基础配置

1、时钟树

配置系统时钟72MHz
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生

2、TIM1

ARR->频率
CCRx+死区->占空比

【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生
在PWM输出模式下,除了CNT(计数器当前值)、ARR(自动重装载值)之外,还多了一个值CCRx(捕获/比较寄存器值)。
当CNT小于CCRx时,TIMx_CHx通道输出低电平;
当CNT等于或大于CCRx时,TIMx_CHx通道输出高电平。 ​PWM的一个周期

定时器从0开始向上计数
当0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平
t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平
当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数...循环此过程
至此一个PWM周期完成

TIMx_ARR寄存器确定PWM频率,
TIMx_CCRx寄存器确定占空比
————————————————
来自:【STM32】HAL库 STM32CubeMX教程七—PWM输出(呼吸灯)

对照上图设置
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生
关于使能问题查看下面这篇文章
参考:什么是自动重装载和预装载寄存器?

二、死区计算讲解:

1、基础计算

参考:STM32F103高级定时器死区时间的计算
PS.文章里的表有个小错,情况1的步长值应为0

【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生

例:设置频率200KHz,占空比为10%的一对互补PWM。
为了保证两路信号对称,需要将占空比设置为50%,死区设为10%,最终的占空比即为40%
1、设置PWM波周期为200kHz,需要设置预分频值PRESCLAER和预装载值ARR,TIM1在APB2上
注意:

定时器的时钟不是直接来自APB1或APB2,而是来自于输入为APB1或APB2的一个倍频器,当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当
APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1的频率两倍。
例如AHP 72M,APB12分频36M,那么TIMER就是APB1的2倍频,即72M
————————————————
来自:理解通用定时器

  • 时钟设置中APB2 为72MHz
  • TIM1CLK = PCLK1
  • 希望TIM1预分频后的计数时钟counter clock 为4MHz:
  • 因此预分频值 PRESCLAER = 72 / 4 - 1 = 18 - 1
  • TIM1输出时钟output clock为200KHz:
  • 因此预装载值 PERIOD_TIM1(ARR) = (TIM1 counter clock / TIM1 output clock) - 1 = 4000/200 - 1=20 - 1
    2、先设置PWM占空比为50%(保证互补波形相同),最终占空比还要扣除死区
  • 因此待比较值 PULSE1_VALUE = 20 * 0.5
    3、设置死区
  • 希望最终波形的占空比为0.1,则死区长度为0.5-0.1=0.4,实际为0.4 / 200k=2us
  • 根据PWM死区计算表,在系统时钟为72MHz情况下,2us为情况2,最终计算为0x88
    【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生

2、代码

tim.h中添加

/* USER CODE BEGIN Private defines */
#define  PRESCALER     (uint16_t)(18 - 1) 
#define  PERIOD_TIM1   (uint16_t)(20 - 1)    
#define  PULSE1_VALUE   (uint16_t)(20 * 0.5) 
#define  DEADTIME       (uint8_t)0x88
/* USER CODE END Private defines */

死区自动计算代码(根据ST官方参考手册变化了一下,供参考,轻喷)
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生

/* 死区计算函数 */
//根据死区计算表,返回死区设置值
//对于步长全为系统时钟的周期T_DTS来说,乘数范围
//情况1:0~127    || DT / T_TDS
//情况2:128~254  || DT / (2 * T_TDS) + 64 
//情况3:256~504  || DT / (8 * T_TDS) + 160
//情况4:512~1008 || DT / (16 * T_TDS) + 192
//传入参数: pwm波频率(pwmfre_KHz,KHz),系统时钟(myCLK_MHz,MHz),占空比(mydutyc,%)
//如deadtime(200,72,10),PWM波频率200KHz,系统时钟72MHz,占空比10%
uint8_t deadtime(uint8_t pwmfre_KHz,uint8_t myCLK_MHz, uint8_t mydutyc)
{
    float T_TDS;
    T_TDS = (float)1000 / myCLK_MHz; 
    printf("T_TDS=%f ns, ", T_TDS);
    float DT;//死区时间
    DT = (50 - mydutyc) *10000 / pwmfre_KHz;
    printf("DT=%f ns\r\n", DT);
    uint8_t dt;
    if ( DT >= 0 && DT <= T_TDS * 127) 
    {
        dt = (uint8_t)(DT / T_TDS);
        printf("situation 1: dt = %f ns, ",DT / T_TDS);
    }
    else if ( DT >= T_TDS * 128 && DT <= T_TDS * 254) 
    {
        dt = (uint8_t)(DT / (2 * T_TDS) + 64 );
        printf("situation 2: dt = %f ns, ",DT / T_TDS);
    }
    else if ( DT >= T_TDS * 256 && DT <= T_TDS * 504) 
    {
        dt = (uint8_t)(DT / (8 * T_TDS) + 160 );
        printf("situation 3: dt = %f ns, ",DT / T_TDS);
    }
    else 
    {
        dt = (uint8_t)(DT / (16 * T_TDS) + 192 );
        printf("situation 4: dt = %f ns, ",DT / T_TDS);
    }
    return dt;
}

结果:
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生

main函数中添加:

/* USER CODE BEGIN 2 */
  pwm_test();// pwm波发送测试函数
  /* USER CODE END 2 */

对应测试函数

void pwm_test(void)// pwm波发送测试函数
{
    if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)//开启TIM1的CH1通道产生PWM
      {
        Error_Handler();
      }
    if (HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)//开启TIM1的CH1N通道产生PWM
      {
        Error_Handler();
      } 
}

三、结果

无示波器可以使用keil的逻辑分析仪观察PWM结果,参考:
Keil 逻辑分析仪观测pwm波
PS.如果在添加观察信号步骤出现Unknown Signal警告,修改为以下配置

【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生

【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生
示波器:
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生
占空比 500ns/5us=10%

四、开始产生PWM后如何改变频率

不太精确的方式:修改预分频值

void pwm_changefre()
{
    uint16_t pre;//预分频值
    //设为200KHz
    pre= 18 - 1;
    __HAL_TIM_SET_PRESCALER(&htim1, pre);//修改频率改预分频的值,如果改period的值占空比也会跟着变,
    HAL_Delay(200);//或者修改period的值和pulse的值
    //设为100KHz
    pre= 36 - 1;
    __HAL_TIM_SET_PRESCALER(&htim1, pre);
    HAL_Delay(200);
} 

【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生
【stm32】基于cubemx+HAL库的带死区互补频率可调PWM产生

精确一点的:
参考:练习STM32动态更改PWM波频率和占空比
参考:基于stm32的2FSK调制解调器设计
参考:STM32 HAL库学习系列第4篇 定时器TIM----- 开始定时器与PWM输出配置

附:源码链接

上一篇:【数字基带信号】基于matlab数字双相码仿真【含Matlab源码 989期】


下一篇:C#实现DataTable行列转置