赞
踩
本文讲解MCSDK中FOC控制的预备步骤——电流采样,电流采样的实现函数是R3_2_GetPhaseCurrents。MCSDK中的电流采样功能和上一篇文章SVPWM一样,也是分扇区来进行电流采样的,电流采样涉及到R3_2_ParamsM1.ADCConfig1和R3_2_ParamsM1.ADCConfig2两个数组的配置,因此电流采样部分也值得一说
UVW和ABC都是表示电机的三个相序,只是说法不同
在FOC控制中,要求在同一时刻同时获得UVW三相的电流,最为理想的情况是一个引脚能同时复用成ADC1,ADC2和ADC3的通道,但这样的引脚并不多,而且有些引脚会复用成其他功能,所以该方法不可行。从上一章文章我们得知,三相电流的标量和为零,因此我们只需要在同一时刻读取到其中两相电流即可,基本上一个引脚可以同时复用成ADC1和ADC2,该方法可行
这里相电流采样时,ADC要配置成注入转换模式,注入转换优先级比规则转化优先级高。比如,当ADC同时满足母线电压采样条件和相电流采样条件时,ADC会优先进行相电流采样,完成后再进行相电压采样
相电流采样的触发源配置为驱动SVPWM所用的定时器的通道4,因此ADC采样频率为SVPWM的频率,一般为10kHz到20kHz之间,即adc中断频率
如图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
},
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)两相的电流值
电流采样的触发是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三相的零漂值没有采样没有说,放到后面的状态机部分一块说
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。