FOC控制库MCSDK5.4.4梳理(2)——电流采样

一、前言

  本文讲解MCSDK中FOC控制的预备步骤——电流采样,电流采样的实现函数是R3_2_GetPhaseCurrents。MCSDK中的电流采样功能和上一篇文章SVPWM一样,也是分扇区来进行电流采样的,电流采样涉及到R3_2_ParamsM1.ADCConfig1和R3_2_ParamsM1.ADCConfig2两个数组的配置,因此电流采样部分也值得一说
  UVW和ABC都是表示电机的三个相序,只是说法不同

二、源代码解析

1、电流采样机制

  在FOC控制中,要求在同一时刻同时获得UVW三相的电流,最为理想的情况是一个引脚能同时复用成ADC1,ADC2和ADC3的通道,但这样的引脚并不多,而且有些引脚会复用成其他功能,所以该方法不可行。从上一章文章我们得知,三相电流的标量和为零,因此我们只需要在同一时刻读取到其中两相电流即可,基本上一个引脚可以同时复用成ADC1和ADC2,该方法可行
  这里相电流采样时,ADC要配置成注入转换模式,注入转换优先级比规则转化优先级高。比如,当ADC同时满足母线电压采样条件和相电流采样条件时,ADC会优先进行相电流采样,完成后再进行相电压采样
  相电流采样的触发源配置为驱动SVPWM所用的定时器的通道4,因此ADC采样频率为SVPWM的频率,一般为10kHz到20kHz之间,即adc中断频率

图1 UVW三相电流与电角度关系

  如图1所示,在电角度从’π/3’到’π’的过程中,即扇区2和扇区3,U相和W相电流变化幅度最大且单调,幅度为3/2。同理,扇区4和扇区5,U相和V相电流变化幅度最大且单调,扇区6和扇区1,V相和W相电流变化幅度最大且单调。看下面ADC通道配置,处于扇区1时ADC1检测V相电流,ADC2检测W相电流,正如我们预料的那样,这就是这两个ADC配置数组的由来。

  .ADCConfig1 = {   MC_ADC_CHANNEL_4<<ADC_JSQR_JSQ4_Pos    //V
                   ,MC_ADC_CHANNEL_5<<ADC_JSQR_JSQ4_Pos    //U
                   ,MC_ADC_CHANNEL_5<<ADC_JSQR_JSQ4_Pos    //U
                   ,MC_ADC_CHANNEL_5<<ADC_JSQR_JSQ4_Pos    //U
                   ,MC_ADC_CHANNEL_5<<ADC_JSQR_JSQ4_Pos    //U
                   ,MC_ADC_CHANNEL_4<<ADC_JSQR_JSQ4_Pos    //V
                  },

  .ADCConfig2 = {   MC_ADC_CHANNEL_3<<ADC_JSQR_JSQ4_Pos    //W
                   ,MC_ADC_CHANNEL_3<<ADC_JSQR_JSQ4_Pos    //W
                   ,MC_ADC_CHANNEL_3<<ADC_JSQR_JSQ4_Pos    //W
                   ,MC_ADC_CHANNEL_4<<ADC_JSQR_JSQ4_Pos    //V
                   ,MC_ADC_CHANNEL_4<<ADC_JSQR_JSQ4_Pos    //V
                   ,MC_ADC_CHANNEL_3<<ADC_JSQR_JSQ4_Pos    //W
                  },

2、电流采样代码

void R3_2_GetPhaseCurrents( PWMC_Handle_t * pHdl, ab_t* pStator_Currents )

  函数入参是两个指针,用于接收返回值:pHdl是FOC库中的一个结构体,存储了SVPWM占空比和采样电流等参数;pStator_Currents是存储了UV两相电流的结构体,用于后续FOC的计算

LL_TIM_CC_DisableChannel(TIMx, LL_TIM_CHANNEL_CH4);

  禁用控制SVPWM定时器的4通道,防止再次触发ADC采样中断,防止中断嵌套的产生

bSector = pHandle->_Super.Sector;
hReg1 = *pHandle->pParams_str->ADCDataReg1[bSector] * 2;
hReg2 = *pHandle->pParams_str->ADCDataReg2[bSector] * 2;
等同于
hReg1 = ADC1->JDR1 * 2;
hReg2 = ADC2->JDR1 * 2;

  此处代码写的有些多余,目的就是将ADC1和ADC2的注入通道1的采样值读取出来。
  此处多说一些,代码中将ADC配置为左对齐模式。规则通道的有效位数是16位,因此ADC采样值范围是0-65535;而注入通道的有效位数是15位,最高位作为符号位存在,乘以2是将采样值范围扩大到0-65535,方便归一化计算。可以在ADC_JOFRx寄存器中配置ADC注入通道的偏移量,以此来改变JDRx寄存器的符号值

