返回列表 发新帖我要提问本帖赏金: 20.00元(功能说明)

基于gd32f4的svpwm产生

[复制链接]
 楼主| 穿西装的强子 发表于 2025-1-16 20:23 | 显示全部楼层 |阅读模式
<
本帖最后由 穿西装的强子 于 2025-1-16 20:31 编辑

#申请原创# #每日话题# @21小跑堂

SVPWM原理
SVPWM基于空间矢量的概念,即将三相交流系统中的电压或电流表示为复平面上的一个旋转矢量。对于一个三相逆变器来说,其输出可以看作是六个基本电压矢量的组合,这些矢量对应于逆变器的六个开关状态。通过适当选择这些基本矢量的作用时间和顺序,可以在任意时刻合成出所需的电压矢量。
在SVPWM中,整个平面被划分为六个扇区,每个扇区由两个相邻的基本电压矢量定义。为了产生所需的输出电压矢量,我们首先确定它所在的扇区,然后计算该扇区内两个有效电压矢量以及零矢量的时间比例,以使得合成矢量尽可能接近所需矢量。
产生计算方法
  • 坐标变换:将三相静止坐标系下的αβ坐标转换到两相旋转坐标系下的dq坐标,或者直接使用αβ坐标进行计算。
  • 扇区识别:根据参考电压矢量的位置确定它所在的扇区。这通常涉及到比较V_alpha和V_beta的值来决定。
  • 时间计算:

    • 计算两个有效矢量(T1, T2)的作用时间。
    • 确定零矢量(T0)的时间,即没有电压施加的时间。
    • 总周期时间Ts = T1 + T2 + T0,其中Ts是PWM周期。
  • 占空比分配:将计算得到的时间转换为相应的PWM占空比。例如,如果一个周期内的总时间为Ts,而某个有效矢量的作用时间为T1,则对应的PWM占空比D1 = T1 / Ts。
  • 生成PWM信号:根据计算出来的占空比设置定时器的比较寄存器,从而生成PWM波形。
  • 重复:在每个PWM周期内更新PWM占空比,以跟踪变化的参考电压矢量。

在深入理解SVPWM之前,我们需要回顾一下三相系统到两相静止坐标系(αβ坐标系)的转换。给定一个三相电压Va,Vb,VcVa​,Vb​,Vc​,可以通过克拉克变换(Clarke Transform)转换为两相静止坐标系下的分量:
[VαVβ]=23[1−12−12032−32][VaVbVc][Vα​Vβ​​]=32​[10​−21​23​​​−21​−23​​​]​Va​Vb​Vc​​​
接着,为了将这些值映射到六边形扇区中,我们使用以下公式来确定参考矢量所在的扇区:
  • 如果 Vβ>0Vβ​>0 并且 Vα>Vβ/3Vα​>Vβ​/3​,则位于扇区1。
  • 如果 Vβ>0Vβ​>0 并且 Vα<=Vβ/3Vα​<=Vβ​/3​,则位于扇区2。
  • 如果 Vβ>=−Vα/3Vβ​>=−Vα​/3​ 并且 Vα<0Vα​<0,则位于扇区3。
  • 如果 Vβ<−Vα/3Vβ​<−Vα​/3​ 并且 Vα<0Vα​<0,则位于扇区4。
  • 如果 Vβ<0Vβ​<0 并且 Vα>=Vβ/3Vα​>=Vβ​/3​,则位于扇区5。
  • 如果 Vβ>=Vα/3Vβ​>=Vα​/3​ 并且 Vα>0Vα​>0,则位于扇区6。
扇区内的矢量分解
一旦确定了扇区,接下来就是根据参考矢量的位置计算两个有效矢量和零矢量的作用时间。设 VrefVref​ 是需要合成的参考电压矢量,VDCVDC​ 是直流母线电压,则每个矢量的时间可以按照如下方式计算:
T1=23⋅∣Vref∣VDC⋅sin⁡(θk)T1​=32​⋅VDC​∣Vref​∣​⋅sin(θk​)T2=23⋅∣Vref∣VDC⋅sin⁡(60°−θk)T2​=32​⋅VDC​∣Vref​∣​⋅sin(60°−θk​)T0=Ts−(T1+T2)T0​=Ts​−(T1​+T2​)
其中 TsTs​ 是PWM周期,θkθk​ 是参考矢量与该扇区起始边界的夹角。
实现细节
在实际应用中,SVPWM的实现需要注意以下几个方面:
  • 死区补偿:由于功率开关器件的导通延迟和关断延迟,可能会引入非理想的波形失真。因此,在设置PWM占空比时需要考虑死区时间,并对其进行适当的补偿。
  • 过调制策略:当输出频率很高或负载要求更大电压时,传统的SVPWM可能无法满足需求。此时可以采用过调制技术,如第三谐波注入法,以提高输出电压利用率。
  • 硬件限制:确保所使用的微控制器有足够的处理能力和定时器资源来实时更新PWM信号。同时也要考虑到ADC采样速率对反馈控制的影响。
  • 软件优化:编写高效的算法代码,尽量减少计算量,保证程序可以在中断服务程序(ISR)中快速执行,维持系统的实时性。

