【嵌入式电机控制#17】电流环(四):电流闭环控制
一、电流闭环控制的算法框图
前面介绍了很多电流相关的代码,内容比较乱,但其实是循序渐进的,这里整理出电流闭环控制所需的所有算法以及对应流程,帮助大家加深印象。
常见电机电流控制系统以上流程调节是不会出现明显问题的。但不代表这个系统设计能有效调节所有电机系统,我这里只给了一个方法论,在大型产品研发过程中,控制算法工程师常会利用自己的控制理论、嵌入式、数学等知识设计新的算法。所以我们需要具体问题具体分析。
注:过采样处理的依据是香农定理和奈奎斯特采样定理,在第一平均时进行刻意的漏除,在实际值计算时将倍数补偿回来。(类似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^2AverSum += 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;elseADC_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);
}