前段时间在芯查查商城上面申请的APM32F035开发板,当初为了学习直流无刷电机的控制方式,也很幸运可以申请到该块开发板。也是苦于忙其他的项目,抽空在的时候在淘宝上面买了一个DC 24V的直流无刷电机,今天就和大家分享一下心得。
1:整体硬件设计
由于我购买的直流无刷电机是DC24V,所以电源输入电压我配置的是DC24V 2A的电源输入, 输入电压并经过U2LM5164转换为 12V,为Gate driver IC提供稳定的电压输入;5V、经过U4SPX3819 3.3V 电压输出给到MCU微处理器,硬件上的电平转换如下:
功率开关管则直接使用 24V 电源。同时,该方案采用可变电阻旋钮调节 0~3.3V 的电压输入作为速度命令的输入端, 以此调节电机转速。
使用可直接通过转动可变电阻旋钮以此调节输入电压,同时当输入电压值超过起动阈值时, 电机将会启动运行, 而当电压值低于阈值时电机将会关闭运行。
当电机启动后, APM32F035 处理器通过内置的运算放大器并经由相应的采样电路可获取三相的相电流 Iu、 Iv 与 Iw, 并将该数据经过坐标轴的转换后进而控制电机的力矩电流大小及相位;通过FOC 控制计算环节后调节 TMR1 外设输出相应的三路互补的 PWM 波控制逆变器的开关元器件。
2:相电流采样电路
这里设计的最大可以到达16.5A的电流,完全可以满足需要。
3:霍尔检测电路:
当时调试的时候这个地方检测也是研究了一段时间,才把这个电路研究明白。
4:电机实物图片
二:软件调试部分:
我这里使用的的是官方提供的DEMO,APM32F035_Sensorless_BLDC,整体的代码架构分为用户层,外设驱动层,电机控制器驱动层,以及电机算法层。
1:部分代码如下所示:
#define _USER_FUNCTION_C_
/** Files includes */
#include "user_function.h"
#include "parameter.h"
#include "BLDC_SensorLess.h"
void spd_pi_init(pi_para_t *pstc)
{
pstc->s16_Error = 0;
pstc->s32_Pterm = 0;
pstc->s32_Iterm = 0;
pstc->s32_ItermPre = 0;
pstc->s32_OutTemp = 0;
pstc->s32_Out = 0;
pstc->s16q10_Kp = M1_SPEED_KP_Q10;
pstc->s16q10_Ki = M1_SPEED_KI_Q10;
pstc->s32_Umax = 8000;
pstc->s32_Umin = 0;
}
void Motor_stcRampInit(void)
{
Motor_stcRampCal.u16Freq = 1;
Motor_stcRampCal.i16Gain = 40u;//ui_u16RampGain;
Motor_stcRampCal.i16Offset =20u;// ui_u16AlignPwmDuty;
Motor_stcRampCal.i16Out = 460u;//ui_u16AlignPwmDuty;
Motor_stcRampCal.i16Angle = 0;
Motor_stcRampCal.i16OutMax = 460u;//ui_u16RampDutyMax;
Motor_stcRampCal.bRampFinFlag = FALSE;
}
/**
* @brief Initializes the Motor control parameters
* @retval None
*/
void Init_Parameter(Motor_TypeDef *Motor)
{
/* PWM setting */
Motor->USER.u16PWMFreq = PWMFREQ; /* PWM Frequence*/
Motor->USER.u16DeadTime = SYSCLK_HSE_72MHz / 1000000 * DEAD_TIME; /* Dead time */
Motor->USER.s16RampUp = Q15(RAMP_UP / SLOWLOOP_FREQ); //1000 rmp/s
Motor->USER.s16RampDown = Q15(RAMP_DOWN / SLOWLOOP_FREQ); //1000 rmp/s
/* Protection */
Motor->USER.s16OVPCmd = Q15(DCBUS_OVER / UDC_MAX);
Motor->USER.s16UVPCmd = Q15(DCBUS_UNDER / UDC_MAX);
Motor->USER.s16OIPCmd = IBUS_OVER_CMD;
Motor->USER.u16FaultReleaseTimeCmd = FAULTRELEASE_TIME * SLOWLOOP_FREQ;
Motor->USER.u16PWMPeriod = SYSCLK_HSE_72MHz / Motor->USER.u16PWMFreq;
Motor->USER.u16SlowLoopDiv = PWMFREQ / SLOWLOOP_FREQ;
Motor->USER.u16AlignTimeCmd = ALIGN_TIME;
Motor->USER.u16StartupTimeCmd = STARTUP_TIME;
Motor->BLDC.u32StartUp_PwmValue = 460;
Motor->BLDC.u8SpeedReachedFlag = FALSE;
Motor->BLDC.u32PwmValue = STARTUP_PWMVALUE1; //Motor startup PWM value
Motor->BLDC.u8Direction = ECCW; //Motor rotation direction
Motor->BLDC.u32ForceCommutationThreshold = FORCED_COMMUTATION_TIME; //Motor's Forced commutation time
Motor->USER.u16ForceCommutationCnt = 133;//此为 40000 / 300 rpm = 133 cnt
Motor->BLDC.u16Startup_CommuteTime = 133;
//Zeroing back electromotive force zero-crossing voltage
MovingAvgInit(&u16ZcdBemfU);
MovingAvgInit(&u16ZcdBemfV);
MovingAvgInit(&u16ZcdBemfW);
spd_pi_init(&Motor->stc_SpdPi);
/*Initialize feedback speed filter array*/
//MovingAvgInit(&SpeedFdk);
InitNormalization(300,3796,3000,&Motor->USER.RP);
/*Initialize acceleration curve parameters*/
LoopCmp_Init(&Motor->BLDC.RPValue);
}
/**
* @brief Reset the Motor control parameters
* @retval None
*/
void variable_reset(Motor_TypeDef *Motor)
{
Motor_1st.USER.u16AlignTimeCnt = 0; /* Motor Align time cnt */
Motor_1st.USER.u16RampCnt = 0; /* Motor ramp time cnt */
Motor_1st.BLDC.u8Phase = 0; /* Motor phase sequence */
Motor_1st.BLDC.u8PhaseShadow = 0; /* Motor phase sequence Shadow */
Motor_1st.BLDC.u8flag_zcd = 0; /* BMEF crossing-zero flag */
Motor_1st.BLDC.u32ForceCommutationCnt = 0; /* Motor's Forced commutation time cnt */
Motor_1st.BLDC.u32PwmValue = 0; /* PWM Output value */
Motor_1st.BLDC.u32CntElectricalPeriodNumber = 0; /* Record the number of electrical cycles */
Motor_1st.BLDC.u16Startup_CommuteTime = 133;
Motor_1st.BLDC.u8_StepCnt = 0;
spd_pi_init(&Motor->stc_SpdPi);
Motor_stcRampInit();
}
#define _USER_FUNCTION_C_
/** Files includes */
#include "user_function.h"
#include "parameter.h"
#include "BLDC_SensorLess.h"
void spd_pi_init(pi_para_t *pstc)
{
pstc->s16_Error = 0;
pstc->s32_Pterm = 0;
pstc->s32_Iterm = 0;
pstc->s32_ItermPre = 0;
pstc->s32_OutTemp = 0;
pstc->s32_Out = 0;
pstc->s16q10_Kp = M1_SPEED_KP_Q10;
pstc->s16q10_Ki = M1_SPEED_KI_Q10;
pstc->s32_Umax = 8000;
pstc->s32_Umin = 0;
}
void Motor_stcRampInit(void)
{
Motor_stcRampCal.u16Freq = 1;
Motor_stcRampCal.i16Gain = 40u;//ui_u16RampGain;
Motor_stcRampCal.i16Offset =20u;// ui_u16AlignPwmDuty;
Motor_stcRampCal.i16Out = 460u;//ui_u16AlignPwmDuty;
Motor_stcRampCal.i16Angle = 0;
Motor_stcRampCal.i16OutMax = 460u;//ui_u16RampDutyMax;
Motor_stcRampCal.bRampFinFlag = FALSE;
}
/**
* @brief Initializes the Motor control parameters
* @retval None
*/
void Init_Parameter(Motor_TypeDef *Motor)
{
/* PWM setting */
Motor->USER.u16PWMFreq = PWMFREQ; /* PWM Frequence*/
Motor->USER.u16DeadTime = SYSCLK_HSE_72MHz / 1000000 * DEAD_TIME; /* Dead time */
Motor->USER.s16RampUp = Q15(RAMP_UP / SLOWLOOP_FREQ); //1000 rmp/s
Motor->USER.s16RampDown = Q15(RAMP_DOWN / SLOWLOOP_FREQ); //1000 rmp/s
/* Protection */
Motor->USER.s16OVPCmd = Q15(DCBUS_OVER / UDC_MAX);
Motor->USER.s16UVPCmd = Q15(DCBUS_UNDER / UDC_MAX);
Motor->USER.s16OIPCmd = IBUS_OVER_CMD;
Motor->USER.u16FaultReleaseTimeCmd = FAULTRELEASE_TIME * SLOWLOOP_FREQ;
Motor->USER.u16PWMPeriod = SYSCLK_HSE_72MHz / Motor->USER.u16PWMFreq;
Motor->USER.u16SlowLoopDiv = PWMFREQ / SLOWLOOP_FREQ;
Motor->USER.u16AlignTimeCmd = ALIGN_TIME;
Motor->USER.u16StartupTimeCmd = STARTUP_TIME;
Motor->BLDC.u32StartUp_PwmValue = 460;
Motor->BLDC.u8SpeedReachedFlag = FALSE;
Motor->BLDC.u32PwmValue = STARTUP_PWMVALUE1; //Motor startup PWM value
Motor->BLDC.u8Direction = ECCW; //Motor rotation direction
Motor->BLDC.u32ForceCommutationThreshold = FORCED_COMMUTATION_TIME; //Motor's Forced commutation time
Motor->USER.u16ForceCommutationCnt = 133;//此为 40000 / 300 rpm = 133 cnt
Motor->BLDC.u16Startup_CommuteTime = 133;
//Zeroing back electromotive force zero-crossing voltage
MovingAvgInit(&u16ZcdBemfU);
MovingAvgInit(&u16ZcdBemfV);
MovingAvgInit(&u16ZcdBemfW);
spd_pi_init(&Motor->stc_SpdPi);
/*Initialize feedback speed filter array*/
//MovingAvgInit(&SpeedFdk);
InitNormalization(300,3796,3000,&Motor->USER.RP);
/*Initialize acceleration curve parameters*/
LoopCmp_Init(&Motor->BLDC.RPValue);
}
/**
* @brief Reset the Motor control parameters
* @retval None
*/
void variable_reset(Motor_TypeDef *Motor)
{
Motor_1st.USER.u16AlignTimeCnt = 0; /* Motor Align time cnt */
Motor_1st.USER.u16RampCnt = 0; /* Motor ramp time cnt */
Motor_1st.BLDC.u8Phase = 0; /* Motor phase sequence */
Motor_1st.BLDC.u8PhaseShadow = 0; /* Motor phase sequence Shadow */
Motor_1st.BLDC.u8flag_zcd = 0; /* BMEF crossing-zero flag */
Motor_1st.BLDC.u32ForceCommutationCnt = 0; /* Motor's Forced commutation time cnt */
Motor_1st.BLDC.u32PwmValue = 0; /* PWM Output value */
Motor_1st.BLDC.u32CntElectricalPeriodNumber = 0; /* Record the number of electrical cycles */
Motor_1st.BLDC.u16Startup_CommuteTime = 133;
Motor_1st.BLDC.u8_StepCnt = 0;
spd_pi_init(&Motor->stc_SpdPi);
Motor_stcRampInit();
}
为了直观的显示电机的运行状态, 这里我使用了一款IIC的屏幕对电路板的工作状态进行显示。
屏幕介绍如下:
一、OLED 简介:
OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。因为具备轻薄、省电等特性,因此从 2003 年开始,这种显示设备在 MP3 播放器上得到了广泛应用,而对于同属数码类产品的 DC 与手机,此前只是在一些展会上展示过采用 OLED 屏幕的工程样品。自 2007 年后,寿命得 到很大提高,具备了许多 LCD 不可比拟的优势。
。
实物图片
二:汉字显示方法:
需要使用软件做自己的字库
由于该电路板的集成度比较高,我只好将串口2模拟IIC驱动屏幕了,部分代码如下:
#define SCL_High_RC GPIO_SetBit(GPIOB, GPIO_PIN_11)
#define SCL_Low_RC GPIO_ClearBit (GPIOB, GPIO_PIN_11)
#define SDA_High_RC GPIO_SetBit(GPIOB, GPIO_PIN_12)
#define SDA_Low_RC GPIO_ClearBit (GPIOB, GPIO_PIN_12)
/**********************************************
//IIC Start
**********************************************/
void IIC_Start()
{
SCL_High_RC;
SDA_High_RC ;
SDA_Low_RC ;
SCL_Low_RC;
}
/**********************************************
//IIC Stop
**********************************************/
void IIC_Stop()
{
SCL_Low_RC;
SDA_Low_RC;
SCL_High_RC;
SDA_High_RC;
}
/**********************************************
// 通过I2C总线写一个字节
**********************************************/
void Write_IIC_Byte(unsigned char IIC_Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(IIC_Byte & 0x80)
SDA_High_RC ;
else
SDA_Low_RC ;
SCL_High_RC;
SCL_Low_RC;
IIC_Byte<<=1;
}
SDA_High_RC;
SCL_High_RC;;
SCL_Low_RC ;
}
/*********************OLED写数据************************************/
void OLED_WrDat(unsigned char IIC_Data)
{
IIC_Start();
// Write_IIC_Byte(0x78);
Write_IIC_Byte(0x40); //write data
Write_IIC_Byte(IIC_Data);
IIC_Stop();
}
/*********************OLED写命令************************************/
void OLED_WrCmd(unsigned char IIC_Command)
{
IIC_Start();
// Write_IIC_Byte(0x78); //Slave address,SA0=0
Write_IIC_Byte(0x00); //write command
Write_IIC_Byte(IIC_Command);
IIC_Stop();
}
/*********************OLED 设置坐标************************************/
void OLED_Set_Pos(unsigned char x, unsigned char y)
{
OLED_WrCmd(0xb0+y);
OLED_WrCmd(((x&0xf0)>>4)|0x10);
OLED_WrCmd((x&0x0f)|0x01);
}
/*********************OLED全屏************************************/
void OLED_Fill(unsigned char bmp_dat)
{
unsigned char y,x;
for(y=0;y<8;y++)
{
OLED_WrCmd(0xb0+y);
OLED_WrCmd(0x01);
OLED_WrCmd(0x10);
for(x=0;x<X_WIDTH;x++)
OLED_WrDat(bmp_dat);
}
}
/*********************OLED复位************************************/
void OLED_CLS(void)
{
unsigned char y,x;
for(y=0;y<8;y++)
{
OLED_WrCmd(0xb0+y);
OLED_WrCmd(0x01);
OLED_WrCmd(0x10);
for(x=0;x<X_WIDTH;x++)
OLED_WrDat(0);
}
}
全部评论