1、开发板原理图介绍
1.1 最小系统电路
MM32SPIN060G 系列内部集成 MM32SPIN0230 系列芯片。芯片内置100V-6NMOS驱动、5.0V LDO(50mA)、一路ADC(12位)、两路模拟比较器、两路运放等。芯片内置四线烧录接口,即支持使用VIN/FG/PWM/GND下载、调试程序。
开发板输入电压范围为5.5V~18V,三相全桥nmos参数为60V/40A,MCU 使用 5V 供电;支持无霍尔,方波/弦波驱动;支持 1 / 2 / 3 Shunt R 三相电流采样;支持 BEMF 电压回授;支持 DC Bus 电压,总电流测量;使用 MCU 内置模拟比较器作为过电流保护。


1.2 电压采集电路
包含电机调速电位器的电压采集电路、母线电压的采集、电机的过流保护电路。采样电压值通过RC低通滤波器输入到单片机的引脚。

1.3 电流采样电路
芯片内部集成两路运算放大器/比较器,只需在芯片外部添加电阻即可实现放大、比较器的功能。
通过跳线帽的选择,可分别测量UV相电流、Hall或BEMF信号、母线电流。
U、V相电流、母线电流的放大倍数为5倍,偏置电压为2.5V。

1.4 三相全桥电路
芯片内部自带NMOS的驱动芯片,三相全桥外围电路可以简化,需要额外增加高侧MOS的自举电容,高低侧NMOS的驱动电阻,提供泄放通路的反接二极管。
U、V、母线电流采样电阻参数为50mΩ-1%-2W。

1.5 Hall和反电动势BEMF检测电路
Hall传感器信号通过电阻上拉到5V,然后连接到单片机的引脚上,通过输入捕获模式进行计数。
三相电机的反电动势通过电阻分压连接到单片机ADC引脚上,虚拟中点电压为BemfO。

