电机转动的本质是在磁场的作用下,转子绕定子运动,从而带动整个机械结构转动。从有无电刷,我们可以将电机分为:有刷电机和无刷电机;从供电方式,我们又可以将电机分为直流电机和交流电机。我最近学习的内容主要集中于无刷直流电机部分。

无刷电机概述

无刷直流电机(Brushless Direct Current Motor,简称BLDCM)由电动机主体和驱动器组成, 是一种典型的机电一体化产品。 无刷电机是指无电刷和换向器 (或集电环)的电机,又称无换向器电机。这是模型中除了有刷电机以外用的最多的一种电机, 无刷直流电机不使用机械的电刷装置,采用方波自控式永磁同步电机,与有刷电机相比,它将转子和定子交换, 即无刷电机中使用电枢绕组作为定子,使用钕铁硼的永磁材料作为转子,以霍尔传感器取代碳刷换向器, 性能上相较一般的传统直流电机有很大优势。

无刷电机的特点

有刷电机示意图如下所示:

有刷电机

无刷电机示意图如下所示:

无刷直流电机(EN: Brushless Direct Current Motor),克服了有刷直流电机的先天性缺陷,以电子换向器取代了机械换向器,所以无刷直流电机既具有直流电机良好的调速性能等特点,又具有交流电机结构简单、无换向火花、运行可靠和易于维护等优点。无刷直流电机的实质是直流电源输入,采用电子逆变器将直流电转换为交流电,有转子位置反馈的三相交流永磁同步电机。性能上相较一般的传统直流电机有很大优势,是当今最理想的调速电机。

无刷电机的KV值及转速

无刷电机的实际转速=KV值*工作电压。KV的物理意义,就是无刷电机在1V工作电压下每分钟的转速。KV值与匝数呈反比例关系。

注意:无刷直流电机的转速与电压呈正比关系,电机的转速会随着电压上升而线性上升。

无刷电机的工作原理

无刷电机的本质是转子、定子因为磁场的原因发生吸引、排斥,从而使转子绕着定子发生转动,从而带动电机整体移动。无刷直流电机相数多为三相,常使用“三相星形联结的二二导通方式”。

img

在A端上电源正极,在B端接电源负极,那么可以在线圈A和B中可以产生如图所示的磁场,因为磁场强度是矢量, 所以由磁场BB和BA可以得到合成磁场B。此时转子就会保持在图中方向。

而当A、B、C三相按一定顺序通电时,会出现以下情况:

img

通过以上循环,我们就能实现无刷电机(转子)的转动。

提到循环通电,我们必须使用一种叫做三相六臂全桥的驱动电路,利用MOS管的导通与关闭,我们就能实现对不同相的供电与断电。其原理图如下所示:

img

控制方法概述

控制BLDC时,我们需要知道转子磁极的位置,从而根据检测的位置进行换相通电。但由于项目需要,我们使用更高精度的编码器对电机位置进行反馈,故我们首先使用开环控制(无霍尔传感器/无反电动势的方法)进行测试。

那么具体怎么实现开环的控制呢,和杨工讨论后,得到了使用PWM波+死区取反的方式来进行MOS管的控制,进而实现纯开环的电机控制。

通过改变单片机的定时器周期来调整频率进而控制速度。主要采用三相六通道的PWM波形式来进行电机驱动,我们的PWM波采取的是SPWM波的形式。所谓SPWM波即通过脉冲调制而使方波变为类正弦波,这样做是为了使得电机转动更均匀。

硬件设计

我们使用STM32CubeMX进行项目构建,这是为了避免写配置和使能代码的麻烦。但需要注意的是HAL库尽管会对配置代码进行自动生成,但通道使能以及功能开启还是需要自己用代码实现。

此外,HAL库最大的特点之一是中断服务函数的多样,具体请查看参考文献。

BLDC_MX0

引脚接口如上图所示,其中高级定时器TIM1的CH1,CH2,CH3(含对应的N)通道用于控制三相电机的三相;TIM3的CH1和CH2通道用于接收编码器传来的信号;ENC_Z是开启了外部中断的普通IO口,用于接收编码器的Z相零点信号。而对于LED灯等其它硬件部分,为节省篇幅按下不表。

关键代码

代码整体思路为:LED_1在开机后长亮,当电机换相时,LED_2闪烁两次。通过按两个按键来调节电机转速,KEY_1增速,KEY_2减速。LED和KEY的配置略去,在这里仅讨论电机控制。

控制电机采用的是TIM定时器的PWM生成模式,通过对三相输出相位角相差120°的PWM波来使电机能够按照我们的需求进行平稳转动。

关于TIM1的配置信息如下表所示:

配置项 配置信息
Prescaler 0
Counter Mode Center Aligned Mode 1
Counter Period 2400
Dead Time 1
PWM Pulse 900
CH4 Pulse 1

