基于单片机控制的步进电机控制器
步进电机的工作原理:
如图1所示为反应式步进电动机内部结构图。其实,步进电机在结构上与普通电动机是大同小异的,像转子、定子和定子绕组等电机的基本组成部分反应式步进电动机也同样具备。只是步进电动机的定子绕组会被划分为若干相,每相的磁极上有若干个极齿,转子在轴上也有若干个齿。步进电机能够旋转起来的最根本原因是错齿。步进电机最大的特色一就是它的转动是以“步”为单位的。总的来说,反应式步进电机的工作原理是利用了物理上的“磁通总是力图使自己所通过的路径的磁阻最小"所产生的磁阻转矩,使电机可以一步步地转动起来的。
1四相五线步进电机的结构
图 1四相步进电机
如图1所示四相步进电机和同普通电动机一样,比如必不可少的定子、转子和定子绕组等电机的几个基本组成部分反应式步进电动机同样也具备。只是四相步进电机的定子绕组会被划分为四相,每一相的磁极上有许多的极齿,同样转子在轴上也有许多的齿。如果按A—B—C—D一A…通电,四相步进电机就会正转;反之就会反转。
2.四相五线步进电机的通电原理
图2为四相步进电机的通电示意图分析其工作原理如下:
图 2步进电机通电原理
控制步进电机的转向
由于步进电机的旋转方向与其内部绕组的通电顺序的先后是直接相关的,所以如果我们想要改变步进电机的转向,只需要改变提供给各相绕组通电顺序的先后就可以了。也就是可以通过改变各相绕组通电顺序的先后来控制步进电机的转向。
四相步进电机的正反转控制如下:
(1)单四拍:
A→B→C→D→A 正转
D→C→B→A→D 反转
(2)双四拍:
AB→BC→CD→DA正转
AD→DC→CB→BA反转
控制步进电机的速度
由于步进电机转动的速度是与单片机输出的脉冲频率直接相关的,所以如果我们想改变步进电机的转动速度的快慢,只需要改变其控制单片机输出的脉冲频率就可以了。
设计思路
本方案采用AT89C52芯片作为主控芯片,AT89C52是一种带4K字节FLASH存储器的低电压、高性能CMOS 8位微处理器,在众多嵌入式控制应用系统中得到广泛应用。AT89C52的基本结构包括中央处理器(CPU) 、存储器、定时/计数器、输入输出接口、中断控制系统和时钟电路六部分。
其次,我们使用数码管模块、步进电机模块等几部分完成步进电机控制的基本逻辑设计。数码管是一种半导体发光器件,其基本单元是发光二极管。数码管实际上是由七个发光管组成8字形而构成的,加上小数点就是8个。我们分别控制它的8个段选引脚和1个位选引脚来进行控制。
本设计所选的步进电机是四相步进电机,采用的方法是利用单片机控制步进电机的驱动。步进电机是一种将电脉冲转化为角位移的执行机构。当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度(称为“步距角”),它的旋转是以固定的角度一步一步运行的。可以通过控制脉冲个数来控制角位移量,从而达到准确定位的目的;同时可以通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。
本次设计就是通过改变脉冲频率来调节步进电机的速度的,并且通过数码管显示其转速的级别。另外通过单片机实现它的正反转,步进电机可以作为一种控制用的特种电机,利用其没有积累误差(精度为100%)的特点,广泛应用于各种开环控制。正因为步进电机此优点,可以通过独立按键系统来控制步进电机正反转,加减速,开始以及停止。
系统框图
根据本次课程设计要求实现的功能具体画出基于AT89C52控制步进电机的系统框图如下图3所示
图 3系统框图
对系统框图各功能模块的文字说明
1.MCU微控制器
AT89C52是一个低电压,高性能CMOS-8位单片机,片内含8KB的可反复擦写的Flash只读程序存储器和256 bytes的随机存取数据存储器(RAM),器件采用ATMEL公司的高密度、非易失性存储技术生产,兼容标准MCS-51指令系统,片内置通用8位中央处理器和Flash存储单元,AT89C52单片机在电子行业中有着广泛的应用。
AT89C52有40个引脚,32个外部双向输入/输出(I/O)端口,同时内含2个外中断口,3个16位可编程定时计数器,2个全双工串行通信口,2个读写口线,AT89C52可以按照常规方法进行编程,也可以在线编程。其将通用的微处理器和Flash存储器结合在一起,特别是可反复擦写的Flash存储器可有效地降低开发成本。同时AT89C52有PDIP、PQFP/TQFP及PLCC等三种封装形式,以适应不同产品的需求。
我们控制AT89C52的I/O口对数码管以及led灯进行控制,它是整个系统的核心器件。
2.速度显示模块
AT89C52的P0口外接上拉电阻与一位共阳极数码管相连,对步进电机的转速进行实时显示。
3.电机模块:
步进电机是一种将电脉冲转化为角位移的执行机构。当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度(称为“步距角”),它的旋转是以固定的角度一步一步运行的。可以通过控制脉冲个数来控制角位移量,从而达到准确定位的目的;同时可以通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。本次设计就是通过改变脉冲频率来调节步进电机的速度的,并且通过数码管显示其转速的级别。另外通过单片机实现它的正反转。ULN2003A是两个三极管复合成的,相当于一个三极管,但比一个三极管的电流放大倍数大了很多,提高了电流驱动能力,从而实现单片机控制电机的目的。
4.按键功能模块
本设计采用独立按键来实现相关功能,独立按键式直接用I/O口线构成的单个按键电路,其特点式每个按键单独占用一根I/O口线,每个按键的工作不会影响其他I/O口线的状态。独立式按键电路配置灵活,软件结构简单。按键功能模块的特点在于用单片机的两个外部中断来控制步进电机进行加、减速,即每引入一次外部中断,步进电机加/减速一次。正转、反转、停止按键分别由单片机的P1.2、P1.3、P3.6口引入,加速、减速按键分别由单片机的P1.4和P3.5口引入,而高低档位则分别由单片机的P1.5、P1.6口引入。
5.步进电机转向指示灯模块
步进电机的正反转分别用绿色和红色的发光二极管(LED)作为转向指示灯。
详细电路原理图及仿真图
图 4电路原理图
图 5电路仿真图
详细系统说明
MCU模块
MCU模块原理图
利用单片机最小系统和少量外围器件可组成基于单片机的步进电机控制系统,其中单片机最小系统是控制的核心。能让单片机运行起来的最小硬件连接就是单片机最小系统电路,单片机最小系统一般是由单片机、时钟晶振电路、复位电路等几部分组成。如图6所示
图 6MCU模块原理图
(1)时钟晶振电路
图 7时钟晶振电路原理图
如图7所示单片机本身是一个复杂的同步时序电路,用时钟晶振电路来产生时钟信号,可以保证同步工作方式的实现,一般晶振频率采用12MHZ。
(2)复位电路
复位是单片机的初始化操作。RST为单片机上电复位输入端,只要在该引脚上连续保持两个机器周期以上的高电平,单片机就可以实现复位操作,复位后开始执行。在一般应用中可以用电路来实现单片机的上电复位,如下图8所示。
图 8复位电路模块原理图
电路分析及作用
(1)时钟电路:单片机内部具有一个高增益反相放大器,用于构成振荡器。通常在引脚XTAL1和XTAL2跨接石英晶体和两个补偿电容构成自激振荡器,系统时钟电路结构如上图所示,根据情况选择12MHz频率的石英晶体,补偿电容选择22pF左右的瓷片电容,以此来形成单片机稳定的系统时钟。
(2)复位电路:单片机采用上电自动复位和手动按键复位两种方式实现系统的复位操作。上电复位要求接通电源后,自动实现复位操作。手动复位要求在电源接通的条件下,在单片机运行期间,用按钮操作使单片机复位。复位电路结构如上图所示。手动按键复位通过按键将电阻R3与VCC接通来实现。
数码管模块
数码管模块原理图
图 9数码管显示模块原理图
电路分析及作用
数码管电路:主要用来实时显示速度的数值。位选信号接到电源,即数码管一直处于工作状态,然后再通过发送段选信号P00~P06,来控制需要显示的数值,以此完成数码管显示速度的功能。
步进电机模块
步进电机模块原理图
图 10步进电机驱动模块原理图
电路分析及作用
ULN2003A是两个三极管复合成的,相当于一个三极管,但比一个三极管的电流放大倍数大了很多,提高了电流驱动能力,从而实现单片机IO口输出的低电压/低电流信号P24~P27经过ULN2003A放大为高电压/高电流信号A~D,控制电机运行。
本设计所选的步进电机是四相步进电机,分别连接ABCD,步进电机是一种将电脉冲转化为角位移的执行机构。当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度(称为“步距角”),它的旋转是以固定的角度一步一步运行的。可以通过控制脉冲个数来控制角位移量,从而达到准确定位的目的;同时可以通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。
按键控制模块
按键控制模块原理图
图 11按键模块原理图
电路分析及作用
本方案采用了独立按键,独立按键式直接用I/O口线构成的单个按键电路,其特点式每个按键单独占用一根I/O口线,每个按键的工作不会影响其他I/O口线的状态。同时犹豫实现所有功能只需要七个独立按键,并不会出现I/O口不足的情况。
系统结构流程图
根据课程设计的要求,开机后,电机不转,按下加速,减速,正转,反转,高档位,低档位任意一按钮,电机能按照所设置的方式转动,当按下停止按钮后,电机停止转动,程序流程图如图12所示
代码:
Key.c
//-----------------------------------------------------------------
//按键处理程序
//-----------------------------------------------------------------
#include "Key.h"
uchar KeyCode = 0; //按键编码值
//共5个独立机械按键,通过短按实现5种按键编码值
uint KeyTimeCnt[] = {0,0,0,0,0,0,0}; //按键去抖动延时计数器,分别对应KEY1/2/3/4/5
uchar KeyLock[] = {0,0,0,0,0,0,0}; //按键触发后自锁的变量标志,分别对应KEY1/2/3/4/5
//-----------------------------------------------------------------
//函数名称: void KeyScan()
//函数功能: 按键扫描程序,放在定时中断里,可较快实现按键响应
//调用子函数: 无
//输入参数: 无
//返回值: 无,说明:返回的按键编码值保存在KeyCode变量里
//-----------------------------------------------------------------
void KeyScan()
{
//------KEY1实现-------------------------------------------------
if(KEY1 == 1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
{
KeyLock[0] = 0; //按键自锁变量标志清零
KeyTimeCnt[0] = 0; //按键去抖动延时计数器清零
}
else if(KeyLock[0] == 0) //如果按键不按下,每次循环确实会进入这个if里,使得KeyTimeCnt[0]++,但是在下一次循环,
{ //又进入了前面那个if,将KeyTimeCnt[0]又等于0了,所以只有在按键按下后,才会进行正常的++,直到大于KEY_TIME_SHORT
KeyTimeCnt[0]++;
if(KeyTimeCnt[0] > KEY_TIME_SHORT)
{
KeyTimeCnt[0] = 0;
KeyLock[0] = 1; //自锁按键置位,避免一直触发
KeyCode = 1;
}
}
//------KEY2实现-----------------------------------
if(KEY2 == 1)
{ KeyLock[1] = 0;
KeyTimeCnt[1] = 0;
}
else if(KeyLock[1] == 0)
{
KeyTimeCnt[1]++;
if(KeyTimeCnt[1] > KEY_TIME_SHORT) //消抖
{
KeyTimeCnt[1] = 0; //按键去抖动延时计数器清零
KeyLock[1] = 1; //自锁按键置位,避免一直触发
KeyCode = 2;
}
}
//------KEY3实现-----------------------------------
if(KEY3 == 1)
{ KeyLock[2] = 0;
KeyTimeCnt[2] = 0;
}
else if(KeyLock[2] == 0)
{
KeyTimeCnt[2]++;
if(KeyTimeCnt[2] > KEY_TIME_SHORT)
{
KeyTimeCnt[2] = 0;
KeyLock[2] = 1;
KeyCode = 3;
}
}
//------KEY4实现-----------------------------------
if(KEY4 == 1)
{ KeyLock[3] = 0;
KeyTimeCnt[3] = 0;
}
else if(KeyLock[3] == 0)
{
KeyTimeCnt[3]++;
if(KeyTimeCnt[3] > KEY_TIME_SHORT)
{
KeyTimeCnt[3] = 0;
KeyLock[3] = 1;
KeyCode = 4;
}
}
//------KEY5实现-----------------------------------
if(KEY5 == 1)
{ KeyLock[4] = 0;
KeyTimeCnt[4] = 0;
}
else if(KeyLock[4] == 0)
{
KeyTimeCnt[4]++;
if(KeyTimeCnt[4] > KEY_TIME_SHORT)
{
KeyTimeCnt[4] = 0;
KeyLock[4] = 1;
KeyCode = 5;
}
}
//------KEY6实现-----------------------------------
if(KEY6 == 1)
{ KeyLock[5] = 0;
KeyTimeCnt[5] = 0;
}
else if(KeyLock[5] == 0)
{
KeyTimeCnt[5]++;
if(KeyTimeCnt[5] > KEY_TIME_SHORT)
{
KeyTimeCnt[5] = 0;
KeyLock[5] = 1;
KeyCode = 6;
}
}
//------KEY7实现-----------------------------------
if(KEY7 == 1)
{ KeyLock[6] = 0;
KeyTimeCnt[6] = 0;
}
else if(KeyLock[6] == 0)
{
KeyTimeCnt[6]++;
if(KeyTimeCnt[6] > KEY_TIME_SHORT)
{
KeyTimeCnt[6] = 0;
KeyLock[6] = 1;
KeyCode = 7;
}
}
}
(2)main.c
//-----------------------------------------------------------------
//步进电机主程序
//-----------------------------------------------------------------
#include "Key.h"
//0~9 的共阳数码管段码,最后一个是黑屏
const uchar SEG_CODE[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF};
//输出励磁序列的频率参数{TH1,TL1}
const uchar Timer[9][2]={{0xDE,0xE4},{0xE1,0xEC},{0xE5,0xD4},{0xE9,0xBC},
{0xEd,0xA4},{0xF1,0x8C},{0xF5,0x74},{0xF9,0x5C},{0xFC,0x18}};
//步进电机正转的励磁序列
const uchar FFW[] = {0x1F,0x3F,0x2F,0x6F,0x4F,0xCF,0x8F,0x9F};
//步进电机反转的励磁序列
const uchar REV[] = {0x9F,0x8F,0xCF,0x4F,0x6F,0x2F,0x3F,0x1F};
//枚举变量--正反转标志
typedef enum
{FwdRun, RevRun} RunFlag;
RunFlag flag1 = FwdRun; //默认正转
uchar speed = 0;
//函数声明
void T0_Init(); //定时器0初始化函数声明
void T1_Init(); //定时器1初始化函数声明
void KeyService(); //按键服务函数声明
void delay_ms(uint t); //毫秒延时函数声明
//-----------------------------------------------------------------
// 主程序
//-----------------------------------------------------------------
void main()
{
T0_Init(); //定时器0初始化
T1_Init(); //定时器1初始化
FWD_LED = 0; //正转指示灯亮,默认初始化是正转
delay_ms(500); //延时500ms,待各个模块上电稳定
EA = 1; //开启中断
while(1)
{
KeyService(); //按键服务程序
}
}
//-----------------------------------------------------------------
// Timer0 初始化,模式1,允许中断,1ms定时
// Timer0 用于按键扫描
//-----------------------------------------------------------------
void T0_Init()
{
TMOD &= 0xF0;
TMOD |= 0x01; //T0工作于模式1,16位定时器
TH0 = (65536 - 1000) >> 8; //1ms定时
TL0 = (65536 - 1000) & 0xFF;
ET0 = 1; //允许T0中断
TR0 = 1; //启动T0
}
//-----------------------------------------------------------------
// Timer1 初始化,模式1,允许中断
// Timer1 用于改变步进电机转速
//-----------------------------------------------------------------
void T1_Init()
{
TMOD &= 0x0F;
TMOD |= 0x10; //定时器1工作于模式1,16位定时器
TH1 = Timer[speed][0];
TL1 = Timer[speed][1]; //定时器1,定时用于步进电机转速控制
ET1 = 1; //允许T1中断
PT1 = 1; //定时器1优先级高
TR1 = 0; //关闭定时器1
}
//-----------------------------------------------------------------
// Timer0 中断服务程序,1ms进行一次按键扫描
//-----------------------------------------------------------------
void InterruptTime0() interrupt 1
{
TH0 = (65536 - 1000) >> 8; //1ms
TL0 = (65536 - 1000) & 0xFF;
KeyScan(); //按键扫描
}
//-----------------------------------------------------------------
// Timer1 中断服务程序
//-----------------------------------------------------------------
void InterruptTime1() interrupt 3
{
static step1 = 0;
static step2 = 0;
TH1 = Timer[speed - 1][0]; //根据不同的转速档位,给定时器1赋定时器值
TL1 = Timer[speed - 1][1];
//按照定时器1的频率循环发送正转励磁序列的脉冲给步进电机,使其不停地转动
if(flag1 == FwdRun)//正转
{
MotorDriver = FFW[step1++];
if(step1 == 8) step1 = 0;
}
//按照定时器1的频率循环发送反转励磁序列的脉冲给步进电机,使其不停地转动
else if(flag1 == RevRun)
{
MotorDriver = REV[step2++];
if(step2 == 8) step2 = 0;
}
}
//-----------------------------------------------------------------
// 按键服务程序
//-----------------------------------------------------------------
void KeyService()
{
switch (KeyCode)
{
case 1: flag1 = FwdRun; //正转
SpeedShow = SEG_CODE[speed];
FWD_LED = 0; REV_LED = 1; //正转指示灯亮
KeyCode = 0;
break;
case 2: flag1 = RevRun; //反转
SpeedShow = SEG_CODE[speed]; //数码管显示速度
FWD_LED = 1; REV_LED = 0; //反转指示灯亮
KeyCode = 0;
break;
case 3: speed++; //速度加
if(speed == 10) speed = 1;
SpeedShow = SEG_CODE[speed]; //数码管显示速度
if(TR1 == 0) TR1 = 1; //打开定时器1,改变电机速度
KeyCode = 0;
break;
case 4: speed--; //速度减
if(speed == 0 || speed == -1 ) speed = 9;
SpeedShow = SEG_CODE[speed]; //数码管显示速度
if(TR1 == 0) TR1 = 1; //打开定时器1,改变电机速度
KeyCode = 0;
break;
case 5: TR1 = 0; //停止
speed =0;
SpeedShow = SEG_CODE[0]; //数码管显示速度
MotorDriver = 0x00;
KeyCode = 0;
break;
case 6: speed = 4;
SpeedShow = SEG_CODE[4];
MotorDriver = 0xE9;
KeyCode = 0;
break;
case 7: speed = 1;
SpeedShow = SEG_CODE[1];
MotorDriver = 0xDE;
KeyCode = 0;
break;
default: ;
}
}
//-----------------------------------------------------------------
//函数名称: void delay_ms(uint t)
//函数功能: 延时ms程序(粗略)
//调用子函数: 无
//输入参数: t
//返回值: 无
//-----------------------------------------------------------------
void delay_ms(uint t)
{
uchar i; while(t--) for(i = 0; i < 125; i++);
}
作者:柒ꪶⅈᧁꫝt.