自动化工程训练
设计报告
学 院: 信息科学与工程学院
班 级:
姓 名:
学 号: 指导老师:
时间:2011年9月7日
目录
一、设计课题名称及要求 .............................. 2
二、设计思想和实施方案 .............................. 2
2.1 硬件部分............................................................................................................ 3
2.1.1 步进电机......................................................................................................... 3
2.1.2 单片机及其他硬件电路................................................................................. 5
三、硬件电路原理图及分析 ........................... 10
四、典型程序模块及典型编程技巧分析 . .................. 11
4.1 单片机程序分析.............................................................................................. 11
4.1.1 主程序步进电机控制模块........................................................................... 11
4.1.2 D ISPLAY 数码管显示模块............................................................................... 12
4.1.3 KEYSCAN 键盘扫描模块 .............................................................................. 12
4.1.4 定时器0的初始化模块和中断服务子程序模块 ...................................... 12
4.1.5 串口的初始化模块和中断服务子程序模块 .............................................. 13
4.1.6 D ELAY M S 软件延时模块 ................................................................................ 13
4.2 D ELPHI 程序分析 ............................................................................................... 13
4.2.1 系统所用组件 ................................................................................................ 13
4.2.2 应用软件的具体操作步骤........................................................................... 17
五、设计中遇到的问题及解决方法 ...................... 19
六、程序清单和程序注释 ............................. 20
七、收获与体会 .................................... 20
八、参考文献 ..................................... 21
附录:........................................... 22
附录1:硬件电路图 . .............................................................................................. 22
附录2:单片机C 语言程序清单 . .......................................................................... 23
附录3:汇编程序清单 . .......................................................................................... 30
一、设计课题名称及要求
1. 课题开发与设计方向
基于SST89E554RC 单片机实验系统的综合实验应用开发与设计:要求在掌握相关基础性实验项目的基础上,完成综合性实验项目,并在此基础上增加键盘输入功能、数据显示功能和通信功能等。
2.设计课题:基于Delphi 和单片机的串行通信的步进电机控制
3.设计要求:
(1)分别用C 语言编程完成硬件接口功能设计;
(2)基于单片机接口硬件电路设计调试;
(3)控制功能要求:小键盘给定分段速度,数码管显示当前步进电机方向、速度信息;
(4)具有本地与远程(串行方式下)功能;
(5)用Delphi 设计人机交互界面,可通过人机界面在上位机上实现对步进电机速度的控制。界面可显示当前电机速度信息。
4.实验设备:PC 机一台、小型单片机试验箱(含STC89C52RC 单片机、数码管、独立键盘)、步进电机。
二、设计思想和实施方案
本次课程设计主要内容是步进电机的控制和运行状态显示,硬件部分主要的涉及的步进电机、小键盘、数码管和相关控制电路。软件包括下位机单片机程序设计和上位机中Delphi 设计的人机交互界面,二者互相协作实现串口通信。
本次设计的思路是由计算机操作Delphi 制作的人机交互界面通过串口通信向单片机发送0到15之间的两位十进制数据或者十六进制数据作为速度设定,由单片机产生一系列脉冲信号实现对步进电机进行控制。单片机发出的脉冲信号控制步进电机的运行速度。同时单片机把步进电机运行状况反馈给计算机,由所编写的DELPHI 组件显示步进电机的运行状况并进行监控。此外还可以通过硬件的独立键盘发给单片机控制信号,实现控制步进电机的启动、停止、转向和速度等级,同时利用数码管显示步进电机当前的运转状态。
该设计实现了上位机对步进电机的控制。通过计算机与单片机通信发出脉冲信号控制步进电机的启动﹑停止运行。通过C 语言编程,能够实现对步进电机正反两个方向运行的控制,单片机控制板用来发送脉冲信号直接控制步进电机各相线路电流的通断。根据计算机所发送的十进制数,再根据驱动电路的原理和步进电机的特性说明来确定步进电机各相的具体通电步骤,同时控制板上可以显示出步进电机的转向和速度等级。
2.1 硬件部分
2.1.1 步进电机
步进电动机已成为除直流电动机和交流电动机以外的第三类电动机。传统电动机作为机电能量转换装置,在人类的生产和生活进入电气化过程中起着关键的作用。可是在人类社会进入自动化时代的今天,传统电动机的功能已不能满足工厂自动化和办公自动化等各种运动控制系统的要求。为适应这些要求,发展了一系列新的具备控制功能的电动机系统,其中较有自己特点,且应用十分广泛的一类便是步进电动机。
步进电动机的发展与计算机工业密切相关。自从步进电动机在计算机外围设备中取代小型直流电动机以后,使其设备的性能提高,很快地促进了步进电动机的发展。另一方面,微型计算机和数字控制技术的发展,又将作为数控系统执行部件的步进电动机推广应用到其他领域,如电加工机床、小功率机械加工机床、测量仪器、光学和医疗仪器以及包装机械等。
步进电动机有如下特点:
(1)步进电动机的角位移与输入脉冲数严格成正比,因此,当它转一转后,没有累计误差,具有良好的跟随性。
(2)由步进电动机与驱动电路组成的开环数控系统,既非常简单、廉价,又非常可靠。同时,它也可以与角度反馈环节组成高性能的闭环数控系统。
(3) 步进电动机的动态响应快,易与起停、正反转及变速。
(4) 速度可在相当宽的范围内平滑调节,低速下仍能保证获得大转 矩,因此,一般可以不用减速器而直接驱动负载。
(5) 步进电动机只能通过脉冲电源供电才能运行,它不能直接使用交流电源和直流电源。
(6) 步进电动机存在振荡和失步现象,必须对控制系统和机械负载采取相应的措施。
(7)步进电动机自身的噪声和振动较大,带惯性负载的能力较差。
随着科学技术的进步,步进电机已广泛运用在需要高定位精度、高分解能、高响应性、信赖性等灵活控制性高的机械系统中。在生产过程中要求自动化、省人力、效率高的机器中,我们很容易发现步进电机的踪迹,尤其以重视速度、位置控制、需要精确操作各项指令动作的灵活控制性场合步进电机用得最多。
步进电机是数字控制电机, 它将脉冲信号转变成角位移, 即给一个脉冲信号, 步进电机就转动一个角度, 因此非常适合对数字系统的控制。步进电机可分为反应式步进电机(简称“VR ”) 、永磁式步进电机(简称“PM ”) 和混合式步进电机(简称“HB ”) 。步进电机区别于其他控制电机的最大特点是, 通过输入脉冲信号来
进行控制, 即电机的总转动角度由输入脉冲数决定, 而电机的转速由脉冲信号频率决定。步进电机的驱动电路根据控制信号工作, 控制信号由各类控制器来产生。其基本原理作用如下:
(1)步进电机的转动和转向控制
在非超载的情况下,电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数。当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度,即步距角。它的旋转是以固定的角度一步一步运行的。通过控制脉冲个数来控制角位移量,可以达到准确定位的目的。通过控制脉冲频率来控制电机转动的速度和加速度,达到调速的目的。步进电机作为执行元件, 广泛应用在各种自动化控制系统中。本次课程设计控制的是实验室提供的四项八拍步进电机,电压时DC12V ,其励磁线圈及励磁顺序如图2.1和表2.1所示。
表2.1 四项八拍步进电机励磁顺序
图2.1 四项八拍步进电机励磁线圈
制换相顺序, 通电换相。这一过程称为“脉冲分配”。例如:
四相步进电机的
单四拍工作方式, 其各相通电顺序为 A —B —C —D 。通电控制脉冲必须严格按照这一顺序分别控制 A、B 、C 、D 相的通断, 控制步进电机的转向。如果给定工作方式正序换相通电, 则步进电机正转; 如果按反序换相通电, 则电机就反转。
(2)步进电机的速度控制
如果给步进电机发一个控制脉冲, 它就转一步, 再发一个脉冲, 它会再转一步。两个脉冲的间隔越短, 步进电机就转得越快。调整控制器发出的脉冲频率, 就可以对步进电机进行调速。
考虑到步进电机的转速、停止的位置只取决于脉冲信号的频率和脉冲,结合单片机课程中所学的硬件知识,决定使用单片机P1向步进电机传送供电脉冲。其中P1.0表示A 相线路,P1.1表示B 相线路,P1.2表示C 相线路,P1.4表示D 相线路。当P1.X=1时,该相通电,当P1.X=0时,该相断电。通过控制P1口的输出来控制步进电机的励磁顺序。设定步进电机共有18个速度等级,数字越大,电机转动速度越快。利用软件编程实现不同延时,得到不同的脉冲频率,从而实现对步进电机的转速控制。
2.1.2 单片机及其他硬件电路
1.单片机
步进电机的控制系统主要由单片机STC89C52RC 、数码管及其显示电路、独立键盘、USB 芯片转串口芯片PL2303 、电源和时钟电路等几个单元组成,单片机系统如图2.2所示:
图2.2 系统整体方案设计
本设计所用的STC89C52RC 是一个低功耗,高性能8位单片机。STC89C52RC 具有如下特点:40个引脚,4k Bytes Flash片内程序存储器,128 bytes的随机存取数据存储器(RAM ),32个外部双向输入/输出(I/O)口,5个中断优先级
2层中断嵌套中断,2个16位可编程定时计数器,2个全双工串行通信口,看门狗(WDT )电路,片内时钟振荡器。如图2.3所示。
图2.3 单片机STC89C52RC 示意图
STC89C52RC 单片机中两条主电源引脚,两条外接晶体引脚,4条控制电源复位引脚,32条I/O引脚。其中19脚、20脚外接晶振11.0592MHZ 及两个30P 电容,9脚外接复位电路,40脚、20脚分别接电源正负极,31脚接+5V电源,以上所述是保证单片机能正常工作的外接条件。
P0口:共8条引脚,即39—32脚,是双向8为三态I/O口。本次设计P0口为数据端口。由于本次设计采用的共阴数码管,所以8位输出信号为欲输出的0-9的共阴极段选码。
P1口:共8条引脚,即1—8脚,P1口是一个带有内部上拉电阻的8为双向I/O口。本次设计中其中P0.0到P0.4控制步进电机驱动器,通过P0.0发出的时钟脉冲来控制步进电机的起停,通过控制P0.0口各脉冲的快慢来实现对步进电机的定速控制,通过P0.0时钟脉冲的次数来实现对步进电机的定位控制。改变P0.0到P0.4输出脉冲的顺序来控制步进电机的正反转。
P3口:共8条引脚,即10—17脚。 P3口的每个引脚都有各自的第二功能。P3.0口的第二功能时RXD ,既串行数据接收端,P3.1口的第二功能是TXD, 既串行数据发送端。因此P3.0、P3.1在系统中起着与计算机互传数据的作用,是步进电机通信控制的主要端口。P3.2、P3.3作为数码管显示电路锁存使能信号。P3.2为段锁存信号,P3.3为位锁存信号。
2. 串口通信
串口是计算机上一种非常通用设备通信的协议(不要与通用串行总线
Universal SerialBus或者USB 混淆)。大多数计算机包含两个基于RS232的串口。串口同时也是仪器仪表设备通用的通信协议;很多GPIB 兼容的设备也带有RS-232口。同时,串口通信协议也可以用于获取远程采集设备的数据。
串口通信的概念非常简单,串口按位(bit )发送和接收字节。尽管比按字节(byte )的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总常不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。
典型地,串口用于ASCII 码字符的传输。通信使用3根线完成:地线,发送,接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通行的端口,这些参数必须匹配:
(1)波特率:这是一个衡量通信速度的参数。它表示每秒钟传送的bit 的个数。例如300波特表示每秒钟发送300个bit 。当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz 。这意味着串口通信在数据线上的采样率为4800Hz 。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB 设备的通信。
(2)数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII 码是0~127(7位)。扩展的ASCII 码是0~255(8位)。如果数据使用简单的文本(标准ASCII 码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
(3)停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
(4)奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位位1,这样就有3个逻辑高位。高位和低
位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
RS-232是IBM-PC 及其兼容机上的串行连接标准。可用于许多用途,比如连接鼠标、打印机或者Modem ,同时也可以接工业仪器仪表。用于驱动和连线的改进,实际应用中RS-232的传输长度或者速度常常超过标准的值。RS-232只限于PC 串口和设备间点对点的通信。RS-232串口通信最远距离是50英尺。目前RS-232是计算机与通信工业中应用最广泛的一种串行接口,考虑到计算机有RS-232接口,可以很方便地与计算机之间通信,该系统通讯模块选用RS-232串行数据接口。在电气特性上RS-232采用负逻辑,要求高、低两信号间有较大的幅度。RS232接口如图2.4所示。
图2.4 RS232接口示意图
USB 中文含义是“通用串行总线”。它不是一种新的总线标准,而是应用在PC 领域的接口技术。它到现在已经发展为2.0版本,成为目前电脑中的标准扩展接口。USB 用一个4针插头作为标准插头,采用菊花链形式可以把所有的外设连接起来,最多可以连接127个外部设备,并且不会损失带宽。USB 具有传输速度快(USB1.1是12Mbps ,USB2.0是480Mbps, USB3.0是5 Gbps),使用方便,支持热插拔,连接灵活,独立供电等优点,可以连接鼠标、键盘、打印机、扫描仪、摄像头、闪存盘、MP3机、手机、数码相机、移动硬盘、外置光软驱、USB 网卡、ADSL Modem、Cable Modem等,几乎所有的外部设备。
USB 需要主机硬件、操作系统和外设三个方面的支持才能工作。目前的主板一般都采用支持USB 功能的控制芯片组,主板上也安装有USB 接口插座,而且除了背板的插座之外,主板上还预留有USB 插针,可以通过连线接到机箱前面作为前置USB 接口以方便使用。而且USB 接口还可以通过专门的USB 连机线实现双机互连,并可以通过Hub 扩展出更多的接口。USB 自从1996年推出后,已成功替代串口和并口,并成为当今个人电脑和大量智能设备的必配的接口之一。
在本次设计中,为了方便直接利用实验箱中的USB 芯片转串口芯片PL2303,
将单片机的串行信号通过转换芯片实现USB 的通信,利用专门的USB 连机线实现双机互连实现单片机和计算机的串行通信。 3.显示系统
本电路显示系统有8个共阴数码管和内部集成的74HC573芯片组成,在单片机系统中,通常用数码显示器来显示各种数字或符号。由于它具有显示清晰、高亮度、使用电压低、寿命长的特点,因此使用非常广泛。
该系统LED 显示方法采用动态扫描的方法。在多位LED 显示时,为了简化电路,降低成本,利用锁存器来实现段码和位码的分别锁存,点亮数码管。要想显示不同的字符,就必须采用扫描的方法轮流点亮各位LED ,即在每一瞬间只使某一位显示字符。在此瞬间,段选控制I/O口输出相应字符段选码(字型码),而位选码则控制I/O口在该显示位送入选通电平(因为LED 为共阴,故应送低电平),以保证该位显示相应字符。如此轮流,使每位分时显示该位应显示的字符。段选码和位选码每送入一次都会延时,,因人眼的视觉暂留时间很短为0.1s ,所以显示的时间间隔不必超过20ms ,并保持延时一段时间,以造成视觉暂留效果,给人看上去每个数码管总在亮。这种方式成为软件扫描显示。
利用8个数码管中的前三个实时显示系统当前状态。左起第一个数码管显示当前电机的运行状态:“+”表示电机处于正转状态,不显示表示电机处于反转运行状态。第二个数码管和第三个数码管用来显示当前步进电机的速度等级。
速度等级分为01-15等共15个等级。其中第二个数码管显示速度等级的十位数,第三个数码管显示速度等级的个位数。当步进电机的转向和速度发生改变时,数码管的显示也会随之发生改变,可实现实时显示步进电机的运转方向和运转素等等信息。显示系统示意图如图2.5所示。
图2.5 显示系统示意图
4. 硬件控制系统
此外,本次设计还用到了实验箱中的独立键盘来控制步进电机的启动、暂停、转动方向和转动速度等控制。其中第一行中的5、6键实现方向设定,使电机转动方向为正向或者反向。3、4键实现运行或暂停功能,使电机运行或者暂停。2、7键依次表示加速和减速,,实现电机速度的设定。独立键盘的按键示意图如图
2.3所示。
图2.3 小键盘按键示意图
三、硬件电路原理图及分析
图3.1 步进电机控制系统硬件电路原理图
如图3.1所示,该原理图为步进电机控制系统硬件电路原理图,主要涉及到单片机STC89C52RC 芯片、独立键盘及数码管显示单元和步进电机及其驱动电路。其中,单片机P0口的P0.0~P0.7分别接数码管的A 、B 、C 、D 、E 、F 、G 和DP 。P1口低四位P1.0~P1.3依次接电机驱动电路的A 、B 、C 和D 。P2口的P2.0~P2.7分别接独立键盘K1~K8。P3口的P3.2和P3.3分别接数码管单元锁存芯片的使能端,P3.2给定段锁存信号,P3.3给定位锁存信号。
四、典型程序模块及典型编程技巧分析
4.1 单片机程序分析
4.1.1 主程序步进电机控制模块
主程序主要是判断操作,综合调用各子函数,从而完成对步进电机的相关控制,并在PC 机及数码管上实时显示电机的状态、转向和速度等级。
本次设计中,实现了电机运行状态、转向和1-15个速度等级的转速控制。当程序开始运行时,电机进入初始状态:正转,速度等级为1级。数码管显示信息为01。操作如下:
(1)当小键盘中“5”键被按下时,电机的转向变为正转。当小键盘中“6”键被按下时,电机的转向变为反转。在转向发生改变的同时,数码管第一位显示也相应发生变化:当电机正转时,数码管不显示;当电机正转时,显示“-”,表示反转;
(2)当小键盘中“3”键被按下时,电机开始转动。在运行过程中,当“4”键被再次按下时,电机暂停转动;
(3)当小键盘中“2”键被按下时,电机的转速发生改变,开始减速。当小键盘中“7”键被按下时,电机的转速发生改变,开始加速。每按一次,速度等级改变一级。速度改变的同时,数码管第二位和第三位显示相应速度档位两位数值。数码管第二位显示十位,第三位显示个位。
主程序主要通过设置标志变量来控制实现以上操作。在C 语言中设置运动状态标志变量StopFlag 、方向标志变量RunFlag 、速度等级Speed 和按键键值num 。通过KeyScan 键盘扫描模块判断键盘哪个按键被按下,将键值赋给num ,根据num 的不同改变运动状态标志变量StopFlag 、方向标志变量RunFlag 、速度等级Speed 。再通过Display 数码管显示模块实现电机运行情况的数码管显示。通过定时器中断服务子程序void Timer0_isr(void) interrupt 1来实现步进电机的速度改变。通过串口中断服务子程序来实现上位机Delphi 界面对于单片机的控制和速度返回。步进电机的控制主要靠主程序中一下步骤实现:
(1)单独开辟区域,存放数码管的段码和位码信息以及返回给上位机的数
字信息。
(2)将单片机定时器0和串口初始化,为下面步进电机转动和与上位机串行通信做好准备。
(3)不断循环利用KeyScan 键盘扫描函数判断是有有按键按下以及判断按键键值,根据键值改变步进电机的运动状态标志变量StopFlag 、方向标志变量RunFlag 、速度等级Speed 。同时,根据改变后的方向标志变量RunFlag 和速度等级Speed 延时经过运算,将其相应的共阴极段选码赋给数组TempData 中的前三位,已根据键盘输入刷新数码管,使数码管常亮。
4.1.2 Display 数码管显示模块
该模块为数码管显示函数,用于动态扫描数码管。主要将主函数模块中保存下的电机的转向和转速档位的信息送到数码管上显示。输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示如输入0表示从第一个显示。Num 表示需要显示的位数,如需要显示99两位数值则该值输入2。为防止有交替重影,先清空数据,在根据输入参数依次锁存各个数码管的位码和段码,从而实现数码管的动态扫描显示。
4.1.3 KEYSCAN 键盘扫描模块
键盘扫描模块主要用来扫描小键盘,确定在操作过程中那个按键被按下,从而将设定信息反馈给系统,实现相关控制和数码管的显示。
在KEYSCAN 子程序中,扫描主要包括以下几个步骤:
(1) 判断是否有按键按下,若无键按下则反复检测
(2) 若有键按下,利用软件延时要消除键抖动,确认有键按下
(3) 根据按下的按键设置相应的键值。
通过单片机的P2口输入K1~K7的信息。首先判断P2口数值,无任何键按下则读入的全是“1”,若有键按下则读入的对应位是“0”,其余全是“1”。调用延时函数,如果右键按下,延时后,键值即P2口数据不改变,为“0”的位为被按下的键,通过对比此时P2口的值,返回当前按下的数字键的键值。如果没有键被按下,则返回键值为“0”。
4.1.4 定时器0的初始化模块和中断服务子程序模块
步进电机的运转由定时器T0来控制。有定时器来改变步进电机的各相通断电时间。定时器T0的初始化模块定义了定时器使用模式1,开中断,并令定时器中断为高优先级。定时器中断服务子程序中首先定义定时器0约1ms 中断一次。首先根据Speed 来控制励磁换相时间间隔,再根据RunFlag 来控制励磁顺序是正
转还是反转。
4.1.5 串口的初始化模块和中断服务子程序模块
由于本次设计要实现上位机Delphi 界面与单片机的串口通信,从而实现上位机对于单片机的速度控制,且单片可将目前步进电机运行状态反馈给上位机显示,因此要利用串口中断来实现上述串行通信。在串口初试化中通过设置TMOD 和SCON 将串行口工作方式设置为方式1,将定时器T1设为方式2,定时器初值为FDH ,即波特率为9600kbps ,然后开中断。在串口中断服务子程序中,首先判断是否为接收中断,如为接收中端,将标志位清零,然后将接收值赋给Speed 。接收后,单片机要将目前步进电机的运行状态返回给上位机,要利用发送中断,根据Speed 和RunFlag 改变发送字符串的内容后将字符串发送给上位机。
4.1.6 DelayMs 软件延时模块
程序中存在两个延时时间不同的延时模块,根据不同情况调用不同模块,可以实现较好的效果。
4.2 Delphi 程序分析
本次设计中的一个难点就是上位机中界面的设计和上位机和单片机中间的串行通信。
本此设计选用界面设计软件是Borland 公司推出的快速应用开发工具Delphi 。它具有功能强大、易于学习和使用、编程效率高以及易于调试等特点。它将可视化应用程序开发环境IDE 、面向对象程序设计语言Object Pascal、快速编译器和数据库编程工具完美地结合在一起,使用它可以更直观、更快捷地开发出高质量的应用程序,具有丰富的组件集、强大的代码自动生成功能和丰富的数据库管理工具等。使用它的集成开发环境,编程人员可以更快地建立应用程序。
4.2.1 系统所用组件
本项目设计所用到的Delphi 组件有:一个BtnSend 、三个Button 、一个CheckBox 、一个CmbBaud 、一个CmbByteSize 、一个CmbParity 、一个CmbPort 、一个CmbStopBit 、两个Image 、十二个Label 、两个Memo 、一个串行通讯组件SPComm 和一个XPMnaifest 组件等。界面如图4.1所示。
图4.1 上位机Delphi 界面
目前,利用 Delphi实现串口通信的常用的方法有 3种:一是利用控件,如 MSCOMM 控件和 SPCOMM控件;二是使用 API函数;三是调用其他串口通信程序。其中利用 API编写串口通信程序较为复杂,需要掌握大量的通信知识。相比较而言,利用 SPCOMM控件则相对较简单,并且该控件具有丰富的与串口通信密切相关的属性及事件,提供了对串口的各种操作,而且还支持多线程。所以此项目所用到的控件是SPCOMM 。SPCOMM 组件需要从网上自行下载。
1.SPCOMM 组件
(1)首先SPCOMM 的安装
○1在Delphi 中打开SPCOMM 组件选择下拉菜单 Component中的 Install Component 选项,弹出如图4.2所示:
图4.2 SPCOMM组件的安装
在 Library path处填写 SPCOMM控件所在的路径,其他各项可用默认值,点击 OK按钮。如图4.3所示:
图4.3 SPCOMM组件属性设置
○2.安装后,在 System控件面板中将出现一个红色控件 COM。现在就可以像 Delphi自带控件一样使用 COM控件了。
(2)其次SPCOMM 的属性、方法和事件
○1属性
●CommName:表示 COM1、 COM2等串口的名字;
●BaudRate:根据实际需要设定的波特率,在串口打开后也可更改此值,实际波特率随之更改;
●ParityCheck:表示是否需要奇偶校验;
●ByteSize:根据实际情况设定的字节长度;
●Parity:奇偶校验位;
●StopBits:停止位;
●SendDataEmpty:这是一个布尔型属性,为 true时表示发送缓存为空,或者发送队列里没有信息;为 false时表示发送缓存不为空,或者发送队列里有信息。
○2方法
●Startcomm方法用于打开串口,当打开失败时通常会报错。错误主要有 7种:⑴串口已经打开;⑵打开串口错误;⑶文件句柄不是通信句柄;⑷不能够安装通信缓存;⑸不能产生事件;⑹不能产生读进程;⑺不能产生写进程;
●StopComm方法用于关闭串口,没有返回值;
●WriteCommData(pDataToWrite: PChar;dwSizeofDataToWrite:Word )方法是个带有布尔型返回值的函数,用于将一个字符串发送到写进程,发送成功返回 true ,发送失败返回 false。执行此函数将立即得到返回值,发送操作随后执行。该函数有两个参数,其中 pDataToWrite是要发送字符串, dwSizeofDataToWrite是发送字符串的长度。
○3事件
●OnReceiveData :procedure (Sender: TObject;Buffer: Pointer;BufferLength: Word) of object 当有数据输入缓存时将触发该事件,在这里可以对从串口收到的数据进行处理。 Buffer 中是收到的数据, BufferLength 是收到的数据长度。
●OnReceiveError : procedure(Sender: TObject; EventMask : DWORD) 当接收数据出现错误时将触发该事件。
2. 实现步骤
(1)制作窗体
启动Delphi7.0,创建一个新的应用程序界面,在新窗体Form 上放置上述的组件。
(2)设置各个组件的属性
根据所要实现的功能设置所用的组件的属性如下:
BtnSend 组件:该组件用来设置数据,当数据设置完成后点击即可将数据发送给单片机。
Button 组件:这个组件是用来做各种开关的,有打开串口、清楚发送接受速度数据。在打开串口(TButton )组件中的Enabled 属性中设置成true 、其它
的Enabled 属性中设置成false ,所起的作用是打开应用程序时默认的值有打开串口开关可以操作其它的都不能操作。当打开以后,打开串口开关变成不可操作的其他的开关可操作。
CheckBox 组件:用于选择发送十六进制数据。
CmbBaud 组件:该组件用来选择串口波特率,在组件的Items 属性中设置出110、300、600、1200、2400、9600、14000和19200八个选项供用户选择。
CmbByteSize 组件:该组件用来选择数据位,在组件的Items 属性中设置出5、6、7和8四个选项供用户选择。
CmbParity 组件:该组件用来选择校验位,在组件的Items 属性中设置出Even 、Mark 、None 、Odd 和Space 五个选项供用户选择。
CmbPort 组件:该组件用来选择端口号,在组件的Items 属性中设置出COM1、COM2、COM3、COM4和COM5五个选项供用户选择。
CmbStopBit 组件:该组件用来选择停止位,在组件的Items 属性中设置出1、
1.5、2三个选项供用户选择。
Image 组件:该组件用于指示串口的打开和关闭状态。
Label 组件:这些组件的作用是用来输入提示文字。
Memo 组件:该组件是用来反映发送和接收到的数据。
4.2.2 应用软件的具体操作步骤
把硬件电路连接好,使用USB 数据线与计算机相连接通上电源做好一切准备。
打开应用软件,在窗口中选择通讯端口,设置好串行通信的波特率、数据位、停止位和校验位后,点击打开串口按钮。当选择端口号错误时会提示错误信息,如图4.4所示。
图4.4 端口错误提示
打开串口成功后,串口显示灯会变红。用户可以根据需要输入十进制或者十六进制的速度等级,该信号就可通过串口发送给单片机,步进电机就可以根据计算机发出的指令来工作。在应用程序的界面上就会返回当前步进电机的速度等级和正反转信息。正常运作的界面如图4.5和图4.6所示。
图4.5 正转时正常运行界面
图4.6 反转时正常运行界面
当设定速度错误时,也会提示错误信息,如图4.7所示。
图4.7 速度设定错误提示
当按下清除信息键,对应信息将被清除。
五、设计中遇到的问题及解决方法
1. 数码管看不到示数或数码管看上去全亮。
开始的时候,数码管总是很暗,可以隐约看到有数字,但是亮度过于微弱,无法辨认。这主要是是调用数码管显示子函数的频率不够高,且子程序里的循环扫描中延时过短,导致数字显示不满明显。
解决方法:提高函数频率,并增加子函数循环扫描时的延时时间。还有,在子函数中每次显示之前都将数据清零,再赋值,防止出现重影,可使数码管有个较好的显示效果。
2.串口通信时返回上位机的信息是乱码。
在串口通信调试过程中,可以实现由上位机发出命令控制单片机,从而控制步进电机的运行状态。但是从单片机返回给上位机的信息是乱码。
解决方法:通过上网查询,出现乱码可能是波特率、数据位、停止位等协议不同导致的。首先应该将上位机接收协议和单片机中设定的协议一致。调试后,仍然出现乱码。除了上述情况会导致出现乱码,硬件线路也可能会导致乱码的出现。最终换了一块STC89C52RC 单片机,返回的信息正确,由此确定出现乱码的原因是硬件线路问题。
3. 程序调试应遵循的步骤
①查看各端口地址是否正确;
②根据程序运行中的出现的现象分析出程序出错的位置;
③在TDDEBUG 中进行单步或设置断点进行调试;
④使程序有少到多有简单到复杂逐一验证其的正确性,以排除错误。
六、程序清单和程序注释
详见附录
七、收获与体会
两个星期的自动化工程训练终于结束了。虽然时间看上去很长,但是我还需要为保研做准备。当验收结束时,可以说得是苦尽甘来。在整个设计过程中,回想起来,从刚开始的完全迷惑到最后的豁然开朗,真是经历了很多,过程虽然艰辛,但是却让我们学到很多的东西,不仅巩固了课上所学过的理论知识,而且学到了很多在书本上所没有学到过的知识。在上课的时候,老师经常强调在写一个程序的时候,磨刀不误砍柴工,一定要事先把程序原理方框图画出来,在这次课程设计,我深切体会到了这种编程方法的重要性。课程设计中设计的问题都需要很多代码相互协作才能完成,画出程序方框图是很有必要的。通过程序方框图,在做设计的过程中,我们每一步要做什么,每一步要完成什么任务都有一个很清楚的思路,而且在程序测试的过程中也有利于查错。
其次,对于编程工具的使用还不太熟悉,虽然上机实验课用到过,但是始终没有掌握得很牢靠。在课程设计时,通过大量调试,我对于如何排错、查错,如何查看每一步的运行结果和每个寄存器的内容有了很明确的了解,从而帮助我在调试过程中确保程序的正确性。这次的步进电机控制,虽然原理上很简单,但是如果对实验箱中的芯片不熟悉不熟悉的话,还是需要下一番苦功的。因此,在开始自动化工程训练后,我通过翻阅大量资料,对于设计中所涉及的芯片有了初步的了解。接着在实验,通过设计,将实际与自己所学的联系起来,最终做到对芯片的功能要熟练掌握。
除了硬件部分,此次设计还要设计人机交互界面。由于以前《数据库》这门课上使用Delphi 做过课程设计,因此我们再次选用了Delphi 来设计界面。时隔两年,再次使用Delphi ,我发现还是有许多东西已经忘记了。于是一边看以前做的设计,一边结合借来的资料,开始了对Delphi 的再次研究。幸好有以前的基础,还算比较顺利。这次设计的难题在于单片机和Delphi 设计界面实现串口通信。而Delphi 中并没相关控件,通过上网查询,了解了串口通信的定义以及相关协议的设定,从网上下载安装了SPCOMM 控件,最终编程实现了上位机和单片机的串口通信。
通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,从而提高自己的实际动手能力和独立思考的能力。在设计的过程中遇到问题,可以说得是困难重重,难免会遇到过各种各样的问题。如果自己单独钻研,很难发
现其中的错误,这时,就应该和老师、同学和有经验的学长交流,发现错误,解决错误。在交流和检查错误的过程中发现自己的不足之处,发现自己对以前所学过的知识理解得不够深刻,掌握得不够牢固,同时对知识的理解和接受能力还不是太强。
这次课程设计任务终于顺利完成了,在设计中遇到了很多问题,最后在自己的努力下以及与同学之间和老师的探讨下,终于一个一个得解决了问题,当看到步进电机按照自己编写的程序显示的时候,心情异常兴奋。这次课程设计在培养我们编程能力的同时,也让我学会了要完成一项任务,除了需要过硬的技术和能力之外,还要有不屈不挠的精神,有些同学就是没有耐心做下去,才没有好好的完成任务。所以,感谢我们的指导老师,谢谢!
八、参考文献
[1] 凌玉华,《单片机原理及应用系统设计》,长沙:中南大学出版社,2006;
[2] 李志民,《单片机程序设计实验指导书》,长沙:中南大学教材科,2006;
[3] 李朝青,《单片机原理及接口技术》(第三版),北京:北京航空航天大学出版社,2005;
[4] 吕伟臣,《Delphi 8入门与提高》,北京:清华大学出版社,2004;
附录:
附录1:硬件电路图
附录2:单片机C 语言程序清单
/*-----------------------------------------------
名称:步进电机
内容:本程序用于控制4相步进电机常规驱动 ,4个按键分别控制加速、减速、开启、停止,数码管显示 01-15速度等级,数字越大,速度越大
------------------------------------------------*/
#include
#define KeyPort P2
#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换
sbit LATCH1=P3^2;//定义锁存使能端口 段锁存
sbit LATCH2=P3^3;// 位锁存
unsigned char rettext[]={"当前速度为 "};
unsigned char code
dofly_DuanMa[12]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00,0x40};// 显示段码值0~9
unsigned char code
dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮, 即位码
unsigned char code rettextzhi[]={'0','1','2','3','4','5','6','7','8','9'}; unsigned char TempData[8]; //存储显示值的全局变量
sbit A1=P1^0; //定义步进电机连接端口
sbit B1=P1^1;
sbit C1=P1^2;
sbit D1=P1^3;
#define Coil_AB1 {A1=1;B1=1;C1=0;D1=0;}//AB相通电,其他相断电
#define Coil_BC1 {A1=0;B1=1;C1=1;D1=0;}//BC相通电,其他相断电
#define Coil_CD1 {A1=0;B1=0;C1=1;D1=1;}//CD相通电,其他相断电
#define Coil_DA1 {A1=1;B1=0;C1=0;D1=1;}//D相通电,其他相断电
#define Coil_A1 {A1=1;B1=0;C1=0;D1=0;}//A相通电,其他相断电
#define Coil_B1 {A1=0;B1=1;C1=0;D1=0;}//B相通电,其他相断电
#define Coil_C1 {A1=0;B1=0;C1=1;D1=0;}//C相通电,其他相断电
#define Coil_D1 {A1=0;B1=0;C1=0;D1=1;}//D相通电,其他相断电
#define Coil_OFF {A1=0;B1=0;C1=0;D1=0;}//全部断电
unsigned char Speed=1;
bit StopFlag;
bit RunFlag;
void Display(unsigned char FirstBit,unsigned char Num);
void Init_Timer0(void);
void initiate(void);
unsigned char KeyScan(void);
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M ,精确延时请使用汇编, 大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M ,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
主函数
------------------------------------------------*/
main()
{
unsigned int i=512;//旋转一周时间
unsigned char num;
unsigned int j=10;
Init_Timer0();
initiate();
Coil_OFF
while(1) //正向
{
num=KeyScan(); //循环调用按键扫描
if(num==7)//第一个按键, 速度等级增加
{
if(Speed
Speed++;
}
else if(num==2)//第二个按键,速度等级减小
{
if(Speed>1) Speed--; }
else if(num==4)
{
Coil_OFF
StopFlag=1;
}
else if(num==3)
{
StopFlag=0;
}
else if(num==5)
{
RunFlag=0;
j=10;
}
else if(num==6)
{
RunFlag=1;
j=11;
}
TempData[0]=dofly_DuanMa[j];
TempData[1]=dofly_DuanMa[Speed/10];//分解显示信息,如要显示68,则68/10=6 68%10=8
TempData[2]=dofly_DuanMa[Speed%10];
}
}
/*------------------------------------------------
显示函数,用于动态扫描数码管
输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示 如输入0表示从第一个显示。
Num表示需要显示的位数,如需要显示99两位数值则该值输入2
------------------------------------------------*/
void Display(unsigned char FirstBit,unsigned char Num)
{
static unsigned char i=0;
DataPort=0; //清空数据,防止有交替重影
LATCH1=1; //段锁存
LATCH1=0;
DataPort=dofly_WeiMa[i+FirstBit]; //取位码
LATCH2=1; //位锁存
LATCH2=0;
DataPort=TempData[i]; //取显示数据,段码
LATCH1=1; //段锁存
LATCH1=0;
i++;
if(i==Num)
i=0;
}
/*------------------------------------------------
定时器初始化子程序
------------------------------------------------*/
void Init_Timer0(void)
{
TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
//TH0=0x00; //给定初值
//TL0=0x00;
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
PT0=1; //优先级打开
}
/*------------------------------------------------
定时器中断子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1
{
static unsigned char times,i;
extern bit RunFlag;
TH0=(65536-1000)/256;
TL0=(65536-1000)%256; //重新赋值 1ms
Display(0,8);
if(!StopFlag)
{
if(times==(17-Speed))//最大值15,所以最小间隔值17-15=2 {
times=0;
if(RunFlag)
{
switch(i)
{
case 0:Coil_D1;i++;break; case 1:Coil_C1;i++;break; case 2:Coil_B1;i++;break; case 3:Coil_A1;i++;break; case 4:i=0;break;
default:break;
}
}
if(!RunFlag)
{
switch(i)
{
case 0:Coil_A1;i++;break;
case 1:Coil_B1;i++;break;
case 2:Coil_C1;i++;break; case 3:Coil_D1;i++;break; case 4:i=0;break; default:break; }
}
}
times++;
}
}
/*------------------------------------------------ 按键扫描函数,返回扫描键值
------------------------------------------------*/ unsigned char KeyScan(void)
{
unsigned char keyvalue;
if(KeyPort!=0xff)
{
DelayMs(10);
if(KeyPort!=0xff)
{
keyvalue=KeyPort;
while(KeyPort!=0xff); switch(keyvalue) { case 0xfe:return 1;break; case 0xfd:return 2;break; case 0xfb:return 3;break; case 0xf7:return 4;break; case 0xef:return 5;break; case 0xdf:return 6;break; case 0xbf:return 7;break; case 0x7f:return 8;break; default:return 0;break; } }
}
return 0;
}
/*------------------------------------------------ 串口初始化
------------------------------------------------*/ void initiate()
{
TMOD|=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
EA=1;
ES=1;
SCON=0x50;
PCON=0x00;
}
/*------------------------------------------------ 串口中断程序
------------------------------------------------*/ void S_inter () interrupt 4//串行中断服务程序 {
int i;
extern bit RunFlag;
if(RI) //判断是接收中断产生 {
RI=0; //标志位清零 Speed=SBUF; rettext[11]=rettextzhi[Speed/10]; rettext[12]=rettextzhi[Speed%10];
if(RunFlag==0)
{rettext[10]='+';}
else if(RunFlag==1)
{rettext[10]='-';}
for(i=0;i
{
SBUF=rettext[i];
while(!TI) ;
TI=0;
}
}
}
附录3:汇编程序清单
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, jpeg, ExtCtrls, SPComm, XPMan;
type
TForm1 = class(TForm)
Comm1: TComm;
CmbPort: TComboBox;
CmbBaud: TComboBox;
CmbParity: TComboBox;
CmbStopBit: TComboBox;
CmbByteSize: TComboBox;
Image1: TImage;
Image2: TImage;
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
Button3: TButton;
Label1: TLabel;
Label2: TLabel;
BtnSend: TButton;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Label7: TLabel;
Memo2: TMemo;
CheckBox1: TCheckBox;
XPManifest1: TXPManifest;
Label8: TLabel;
Label9: TLabel;
Label10: TLabel;
Label11: TLabel;
Label12: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure BtnSendClick(Sender: TObject);
procedure Comm1ReceiveData(Sender: TObject; Buffer: Pointer; BufferLength: Word);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
flag:integer=1;//串口开关标志
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
flag:=flag+1;
if (flag mod 2=0) then
begin
CmbPort.Enabled:=false;
CmbBaud.Enabled:=false;
CmbParity.Enabled:=false;
CmbStopBit.Enabled:=false;
CmbByteSize.Enabled:=false;
BtnSend.Enabled:=true;
Comm1.CommName:=CmbPort.Text;
Comm1.BaudRate:=strtoint(CmbBaud.Text);
case CmbParity.ItemIndex of
0: Comm1.Parity:=Even;
1: Comm1.Parity:=Mark;
2: Comm1.Parity:=None;
3: Comm1.Parity:=Odd;
4: Comm1.Parity:=Space;
end;
case CmbStopBit.ItemIndex of
0: Comm1.StopBits:=_1;
1: Comm1.StopBits:=_1_5;
2: Comm1.StopBits:=_2;
end;
case CmbByteSize.ItemIndex of
0: Comm1.ByteSize:=_5;
1: Comm1.ByteSize:=_6;
2: Comm1.ByteSize:=_7;
3: Comm1.ByteSize:=_8;
end;
Comm1.StartComm;
Image1.Visible:=true;
Image2.Visible:=false;
Button1.Caption:='关闭串口'
end
else
begin
CmbPort.Enabled:=true;
CmbBaud.Enabled:=true;
CmbParity.Enabled:=true;
CmbStopBit.Enabled:=true;
CmbByteSize.Enabled:=true;
BtnSend.Enabled:=false;
Comm1.StopComm;
Image1.Visible:=false;
Image2.Visible:=true;
Button1.Caption:='打开串口'
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Memo1.Lines.Clear;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
Memo2.Lines.Clear;
end;
procedure TForm1.BtnSendClick(Sender: TObject);
var
i,x:integer;
p:pchar;
ch1,ch2:integer;
begin
if CheckBox1.Checked then
begin
p:=Pchar(Memo2.Lines.Text);
x:=Length(Memo2.Lines.Text);
if ((p)^ in ['0'..'9','A'..'F','a'..'f'])
['0'..'9','A'..'F','a'..'f']) then
and ((p+1)^ in
begin
if x
begin
if ((p)^ in ['0'..'9']) then
ch1:=strtoint(p^)
else if ((p)^ in ['A'..'F']) then
ch1:=ord(p^)-55
else ch1:=ord(p^)-87;
if ((p+1)^ in ['0'..'9']) then
ch2:=strtoint((p+1)^)
else if ((p+1)^ in ['A'..'F']) then
ch2:=ord((p+1)^)-55
else ch2:=ord((p+1)^)-87;
i:=ch1*16+ch2;
Comm1.WriteCommData(@i,1);
end
else showmessage('只输入两位,谢谢!')
end
else showmessage('请输入两位16进制数!');
end
else
begin
i:=strtoint(Memo2.Lines.Text);
if i>15 then
showmessage('请输入01~15之间的数!')
else if i
showmessage('请输入01~15之间的数!')
else
Comm1.WriteCommData(@i,1);
end;
end;
procedure TForm1.Comm1ReceiveData(Sender: TObject; Buffer: Pointer; BufferLength: Word);
var
p:pchar;
begin
p:=Buffer;
Memo1.Lines.Add(p); end;
end.
自动化工程训练
设计报告
学 院: 信息科学与工程学院
班 级:
姓 名:
学 号: 指导老师:
时间:2011年9月7日
目录
一、设计课题名称及要求 .............................. 2
二、设计思想和实施方案 .............................. 2
2.1 硬件部分............................................................................................................ 3
2.1.1 步进电机......................................................................................................... 3
2.1.2 单片机及其他硬件电路................................................................................. 5
三、硬件电路原理图及分析 ........................... 10
四、典型程序模块及典型编程技巧分析 . .................. 11
4.1 单片机程序分析.............................................................................................. 11
4.1.1 主程序步进电机控制模块........................................................................... 11
4.1.2 D ISPLAY 数码管显示模块............................................................................... 12
4.1.3 KEYSCAN 键盘扫描模块 .............................................................................. 12
4.1.4 定时器0的初始化模块和中断服务子程序模块 ...................................... 12
4.1.5 串口的初始化模块和中断服务子程序模块 .............................................. 13
4.1.6 D ELAY M S 软件延时模块 ................................................................................ 13
4.2 D ELPHI 程序分析 ............................................................................................... 13
4.2.1 系统所用组件 ................................................................................................ 13
4.2.2 应用软件的具体操作步骤........................................................................... 17
五、设计中遇到的问题及解决方法 ...................... 19
六、程序清单和程序注释 ............................. 20
七、收获与体会 .................................... 20
八、参考文献 ..................................... 21
附录:........................................... 22
附录1:硬件电路图 . .............................................................................................. 22
附录2:单片机C 语言程序清单 . .......................................................................... 23
附录3:汇编程序清单 . .......................................................................................... 30
一、设计课题名称及要求
1. 课题开发与设计方向
基于SST89E554RC 单片机实验系统的综合实验应用开发与设计:要求在掌握相关基础性实验项目的基础上,完成综合性实验项目,并在此基础上增加键盘输入功能、数据显示功能和通信功能等。
2.设计课题:基于Delphi 和单片机的串行通信的步进电机控制
3.设计要求:
(1)分别用C 语言编程完成硬件接口功能设计;
(2)基于单片机接口硬件电路设计调试;
(3)控制功能要求:小键盘给定分段速度,数码管显示当前步进电机方向、速度信息;
(4)具有本地与远程(串行方式下)功能;
(5)用Delphi 设计人机交互界面,可通过人机界面在上位机上实现对步进电机速度的控制。界面可显示当前电机速度信息。
4.实验设备:PC 机一台、小型单片机试验箱(含STC89C52RC 单片机、数码管、独立键盘)、步进电机。
二、设计思想和实施方案
本次课程设计主要内容是步进电机的控制和运行状态显示,硬件部分主要的涉及的步进电机、小键盘、数码管和相关控制电路。软件包括下位机单片机程序设计和上位机中Delphi 设计的人机交互界面,二者互相协作实现串口通信。
本次设计的思路是由计算机操作Delphi 制作的人机交互界面通过串口通信向单片机发送0到15之间的两位十进制数据或者十六进制数据作为速度设定,由单片机产生一系列脉冲信号实现对步进电机进行控制。单片机发出的脉冲信号控制步进电机的运行速度。同时单片机把步进电机运行状况反馈给计算机,由所编写的DELPHI 组件显示步进电机的运行状况并进行监控。此外还可以通过硬件的独立键盘发给单片机控制信号,实现控制步进电机的启动、停止、转向和速度等级,同时利用数码管显示步进电机当前的运转状态。
该设计实现了上位机对步进电机的控制。通过计算机与单片机通信发出脉冲信号控制步进电机的启动﹑停止运行。通过C 语言编程,能够实现对步进电机正反两个方向运行的控制,单片机控制板用来发送脉冲信号直接控制步进电机各相线路电流的通断。根据计算机所发送的十进制数,再根据驱动电路的原理和步进电机的特性说明来确定步进电机各相的具体通电步骤,同时控制板上可以显示出步进电机的转向和速度等级。
2.1 硬件部分
2.1.1 步进电机
步进电动机已成为除直流电动机和交流电动机以外的第三类电动机。传统电动机作为机电能量转换装置,在人类的生产和生活进入电气化过程中起着关键的作用。可是在人类社会进入自动化时代的今天,传统电动机的功能已不能满足工厂自动化和办公自动化等各种运动控制系统的要求。为适应这些要求,发展了一系列新的具备控制功能的电动机系统,其中较有自己特点,且应用十分广泛的一类便是步进电动机。
步进电动机的发展与计算机工业密切相关。自从步进电动机在计算机外围设备中取代小型直流电动机以后,使其设备的性能提高,很快地促进了步进电动机的发展。另一方面,微型计算机和数字控制技术的发展,又将作为数控系统执行部件的步进电动机推广应用到其他领域,如电加工机床、小功率机械加工机床、测量仪器、光学和医疗仪器以及包装机械等。
步进电动机有如下特点:
(1)步进电动机的角位移与输入脉冲数严格成正比,因此,当它转一转后,没有累计误差,具有良好的跟随性。
(2)由步进电动机与驱动电路组成的开环数控系统,既非常简单、廉价,又非常可靠。同时,它也可以与角度反馈环节组成高性能的闭环数控系统。
(3) 步进电动机的动态响应快,易与起停、正反转及变速。
(4) 速度可在相当宽的范围内平滑调节,低速下仍能保证获得大转 矩,因此,一般可以不用减速器而直接驱动负载。
(5) 步进电动机只能通过脉冲电源供电才能运行,它不能直接使用交流电源和直流电源。
(6) 步进电动机存在振荡和失步现象,必须对控制系统和机械负载采取相应的措施。
(7)步进电动机自身的噪声和振动较大,带惯性负载的能力较差。
随着科学技术的进步,步进电机已广泛运用在需要高定位精度、高分解能、高响应性、信赖性等灵活控制性高的机械系统中。在生产过程中要求自动化、省人力、效率高的机器中,我们很容易发现步进电机的踪迹,尤其以重视速度、位置控制、需要精确操作各项指令动作的灵活控制性场合步进电机用得最多。
步进电机是数字控制电机, 它将脉冲信号转变成角位移, 即给一个脉冲信号, 步进电机就转动一个角度, 因此非常适合对数字系统的控制。步进电机可分为反应式步进电机(简称“VR ”) 、永磁式步进电机(简称“PM ”) 和混合式步进电机(简称“HB ”) 。步进电机区别于其他控制电机的最大特点是, 通过输入脉冲信号来
进行控制, 即电机的总转动角度由输入脉冲数决定, 而电机的转速由脉冲信号频率决定。步进电机的驱动电路根据控制信号工作, 控制信号由各类控制器来产生。其基本原理作用如下:
(1)步进电机的转动和转向控制
在非超载的情况下,电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数。当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度,即步距角。它的旋转是以固定的角度一步一步运行的。通过控制脉冲个数来控制角位移量,可以达到准确定位的目的。通过控制脉冲频率来控制电机转动的速度和加速度,达到调速的目的。步进电机作为执行元件, 广泛应用在各种自动化控制系统中。本次课程设计控制的是实验室提供的四项八拍步进电机,电压时DC12V ,其励磁线圈及励磁顺序如图2.1和表2.1所示。
表2.1 四项八拍步进电机励磁顺序
图2.1 四项八拍步进电机励磁线圈
制换相顺序, 通电换相。这一过程称为“脉冲分配”。例如:
四相步进电机的
单四拍工作方式, 其各相通电顺序为 A —B —C —D 。通电控制脉冲必须严格按照这一顺序分别控制 A、B 、C 、D 相的通断, 控制步进电机的转向。如果给定工作方式正序换相通电, 则步进电机正转; 如果按反序换相通电, 则电机就反转。
(2)步进电机的速度控制
如果给步进电机发一个控制脉冲, 它就转一步, 再发一个脉冲, 它会再转一步。两个脉冲的间隔越短, 步进电机就转得越快。调整控制器发出的脉冲频率, 就可以对步进电机进行调速。
考虑到步进电机的转速、停止的位置只取决于脉冲信号的频率和脉冲,结合单片机课程中所学的硬件知识,决定使用单片机P1向步进电机传送供电脉冲。其中P1.0表示A 相线路,P1.1表示B 相线路,P1.2表示C 相线路,P1.4表示D 相线路。当P1.X=1时,该相通电,当P1.X=0时,该相断电。通过控制P1口的输出来控制步进电机的励磁顺序。设定步进电机共有18个速度等级,数字越大,电机转动速度越快。利用软件编程实现不同延时,得到不同的脉冲频率,从而实现对步进电机的转速控制。
2.1.2 单片机及其他硬件电路
1.单片机
步进电机的控制系统主要由单片机STC89C52RC 、数码管及其显示电路、独立键盘、USB 芯片转串口芯片PL2303 、电源和时钟电路等几个单元组成,单片机系统如图2.2所示:
图2.2 系统整体方案设计
本设计所用的STC89C52RC 是一个低功耗,高性能8位单片机。STC89C52RC 具有如下特点:40个引脚,4k Bytes Flash片内程序存储器,128 bytes的随机存取数据存储器(RAM ),32个外部双向输入/输出(I/O)口,5个中断优先级
2层中断嵌套中断,2个16位可编程定时计数器,2个全双工串行通信口,看门狗(WDT )电路,片内时钟振荡器。如图2.3所示。
图2.3 单片机STC89C52RC 示意图
STC89C52RC 单片机中两条主电源引脚,两条外接晶体引脚,4条控制电源复位引脚,32条I/O引脚。其中19脚、20脚外接晶振11.0592MHZ 及两个30P 电容,9脚外接复位电路,40脚、20脚分别接电源正负极,31脚接+5V电源,以上所述是保证单片机能正常工作的外接条件。
P0口:共8条引脚,即39—32脚,是双向8为三态I/O口。本次设计P0口为数据端口。由于本次设计采用的共阴数码管,所以8位输出信号为欲输出的0-9的共阴极段选码。
P1口:共8条引脚,即1—8脚,P1口是一个带有内部上拉电阻的8为双向I/O口。本次设计中其中P0.0到P0.4控制步进电机驱动器,通过P0.0发出的时钟脉冲来控制步进电机的起停,通过控制P0.0口各脉冲的快慢来实现对步进电机的定速控制,通过P0.0时钟脉冲的次数来实现对步进电机的定位控制。改变P0.0到P0.4输出脉冲的顺序来控制步进电机的正反转。
P3口:共8条引脚,即10—17脚。 P3口的每个引脚都有各自的第二功能。P3.0口的第二功能时RXD ,既串行数据接收端,P3.1口的第二功能是TXD, 既串行数据发送端。因此P3.0、P3.1在系统中起着与计算机互传数据的作用,是步进电机通信控制的主要端口。P3.2、P3.3作为数码管显示电路锁存使能信号。P3.2为段锁存信号,P3.3为位锁存信号。
2. 串口通信
串口是计算机上一种非常通用设备通信的协议(不要与通用串行总线
Universal SerialBus或者USB 混淆)。大多数计算机包含两个基于RS232的串口。串口同时也是仪器仪表设备通用的通信协议;很多GPIB 兼容的设备也带有RS-232口。同时,串口通信协议也可以用于获取远程采集设备的数据。
串口通信的概念非常简单,串口按位(bit )发送和接收字节。尽管比按字节(byte )的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总常不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。
典型地,串口用于ASCII 码字符的传输。通信使用3根线完成:地线,发送,接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通行的端口,这些参数必须匹配:
(1)波特率:这是一个衡量通信速度的参数。它表示每秒钟传送的bit 的个数。例如300波特表示每秒钟发送300个bit 。当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz 。这意味着串口通信在数据线上的采样率为4800Hz 。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB 设备的通信。
(2)数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII 码是0~127(7位)。扩展的ASCII 码是0~255(8位)。如果数据使用简单的文本(标准ASCII 码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
(3)停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
(4)奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位位1,这样就有3个逻辑高位。高位和低
位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
RS-232是IBM-PC 及其兼容机上的串行连接标准。可用于许多用途,比如连接鼠标、打印机或者Modem ,同时也可以接工业仪器仪表。用于驱动和连线的改进,实际应用中RS-232的传输长度或者速度常常超过标准的值。RS-232只限于PC 串口和设备间点对点的通信。RS-232串口通信最远距离是50英尺。目前RS-232是计算机与通信工业中应用最广泛的一种串行接口,考虑到计算机有RS-232接口,可以很方便地与计算机之间通信,该系统通讯模块选用RS-232串行数据接口。在电气特性上RS-232采用负逻辑,要求高、低两信号间有较大的幅度。RS232接口如图2.4所示。
图2.4 RS232接口示意图
USB 中文含义是“通用串行总线”。它不是一种新的总线标准,而是应用在PC 领域的接口技术。它到现在已经发展为2.0版本,成为目前电脑中的标准扩展接口。USB 用一个4针插头作为标准插头,采用菊花链形式可以把所有的外设连接起来,最多可以连接127个外部设备,并且不会损失带宽。USB 具有传输速度快(USB1.1是12Mbps ,USB2.0是480Mbps, USB3.0是5 Gbps),使用方便,支持热插拔,连接灵活,独立供电等优点,可以连接鼠标、键盘、打印机、扫描仪、摄像头、闪存盘、MP3机、手机、数码相机、移动硬盘、外置光软驱、USB 网卡、ADSL Modem、Cable Modem等,几乎所有的外部设备。
USB 需要主机硬件、操作系统和外设三个方面的支持才能工作。目前的主板一般都采用支持USB 功能的控制芯片组,主板上也安装有USB 接口插座,而且除了背板的插座之外,主板上还预留有USB 插针,可以通过连线接到机箱前面作为前置USB 接口以方便使用。而且USB 接口还可以通过专门的USB 连机线实现双机互连,并可以通过Hub 扩展出更多的接口。USB 自从1996年推出后,已成功替代串口和并口,并成为当今个人电脑和大量智能设备的必配的接口之一。
在本次设计中,为了方便直接利用实验箱中的USB 芯片转串口芯片PL2303,
将单片机的串行信号通过转换芯片实现USB 的通信,利用专门的USB 连机线实现双机互连实现单片机和计算机的串行通信。 3.显示系统
本电路显示系统有8个共阴数码管和内部集成的74HC573芯片组成,在单片机系统中,通常用数码显示器来显示各种数字或符号。由于它具有显示清晰、高亮度、使用电压低、寿命长的特点,因此使用非常广泛。
该系统LED 显示方法采用动态扫描的方法。在多位LED 显示时,为了简化电路,降低成本,利用锁存器来实现段码和位码的分别锁存,点亮数码管。要想显示不同的字符,就必须采用扫描的方法轮流点亮各位LED ,即在每一瞬间只使某一位显示字符。在此瞬间,段选控制I/O口输出相应字符段选码(字型码),而位选码则控制I/O口在该显示位送入选通电平(因为LED 为共阴,故应送低电平),以保证该位显示相应字符。如此轮流,使每位分时显示该位应显示的字符。段选码和位选码每送入一次都会延时,,因人眼的视觉暂留时间很短为0.1s ,所以显示的时间间隔不必超过20ms ,并保持延时一段时间,以造成视觉暂留效果,给人看上去每个数码管总在亮。这种方式成为软件扫描显示。
利用8个数码管中的前三个实时显示系统当前状态。左起第一个数码管显示当前电机的运行状态:“+”表示电机处于正转状态,不显示表示电机处于反转运行状态。第二个数码管和第三个数码管用来显示当前步进电机的速度等级。
速度等级分为01-15等共15个等级。其中第二个数码管显示速度等级的十位数,第三个数码管显示速度等级的个位数。当步进电机的转向和速度发生改变时,数码管的显示也会随之发生改变,可实现实时显示步进电机的运转方向和运转素等等信息。显示系统示意图如图2.5所示。
图2.5 显示系统示意图
4. 硬件控制系统
此外,本次设计还用到了实验箱中的独立键盘来控制步进电机的启动、暂停、转动方向和转动速度等控制。其中第一行中的5、6键实现方向设定,使电机转动方向为正向或者反向。3、4键实现运行或暂停功能,使电机运行或者暂停。2、7键依次表示加速和减速,,实现电机速度的设定。独立键盘的按键示意图如图
2.3所示。
图2.3 小键盘按键示意图
三、硬件电路原理图及分析
图3.1 步进电机控制系统硬件电路原理图
如图3.1所示,该原理图为步进电机控制系统硬件电路原理图,主要涉及到单片机STC89C52RC 芯片、独立键盘及数码管显示单元和步进电机及其驱动电路。其中,单片机P0口的P0.0~P0.7分别接数码管的A 、B 、C 、D 、E 、F 、G 和DP 。P1口低四位P1.0~P1.3依次接电机驱动电路的A 、B 、C 和D 。P2口的P2.0~P2.7分别接独立键盘K1~K8。P3口的P3.2和P3.3分别接数码管单元锁存芯片的使能端,P3.2给定段锁存信号,P3.3给定位锁存信号。
四、典型程序模块及典型编程技巧分析
4.1 单片机程序分析
4.1.1 主程序步进电机控制模块
主程序主要是判断操作,综合调用各子函数,从而完成对步进电机的相关控制,并在PC 机及数码管上实时显示电机的状态、转向和速度等级。
本次设计中,实现了电机运行状态、转向和1-15个速度等级的转速控制。当程序开始运行时,电机进入初始状态:正转,速度等级为1级。数码管显示信息为01。操作如下:
(1)当小键盘中“5”键被按下时,电机的转向变为正转。当小键盘中“6”键被按下时,电机的转向变为反转。在转向发生改变的同时,数码管第一位显示也相应发生变化:当电机正转时,数码管不显示;当电机正转时,显示“-”,表示反转;
(2)当小键盘中“3”键被按下时,电机开始转动。在运行过程中,当“4”键被再次按下时,电机暂停转动;
(3)当小键盘中“2”键被按下时,电机的转速发生改变,开始减速。当小键盘中“7”键被按下时,电机的转速发生改变,开始加速。每按一次,速度等级改变一级。速度改变的同时,数码管第二位和第三位显示相应速度档位两位数值。数码管第二位显示十位,第三位显示个位。
主程序主要通过设置标志变量来控制实现以上操作。在C 语言中设置运动状态标志变量StopFlag 、方向标志变量RunFlag 、速度等级Speed 和按键键值num 。通过KeyScan 键盘扫描模块判断键盘哪个按键被按下,将键值赋给num ,根据num 的不同改变运动状态标志变量StopFlag 、方向标志变量RunFlag 、速度等级Speed 。再通过Display 数码管显示模块实现电机运行情况的数码管显示。通过定时器中断服务子程序void Timer0_isr(void) interrupt 1来实现步进电机的速度改变。通过串口中断服务子程序来实现上位机Delphi 界面对于单片机的控制和速度返回。步进电机的控制主要靠主程序中一下步骤实现:
(1)单独开辟区域,存放数码管的段码和位码信息以及返回给上位机的数
字信息。
(2)将单片机定时器0和串口初始化,为下面步进电机转动和与上位机串行通信做好准备。
(3)不断循环利用KeyScan 键盘扫描函数判断是有有按键按下以及判断按键键值,根据键值改变步进电机的运动状态标志变量StopFlag 、方向标志变量RunFlag 、速度等级Speed 。同时,根据改变后的方向标志变量RunFlag 和速度等级Speed 延时经过运算,将其相应的共阴极段选码赋给数组TempData 中的前三位,已根据键盘输入刷新数码管,使数码管常亮。
4.1.2 Display 数码管显示模块
该模块为数码管显示函数,用于动态扫描数码管。主要将主函数模块中保存下的电机的转向和转速档位的信息送到数码管上显示。输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示如输入0表示从第一个显示。Num 表示需要显示的位数,如需要显示99两位数值则该值输入2。为防止有交替重影,先清空数据,在根据输入参数依次锁存各个数码管的位码和段码,从而实现数码管的动态扫描显示。
4.1.3 KEYSCAN 键盘扫描模块
键盘扫描模块主要用来扫描小键盘,确定在操作过程中那个按键被按下,从而将设定信息反馈给系统,实现相关控制和数码管的显示。
在KEYSCAN 子程序中,扫描主要包括以下几个步骤:
(1) 判断是否有按键按下,若无键按下则反复检测
(2) 若有键按下,利用软件延时要消除键抖动,确认有键按下
(3) 根据按下的按键设置相应的键值。
通过单片机的P2口输入K1~K7的信息。首先判断P2口数值,无任何键按下则读入的全是“1”,若有键按下则读入的对应位是“0”,其余全是“1”。调用延时函数,如果右键按下,延时后,键值即P2口数据不改变,为“0”的位为被按下的键,通过对比此时P2口的值,返回当前按下的数字键的键值。如果没有键被按下,则返回键值为“0”。
4.1.4 定时器0的初始化模块和中断服务子程序模块
步进电机的运转由定时器T0来控制。有定时器来改变步进电机的各相通断电时间。定时器T0的初始化模块定义了定时器使用模式1,开中断,并令定时器中断为高优先级。定时器中断服务子程序中首先定义定时器0约1ms 中断一次。首先根据Speed 来控制励磁换相时间间隔,再根据RunFlag 来控制励磁顺序是正
转还是反转。
4.1.5 串口的初始化模块和中断服务子程序模块
由于本次设计要实现上位机Delphi 界面与单片机的串口通信,从而实现上位机对于单片机的速度控制,且单片可将目前步进电机运行状态反馈给上位机显示,因此要利用串口中断来实现上述串行通信。在串口初试化中通过设置TMOD 和SCON 将串行口工作方式设置为方式1,将定时器T1设为方式2,定时器初值为FDH ,即波特率为9600kbps ,然后开中断。在串口中断服务子程序中,首先判断是否为接收中断,如为接收中端,将标志位清零,然后将接收值赋给Speed 。接收后,单片机要将目前步进电机的运行状态返回给上位机,要利用发送中断,根据Speed 和RunFlag 改变发送字符串的内容后将字符串发送给上位机。
4.1.6 DelayMs 软件延时模块
程序中存在两个延时时间不同的延时模块,根据不同情况调用不同模块,可以实现较好的效果。
4.2 Delphi 程序分析
本次设计中的一个难点就是上位机中界面的设计和上位机和单片机中间的串行通信。
本此设计选用界面设计软件是Borland 公司推出的快速应用开发工具Delphi 。它具有功能强大、易于学习和使用、编程效率高以及易于调试等特点。它将可视化应用程序开发环境IDE 、面向对象程序设计语言Object Pascal、快速编译器和数据库编程工具完美地结合在一起,使用它可以更直观、更快捷地开发出高质量的应用程序,具有丰富的组件集、强大的代码自动生成功能和丰富的数据库管理工具等。使用它的集成开发环境,编程人员可以更快地建立应用程序。
4.2.1 系统所用组件
本项目设计所用到的Delphi 组件有:一个BtnSend 、三个Button 、一个CheckBox 、一个CmbBaud 、一个CmbByteSize 、一个CmbParity 、一个CmbPort 、一个CmbStopBit 、两个Image 、十二个Label 、两个Memo 、一个串行通讯组件SPComm 和一个XPMnaifest 组件等。界面如图4.1所示。
图4.1 上位机Delphi 界面
目前,利用 Delphi实现串口通信的常用的方法有 3种:一是利用控件,如 MSCOMM 控件和 SPCOMM控件;二是使用 API函数;三是调用其他串口通信程序。其中利用 API编写串口通信程序较为复杂,需要掌握大量的通信知识。相比较而言,利用 SPCOMM控件则相对较简单,并且该控件具有丰富的与串口通信密切相关的属性及事件,提供了对串口的各种操作,而且还支持多线程。所以此项目所用到的控件是SPCOMM 。SPCOMM 组件需要从网上自行下载。
1.SPCOMM 组件
(1)首先SPCOMM 的安装
○1在Delphi 中打开SPCOMM 组件选择下拉菜单 Component中的 Install Component 选项,弹出如图4.2所示:
图4.2 SPCOMM组件的安装
在 Library path处填写 SPCOMM控件所在的路径,其他各项可用默认值,点击 OK按钮。如图4.3所示:
图4.3 SPCOMM组件属性设置
○2.安装后,在 System控件面板中将出现一个红色控件 COM。现在就可以像 Delphi自带控件一样使用 COM控件了。
(2)其次SPCOMM 的属性、方法和事件
○1属性
●CommName:表示 COM1、 COM2等串口的名字;
●BaudRate:根据实际需要设定的波特率,在串口打开后也可更改此值,实际波特率随之更改;
●ParityCheck:表示是否需要奇偶校验;
●ByteSize:根据实际情况设定的字节长度;
●Parity:奇偶校验位;
●StopBits:停止位;
●SendDataEmpty:这是一个布尔型属性,为 true时表示发送缓存为空,或者发送队列里没有信息;为 false时表示发送缓存不为空,或者发送队列里有信息。
○2方法
●Startcomm方法用于打开串口,当打开失败时通常会报错。错误主要有 7种:⑴串口已经打开;⑵打开串口错误;⑶文件句柄不是通信句柄;⑷不能够安装通信缓存;⑸不能产生事件;⑹不能产生读进程;⑺不能产生写进程;
●StopComm方法用于关闭串口,没有返回值;
●WriteCommData(pDataToWrite: PChar;dwSizeofDataToWrite:Word )方法是个带有布尔型返回值的函数,用于将一个字符串发送到写进程,发送成功返回 true ,发送失败返回 false。执行此函数将立即得到返回值,发送操作随后执行。该函数有两个参数,其中 pDataToWrite是要发送字符串, dwSizeofDataToWrite是发送字符串的长度。
○3事件
●OnReceiveData :procedure (Sender: TObject;Buffer: Pointer;BufferLength: Word) of object 当有数据输入缓存时将触发该事件,在这里可以对从串口收到的数据进行处理。 Buffer 中是收到的数据, BufferLength 是收到的数据长度。
●OnReceiveError : procedure(Sender: TObject; EventMask : DWORD) 当接收数据出现错误时将触发该事件。
2. 实现步骤
(1)制作窗体
启动Delphi7.0,创建一个新的应用程序界面,在新窗体Form 上放置上述的组件。
(2)设置各个组件的属性
根据所要实现的功能设置所用的组件的属性如下:
BtnSend 组件:该组件用来设置数据,当数据设置完成后点击即可将数据发送给单片机。
Button 组件:这个组件是用来做各种开关的,有打开串口、清楚发送接受速度数据。在打开串口(TButton )组件中的Enabled 属性中设置成true 、其它
的Enabled 属性中设置成false ,所起的作用是打开应用程序时默认的值有打开串口开关可以操作其它的都不能操作。当打开以后,打开串口开关变成不可操作的其他的开关可操作。
CheckBox 组件:用于选择发送十六进制数据。
CmbBaud 组件:该组件用来选择串口波特率,在组件的Items 属性中设置出110、300、600、1200、2400、9600、14000和19200八个选项供用户选择。
CmbByteSize 组件:该组件用来选择数据位,在组件的Items 属性中设置出5、6、7和8四个选项供用户选择。
CmbParity 组件:该组件用来选择校验位,在组件的Items 属性中设置出Even 、Mark 、None 、Odd 和Space 五个选项供用户选择。
CmbPort 组件:该组件用来选择端口号,在组件的Items 属性中设置出COM1、COM2、COM3、COM4和COM5五个选项供用户选择。
CmbStopBit 组件:该组件用来选择停止位,在组件的Items 属性中设置出1、
1.5、2三个选项供用户选择。
Image 组件:该组件用于指示串口的打开和关闭状态。
Label 组件:这些组件的作用是用来输入提示文字。
Memo 组件:该组件是用来反映发送和接收到的数据。
4.2.2 应用软件的具体操作步骤
把硬件电路连接好,使用USB 数据线与计算机相连接通上电源做好一切准备。
打开应用软件,在窗口中选择通讯端口,设置好串行通信的波特率、数据位、停止位和校验位后,点击打开串口按钮。当选择端口号错误时会提示错误信息,如图4.4所示。
图4.4 端口错误提示
打开串口成功后,串口显示灯会变红。用户可以根据需要输入十进制或者十六进制的速度等级,该信号就可通过串口发送给单片机,步进电机就可以根据计算机发出的指令来工作。在应用程序的界面上就会返回当前步进电机的速度等级和正反转信息。正常运作的界面如图4.5和图4.6所示。
图4.5 正转时正常运行界面
图4.6 反转时正常运行界面
当设定速度错误时,也会提示错误信息,如图4.7所示。
图4.7 速度设定错误提示
当按下清除信息键,对应信息将被清除。
五、设计中遇到的问题及解决方法
1. 数码管看不到示数或数码管看上去全亮。
开始的时候,数码管总是很暗,可以隐约看到有数字,但是亮度过于微弱,无法辨认。这主要是是调用数码管显示子函数的频率不够高,且子程序里的循环扫描中延时过短,导致数字显示不满明显。
解决方法:提高函数频率,并增加子函数循环扫描时的延时时间。还有,在子函数中每次显示之前都将数据清零,再赋值,防止出现重影,可使数码管有个较好的显示效果。
2.串口通信时返回上位机的信息是乱码。
在串口通信调试过程中,可以实现由上位机发出命令控制单片机,从而控制步进电机的运行状态。但是从单片机返回给上位机的信息是乱码。
解决方法:通过上网查询,出现乱码可能是波特率、数据位、停止位等协议不同导致的。首先应该将上位机接收协议和单片机中设定的协议一致。调试后,仍然出现乱码。除了上述情况会导致出现乱码,硬件线路也可能会导致乱码的出现。最终换了一块STC89C52RC 单片机,返回的信息正确,由此确定出现乱码的原因是硬件线路问题。
3. 程序调试应遵循的步骤
①查看各端口地址是否正确;
②根据程序运行中的出现的现象分析出程序出错的位置;
③在TDDEBUG 中进行单步或设置断点进行调试;
④使程序有少到多有简单到复杂逐一验证其的正确性,以排除错误。
六、程序清单和程序注释
详见附录
七、收获与体会
两个星期的自动化工程训练终于结束了。虽然时间看上去很长,但是我还需要为保研做准备。当验收结束时,可以说得是苦尽甘来。在整个设计过程中,回想起来,从刚开始的完全迷惑到最后的豁然开朗,真是经历了很多,过程虽然艰辛,但是却让我们学到很多的东西,不仅巩固了课上所学过的理论知识,而且学到了很多在书本上所没有学到过的知识。在上课的时候,老师经常强调在写一个程序的时候,磨刀不误砍柴工,一定要事先把程序原理方框图画出来,在这次课程设计,我深切体会到了这种编程方法的重要性。课程设计中设计的问题都需要很多代码相互协作才能完成,画出程序方框图是很有必要的。通过程序方框图,在做设计的过程中,我们每一步要做什么,每一步要完成什么任务都有一个很清楚的思路,而且在程序测试的过程中也有利于查错。
其次,对于编程工具的使用还不太熟悉,虽然上机实验课用到过,但是始终没有掌握得很牢靠。在课程设计时,通过大量调试,我对于如何排错、查错,如何查看每一步的运行结果和每个寄存器的内容有了很明确的了解,从而帮助我在调试过程中确保程序的正确性。这次的步进电机控制,虽然原理上很简单,但是如果对实验箱中的芯片不熟悉不熟悉的话,还是需要下一番苦功的。因此,在开始自动化工程训练后,我通过翻阅大量资料,对于设计中所涉及的芯片有了初步的了解。接着在实验,通过设计,将实际与自己所学的联系起来,最终做到对芯片的功能要熟练掌握。
除了硬件部分,此次设计还要设计人机交互界面。由于以前《数据库》这门课上使用Delphi 做过课程设计,因此我们再次选用了Delphi 来设计界面。时隔两年,再次使用Delphi ,我发现还是有许多东西已经忘记了。于是一边看以前做的设计,一边结合借来的资料,开始了对Delphi 的再次研究。幸好有以前的基础,还算比较顺利。这次设计的难题在于单片机和Delphi 设计界面实现串口通信。而Delphi 中并没相关控件,通过上网查询,了解了串口通信的定义以及相关协议的设定,从网上下载安装了SPCOMM 控件,最终编程实现了上位机和单片机的串口通信。
通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,从而提高自己的实际动手能力和独立思考的能力。在设计的过程中遇到问题,可以说得是困难重重,难免会遇到过各种各样的问题。如果自己单独钻研,很难发
现其中的错误,这时,就应该和老师、同学和有经验的学长交流,发现错误,解决错误。在交流和检查错误的过程中发现自己的不足之处,发现自己对以前所学过的知识理解得不够深刻,掌握得不够牢固,同时对知识的理解和接受能力还不是太强。
这次课程设计任务终于顺利完成了,在设计中遇到了很多问题,最后在自己的努力下以及与同学之间和老师的探讨下,终于一个一个得解决了问题,当看到步进电机按照自己编写的程序显示的时候,心情异常兴奋。这次课程设计在培养我们编程能力的同时,也让我学会了要完成一项任务,除了需要过硬的技术和能力之外,还要有不屈不挠的精神,有些同学就是没有耐心做下去,才没有好好的完成任务。所以,感谢我们的指导老师,谢谢!
八、参考文献
[1] 凌玉华,《单片机原理及应用系统设计》,长沙:中南大学出版社,2006;
[2] 李志民,《单片机程序设计实验指导书》,长沙:中南大学教材科,2006;
[3] 李朝青,《单片机原理及接口技术》(第三版),北京:北京航空航天大学出版社,2005;
[4] 吕伟臣,《Delphi 8入门与提高》,北京:清华大学出版社,2004;
附录:
附录1:硬件电路图
附录2:单片机C 语言程序清单
/*-----------------------------------------------
名称:步进电机
内容:本程序用于控制4相步进电机常规驱动 ,4个按键分别控制加速、减速、开启、停止,数码管显示 01-15速度等级,数字越大,速度越大
------------------------------------------------*/
#include
#define KeyPort P2
#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换
sbit LATCH1=P3^2;//定义锁存使能端口 段锁存
sbit LATCH2=P3^3;// 位锁存
unsigned char rettext[]={"当前速度为 "};
unsigned char code
dofly_DuanMa[12]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00,0x40};// 显示段码值0~9
unsigned char code
dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分别对应相应的数码管点亮, 即位码
unsigned char code rettextzhi[]={'0','1','2','3','4','5','6','7','8','9'}; unsigned char TempData[8]; //存储显示值的全局变量
sbit A1=P1^0; //定义步进电机连接端口
sbit B1=P1^1;
sbit C1=P1^2;
sbit D1=P1^3;
#define Coil_AB1 {A1=1;B1=1;C1=0;D1=0;}//AB相通电,其他相断电
#define Coil_BC1 {A1=0;B1=1;C1=1;D1=0;}//BC相通电,其他相断电
#define Coil_CD1 {A1=0;B1=0;C1=1;D1=1;}//CD相通电,其他相断电
#define Coil_DA1 {A1=1;B1=0;C1=0;D1=1;}//D相通电,其他相断电
#define Coil_A1 {A1=1;B1=0;C1=0;D1=0;}//A相通电,其他相断电
#define Coil_B1 {A1=0;B1=1;C1=0;D1=0;}//B相通电,其他相断电
#define Coil_C1 {A1=0;B1=0;C1=1;D1=0;}//C相通电,其他相断电
#define Coil_D1 {A1=0;B1=0;C1=0;D1=1;}//D相通电,其他相断电
#define Coil_OFF {A1=0;B1=0;C1=0;D1=0;}//全部断电
unsigned char Speed=1;
bit StopFlag;
bit RunFlag;
void Display(unsigned char FirstBit,unsigned char Num);
void Init_Timer0(void);
void initiate(void);
unsigned char KeyScan(void);
/*------------------------------------------------
uS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M ,精确延时请使用汇编, 大致延时
长度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M ,精确延时请使用汇编
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
主函数
------------------------------------------------*/
main()
{
unsigned int i=512;//旋转一周时间
unsigned char num;
unsigned int j=10;
Init_Timer0();
initiate();
Coil_OFF
while(1) //正向
{
num=KeyScan(); //循环调用按键扫描
if(num==7)//第一个按键, 速度等级增加
{
if(Speed
Speed++;
}
else if(num==2)//第二个按键,速度等级减小
{
if(Speed>1) Speed--; }
else if(num==4)
{
Coil_OFF
StopFlag=1;
}
else if(num==3)
{
StopFlag=0;
}
else if(num==5)
{
RunFlag=0;
j=10;
}
else if(num==6)
{
RunFlag=1;
j=11;
}
TempData[0]=dofly_DuanMa[j];
TempData[1]=dofly_DuanMa[Speed/10];//分解显示信息,如要显示68,则68/10=6 68%10=8
TempData[2]=dofly_DuanMa[Speed%10];
}
}
/*------------------------------------------------
显示函数,用于动态扫描数码管
输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示 如输入0表示从第一个显示。
Num表示需要显示的位数,如需要显示99两位数值则该值输入2
------------------------------------------------*/
void Display(unsigned char FirstBit,unsigned char Num)
{
static unsigned char i=0;
DataPort=0; //清空数据,防止有交替重影
LATCH1=1; //段锁存
LATCH1=0;
DataPort=dofly_WeiMa[i+FirstBit]; //取位码
LATCH2=1; //位锁存
LATCH2=0;
DataPort=TempData[i]; //取显示数据,段码
LATCH1=1; //段锁存
LATCH1=0;
i++;
if(i==Num)
i=0;
}
/*------------------------------------------------
定时器初始化子程序
------------------------------------------------*/
void Init_Timer0(void)
{
TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
//TH0=0x00; //给定初值
//TL0=0x00;
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
PT0=1; //优先级打开
}
/*------------------------------------------------
定时器中断子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1
{
static unsigned char times,i;
extern bit RunFlag;
TH0=(65536-1000)/256;
TL0=(65536-1000)%256; //重新赋值 1ms
Display(0,8);
if(!StopFlag)
{
if(times==(17-Speed))//最大值15,所以最小间隔值17-15=2 {
times=0;
if(RunFlag)
{
switch(i)
{
case 0:Coil_D1;i++;break; case 1:Coil_C1;i++;break; case 2:Coil_B1;i++;break; case 3:Coil_A1;i++;break; case 4:i=0;break;
default:break;
}
}
if(!RunFlag)
{
switch(i)
{
case 0:Coil_A1;i++;break;
case 1:Coil_B1;i++;break;
case 2:Coil_C1;i++;break; case 3:Coil_D1;i++;break; case 4:i=0;break; default:break; }
}
}
times++;
}
}
/*------------------------------------------------ 按键扫描函数,返回扫描键值
------------------------------------------------*/ unsigned char KeyScan(void)
{
unsigned char keyvalue;
if(KeyPort!=0xff)
{
DelayMs(10);
if(KeyPort!=0xff)
{
keyvalue=KeyPort;
while(KeyPort!=0xff); switch(keyvalue) { case 0xfe:return 1;break; case 0xfd:return 2;break; case 0xfb:return 3;break; case 0xf7:return 4;break; case 0xef:return 5;break; case 0xdf:return 6;break; case 0xbf:return 7;break; case 0x7f:return 8;break; default:return 0;break; } }
}
return 0;
}
/*------------------------------------------------ 串口初始化
------------------------------------------------*/ void initiate()
{
TMOD|=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
EA=1;
ES=1;
SCON=0x50;
PCON=0x00;
}
/*------------------------------------------------ 串口中断程序
------------------------------------------------*/ void S_inter () interrupt 4//串行中断服务程序 {
int i;
extern bit RunFlag;
if(RI) //判断是接收中断产生 {
RI=0; //标志位清零 Speed=SBUF; rettext[11]=rettextzhi[Speed/10]; rettext[12]=rettextzhi[Speed%10];
if(RunFlag==0)
{rettext[10]='+';}
else if(RunFlag==1)
{rettext[10]='-';}
for(i=0;i
{
SBUF=rettext[i];
while(!TI) ;
TI=0;
}
}
}
附录3:汇编程序清单
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, jpeg, ExtCtrls, SPComm, XPMan;
type
TForm1 = class(TForm)
Comm1: TComm;
CmbPort: TComboBox;
CmbBaud: TComboBox;
CmbParity: TComboBox;
CmbStopBit: TComboBox;
CmbByteSize: TComboBox;
Image1: TImage;
Image2: TImage;
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
Button3: TButton;
Label1: TLabel;
Label2: TLabel;
BtnSend: TButton;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Label7: TLabel;
Memo2: TMemo;
CheckBox1: TCheckBox;
XPManifest1: TXPManifest;
Label8: TLabel;
Label9: TLabel;
Label10: TLabel;
Label11: TLabel;
Label12: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure BtnSendClick(Sender: TObject);
procedure Comm1ReceiveData(Sender: TObject; Buffer: Pointer; BufferLength: Word);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
flag:integer=1;//串口开关标志
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
flag:=flag+1;
if (flag mod 2=0) then
begin
CmbPort.Enabled:=false;
CmbBaud.Enabled:=false;
CmbParity.Enabled:=false;
CmbStopBit.Enabled:=false;
CmbByteSize.Enabled:=false;
BtnSend.Enabled:=true;
Comm1.CommName:=CmbPort.Text;
Comm1.BaudRate:=strtoint(CmbBaud.Text);
case CmbParity.ItemIndex of
0: Comm1.Parity:=Even;
1: Comm1.Parity:=Mark;
2: Comm1.Parity:=None;
3: Comm1.Parity:=Odd;
4: Comm1.Parity:=Space;
end;
case CmbStopBit.ItemIndex of
0: Comm1.StopBits:=_1;
1: Comm1.StopBits:=_1_5;
2: Comm1.StopBits:=_2;
end;
case CmbByteSize.ItemIndex of
0: Comm1.ByteSize:=_5;
1: Comm1.ByteSize:=_6;
2: Comm1.ByteSize:=_7;
3: Comm1.ByteSize:=_8;
end;
Comm1.StartComm;
Image1.Visible:=true;
Image2.Visible:=false;
Button1.Caption:='关闭串口'
end
else
begin
CmbPort.Enabled:=true;
CmbBaud.Enabled:=true;
CmbParity.Enabled:=true;
CmbStopBit.Enabled:=true;
CmbByteSize.Enabled:=true;
BtnSend.Enabled:=false;
Comm1.StopComm;
Image1.Visible:=false;
Image2.Visible:=true;
Button1.Caption:='打开串口'
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Memo1.Lines.Clear;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
Memo2.Lines.Clear;
end;
procedure TForm1.BtnSendClick(Sender: TObject);
var
i,x:integer;
p:pchar;
ch1,ch2:integer;
begin
if CheckBox1.Checked then
begin
p:=Pchar(Memo2.Lines.Text);
x:=Length(Memo2.Lines.Text);
if ((p)^ in ['0'..'9','A'..'F','a'..'f'])
['0'..'9','A'..'F','a'..'f']) then
and ((p+1)^ in
begin
if x
begin
if ((p)^ in ['0'..'9']) then
ch1:=strtoint(p^)
else if ((p)^ in ['A'..'F']) then
ch1:=ord(p^)-55
else ch1:=ord(p^)-87;
if ((p+1)^ in ['0'..'9']) then
ch2:=strtoint((p+1)^)
else if ((p+1)^ in ['A'..'F']) then
ch2:=ord((p+1)^)-55
else ch2:=ord((p+1)^)-87;
i:=ch1*16+ch2;
Comm1.WriteCommData(@i,1);
end
else showmessage('只输入两位,谢谢!')
end
else showmessage('请输入两位16进制数!');
end
else
begin
i:=strtoint(Memo2.Lines.Text);
if i>15 then
showmessage('请输入01~15之间的数!')
else if i
showmessage('请输入01~15之间的数!')
else
Comm1.WriteCommData(@i,1);
end;
end;
procedure TForm1.Comm1ReceiveData(Sender: TObject; Buffer: Pointer; BufferLength: Word);
var
p:pchar;
begin
p:=Buffer;
Memo1.Lines.Add(p); end;
end.