在前文中,我们提到我们使用的是TIM1的CH1、CH2、CH3三个通道控制电机,那么为什么还设置了CH4 Pulse呢?这是因为我们需要一个时序信号来对三个通道进行协调,如果没有CH4的时序,我们很难恰好让3个通道按照120°相位差输出PWM波。

首先我们需要一个函数来改变定时器一个周期的脉冲数:

static void TIM_SetPeriod(TIM_TypeDef *TIMx, float myfreq){
    TIMx -> ARR = (uint16_t)(72000000/300/myfreq);
}

其中,myfreq通过调节PWM波的频率来调节电机转速。

我们看看最重要的PWM中断服务函数:

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim){
    static uint16_t temp = 0;
    if(htim -> Instance == TIM1){
        if(htim -> Channel == HAL_TIM_ACTIVE_CHANNEL_4){
            if(temp < 100){
                TIM_SetCompare1(TIM1,(uint16_t)(period_percent*(sin_val[temp])));
                TIM_SetCompare2(TIM1,(uint16_t)(period_percent*(sin_val[temp+200])));
                TIM_SetCompare3(TIM1,(uint16_t)(period_percent*(sin_val[temp+100])));
            }
            else if(temp < 200){
                TIM_SetCompare1(TIM1,(uint16_t)(period_percent*(sin_val[temp])));
                TIM_SetCompare2(TIM1,(uint16_t)(period_percent*(sin_val[temp-100])));
                TIM_SetCompare3(TIM1,(uint16_t)(period_percent*(sin_val[temp+100])));
            }
            else{
                TIM_SetCompare1(TIM1,(uint16_t)(period_percent*(sin_val[temp])));
                TIM_SetCompare2(TIM1,(uint16_t)(period_percent*(sin_val[temp-100])));
                TIM_SetCompare3(TIM1,(uint16_t)(period_percent*(sin_val[temp-200])));
            }
            temp++;
            if(temp >= 300)    temp = 0;
        }
    }
}

其中的TIM_SetComparex(x=1,2,3)是我们自己定义的函数,如下:

static void TIM_SetCompare1(TIM_TypeDef *TIMx, uint16_t Compare){
    if(TIMx == TIM1)    TIM1 -> CCR1 = Compare;
    if(TIMx == TIM2)    TIM2 -> CCR1 = Compare;
    if(TIMx == TIM3)    TIM3 -> CCR1 = Compare;
}

此外,sin_val[300]是一个正弦转化数组,即通过这个数组的配置,外加TIM_SetComparex函数,我们就可以将PWM方波转化为SPWM正弦波。(当然,这里的转化为正弦波指的是通过PWM占空比的调节,使得数字信号在一个周期内可以被当作模拟正弦信号)

period_percent的计算公式如下所示:period_percent = (72000000.0/300.0)/myfreq/32678.0。该公式计算的是一个周期内某一时刻的PWM占空比。

如何得到sin_val[300]内的数值呢?我们可以通过数学方法进行计算:即将一个周期的sin函数均分为300个点,算出每个点的值与幅值的比,再乘以一个系数,即可得到sin_val[300]内的数值。当然,我们还可以下载网上的一些小工具来直接得到参数值,比如这个:sin_cal

至此,大部分控制代码都已经完成了,但对于HAL库来说,它会帮助我们使能GPIO口、完善相关参数配置,但不会帮我们开启功能。所以我们如果要开启功能,需要进行以下操作:

HAL_TIM_Base_Start(&htim1);                        //开启TIM基础功能

HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);        //开启各通道的PWM输出功能
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);        
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);        
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4);

HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);        //开启各N通道的PWM输出功能
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_3);

HAL_TIM_PWM_Start_IT(&htim1,TIM_CHANNEL_4);        //开启通道4的PWM中断

最后,补上一个令电机暂停(停止)的函数,该方法是让电机缓停止,即不会立刻停止(这样可能会损坏电机),而是不给电,让其自动停止。

一些补充知识

1、预装载寄存器的作用:需要用户修改的预装数据立即生效时将预装功能关闭,希望用户修改的数据不影响当前周期的计数或波形输出时,我们就打开其预装功能。

2、中央对齐模式1:计数器交替地向上和向下计数。输出比较中断标志位,只在计数器向下计数时被设置。

3、HAL_TIM_PWM_PulseFinishedCallback和HAL_TIM_PWM_PulseElapsedCallback的区别:前者是比较中断触发的中断函数,后者是一个时钟周期结束后触发的中断函数。

待解决的问题

1、电机缓启动的代码还没有写;

2、电机速度调整还没有实现;

3、电机闭环控制还没有实现。

参考文献

1、野火 - 电机开发应用实战指南

2、搜狐 - 一文带你读懂什么是无刷电机

3、知乎 - STM32定时器比较输出话题

4、CSDN - 定时器的PWM设置详解

5、CSDN - GPIO的输入输出模式

6、博客园 - TIM计数器及中断开启

7、博客园 - 生成两路互补SPWM波