前段时间公务繁忙,收到的MM32SPIN160C开发板也无暇顾及,趁周末时间,来体验一下这款开发板的电机驱动能力。
一、硬件方面:
板卡为专用电机驱动板,因此底板加了一块散热片,开发板的四脚加了脚垫,以便更好地散热,防止底板电路与金属物件接触而导致短路。开发板采用ARM Cortex-M0的32位MCU,MCU最高工作频率可达72MHz,内置高速存储器,丰富的I/O 端口和外设连接到外部总线。具有1个12位的ADC、1个比较器、1个16位通用定时器、1个32位通用定时器、3个16位基本定时器和1个16位高级定时器。还包含标准的通信接口,即1个I2C接口、1个SPI接口和1个UART接口。适合开发吊风扇,电动工具,三相永磁无刷电机等多种应用场合。
由MM32SPIN160C的名称可知该型号的基础配置
开发板主要功能列表:
①、输入电压范围: 12V~28V
②、使用60V/40A N-MOS管*6
③、使用外挂(SPIN0x) GBW 6MHz 高速运放*4
④、MCU 电源使用5V
⑤、支持有/无霍, 方波/弦波驱动
⑥、支持1/2 Shunt R 三相电流采样
⑦、BEMF 电压回授使用ADC 采样
⑧、DC Bus 电压, 总电流量测
⑨、使用MCU 内建比较器做为过电流保护
在淘宝上买了个尼得科无刷电机,无刷电机最大优点是没有碳刷转换器,寿命长,适合长时间工作的场所。
【额定电压】DC 12V
【空载电流】1A
【额定功率】10W
【空载转速】12000转/分钟
【适合电压】6-12V(6000-12000转)
【重量】130克
电机的接口线路如下图所示:
二、电路图方面
关于该开发板的PCBA点位图如下:
开发板的电路原理图如下:
三、开发环境方面
板上集成了JLink调试接口,支持串行调试(SWD),官方提供的资源包中包括基本工程示例,可使用IAR、MDK工具打开,本人习惯使用MDK,因此将基本工程示例导入进Keil中,如果电脑联网,此时会提示安装“MindMotion.MM32SPIN0x_DFP.1.0.8”pack包;如果电脑处于离线状态,可从灵动微官网下载基于MM32SPIN160C开发板的pack包,如下附件,然后将pack导入Keil工具中,而后直接编译即可。
将JLink烧录器与开发板上的CN7接口相连接,Keil中设置SWD模式,即可识别到调试器,并设置烧录后自动重启。
然后将开发板上CN1、CN2、CN3与电机、电源适配器正确连接,由于板卡支持12~28V的电压输入,而电机的额定电压是12V,因此采用12V/1A的电源适配器,实物连线图如下:
采用不带霍尔的轮询控制方式,直接烧录后,电机起震,没有如期得向逆时针或顺时针旋转,现象如同是前进两步后退两步,一直在抖动。在线调试,发现驱动换相函数存在问题,每种无刷电机的接线有所差异。关于BLDC电机,推荐看看TI的技术视频教程深入理解无刷直流电机(BLDC)原理以及控制,关于有刷电机与无刷电机的工作原理如下:
由上图,有刷电机的主要结构就是定子+转子+电刷,通过旋转磁场获得转动力矩,从而输出动能。电刷与换向器不断接触摩擦,在转动中起到导电和换相作用。有刷电机采用机械换向,磁极不动,线圈旋转。电机工作时,线圈和换向器旋转,磁钢和碳刷不转,线圈电流方向的交替变化是随电机转动的换相器和电刷来完成的。
由上图,无刷电机中,换相的工作交由控制器中的控制电路(一般为霍尔传感器+控制器,更先进的技术是磁编码器)来完成。无刷电机采取电子换向,线圈不动,磁极旋转。无刷电机,是使用一套电子设备,通过霍尔元件,感知永磁体磁极的位置,根据这种感知,使用电子线路,适时切换线圈中电流的方向,保证产生正确方向的磁力,来驱动电机,消除了有刷电机的缺点。
四、工程源码
部分源码如下:
#include "sys.h"
typedef enum {
CW = 0, // 顺时钟方向
CCW = 1 // 逆时针方向
} motor_dir_t;
typedef enum {
START = 0, // 启动
STOP = 1, // 停机
RUN = 2 // 运行
} motor_state_t;
typedef struct{
motor_dir_t tMotorDirection; // 电机旋转方向
motor_state_t tMotorState; // 电机状态
uint16_t tDuty; // 速度占空比:0~100 为100是占空比为100%
uint16_t tSpeed; // 电机转速
uint8_t chHallValue; // 霍尔状态
struct{
uint8_t chStartCount; // 启动计数
uint16_t hwTimeCount; // 堵转超时溢出计数
}tCount;
}User_TypeDef_t;
volatile User_TypeDef_t g_tMotor = {
CW,
START,
40,
0,
1,
0,
0
};
extern void Bldc_Phase_Chaneg(uint8_t step);
extern void Hall_Exti_Callback(void);
extern void Systick_Callback(void);
int main(void)
{
Systick_Init();
Led_Init();
Hall_Init();
Bldc_Pwm_Init();
Gate_Driver_Init();
while(1) {
switch(g_tMotor.tMotorState) {
case START: // 电机启动
if(++g_tMotor.tCount.chStartCount > 20){ //电机启动失败
g_tMotor.tMotorState = STOP;
g_tMotor.tCount.chStartCount = 0;
}
SET_DUTY_U(BLDC_TIM_PERIOD * g_tMotor.tDuty / 100);
SET_DUTY_V(BLDC_TIM_PERIOD * g_tMotor.tDuty / 100);
SET_DUTY_W(BLDC_TIM_PERIOD * g_tMotor.tDuty / 100);
GATE_DRIVER_ENABLE();
Hall_Exti_Callback();
Systick_Delay(5);
break;
case RUN: // 电机运行
if (0){//some err
g_tMotor.tMotorState = STOP;
}
Hall_Exti_Callback();
break;
case STOP: // 停机
GATE_DRIVER_DISABLE();
BLDC_UH_DISABLE();
BLDC_UL_DISABLE();
BLDC_VH_DISABLE();
BLDC_VL_DISABLE();
BLDC_WH_DISABLE();
BLDC_WL_DISABLE();
break;
}
}
}
void Bldc_Phase_Chaneg(uint8_t step)
{
Systick_Delay(5); //为了更好得看清楚,稍加延时
switch(step) {
case 3: //A+ C-
BLDC_UH_ENABLE();
BLDC_UL_DISABLE();
BLDC_VH_DISABLE();;
BLDC_VL_DISABLE();
BLDC_WH_DISABLE();
BLDC_WL_ENABLE();
break;
case 1: //B+ C-
BLDC_UH_DISABLE();
BLDC_UL_DISABLE();
BLDC_VH_ENABLE();
BLDC_VL_DISABLE();
BLDC_WH_DISABLE();
BLDC_WL_ENABLE();
break;
case 5: //B+ A-
BLDC_UH_DISABLE();
BLDC_UL_ENABLE();
BLDC_VH_ENABLE();
BLDC_VL_DISABLE();
BLDC_WH_DISABLE();
BLDC_WH_DISABLE();
break;
case 4: //C+ A-
BLDC_UH_DISABLE();
BLDC_UL_ENABLE();
BLDC_VH_DISABLE();
BLDC_VL_DISABLE();
BLDC_WH_ENABLE();
BLDC_WL_DISABLE();
break;
case 6: //C+ B-
BLDC_UH_DISABLE();
BLDC_UL_DISABLE();
BLDC_VH_DISABLE();
BLDC_VL_ENABLE();
BLDC_WH_ENABLE();
BLDC_WL_DISABLE();
break;
case 2: //A+ B-
BLDC_UH_ENABLE();
BLDC_UL_DISABLE();
BLDC_VH_DISABLE();
BLDC_VL_ENABLE();
BLDC_WH_DISABLE();
BLDC_WL_DISABLE();
break;
default:
BLDC_UH_DISABLE();
BLDC_UL_DISABLE();
BLDC_VH_DISABLE();
BLDC_VL_DISABLE();
BLDC_WH_DISABLE();
BLDC_WL_DISABLE();
break;
}
}
void Systick_Callback(void)
{
if(RUN == g_tMotor.tMotorState) {
g_tMotor.tCount.hwTimeCount ++;
if(g_tMotor.tCount.hwTimeCount > 2000) { // 2s超时,电机卡住不运转超过2s时间
GATE_DRIVER_DISABLE();
BLDC_UH_DISABLE();
BLDC_UL_DISABLE();
BLDC_VH_DISABLE();
BLDC_VL_DISABLE();
BLDC_WH_DISABLE();
BLDC_WL_DISABLE();
g_tMotor.tMotorState = STOP;
g_tMotor.tCount.hwTimeCount = 0;
}
}
}
void Hall_Exti_Callback(void)
{
volatile uint8_t chStep = 0;
// chStep = (uint8_t)((HALL_PORT_U->IDR >> HALL_PINSOURCE_U) |
// (HALL_PORT_V->IDR >> (HALL_PINSOURCE_V - 1)) |
// (HALL_PORT_W->IDR >> (HALL_PINSOURCE_W - 2))) & 0x07;
if(g_tMotor.tMotorState == STOP) {
return;
}
if((HALL_PORT_U ->IDR & HALL_PIN_U) != (uint32_t)Bit_RESET) {
chStep |= 0x01;
}
if((HALL_PORT_V ->IDR & HALL_PIN_V) != (uint32_t)Bit_RESET) {
chStep |= 0x02;
}
if((HALL_PORT_W ->IDR & HALL_PIN_W) != (uint32_t)Bit_RESET) {
chStep |= 0x04;
}
if (g_tMotor.chHallValue != chStep) { //判断是否换向
g_tMotor.tMotorState = RUN;
g_tMotor.chHallValue = chStep ;
}
if(g_tMotor.tMotorDirection == CCW) { // 方向判断
chStep = 7 - chStep;
}
Bldc_Phase_Chaneg(chStep); //驱动换相
g_tMotor.tCount.hwTimeCount = 0;
}
Jumper设定为出厂设置状态,工程编译后,烧录完成,电机顺利启动,代码中稍加了点延时,方便更清楚得看到电机的转动,电机转动又快又有劲,可与通用电动螺丝批相比较,但噪声有点大,有待优化。驱动工程源码见如下附件,通过MM32SPIN160C电机驱动板的使用,体验了灵动微MCU的库函数与ST的相兼容,移植起来更便捷,如果灵动微有像ST一样CubeMX开发工具就更强了。
下载程序源代码等资料,请点击阅读原文!