2、无感FOC双电阻程序介绍
2.1 外设初始化:
pwm初始化:定时器计数模式为中心对齐模式2,配置死区时间、刹车控制,配置PWM模式为模式2,触发源输出,用于触发adc转换。
void Drv_Pwm_Init(TIM_TypeDef * pTim, uint16_t u16Period,uint16_t u16DeadTime)
{
/** Define the struct of the PWM configuration */
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStruct;
TIM_OCInitTypeDef TIM_OCInitStructure;
/** Enable the TIM1 clock */
RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM1_Msk, ENABLE);
/**
* Sets the value of the automatic reload register Period for the next update event load activity
* Set the Prescaler value used as the divisor of the TIMx clock frequency
* Set clock split :TDTS = TIM_CKD_DIV1
* TIM center aligned mode1
*/
TIM_TimeBaseStructure.TIM_Period = u16Period;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned2;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(pTim, &TIM_TimeBaseStructure);
/**
* Enable state selection in running mode
* Enable state selection in idle mode
* Software error lock configuration: lock closed without protection
* DTG[7:0] dead zone generator configuration (dead zone time DT)
*/
/**
* TDTS = 125nS(8MHz)
* DTG[7: 5] = 0xx => DT = DTG[7: 0] * Tdtg, Tdtg = TDTS;
* DTG[7: 5] = 10x => DT =(64+DTG[5: 0]) * Tdtg, Tdtg = 2 * TDTS;
* DTG[7: 5] = 110 => DT =(32+DTG[4: 0]) * Tdtg, Tdtg = 8 * TDTS;
* DTG[7: 5] = 111=> DT =(32 + DTG[4: 0]) * Tdtg, Tdtg = 16 * TDTS;
*/
TIM_BDTRInitStruct.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStruct.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStruct.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
TIM_BDTRInitStruct.TIM_DeadTime = u16DeadTime;
/**
* Brake configuration: enable brake
* Brake input polarity: active in low level
* Auto output enable configuration: Disable MOE bit hardware control
*/
TIM_BDTRInitStruct.TIM_Break = TIM_Break_Enable;
TIM_BDTRInitStruct.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStruct.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable;
TIM_BDTRConfig(pTim, &TIM_BDTRInitStruct);
TIM_BreakInputFilterConfig(pTim,TIM_COMPBKIN_COMP2,TIM_BKINF_2);
TIM_BreakInputFilterCmd(pTim, ENABLE);
/**
* Mode configuration: PWM mode 1
* Output status setting: enabl0Ce output
* Complementary channel output status setting: enable output
* Sets the pulse value to be loaded into the capture comparison register
* Output polarity is high
* N Output polarity is high
*/
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
TIM_OC1Init(pTim, &TIM_OCInitStructure);
TIM_OC2Init(pTim, &TIM_OCInitStructure);
TIM_OC3Init(pTim, &TIM_OCInitStructure);
/** Initialize the CCR4 trigger point */
TIM_OCInitStructure.TIM_Pulse = 10;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OC4Init(pTim, &TIM_OCInitStructure);
/** Enable CH1, 2, and 3 to be preloaded */
TIM_OC1PreloadConfig(pTim, TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(pTim, TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(pTim, TIM_OCPreload_Enable);
/** Enable TIMx's preloaded register on ARR */
TIM_ARRPreloadConfig(pTim, ENABLE);
/** Enable the TIM1 */
TIM_Cmd(pTim, ENABLE);
/** Main Output Enable:Disable the MOE bit */
TIM_CtrlPWMOutputs(pTim, DISABLE);
}adc初始化:配置adc转换顺序(U相、V相采样电阻电压,直流母线电压、调速电位器电压),设置ADC1为外部触发源(T1_CC4)。
void Board_ADC_Init(void)
{
/*ADC RANK Array*/
ADC_Channel_TypeDef sUserAdc1Channel[4];
/* Configure the ADC RANK Sequence*/
sUserAdc1Channel[0].u8Rank = IR_U_RANK;
sUserAdc1Channel[0].sAdcChannel = IR_U_CHANNEL;
sUserAdc1Channel[0].pNext = &sUserAdc1Channel[1];
sUserAdc1Channel[1].u8Rank = IR_V_RANK;
sUserAdc1Channel[1].sAdcChannel = IR_V_CHANNEL;
sUserAdc1Channel[1].pNext = &sUserAdc1Channel[2];
sUserAdc1Channel[2].u8Rank = VBUS_RANK;
sUserAdc1Channel[2].sAdcChannel = VBUS_CHANNEL;
sUserAdc1Channel[2].pNext = &sUserAdc1Channel[3];
sUserAdc1Channel[3].u8Rank = VR_RANK;
sUserAdc1Channel[3].sAdcChannel = VR_CHANNEL;
sUserAdc1Channel[3].pNext = NULL;
/* Select the ADC external trigger source of the ADC is T1_CC4*/
Drv_Adc_Basic_Init(ADC1, ADC_ExtTrig_T1_CC4);
/* Select the ADC sample time*/
Drv_Adc_Channel_Init(ADC1, sUserAdc1Channel, ADC_SampleTime_2_5);
}2.2 主函数
/* Infinite loop */
while(1)
{
/*IWDG_ReloadCounter*/
IWDG_RELOAD_COUNT();
if(Motor_1st.USER.bSlowLoopFlag)
{
/* Slow Loop Statemachine */
s_STATE_SLOW[eM1_MainState]();
Motor_1st.USER.bSlowLoopFlag = 0;
}
}2.3 adc断服务函数-16kHz
读取adc转换离散电压值:在前 128 次采样中,累加 Ia 和 Ib 电流的 ADC 值;当达到第 128 次采样时,计算 Ia 和 Ib 的平均值,并将其存储为偏移值;在完成偏移值计算后,调用 Get_ADC_Result 函数获取当前的 ADC 结果,并进行处理,调用快速循环状态机函数,根据当前的主状态执行相应的操作。
void ADC_IRQHandler(void)
{
static uint8_t u8ADCTimeCnt = 0;
static uint32_t u32IaSum = 0;
static uint32_t u32IbSum = 0;
static uint16_t u16Cnt = 0;
if( ADC_GetITStatus(ADC1, ADC_IT_EOS))
{
/* Calculae the offset value of current*/
if(u16Cnt <= 127)
{
u16Cnt++;
u32IaSum += (int16_t)GET_ADC_VALUE(IR_U_CHANNEL);
u32IbSum += (int16_t)GET_ADC_VALUE(IR_V_CHANNEL);
}
else if(u16Cnt == 128)
{
u16Cnt++;
u32IaSum = u32IaSum>>7;
u32IbSum = u32IbSum>>7;
Motor_1st.FOC.sIabc_offset.s16A = u32IaSum << 3;
Motor_1st.FOC.sIabc_offset.s16B = u32IbSum << 3;
u32IaSum = 0;
u32IbSum = 0;
}
else
{
Get_ADC_Result(&Motor_1st);
/* Fast Loop Statemachine */
s_STATE_FAST[eM1_MainState]();
if(++u8ADCTimeCnt >= Motor_1st.USER.u16SlowLoopDiv) //For slow loop state machine
{
u8ADCTimeCnt = 0;
Motor_1st.USER.bSlowLoopFlag = 1;
}
}
ADC_ClearITPendingBit(ADC1, ADC_IT_EOS);
}
}2.4 矢量控制
该函数实现了电机磁场定向控制(FOC)的电流控制环路:
使用 MLIB_SinCos 函数计算指令转子角度(s16CmdTheta)的正弦和余弦值。
Park 变换将定子电流从静止坐标系(α-β)转换到旋转坐标系(d-q)。
根据占空比限制和平均母线电压计算最大电压限制(s16VMAX)。
计算 d 轴电流的指令值与测量值之间的误差。使用 PI 控制器(MLIB_PIControl_Q15)生成 d 轴电压指令(sVdq.s16D)。根据剩余电压(扣除 d 轴电压后的电压)计算 q 轴的最大电压限制。确保总电压矢量不超过可用母线电压。
计算 q 轴电流的指令值与测量值之间的误差。使用 PI 控制器(MLIB_PIControl_Q15)生成 q 轴电压指令(sVdq.s16Q)。
更新正弦/余弦值,用于 Inverse Park 变换。Inverse Park 变换将 d-q 轴电压转换回静止坐标系(α-β)。
调用 DcBus_Ripple_suppress 函数补偿母线电压的纹波,确保电压指令的准确性。
void FOC_Current_Controller(Motor_TypeDef *Motor)
{
int32_t s32Temp;
int16_t s16Temp;
/* Calculate the sincos of s16CmdTheta */
Motor->FOC.sAngle_cmd = MLIB_SinCos(Motor->FOC.s16CmdTheta);
/* PARK transform */
MCFLIB_Park_S16(&Motor->FOC.sIAlBe, &Motor->FOC.sAngle_cmd, &Motor->FOC.sIdq);
/* Calculate the PI saturation for Vd */
s16Temp = MLIB_Mul_Q15(Motor->USER.s16DutyLimitCmd, Motor->FOC.s16VbusAvg);
Motor->FOC.s16VMAX = MLIB_Mul_Q15(s16Temp, Q15(0.6667)); //Amplitude invariant transformation
Motor->USER.sId_PI.s16UpperLim = Motor->FOC.s16VMAX;
Motor->USER.sId_PI.s16LowerLim = MLIB_Neg_Q15(Motor->USER.sId_PI.s16UpperLim);
/* PI regulator for Vd*/
Motor->FOC.sIdq_err.s16D = Motor->FOC.sIdq_cmd.s16D - Motor->FOC.sIdq.s16D;
Motor->FOC.sVdq.s16D = MLIB_PIControl_Q15(Motor->FOC.sIdq_err.s16D, &Motor->USER.sId_PI);
/* Calculate the PI saturation for Vq */
s32Temp = Motor->FOC.s16VMAX * Motor->FOC.s16VMAX - Motor->FOC.sVdq.s16D * Motor->FOC.sVdq.s16D;
Motor->USER.sIq_PI.s16UpperLim = Sqrt_Q31(s32Temp);
Motor->USER.sIq_PI.s16LowerLim = MLIB_Neg_Q15(Motor->USER.sIq_PI.s16UpperLim);
/* PI regulator for Vq*/
Motor->FOC.sIdq_err.s16Q = Motor->FOC.sIdq_cmd.s16Q - Motor->FOC.sIdq.s16Q;
Motor->FOC.sVdq.s16Q = MLIB_PIControl_Q15(Motor->FOC.sIdq_err.s16Q, &Motor->USER.sIq_PI);
/* Update the sincos for invpark */
Motor->FOC.sAngle_cmd_update = Motor->FOC.sAngle_cmd;
/* Inv PARK transform*/
MCFLIB_InvPark_S16(&Motor->FOC.sVdq, &Motor->FOC.sAngle_cmd_update, &Motor->FOC.sVAlBe);
/* Elimination of the DC-bus voltage ripple */
DcBus_Ripple_suppress(&Motor->FOC.sVAlBe, Motor->FOC.s16VbusAvg, &Motor->FOC.sVAlBe_cmd);
}
