STM32平台三电阻采样实现FOC

准备条件:

使用STM32 MOTORCONTROL WORKBENCH 软件新建FOC案例(控制板、驱动板按硬件选取),电流采样选三电阻模式。

 配置好电机参数生成工程代码。

打开项目文件夹中的.ioc文件

用定时器中断触发ADC采样,设置定时器TIM1的通道4为PWM Generation No Output,中断输出的Trigger Event Selection TRGO为输出比较模式,比较参考为OC4REF,即通道4上的参考量。

启用ADC注入通道,外部触发源选择定时器的触发事件,也就是我们上面设置的通道4比较。

打开Keil工程文件,r3_2_xxxx_pwm_curr_fdbk.c中存放电流采样的执行函数__weak void R3_2_GetPhaseCurrents( PWMC_Handle_t * pHdl, ab_t * Iab ),该函数的输入为PWM和电流反馈的数据结构体,输出为两相采样电流值。

以下代码分别获取当前扇区和两个ADC通道对应的采样值。

  Sector = ( uint8_t )pHandle->_Super.Sector;
  ADCDataReg1 = *pHandle->pParams_str->ADCDataReg1[Sector];
  ADCDataReg2 = *pHandle->pParams_str->ADCDataReg2[Sector];

接下来运行switch(Sector),每个扇区不对PWM下桥低电平占空比较小的相进行电流采样,避免去采样PWM占空比接近100%的那一相电流。

这里就很奇怪了,ADC通道内的数据是怎样与扇区对应上的?

搜索ADCDataReg1,发现里面存放了ADC采样数据的地址,而它上面ADCConfig1存放了通道的地址,可想而知,应该是不同扇区采样的通道不同。

  .ADCConfig1 = { MC_ADC_CHANNEL_7<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_1<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_1<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_1<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_1<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_7<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                },
  .ADCConfig2 = { MC_ADC_CHANNEL_6<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_6<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_6<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_7<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_7<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                , MC_ADC_CHANNEL_6<<ADC_JSQR_JSQ1_Pos | (LL_ADC_INJ_TRIG_EXT_TIM1_TRGO & ~ADC_INJ_TRIG_EXT_EDGE_DEFAULT)
                },
  .ADCDataReg1 = { &ADC1->JDR1
                 , &ADC1->JDR1
                 , &ADC1->JDR1
                 , &ADC1->JDR1
                 , &ADC1->JDR1
                 , &ADC1->JDR1
                 },
  .ADCDataReg2 = { &ADC2->JDR1
                 , &ADC2->JDR1
                 , &ADC2->JDR1
                 , &ADC2->JDR1
                 , &ADC2->JDR1
                 , &ADC2->JDR1
                 },

继续搜索 ADCConfig1,发现它在__weak void * R3_2_TIMx_UP_IRQHandler( PWMC_R3_2_Handle_t * pHandle )函数中执行,这个函数由定时器1更新中断执行,ADC的注入采样通道通过扇区进行选择。然后开启通道四的比较触发和ADC采样触发。

  ADCx_1->JSQR = pHandle->pParams_str->ADCConfig1[pHandle->_Super.Sector] | (uint32_t) pHandle->ADC_ExternalPolarityInjected;
  ADCx_2->JSQR = pHandle->pParams_str->ADCConfig2[pHandle->_Super.Sector] | (uint32_t) pHandle->ADC_ExternalPolarityInjected;

  LL_TIM_SetTriggerOutput(TIMx, LL_TIM_TRGO_OC4REF);
   
  pHandle->ADC_ExternalPolarityInjected = (uint16_t)LL_ADC_INJ_TRIG_EXT_RISING;

最后就是采样点的选取问题,根据STM32的官方文件,通过比较PWM的宽度来选取采样点。 

uint16_t R3_2_SetADCSampPointSectX( PWMC_Handle_t * pHdl )函数中给出了具体代码。首先判断中间窗口区域是否足够大,这部分代码给出的采样点位置与官方说明略有不同,有兴趣的同学可以深入比较一下。

  /* Verify that sampling is possible in the middle of PWM by checking the smallest duty cycle */
   if ( ( uint16_t )( pHandle->Half_PWMPeriod - pHdl->lowDuty ) > pHandle->pParams_str->Tafter )
  {
    /* When it is possible to sample in the middle of the PWM period, always sample the same phases
     * (AB are chosen) for all sectors in order to not induce current discontinuities when there are differences
     * between offsets */

    /* sector number needed by GetPhaseCurrent, phase A and B are sampled which corresponds
     * to sector 4 or 5  */
    pHandle->_Super.Sector = SECTOR_5;

    /* set sampling  point trigger in the middle of PWM period */
    SamplingPoint =  pHandle->Half_PWMPeriod - (uint16_t) 1;
  }

  else
  {
    /* In this case it is necessary to convert phases with Maximum and variable complementary duty cycle.*/

    /* ADC Injected sequence configuration. The stator phase with minimum value of complementary
        duty cycle is set as first. In every sector there is always one phase with maximum complementary duty,
        one with minimum complementary duty and one with variable complementary duty. In this case, phases
        with variable complementary duty and with maximum duty are converted and the first will be always
        the phase with variable complementary duty cycle */
    DeltaDuty = ( uint16_t )( pHdl->lowDuty - pHdl->midDuty );

    /* Definition of crossing point */
    if ( DeltaDuty > ( uint16_t )( pHandle->Half_PWMPeriod - pHdl->lowDuty ) * 2u )
    {
      SamplingPoint = pHdl->lowDuty - pHandle->pParams_str->Tbefore;
    }
    else
    {
      SamplingPoint = pHdl->lowDuty + pHandle->pParams_str->Tafter;

      if ( SamplingPoint >= pHandle->Half_PWMPeriod )
      {
         /* ADC trigger edge must be changed from positive to negative */
        pHandle->ADC_ExternalPolarityInjected = (uint16_t) LL_ADC_INJ_TRIG_EXT_FALLING;
        SamplingPoint = ( 2u * pHandle->Half_PWMPeriod ) - SamplingPoint - (uint16_t) 1;
      }
    }
  }

最后输出采样点:R3_2_WriteTIMRegisters( &pHandle->_Super, SamplingPoint );

物联沃分享整理
物联沃-IOTWORD物联网 » STM32平台三电阻采样实现FOC

发表评论