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