svpwm计算源码

  1. void pwm_init()
  2. {
  3. timer_oc_parameter_struct timer_ocintpara;
  4.     timer_parameter_struct timer_initpara;
  5.     timer_break_parameter_struct timer_breakpara;
  6.           /*gpio set*/
  7.     gpio_timer0_config();

  8.     rcu_periph_clock_enable(RCU_TIMER0);
  9.     rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);

  10.     nvic_irq_enable(TIMER0_UP_TIMER9_IRQn,1,0);
  11.    
  12.     timer_deinit(TIMER0);

  13.     /* TIMER0 configuration */
  14.     timer_initpara.prescaler         = 0; // 200M
  15.     timer_initpara.alignedmode       = TIMER_COUNTER_CENTER_UP;
  16.     timer_initpara.counterdirection  = TIMER_COUNTER_UP;
  17.     timer_initpara.period            = PWM_PERIOD_VALUE;
  18.     timer_initpara.clockdivision     = TIMER_CKDIV_DIV2;  //Tds = fclk/2
  19.     timer_initpara.repetitioncounter = 1;
  20.     timer_init(TIMER0,&timer_initpara);

  21.     /* CH0/CH0N configuration in PWM mode0 */
  22.     timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
  23.     timer_ocintpara.outputnstate = TIMER_CCXN_ENABLE;
  24.     timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
  25.     timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
  26.     timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
  27.     timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

  28.     timer_channel_output_config(TIMER0,TIMER_CH_0,&timer_ocintpara);
  29.     timer_channel_output_config(TIMER0,TIMER_CH_1,&timer_ocintpara);
  30.     timer_channel_output_config(TIMER0,TIMER_CH_2,&timer_ocintpara);
  31.    
  32.     timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_0,4999); //设定比较值
  33.     timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_1,4999);
  34.     timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_2,4999);
  35.     //GD32的pwm0模式与STM32的pwm1模式一致
  36.     timer_channel_output_mode_config(TIMER0,TIMER_CH_0,TIMER_OC_MODE_PWM0);
  37.     timer_channel_output_mode_config(TIMER0,TIMER_CH_1,TIMER_OC_MODE_PWM0);
  38.     timer_channel_output_mode_config(TIMER0,TIMER_CH_2,TIMER_OC_MODE_PWM0);
  39.     timer_channel_output_shadow_config(TIMER0,TIMER_CH_0,TIMER_OC_SHADOW_ENABLE);
  40.     timer_channel_output_shadow_config(TIMER0,TIMER_CH_1,TIMER_OC_SHADOW_ENABLE);
  41.     timer_channel_output_shadow_config(TIMER0,TIMER_CH_2,TIMER_OC_SHADOW_ENABLE);

  42.     /* automatic output enable, break, dead time and lock configuration*/
  43.     timer_breakpara.runoffstate      = TIMER_ROS_STATE_ENABLE;
  44.     timer_breakpara.ideloffstate     = TIMER_IOS_STATE_ENABLE ;
  45.     timer_breakpara.deadtime         = 138; //tdts = 1/100M  1.5us死区
  46.     timer_breakpara.breakpolarity    = TIMER_BREAK_POLARITY_LOW;
  47.     timer_breakpara.outputautostate  = TIMER_OUTAUTO_DISABLE;
  48.     timer_breakpara.protectmode      = TIMER_CCHP_PROT_OFF;
  49.     timer_breakpara.breakstate       = TIMER_BREAK_DISABLE;
  50.     timer_break_config(TIMER0,&timer_breakpara);

  51.         timer_master_slave_mode_config(TIMER0,TIMER_MASTER_SLAVE_MODE_ENABLE);
  52. //                 timer_slave_mode_select(TIMER0,TIMER_TRI_OUT_SRC_UPDATE);
  53.     timer_master_output_trigger_source_select(TIMER0,TIMER_TRI_OUT_SRC_UPDATE);
  54.     /* TIMER0 primary output function enable */
  55.     timer_primary_output_config(TIMER0,DISABLE);

  56.     /* auto-reload preload enable */
  57.     timer_auto_reload_shadow_enable(TIMER0);

  58.     /* TIMER0 counter enable */
  59.     timer_enable(TIMER0);
  60. }
  61. void process_values(struct YourStruct *P) {
  62.     if (P == NULL || P->Tonmin > P->Tonmax) {
  63.         // Handle error: invalid input parameters
  64.         return;
  65.     }

  66.     INT32 Temp0 = 0;
  67.     REAL32 Va, Vb, Vc;
  68.     INT16 Sector, X, Y, Z, T1, T2, Ta, Tb, Tc;

  69.     Va = P->Vbeta;
  70.     Vb = (-P->Vbeta + C_FSqrt3 * P->Valfa) * C_F1D2;
  71.     Vc = (-P->Vbeta - C_FSqrt3 * P->Valfa) * C_F1D2;

  72.     /* 60 degrees sector determination */
  73.     Sector = 0;
  74.     if (Va > EPSILON) {
  75.         Sector += 1;
  76.     }
  77.     if (Vb > EPSILON) {
  78.         Sector += 2;
  79.     }
  80.     if (Vc > EPSILON) {
  81.         Sector += 4;
  82.     }

  83.     X = (INT16)(2 * P->Vbeta * P->VDCinvTSQRT);
  84.     Y = (INT16)(P->Vbeta * P->VDCinvTSQRT + P->Valfa * P->VDCinvTCon0);
  85.     Z = (INT16)(P->Vbeta * P->VDCinvTSQRT - P->Valfa * P->VDCinvTCon0);

  86.     /* T1 and T2 calculation depending on the sector number */
  87.     switch (Sector) {
  88.     case 0:
  89.     case 6:
  90.     case 7:
  91.         T1 = -Y;
  92.         T2 = -Z;
  93.         break;
  94.     case 1:
  95.         T1 = Z;
  96.         T2 = Y;
  97.         break;
  98.     case 2:
  99.         T1 = Y;
  100.         T2 = -X;
  101.         break;
  102.     case 3:
  103.         T1 = -Z;
  104.         T2 = X;
  105.         break;
  106.     case 4:
  107.         T1 = -X;
  108.         T2 = Z;
  109.         break;
  110.     case 5:
  111.         T1 = X;
  112.         T2 = -Y;
  113.         break;
  114.     default:
  115.         // Handle unexpected sector value
  116.         return;
  117.     }

  118.     // Ensure T1 and T2 are within bounds
  119.     T1 = (T1 < P->Tonmin) ? P->Tonmin : T1;
  120.     T2 = (T2 < P->Tonmin) ? P->Tonmin : T2;

  121.     Temp0 = T1 + T2;
  122.     if (Temp0 > P->Tonmax) {
  123.         T1 = (T1 * P->Tonmax) / Temp0;
  124.         T2 = (T2 * P->Tonmax) / Temp0;
  125.     }

  126.     // Ensure T1 and T2 are within INT16 range
  127.     T1 = (T1 > SHRT_MAX) ? SHRT_MAX : T1;
  128.     T2 = (T2 > SHRT_MAX) ? SHRT_MAX : T2;

  129.     Ta = ((P->PWMPRD - T1 - T2) >> 1);
  130.     Tb = Ta + T1;
  131.     Tc = Tb + T2;

  132.     switch (Sector) {
  133.     case 0:
  134.     case 6:
  135.     case 7:
  136.         P->TaNumber = Tb;
  137.         P->TbNumber = Tc;
  138.         P->TcNumber = Ta;
  139.         break;
  140.     case 1:
  141.         P->TaNumber = Tb;
  142.         P->TbNumber = Ta;
  143.         P->TcNumber = Tc;
  144.         break;
  145.     case 2:
  146.         P->TaNumber = Ta;
  147.         P->TbNumber = Tc;
  148.         P->TcNumber = Tb;
  149.         break;
  150.     case 3:
  151.         P->TaNumber = Ta;
  152.         P->TbNumber = Tb;
  153.         P->TcNumber = Tc;
  154.         break;
  155.     case 4:
  156.         P->TaNumber = Tc;
  157.         P->TbNumber = Tb;
  158.         P->TcNumber = Ta;
  159.         break;
  160.     case 5:
  161.         P->TaNumber = Tc;
  162.         P->TbNumber = Ta;
  163.         P->TcNumber = Tb;
  164.         break;
  165.     default:
  166.         // Handle unexpected sector value
  167.         return;
  168.     }
  169. }

  170. PUBLIC void pwm_out(UINT16 PowerFlag, UINT16 Ta, UINT16 Tb, UINT16 Tc)
  171. {
  172.         TIMER_CH0CV(TIMER0) = Ta;
  173.         TIMER_CH1CV(TIMER0) = Tb;
  174.         TIMER_CH2CV(TIMER0) = Tc;
  175. }


使用pwm_init()初始化timer0
使用process_values函数产生svpwm
使用pwm_out函数输出波形



打赏榜单

21小跑堂 打赏了 20.00 元 2025-01-20
理由:恭喜通过原创审核!期待您更多的原创作品~~

评论

从SVPWM原理概述到计算方法再到细节描述,并在gd32f4上以代码形式实现  发表于 2025-1-20 16:59
dukedz 发表于 2025-1-22 15:34 | 显示全部楼层
其实没这么复杂,看我这篇文章,超简单: https://blog.d-l.io/svpwm-cn
MarquisHou 发表于 2025-2-11 20:02 | 显示全部楼层
dukedz 发表于 2025-1-22 15:34
其实没这么复杂,看我这篇文章,超简单: https://blog.d-l.io/svpwm-cn

您这个其实就是零序注入
您需要登录后才可以回帖 登录 | 注册

本版积分规则

62

主题

259

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部

62

主题

259

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部