电机开发板MM32SPIN060G测评

分享作者:njust123
评测品牌:灵动微电子
评测型号:MM32MDK-SPIN060G
发布时间:2025-02-05 10:39:28
0
概要
MM32SPIN060G 是灵动 MindSPIN 旗下高性能的单电机控制 SOC 产品,采用 Cortex-M0 内核,集成了运动控制所需的专用模拟外设,包括 12 位高精度 ADC、2 路模拟比较器 COMP、2 路运算放大器 OPAMP、三相 N 沟道栅极驱动器,另配备有 MC-TIM、硬件除法器 HW-Div、DMA 控制器等专用资源,以及 32KB Flash、4KB SRAM、11 个 GPIO 等基础资源。
开源口碑分享内容

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);

}

3、实物演示



全部评论
暂无评论
0/144