设计一个基于单片机AT89C52的计算机步进电机控制系统,使用数字控制器进行步进电机的起停、正、反转控制以及步进电机的加速、减速控制。 其中,用K0-K5作为通电方式选择键,K0为单四拍正转,K1为单四拍反转,K2为双四拍正转,K3为双四拍反转,K4为八拍正转,K5为八拍反转;K6、K7分别为启动和停止控制;K8,K9为加减速控制,K10,K11为给定步数加减控制。正转时红色指示灯亮,反转时黄色指示灯亮,不转时绿色指示灯亮;LED第一位显示挡位,用后4位LED显示剩余工作步数[6]。 2.2 设计目标步进电机控制仿真,应该需要良好的人机界面,使人们操作更为简便,设计实现了步进电机的正反转,启停,工作方式选择,步数设定,加减速功能。在元器件的选用上应保证其技术在近5到10年不会被淘汰,应从其性能,价格方面考虑。 2.3 需求分析2.4 技术方案在该系统中,以AT89C52为中心,ULN2003A和28BYJ48四相28BYJ-48-5VDC步进电机是四六线制步进电机构成电机电路,通过单片机进行控制;采用3个LED灯构成状态显示电路,实时显示电机状态;矩阵键盘K0~K11将用户所需来选择步进电机的工作状态;6位数码管显示步数和速度挡位。如下图 2?1所示,单片机作为主控芯片,控制各个模块。步进电机调速需要调节脉冲频率,所以使用单片机的定时器中断是可行。 图 2?1 总体设计框图 2.5 方案论证通过Proteus仿真软件实现步进电机的模拟控制。系统各部分时序控制的配合:启动电源后,通过矩阵键盘选择功能;然后启动,通过单片机发出高低脉冲到电机驱动电机对电机进行驱动;通过定时器中断方式实现加减速功能是电机控制的关键点和难点。动态扫描方式进行数码管显示;在对单片机编程时,通过查阅有关资料进行理解分析,得到自己想要实现的程序。 最困难的是对定时器中断进行操作,因为对定时器中断的不熟悉,一直无法实现理想的控制。通过查阅单片机手册解决问题。 2.6 本章小结本章主要对此次设计进行总体方案的叙述。2.1介绍了课题需求,对课题的要求进行了详细的叙述;2.2主要对课题的最终要实现的目标进行叙述,对整个时间安排及器件选型时要注意的一些细节;2.3对要完成课题目标需要采用的软件,外部器件及实现方式进行了叙述;2.4对课程设计的总体方案进行叙述,并给出了系统总体框图;2.5对实现此次课程设计所采用的方案进行了论证 第3章 硬件设计3.1 单片机AT89S52是一种低功耗、高性能CMOS8位微控制器,具有8K 系统可编程Flash存储器。使用Atmel 公司高密度非易失性存储器技术制造,与工业80C51 产品指令和引脚完全兼容。片上Flash允许程序存储器在系统内编程,亦适于常规编程器。在单芯片上,拥有灵巧的8位CPU和在系统可编程Flash,使得AT89S52在众多嵌入式控制应用系统中得到广泛应用。CPU为Atmel公司生产的89C51/89C52/89C55等。出厂所配晶振频率为11.0592MH,每个机器周期为1.085us,用户更换晶振以提高速度。单片机引脚图如下图 3?1所示: 图 3?1 单片机引脚图 引脚介绍: P0口是一个8位漏极开路的双向I/O口。作为输出口,每位能驱动8个TTL逻 辑电平。对P0端口写“1”时,引脚用作高阻抗输入。当访问外部程序和数据存储器时,P0口也被作为低8位地址/数据复用。在这种模式下,P0不具有内部上拉电阻。在flash编程时,P0口也用来接收指令字节;在程序校验时,输出指令字节。程序校验 时,需要外部上拉电阻。P1口是一个具有内部上拉电阻的8 位双向I/O口,p1输出缓冲器能驱动4 个 TTL 逻辑电平。 此外,P1.0和P1.1分别作定时器/计数器2的外部计数输入(P1.0/T2)和定时器/计数器2 的触发输入(P1.1/T2EX)。 在flash编程和校验时,P1口接收低8位地址字节。P2 口是一个具有内部上拉电阻的8 位双向I/O 口,P2 输出缓冲器能驱动 4个TTL逻辑电平。对P2端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入 口使用。作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)。在访问外部程序存储器或用16位地址读取外部数据存储器(例如执行MOVX @DPTR)时,P2口送出高八位地址。在这种应用中,P2 口使用很强的内部上拉发送1。在使用8位地址(如MOVX @RI)访问外部数据存储器时,P2口输出P2锁存器的内容。在flash编程和校验时,P2口也接收高8位地址字节和一些控制信号。P3口是一个具有内部上拉电阻的8 位双向I/O 口,p3 输出缓冲器能驱动4个TTL逻辑电平。 P3口亦作为AT89S52特殊功能(第二功能)使用,如下表所示。在flash编程和校验时,P3口也接收一些控制信号。 RST:复位输入。当振荡器工作时,RST引脚出现两个机器周期以上高电平将使单片机复位。 ALE/PROG:当访问外部程序存储器或数据存储器时,ALE(地址锁存允许)输出脉冲用于锁存地址的低8位字节。一般情况下,ALE仍以时钟振荡频率的1/6输出固定的脉冲信号,因此它可对外输出时钟或用于定时目的。要注意的是:每当访问外部数据存储器时将跳过一个ALE脉冲。对FLASH存储器编程期间,该引脚还用于输入编程脉冲(PROG)。如有必要,可通过对特殊功能寄存器(SFR)区中的8EH单元的D0位置位,可禁止ALE操作。该位置位后,只有一条MOVX和MOVC指令才能将ALE激活。此外,该引脚会被微弱拉高,单片机执行外部程序时,应设置ALE禁止位无效. PSEN:程序储存允许(PSEN)输出是外部程序存储器的读选通信号,当AT89S52由外部程序存储器取指令(或数据)时,每个机器周期两次PSEN有效,即输出两个脉冲,在此期间,当访问外部数据存储器,将跳过两次PSEN信号。 EA/VPP:外部访问允许,欲使CPU仅访问外部程序存储器(地址为0000H-FFFFH),EA端必须保持低电平(接地)。需注意的是:如果加密位LB1被编程,复位时内部会锁存EA端状态。如EA端为高电平(接Vcc端),CPU则执行内部程序存储器的指令。FLASH存储器编程时,该引脚加上+12V的编程允许电源Vpp,当然这必须是该器件是使用12V编程电压Vpp。 3.2 晶振电路
图 3?2 晶振电路 3.3 复位电路复位是单片机的初始化工作,复位后中央处理器CPU和单片机内的其它功能部件都处在一定的初始状态,并从这个状态开始工作。为了防止程序执行过程中失步或运行紊乱,此处我们采用了上电复位,电路图如下图 3?3所示:
图 3?3 复位电路 3.4 单片机最小系统单片机最小系统是单片机运行的最基本条件:电源,单片机芯片,晶振电路,复位电路。电源为整个系统供能,单片机芯片运行程序,处理数据;晶振电路为单片机工作提供节拍,常被理解为单片机心脏;复位电路,在单片机上电时需要复位使程序从头开始运行。 3.5 键盘模块图 3?4 矩阵键盘 3.6 电动机及驱动步进电机模块如下图 3?5所示
图 3?5 电动机模块 3.6.1 步进电机本次设计采用的是
图 3?6 步进电机 开始时,开关SB接通电源,SA、SC、SD断开,B相磁极和转子0、3号齿对齐,同时,转子的1、4号齿就和C、D相绕组磁极产生错齿,2、5号齿就和D、A相绕组磁极产生错齿。 当开关SC接通电源,SB、SA、SD断开时,由于C相绕组的磁力线和1、4号齿之间磁力线的作用,使转子转动,1、4号齿和C相绕组的磁极对齐。而0、3号齿和A、B相绕组产生错齿,2、5号齿就和A、D相绕组磁极产生错齿。依次类推,A、B、C、D四相绕组轮流供电,则转子会沿着A、B、C、D方向转动。步进电机的按通电顺序不同,可分为单四拍,双四拍,八拍三种工作方式。如下图 3?7所示:
图 3?7 步进电机工作时序 当步进电机从A,B,C,D四相依次通电时,电动机正转,当反向通电则电动机反转。 3.6.2 驱动电路ULN2003A电路是美国Texas+Instruments公司和Sprague公司开发的高压大电流达林顿晶体管阵列电路。ULN2003是高耐压、大电流、内部由七个硅NPN和达林顿管组成的驱动芯片,如图 3?8所示。经常在以下电路中使用,作为:显示驱动、继电器驱动、照明灯驱动、电磁阀驱动、伺服电机、步进电机驱动等电路中。该电路的特点如下:ULN2003的每一对达林顿都串联一个2.7K的基极电阻,在5V的工作电压下它能与TTL和CMOS电路直接相连,可以直接处理原先需要标准逻辑缓冲器来处理的数据。ULN2003 是高压大电流达林顿晶体管阵列系列产品,具有电流增益高、工作电压高、温度范围宽、带负载能力强等特点,适应于各类要求高速大功率驱动的系统。ULN2003 工作电压高,工作电流大,灌电流可达500mA,并能在关态时承受50V 的电压,输出还可以在高负载电流并行运行。
图 3?8 ULN2003逻辑图 3.7 状态显示模块LED发光二极管显示步进电机的工作状态,它们分别接到单片机的P3.4~P3.6。如下图 3?9所示,让单片机连接到LED阴极,LED阳极接VCC。这样通过设置端口电平就能使LED亮灭,达到显示效果。本设计中,绿灯代表停止,红灯代表正转,黄灯代表反转。
图 3?9 LED状态显示 3.8 数码管显示LED数码管实际上是由七个发光管组成8字形构成的,加上小数点就是8个。这些段分别由字母a,b,c,d,e,f,g,dp来表示。当数码管特定的段加上电压后,这些特定的段就会发亮,以形成我们眼睛看到的字样了。通过分时轮流控制各个LED数码管的COM端,就使各个数码管轮流受控显示,这就是动态驱动。 本课题采用六位共阴极数码管来对电机步数,速度挡位进行显示。电路如下,接在单片机P0口i,P0必须添加上拉电阻,以为P0扣不加上拉电阻时处于开漏状态,只允许输出低电平,加上拉电阻后可输出高电平。电路如下图 3?10所示。
图 3?10 数码管电路 3.9 本章小结本章主要是对硬件设计的描述。先是呈现出总硬件系统框图及AT89S52单片机的接口及各种特性,再进行各部分电路的设计的原理,即:晶振电路,复位电路,键盘设计,驱动电路,电机工作状态显示部分,步数显示部分。 第4章 软件设计4.1 功能框图步进电机控制系统功能框图如下图 4?1所示,说明了每个模块与单片机之间的联系,控制逻辑;通过单片机控制键盘扫描,数码管显示,LED显示,步进电机控制等。 图 4?1功能框图 4.2 主流程图图 4?2 主流程图 首先进行工作方式选择,按下启动按钮,单片机判断是否启动,如果没启动绿灯亮,启动后默认正转红灯亮,可通过选择反转,反转时红灯亮。由此可以让步进电机按照指定的方式运转,并且剩余步数显示到数码管上;单片机判断步数是否走完。
4.3 步进电机模块4.3.1 步进电机工作方式说明4.3.2 实现流程
图 4?3 工作方式选择 4.4 矩阵键盘模块矩阵键盘是整个系统中最重要的环节,本设计采用3*4矩阵键盘,通过行列扫描获取键值。流程图如下图 4?4所示:
图 4?4 键盘扫描 4.5 数码管显示模块
图 4?5 数码管显示 4.6 本章小结本章主要描述了步进电机实现过程软件的设计思路,包括功能框图,主流程图,步进电机模块,关于工作方式的说明,键盘模块设计,数码管显示设计。 第5章 测试和验证5.1 任务分工5.2 搭建环境使用Kiel编写单片机程序,在Proteus中搭建仿真环境,将可执行程序加载到仿真单片机中,进行仿真,验证设计的正确性,可行性。 5.2.1 建立编辑编译环境- 创建AT89C52工程,C程序文档,进行编辑。如下图 5?1所示:
图 5?1 创建工程 - 编辑代码,选择生成.Hex文件。如下图 5?2所示:
图 5?2 生成目标文件 5.2.2 建立仿真和测试环境根据电路原理图在Proteus中搭建仿真模型。分别由数码管显示模块,复位电路,矩阵键盘,晶振电路,单片机主控芯片,步进电机模块,工作状态显示模块。如下图 5?3所示:
图 5?3 仿真模型 5.3 方案验证通过仿真来验证步进电机的控制是否合理。验证如下: 5.3.1 启动系统系统默认,步进电机启动转速为1档,给定步数500步;停止状态,绿灯亮。如下图 5?4所示:
图 5?4 启动系统 5.3.2 选择工作方式选择8拍正转工作方式,速度调为3档,步数给定500步,此时正转灯亮。仿真如下图 5?5所示。 选择8拍反转工作方式,速度调为6档,步数给定1000步,此时反转灯亮。仿真如下图 5?6所示。 选择单4拍正转,速度5档,给定步数300步,此时正转灯亮。仿真如下图 5?7所示。 选择双4拍反转,速度6档,步数给定500步,此时反转灯亮。仿真如下图 5?8所示。
图 5?5 8拍正转
图 5?6 8拍反转
图 5?7 单4拍正转
图 5?8 双四拍反转 5.4 问题与分析在本次课题中,我遇到一些问题如下: - 由于我对定时器中断的功能不是很熟悉,所以在使用定时器中断调节步进电机转速时出现问题,通过翻看单片机手册,学会了定时器中断的使用方法,和定时器时间的设定。
- 在程序的编写过程中,忽略了键盘复位检测,导致在仿真过程中,进行加减档,步数加减时,出现多加,多减的情况。后查阅资料添加了复位检测后得到了解决。
- 课程设计中,很多元器件的选用以及电路的设计都无法很快的完成。我们通过请教老师,以及同学之间的讨论,上网查阅资料来对这些问题进行了一一解决。
5.5 创新与扩展本次课程设计的核心在于利用51单片机作为主控芯片控制步进电机的基本功能,但需要进一步对其功能强化。步进电机可运用于很多场合,可以在系统中添加,WIFI模块,传感器模块,红外模块,通过外界环境变化,经过AD转化,改变步进电机工作情况。红外模块和WIFI模块,可以对电机进行无线控制。 5.6 本章小结本章主要对本次课程设计做了总结,人员的分工,控制的仿真实现,通过在PC机上搭建模拟平台来实现对步进电机控制的仿真,并记录了每个步骤的情况。 - #include <reg52.h>
- #include <stdio.h>
-
- #define uchar unsigned char
- #define uint unsigned int
-
- uchar code table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f}; //0~9段选码
- uchar Way_1[] = {0xfe, 0xfd, 0xfb, 0xf7};//单四拍正转
- uchar Way_2[] = {0xf7, 0xfb, 0xfd, 0xfe};//单四拍反转
- uchar Way_3[] = {0xfc, 0xf9, 0xf3, 0xf6};//双四拍正转
- uchar Way_4[] = {0xf6, 0xf3, 0xf9, 0xfc};//双四拍反转
- uchar Way_5[] = {0xfe, 0xfc, 0xfd, 0xf9, 0xfb, 0xf3, 0xf7, 0xf6};//八拍正转
- uchar Way_6[] = {0xf6, 0xf7, 0xf3, 0xfb, 0xf9, 0xfd, 0xfc, 0xfe};//八拍反转
- uchar code time_counter[6][2]={{0xdb,0xff},{0xb7,0xfe},{0x93,0xfe},{0x6f,0xfd},{0x4b,0xfd},{0x27, 0xfb}};
-
-
- uchar KeyValue; //键值
- uchar time; //定时器次数
- uchar way = -1; //工作方式选择
- uchar run = 0; //默认初始为停止状态
- uchar speed = 1;//速度调节,挡位1
- uint step = 500;//初始步数
- uchar num[4];
-
- sbit WE1 = P2^0; //数码管位选1
- sbit WE2 = P2^1; //数码管位选2
- sbit WE3 = P2^2; //数码管位选3
- sbit WE4 = P2^3; //数码管位选4
- sbit WE5 = P2^4; //数码管位选5
- sbit WE6 = P2^5; //数码管位选6
-
-
- sbit S = P3^4;//停止
- sbit F = P3^5;//正转
- sbit R = P3^6;//反转
-
-
- void delay(uint z)//延时函数
- {
- uint x, y;
-
- for(x = z; x > 0; x--)
- {
- for(y = 114; y > 0; y--);
- }
- }
-
-
- void display(uint n, uint m)//显示函数,n为步数,m为挡位
- {
- num[0] = n%10;//个位
- num[1] = n/10%10; //十位
- num[2] =(n/100)%10; //百位
- num[3] = (n/1000)%10; //千位
-
- P2 = 0xff; //挡位显示
- WE1 = 0;
- WE2 = 1;
- WE3 = 1;
- WE4 = 1;
- WE5 = 1;
- WE6 = 1;
- P0 = table[m];
- delay(5);
-
- P2 = 0xff; //"-"
- WE1 = 1;
- WE2 = 0;
- WE3 = 1;
- WE4 = 1;
- WE5 = 1;
- WE6 = 1;
- P0 = 0x40;
- delay(5);
-
-
- P2 = 0xff;
- WE1 = 1;
- WE2 = 1;
- WE3 = 0; //步数千位
- WE4 = 1;
- WE5 = 1;
- WE6 = 1;
- P0 = table[num[3]];
- delay(5);
-
- P2 = 0xff;
- WE1 = 1;
- WE2 = 1;
- WE4 = 0; //步数百位
- WE3 = 1;
- WE5 = 1;
- WE6 = 1;
- P0 = table[num[2]];
- delay(5);
-
- P2 = 0xff;
- WE1 = 1;
- WE2 = 1;
- WE5 = 0; //步数十位
- WE4 = 1;
- WE3 = 1;
- WE6 = 1;
- P0 = table[num[1]];
- delay(5);
-
- P2 = 0xff;
- WE1 = 1;
- WE2 = 1;
- WE6 = 0; //步数个位
- WE4 = 1;
- WE5 = 1;
- WE3 = 1;
- P0 = table[num[0]];
- delay(5);
-
- }
-
-
- void KeyScan()
- {
- KeyValue = 0;
- //列扫描
- P1 = 0xf0;
-
- if(P1 != 0xf0)
- {
- delay(10);//软件消抖
- if(P1!= 0xf0)
- {
- switch(P1)
- {
- case 0xe0: KeyValue = 1; break;
- case 0xd0: KeyValue = 2; break;
- case 0xb0: KeyValue = 3; break;
- }
-
- P1 = 0x0f;//行扫描
- switch(P1)
- {
- case 0x0e: KeyValue = KeyValue; break;
- case 0x0d: KeyValue = KeyValue+3; break;
- case 0x0b: KeyValue = KeyValue+6; break;
- case 0x07: KeyValue = KeyValue+9; break;
- }
- while(P1 != 0x0f);//松手检测
- }
-
- }
- return ;
- }
-
-
- void Select(void)//功能选择
- {
- switch(KeyValue)
- {
- case 1: way = 0;break; //单身拍正转
- case 2: way = 1;break; //单四拍反转
- case 3: way = 2;break; //双四拍正转
- case 4: way = 3;break; //双四拍反转
- case 5: way = 4;break; //八拍正转
- case 6: way = 5;break; //八拍反转
- case 7: run = 1;break; //启动按钮
- case 8: run = 0;time = 0;break; //停止按钮
- case 9:
- {
- speed++;
- if(speed > 6)
- {
- speed = 1;
- }
- break; //加速
- }
-
- case 10:
- {
- speed--;
- if(speed == 0)
- {
- speed = 6;
- }
- break;//减速
- }
- case 11:step += 10;break; //步数加
- case 12:step -= 10;break; //步数减
- }
- KeyValue = 0;
- }
-
- void Timer_Init()
- {
- EA = 1; //中断开关
- ET0 = 1;//打开定时器0中断
- TR0 = 1;//启动定时器0
- TMOD = 0x01; //16位计时模式
- TH0 = time_counter[speed-1][0]; //定时
- TL0 = time_counter[speed-1][1];
-
- }
-
-
- void main() //主函数
- {
- Timer_Init(); //定时器初始化,启动中断
- while(1)
- {
- KeyScan(); //键盘扫描
- Select(); //功能选择
- display(step, speed); //步数显示
- }
-
- }
-
- void Timer0() interrupt 1
- {
- TH0 = time_counter[speed-1][0]; //定时
- TL0 = time_counter[speed-1][1];
-
- if(run)
- {
- if(step == 0) //判断步数走完
- {
- run = 0;
- way = -1;
- }
- switch (way) //选择通电方式
- {
-
- case 0: //单四拍正转
- {
- step--;//步数--
- P3 = Way_1[time++];
- F = 0; //正转灯亮
- if(time == 4)
- {
- time = 0;
- }
- break;
- }
- case 1: //单四拍反转
- {
- step--;
- P3 = Way_2[time++];
- R = 0;
- if(time == 4)
- {
- time = 0;
- }
- break;
- }
- case 2: //双四拍正转
- {
- step--;
- P3 = Way_3[time++];
- F = 0;
- if(time == 4)
- {
- time = 0;
- }
- break;
- }
- case 3: //双四拍反转
- {
- step--;
- P3 = Way_4[time++];
- R = 0;
- if(time == 4)
- {
- time = 0;
- }
- break;
- }
- case 4: //八拍正转
- {
- step--;
- P3 = Way_5[time++];
- F = 0;
- if(time == 8)
- {
- time = 0;
- }
- break;
- }
- case 5: //八拍反转
- {
- step--;
- P3 = Way_6[time++];
- R = 0;
- if(time == 8)
- {
- time = 0;
- }
- break;
- }
- default: break;
- }
- }
- else
- {
- F = 1; //正转灯灭
- R = 1; //反转灯灭
- S = 0; //停止灯亮
- }
-
- }
复制代码
|