【嵌入式电机控制#17】电流环(四):电流闭环控制

发布于:2025-07-30 ⋅ 阅读:(15) ⋅ 点赞:(0)

一、电流闭环控制的算法框图

        前面介绍了很多电流相关的代码,内容比较乱,但其实是循序渐进的,这里整理出电流闭环控制所需的所有算法以及对应流程,帮助大家加深印象。

        

        常见电机电流控制系统以上流程调节是不会出现明显问题的。但不代表这个系统设计能有效调节所有电机系统,我这里只给了一个方法论,在大型产品研发过程中,控制算法工程师常会利用自己的控制理论、嵌入式、数学等知识设计新的算法。所以我们需要具体问题具体分析。

        注:过采样处理的依据是香农定理和奈奎斯特采样定理,在第一平均时进行刻意的漏除,在实际值计算时将倍数补偿回来。(类似3D游戏的DLSS)

二、关键环节驱动解析 

        接下来进行关键代码解析,默认硬件层过压过流保护已经生效,驱动层不再处理。

        1. DAC的DMA中断处理函数

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)  
{
  uint16_t ConvCnt = 0;
  int32_t ADConv = 0 ; 
    // ADC进入次数初始化(动态窗口样本量),//本次ADC结果初始化
  HAL_ADC_Stop_DMA(hadc);
  //停掉DMA,即使丢掉部分数据,但也不影响时序
  
  for(ConvCnt = 0; ConvCnt < ADC_BUFFER; ConvCnt++)
  {
    ADConv += ((int32_t)ADC_ConvValueHex[ConvCnt]);
    //从DMA寄存器中依次读出数据
  }

  ADConv >>= ADC_Base;
    //进行首次均值,注意刻意漏移2位,也就是漏除2^2
  AverSum += ADConv;
    //本次ADC结果累加进动态窗口的数据总和
  AverCnt++;
    //动态窗口样本量,注意这个量是动态使用的,没有固定值,使用一次即销毁
  
  HAL_ADC_Start_DMA(hadc,(uint32_t*)ADC_ConvValueHex,ADC_BUFFER);
    //重新开启DMA接收
}

     2. 任意定时器的中断回调

        注意初始化时频率必须比ADC采样频率高,并且最好把优先级设置低于ADC中断,不然ADC还没采集完就一直被定时器中断打断

void HAL_SYSTICK_Callback(void)
{
  __IO int32_t ADC_Resul= 0;
  __IO float Volt_Result = 0;
  __IO float ADC_CurrentValue;
  // 不用volitile变量其实也可以,如果你用纯寄存器写代码,那还是建议加上吧
  if((uwTick % 50) == 0)
    //这里用了个寄存器与HAL库混合写法,意思就是50ms中断一次
  {
    if(AverCnt < 0)
      ADC_Resul  = 0;
    else
      ADC_Resul = AverSum/AverCnt ;
  //注意清除ADC异常值,如果出现负值一定是出错了,如果带进我们的动态偏差里,后果比较严重
    OffsetCnt_Flag++;
    //状态机标志位++,进行状态切换
    if(OffsetCnt_Flag >= 16)
    {
    //如果大于等于16进入一个处理判断
      if(OffsetCnt_Flag == 16)
      {
        //其中如果等于16则取它为动态偏差
        //这个16是我们不断结合万用表测试获得的,表示采样电路稳定工作前一时刻的采样值,换了硬件后需要重新确定
        OffSetHex = ADC_Resul;
      }
        //取了动态偏差后,给标志位赋32,以严格区分它和未取偏差时的状态
      OffsetCnt_Flag = 32;
        //减去偏差值获取优化值
      ADC_Resul -= OffSetHex;//¼õȥƫ²îÖµ
    }
    //换算电流采样电阻两头的电压,在换算电流,注意把过采样处理补偿回去
    Volt_Result = ( (float)( (float)(ADC_Resul) * VOLT_RESOLUTION) );
    ADC_CurrentValue = (float)( (Volt_Result / GAIN) / SAMPLING_RES);
    //这个ADC电流值就是我们真正的反馈信号
    if(Volt_Result<0)
      Volt_Result = 0;

    //动态窗口参数清零
    AverCnt = 0;
    AverSum = 0;
    
    //如果PID标志位使能则进行PID运算
    if(start_flag == 1)
    {
      PWM_Duty = CurPIDCalc( (int32_t)ADC_CurrentValue);
      //PID获取占空比
      if(PWM_Duty >= BDCMOTOR_DUTY_FULL)
        PWM_Duty = BDCMOTOR_DUTY_FULL;
        //进行PID输出限幅,避免输出错误值
      if(PWM_Duty <=0)
          PWM_Duty = 0;
        //异常值处理
      __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,PWM_Duty);
        //PWM输出
    }
    
#ifdef USE_PRINTF         
      printf("Volt: %.1f mV -- Curr: %d mA\n",Volt_Result,(int32_t)ADC_CurrentValue);
#else 
      CaptureNumber = (int32_t)ADC_CurrentValue;
      Transmit_FB(&CaptureNumber);
#endif
  }
}

         3. 位置PID

        不详细解释了,最好再做个积分限幅

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)  
{
  uint16_t ConvCnt = 0;
  int32_t ADConv = 0 ; 
  /* ADC²É¼¯Ì«¿ì,ÐèÒªÏÈÍ£Ö¹ÔÙ´¦ÀíÊý¾Ý */
  HAL_ADC_Stop_DMA(hadc);
  
  
  /* ȡƽ¾ù */
  for(ConvCnt = 0; ConvCnt < ADC_BUFFER; ConvCnt++)
  {
    ADConv += ((int32_t)ADC_ConvValueHex[ConvCnt]);
  }
  /* ¼ÆËãÆ½¾ùÖµ,²ÉÑùÊý¾ÝÉèÖÃΪ2µÄÕûÊý±¶,»ñµÃ14bitsADCÖµ*/
  ADConv >>= ADC_Base;
  /* ÀÛ¼Ó²ÉÑù½á¹û²¢¼Ç¼²ÉÑù´ÎÊý*/
  AverSum += ADConv;
  AverCnt++;
 
  
  
  HAL_ADC_Start_DMA(hadc,(uint32_t*)ADC_ConvValueHex,ADC_BUFFER);
}


网站公告

今日签到

点亮在社区的每一天
去签到