STM32
直播中

刘玉兰

7年用户 994经验值
私信 关注
[问答]

如何去生成一个开环的三相SPWM呢

什么是SPWM呢?
如何去生成一个开环的三相SPWM呢?

回帖(1)

张红玲

2021-11-17 14:50:09
  SPWM在单向逆变器中运用的比较多,在电能与电机控制领域现在大都是用SVPWM。先学好spwm,为以后的进阶做准备!本文主要是我学习spwm的一些理解,然后后通过实例代码生成spwm,可以直接复制测试。
  一。什么是SPWM
  要说SPWM先得说PWM,英文全名Pluse Width Modulation,即脉冲宽度调制,实际上就是周期的矩形波,然后每个周期的占空比都可以自己设置就叫调制。比如Buck电路中开关管的PWM波,当输入不变时可以将它的占空比从40%变到60%就能提高母线输出电压。
  
  图一 。占空比固定pwm波与载波
  而SPWM前面加了个S(Sine),即正弦脉宽调制,意思就是:还是这个周期的矩形波,但不同的是占空比不是如40%固定的,而是按照正弦规律变化的。
  SPWM一般由三角波(载波)和正弦波(调制波)比较而成。硬件生成方法是将三角波和正弦波加入比较器得到;软件是通过定时器或者Epwm模块,按照中央计数模式生成三角波,经由CCR比较模块动作产生对应高低电平,即SPWM。
  
  如图,我们将正弦函数和三角载波都归一化处理,正弦波幅值要小于三角波,这里取0.8。在一个很小的Δt时段内正弦函数值和占空比的值相等。例如图中Δt时段正弦波的值是0.8左右(A点电压值),它的矩形波占空比也是80%。同时我们可以看出矩形波的频率和三角波相等。正弦波被三角波“切割”成高频的spwm,用以控制开关管,然后spwm通过低通滤波滤除高次谐波后,得到原来的正弦波。图示情况下从0开始半个期内SPWM占空比是0-》0.8-》0按照正弦函数变化,要得到某时的占空比,得到此时的sine值就可以了,sine值可以事先放入表中。
  二.SPWM软件生成
  利用单片机输出PWM波,然后让占空比正弦规律变化。
  实现步骤可以简单分为三步,以stm32为例:
  (1)生成载波。比如要生成一个10KHZ的三角波,将计数器设置加减计数、周期设为1/10K就ok啦。这样生成的三角波的幅值是多少呀,3.3V?其实在单片机里面都是数字信号,三角波最高点的时候可以用一个计数值来表示,比如8400,最低点是0。不用管他的电压是多少。
  (2)生成正弦波。这一步用软件生成一个正弦表即可。比如将正弦波取200个点,即将一个正弦分割成200份,每个点代表一个幅值。用离散的数字量表达正弦模拟量。
  (3)将正弦波和三角波进行比较。
  a.什么时候进行比较。设置计数值达到比较值产生动作。
  b.比较完之后,需要改变比较值,用于下一个周期进行比较(比较值可以理解为占空比),比较值查正弦表获得,这样就生成了占空比正弦规律变化的SPWM。
  C.调制度m。m=正弦表最大值/三角波最大计数值。 如正弦表最大值4200,三角波最大计数值8400,m=4200/8400=0.5,此时spwm最大占空比为50%,设置m=1,spwm最大占空比为100%。
  
  
  如图所示,要注意因为是单极性调制,spwm和三角载波都是大于0的。在单相全桥逆变电路中,开关管交替导通时输出电压Ud自然会倒过来为负,Ud经过滤波就是一个正弦波。
  三。实例分析
  下面我们来看一个例子,来生成一个开环的三相spwm。单相逆变需生成两组相差180度的spwm,三相即生成三组两两相差120度的spwm,且每组的spwm都是互补的。
  
  如图,将生成的三组spwm分别加到1、2、3组桥臂,每组spwm互补防止了同一桥臂上下管同时导通会引起短路。
  uint16_t Counter_sine1 = 0; //A相
  uint16_t Counter_sine2 = 83; //滞后A相120度
  uint16_t Counter_sine3 = 166; //超前A相120度
  /**
  * 说明 TIM1GPIO初始化 CH1--A8 CH2--A9 CH3--A10
  * CH1N-B13 CH2N-B14 CH3N-B15
  *
  * 入口参数 None
  *
  * 出口参数 None
  **/
  void TIM1_GPIO_Config(void)
  {
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9| GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13| GPIO_Pin_14 | GPIO_Pin_15;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  }
  #define CKTIM ((u32)72000000uL) //主频
  #define PWM_PRSC ((u8)0) //TIM1分频系数
  #define PWM_FREQ ((u16) 10000) //PWM频率(Hz)
  #define PWM_PERIOD ((u16) (CKTIM / (u32)(2 * PWM_FREQ *(PWM_PRSC+1))))
  #define MODULAT (float)0.7 //调制度
  /**
  * 说明 TIM1模式
  *
  * 入口参数 None
  *
  * 出口参数 None
  **/
  void TIM1_Mode_Config(void)
  {
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  TIM_OCInitTypeDef TIM_OCInitStructure;
  TIM_BDTRInitTypeDef TIM1_BDTRInitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD; //计数周期
  TIM_TimeBaseStructure.TIM_Prescaler = PWM_PRSC; //分频系数
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV2;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能CHx的PWM输出
  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;//互补输出使能,使能CHxN的PWM输出
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;
  TIM_OC1Init(TIM1, &TIM_OCInitStructure); //配置CH1
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OC2Init(TIM1, &TIM_OCInitStructure); //配置CH2
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OC3Init(TIM1, &TIM_OCInitStructure); //配置CH3
  //死区时间
  TIM1_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
  TIM1_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
  TIM1_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
  TIM1_BDTRInitStructure.TIM_DeadTime = 360; //设置死区时间
  TIM1_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
  TIM1_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
  TIM1_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable;
  TIM_BDTRConfig(TIM1, &TIM1_BDTRInitStructure);
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //4个抢先级、4个子优先级
  NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  TIM_ITConfig(TIM1,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3,ENABLE); //使能中断
  TIM_CtrlPWMOutputs(TIM1, ENABLE); //PWM输出使能
  TIM_Cmd(TIM1, ENABLE); //使能TIM1
  }
  void TIM1_PWM_Init(void)
  {
  TIM1_GPIO_Config();
  TIM1_Mode_Config();
  }
  //定时器1中断服务函数
  void TIM1_CC_IRQHandler(void)
  {
  if(Counter_sine1》=250)
  {
  Counter_sine1 = 0;
  }
  if(Counter_sine2》=250)
  {
  Counter_sine2 = 0;
  }
  if(Counter_sine3》=250)
  {
  Counter_sine3 = 0;
  }
  //CCR1
  if (TIM_GetITStatus(TIM1, TIM_IT_CC1)!=RESET)
  {
  TIM_SetCompare1(TIM1,(uint32_t)(talab[Counter_sine1])*MODULAT); //A相
  Counter_sine1++;
  TIM_ClearITPendingBit(TIM1 , TIM_IT_CC1);
  }
  //CCR2
  if (TIM_GetITStatus(TIM1, TIM_IT_CC2) !=RESET)
  {
  TIM_SetCompare2(TIM1,((uint32_t)talab[Counter_sine2])*MODULAT); //B相
  Counter_sine2++;
  TIM_ClearITPendingBit(TIM1 , TIM_IT_CC2);
  }
  //CCR3
  if (TIM_GetITStatus(TIM1, TIM_IT_CC3) !=RESET)
  {
  TIM_SetCompare3(TIM1,(uint32_t)(talab[Counter_sine3])*MODULAT); //C相
  Counter_sine3++;
  TIM_ClearITPendingBit(TIM1 , TIM_IT_CC3);
  }
  }
  int const talab[250]=
  {
  100 , 102 , 108 , 116 , 126 , 140 , 154 , 172 , 194 , 216 ,
  242 , 270 , 300 , 334 , 370 , 408 , 448 , 490 , 536 , 582 ,
  632 , 684 , 738 , 794 , 854 , 914 , 976 ,1040 ,1108 ,1176 ,
  1246 ,1320 ,1394 ,1470 ,1548 ,1626 ,1708 ,1790 ,1874 ,1960 ,
  2046 ,2136 ,2224 ,2316 ,2408 ,2502 ,2596 ,2690 ,2786 ,2884 ,
  2982 ,3080 ,3180 ,3280 ,3382 ,3482 ,3584 ,3686 ,3788 ,3892 ,
  3994 ,4096 ,4200 ,4304 ,4406 ,4508 ,4612 ,4714 ,4816 ,4918 ,
  5018 ,5120 ,5220 ,5320 ,5418 ,5516 ,5614 ,5710 ,5804 ,5898 ,
  5992 ,6084 ,6176 ,6264 ,6354 ,6440 ,6526 ,6610 ,6692 ,6774 ,
  6852 ,6930 ,7006 ,7080 ,7154 ,7224 ,7292 ,7360 ,7424 ,7486 ,
  7546 ,7606 ,7662 ,7716 ,7768 ,7818 ,7864 ,7910 ,7952 ,7992 ,
  8030 ,8066 ,8100 ,8130 ,8158 ,8184 ,8206 ,8228 ,8246 ,8260 ,
  8274 ,8284 ,8292 ,8298 ,8300 ,8300 ,8298 ,8292 ,8284 ,8274 ,
  8260 ,8246 ,8228 ,8206 ,8184 ,8158 ,8130 ,8100 ,8066 ,8030 ,
  7992 ,7952 ,7910 ,7864 ,7818 ,7768 ,7716 ,7662 ,7606 ,7546 ,
  7486 ,7424 ,7360 ,7292 ,7224 ,7154 ,7080 ,7006 ,6930 ,6852 ,
  6774 ,6692 ,6610 ,6526 ,6440 ,6354 ,6264 ,6176 ,6084 ,5992 ,
  5898 ,5804 ,5710 ,5614 ,5516 ,5418 ,5320 ,5220 ,5120 ,5018 ,
  4918 ,4816 ,4714 ,4612 ,4508 ,4406 ,4304 ,4200 ,4096 ,3994 ,
  3892 ,3788 ,3686 ,3584 ,3482 ,3382 ,3280 ,3180 ,3080 ,2982 ,
  2884 ,2786 ,2690 ,2596 ,2502 ,2408 ,2316 ,2224 ,2136 ,2046 ,
  1960 ,1874 ,1790 ,1708 ,1626 ,1548 ,1470 ,1394 ,1320 ,1246 ,
  1176 ,1108 ,1040 , 976 , 914 , 854 , 794 , 738 , 684 , 632 ,
  582 , 536 , 490 , 448 , 408 , 370 , 334 , 300 , 270 , 242 ,
  216 , 194 , 172 , 154 , 140 , 126 , 116 , 108 , 102 , 100
  };
  TIM1设置为中央计数模式,开启互补通道,设置死区时间,死区时间是多少个时钟计数周期,比如TIM1计数周期是72M,设置为72就是1000ns。
  spwm频率设置为10k,然后TIM1每个通道的比较值达到时更新比较值。
  调制度m范围为0~1,设为0.7。
  正弦调制波的频率是自己设置的,方法是f=载波频率/表中点数,这里设置的是载波频率10K,取250个点
  得到的正弦频率就是40HZ,改变载波频率为50*250就得到了50hz的正弦波输出。正弦表可以用取点工具。
  
  将程序下载到stm32f103单片机,PA8、PA9两个引脚输出spwm波形是相差120度的,如图。
  互补通道波形相反,且有死区时间。
  将以上程序加到一起就可以实现啦!
  刚学习不久,有很多不足之处还请大家多多包涵和指正。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分