FPGA 非常适合精密电机控制,在这个项目中,我们将创建一个简单的电机控制程序,在此基础上可以构建更复杂的应用。
需要的硬件
介绍
我们可以用一个简单的 8 位微控制器来控制电机,输出一个简单的脉宽调制波形。然而,当想要进行精密或高级电机控制时,没有什么比 FPGA 的确定性和实时响应更好的了。接口的灵活性还使得可以通过单个设备控制多个电机,从而提供更加集成的解决方案。
首先,我们将学习一些有关电机控制理论的知识,并创建一个简单的示例。我们都知道,我们可以通过PWM信号来驱动直流电机并控制其速度。然而,高效、精确地驱动它需要对电机控制理论有更多的了解。
电机
不管你信不信,我在大学最喜欢的课程之一是控制理论。在该模块中,我们研究了交流电机和直流电机,了解理论和实际用例。有多种类型的交流电机由交流电源供电,可分为同步电机和感应电机。例如,交流电机通常用于泵和压缩机。
直流电机分为有刷电机和无刷电机两种。在两种类型中,有刷是最容易驱动的,因为它们只需要一个电源。在有刷直流电机中,电刷向连接有转子和线圈的换向器提供电流。电流在线圈中感应出电场,该电场被外部磁体(定子)排斥。为了确保旋转,换向器的设计使得电流反向流动以确保连续旋转。
第二种类型的直流电机是无刷电机,它们的驱动稍微复杂一些,因为它们没有换向器。相反,磁体安装在转子上,线圈缠绕在定子周围,这样线圈的电流就可以从外部控制和排序。
两者中最容易控制的是有刷直流电机,所以我们就以这类电机为例。
脉宽调制驱动
使用 PWM 驱动电机的理论是,可以控制电机得到的平均电压,从而控制其速度。在 PWM 信号占空比为 100% 时,电机处于满电压并全速运行。如果提供 10% 的占空比,电机就会以其全速的 10% 运行。
然而,为了有效地运行电机,我们需要正确确定 PWM 周期。直流电机具有串联电感和串联电阻,这意味着电机将充当低通滤波器。频率削减为
其中时间常数由 L/R 给出 - 我们可以从电机数据表中获得这些值。
因此,为了确保稳定的速度,我们需要选择高于电机频率截止的 PWM 频率,以确保观察到直流分量。
因此,我们要选择一个至少是截止频率 5 倍的频率。
FPGA
为了开始这个项目,我们首先要创建一个针对 FPGA 板的硬件设计。
开始创建一个新项目
为项目命名
选择 RTL 项目但不指定来源
创建项目后,创建一个新的框图
从“板”选项卡将系统时钟拉到框图上
对 USB UART 也执行相同操作
从 IP 库添加 MicroBlaze 处理器
运行块自动化连接,选择本地内存大小为32KB并取消选中中断控制器
添加 AXI 定时器
运行连接自动化
打开时钟向导并取消选择复位输入
添加 GPIO
重新定制GPIO为1位宽,仅输出
选择 GPIO 输出和 AXI 定时器 PWM 并将其设引出
完成后应如下所示。
综合完成后,我们可以打开综合视图并将 IO 分配给 GPIO 和定时器输出 - 对于 GPIO,引脚是 J1,对于 PWM,引脚是 L2
构建比特流并导出平台
vitis设计
打开Vitis创建一个新的应用程序项目并选择刚刚导出的XSA。
输入项目名称
选择独立
创建一个新的 hello world 应用程序
应用软件非常简单,我们将根据所需的 PWM 周期以及所需的占空比配置 AXI 定时器。
#include
#include "platform.h"
#include "xil_printf.h"
#include "xtmrctr.h"
#define TMRCTR_DEVICE_ID XPAR_TMRCTR_0_DEVICE_ID
#define PWM_PERIOD 1000000 /* PWM period in (500 ms) */
#define TMRCTR_0 0 /* Timer 0 ID */
#define TMRCTR_1 1 /* Timer 1 ID */
#define CYCLE_PER_DUTYCYCLE 10 /* Clock cycles per duty cycle */
#define MAX_DUTYCYCLE 100 /* Max duty cycle */
#define DUTYCYCLE_DIVISOR 2 /* Duty cycle Divisor */
XTmrCtr TimerCounterInst;
void display_menu()
{
//Clear the screen
xil_printf("�33[2J");
//Display the main menu
xil_printf("*******************************************
");
xil_printf("**** www.adiuvoengineering.com ****
");
xil_printf("**** Motor Control Example ****
");
xil_printf("*******************************************
");
xil_printf("
");
xil_printf(" MM10 Motor Control
");
xil_printf("------------------------------------------
");
xil_printf("
");
xil_printf("Select a Speed:
");
xil_printf(" (1) - Stop
");
xil_printf(" (2) - 25 %
");
xil_printf(" (3) - 33 %
");
xil_printf(" (4) - 50 %
");
xil_printf(" (5) - 66 %
");
xil_printf(" (6) - 75 %
");
xil_printf(" (7) - 100 %
");
xil_printf("
");
}
void set_pwm(u32 cycle)
{
u32 HighTime;
XTmrCtr_PwmDisable(&TimerCounterInst);
HighTime = PWM_PERIOD * (( float) cycle / 100.0 );
XTmrCtr_PwmConfigure(&TimerCounterInst, PWM_PERIOD, HighTime);
XTmrCtr_PwmEnable(&TimerCounterInst);
}
int main()
{
u8 Div;
u32 Period;
u32 HighTime;
char key_input;
u8 DutyCycle;
init_platform();
print("Hello World
");
print("Successfully ran Hello World application");
XTmrCtr_Initialize(&TimerCounterInst, TMRCTR_DEVICE_ID);
Div = DUTYCYCLE_DIVISOR;
XTmrCtr_PwmDisable(&TimerCounterInst);
Period = PWM_PERIOD;
HighTime = PWM_PERIOD / Div--;
XTmrCtr_PwmConfigure(&TimerCounterInst, Period, HighTime);
XTmrCtr_PwmEnable(&TimerCounterInst);
while(1){
display_menu();
read(1, (char*)&key_input, 1);
xil_printf("Echo %c
",key_input);
switch (key_input) {
case '1': //stop
XTmrCtr_PwmDisable(&TimerCounterInst);
break;
case '2': //25%
xil_printf("25%
");
DutyCycle = 25;
set_pwm(DutyCycle);
break;
case '3': //33%
DutyCycle = 33;
set_pwm(DutyCycle);
break;
case '4': //50%
DutyCycle = 50;
set_pwm(DutyCycle);
break;
case '5': //66%
DutyCycle = 66;
set_pwm(DutyCycle);
break;
case '6': //75%
DutyCycle = 75;
set_pwm(DutyCycle);
break;
case '7': //100%
DutyCycle = 100;
set_pwm(DutyCycle);
break;
}
}
cleanup_platform();
return 0;
}
当然,我选择的电机包含两个霍尔效应传感器.旋转方向可以通过一个霍尔效应传感器位于另一个霍尔效应传感器前面的输出来确定。
顺时针旋转
逆时针旋转
我们可以使用脉冲频率来确定电机的速度,我们将在后面项目中更详细地研究这一点。