switch(bSector)
{
    case SECTOR_6:
    case SECTOR_1:
      /* A相电流不可用     */
      /* B相电流 = B相零漂 - ADC采样值 */
      wAux = ( int32_t )( pHandle->PhaseBOffset ) - ( int32_t )( hReg1 );
      /* B相电流异常判断 */
      if ( wAux < -INT16_MAX )
      {
        pStator_Currents->b = -INT16_MAX;
      }
      else  if ( wAux > INT16_MAX )
      {
        pStator_Currents->b = INT16_MAX;
      }
      else
      {
        pStator_Currents->b = ( int16_t )wAux;
      }

      /* C相电流 = C相零漂 - ADC采样值 */
      /* A相电流 = -C相电流 -B相电流 */
      wAux = ( int32_t )( pHandle->PhaseCOffset ) - ( int32_t )( hReg2 );
      wAux = -wAux - ( int32_t )pStator_Currents->b;

      /* A相电流异常判断 */
      if ( wAux > INT16_MAX )
      {
        pStator_Currents->a = INT16_MAX;
      }
      else  if ( wAux < -INT16_MAX )
      {
        pStator_Currents->a = -INT16_MAX;
      }
      else
      {
        pStator_Currents->a = ( int16_t )wAux;
      }
      break;
}

  从上一节中我们得知,在扇区6和扇区1内,检测的是V相和W相的电流,因此会有pHandle->PhaseBOffset - hReg1得到V相电流,会有pHandle->PhaseCOffset - hReg2得到W相电流,因为三相电流标量和等于零,所以再通过-wAux - pStator_Currents->b得到U相电流,如此便得到AB(UV)两相的电流值

3、电流采样的触发

  电流采样的触发是R3_2_TIMx_UP_IRQHandler,该函数在SVPWM的每个周期都会调用一次,以下是该函数的实现

void * R3_2_TIMx_UP_IRQHandler( PWMC_R3_2_Handle_t * pHandle)
{
  TIM_TypeDef* TIMx = pHandle->pParams_str->TIMx;
  ADC_TypeDef * ADCx_1 = pHandle->pParams_str->ADCx_1;
  ADC_TypeDef * ADCx_2 = pHandle->pParams_str->ADCx_2;
  uint32_t ADCInjFlags;
  
  /* ADC状态检测 LL_ADC_FLAG_JSTRT为通道开始标志 LL_ADC_FLAG_JEOS为通道转换结束标志*/
  ADCInjFlags = ADCx_1->SR & (LL_ADC_FLAG_JSTRT|LL_ADC_FLAG_JEOS);
  if ( ADCInjFlags == LL_ADC_FLAG_JSTRT )
  {
    /* ADC转换已开始,但未完成*/
    do
    {
      /* 等待转换结束  */
      ADCInjFlags = ADCx_1->SR & (LL_ADC_FLAG_JSTRT|LL_ADC_FLAG_JEOS);
    } 
    while ( ADCInjFlags != (LL_ADC_FLAG_JSTRT|LL_ADC_FLAG_JEOS) );
  }
  else if ( ADCInjFlags == 0 )
  {
    /* ADC转换未开始*/
    while ( ( TIMx->CNT ) < ( pHandle->pParams_str->Tw ) )
    {
      /* 等待一段时间,我配置的是3us */
    }
    
    ADCInjFlags = ADCx_1->SR & (LL_ADC_FLAG_JSTRT|LL_ADC_FLAG_JEOS);
    if ( ADCInjFlags == LL_ADC_FLAG_JSTRT )
    {
      /* ADC转换已开始,但未完成 */
      do
      {
        /* 等待转换结束 */
        ADCInjFlags = ADCx_1->SR & (LL_ADC_FLAG_JSTRT|LL_ADC_FLAG_JEOS);
      }
      while ( ADCInjFlags != (LL_ADC_FLAG_JSTRT|LL_ADC_FLAG_JEOS) );
    }
  }
  else
  {
    /* ADC转换已结束 */
  }
  
  /* 关闭ADC1和ADC2转换 */
  LL_ADC_INJ_StopConversionExtTrig(ADCx_1);
  LL_ADC_INJ_StopConversionExtTrig(ADCx_2);

  /* 根据扇区设置下一个电流转换通道  */
  ADCx_1->JSQR = pHandle->pParams_str->ADCConfig1[pHandle->_Super.Sector];
  ADCx_2->JSQR = pHandle->pParams_str->ADCConfig2[pHandle->_Super.Sector];

  /* 设置ADC1和ADC2触发源为内部 */
  LL_ADC_INJ_SetTriggerSource(ADCx_1,pHandle->ADC_ExternalTriggerInjected);
  LL_ADC_INJ_SetTriggerSource(ADCx_2,pHandle->ADC_ExternalTriggerInjected);
    
  /* 使能ADC触发源 */
  LL_TIM_CC_EnableChannel(TIMx, LL_TIM_CHANNEL_CH4);
  /* 开始转换 */
  LL_ADC_INJ_StartConversionExtTrig(ADCx_1, pHandle->ADCTriggerEdge);
  LL_ADC_INJ_StartConversionExtTrig(ADCx_2, pHandle->ADCTriggerEdge);

  return &( pHandle->_Super.Motor );
}

三、结语

  调试过程中遇到过有些电机线序倒相的情况,需要根据电机改动ADCConfig1和ADCConfig2两个数组,这个地方让我困惑了很久,如今问题解决了,拿出来分享一下
  电流采样还有UVW三相的零漂值没有采样没有说,放到后面的状态机部分一块说

  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值