采用VC的伺服电机控制毕业论文

更新时间:2024-03-22 20:34:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

基于VC++的伺服电机速度控制系统设计

摘 要

本设计完成是基于Visual C++6.0软件下实现伺服电机控制界面的编程,并完成PID控制算法。该控制界面不仅简洁实用,而且具有良好的人机交流部分。

上位机控制界面所实现的功能有电机的启动、停止、正传、反转、速度给定、实时曲线显示等,在完成PID算法后同时下发PID调节后的PWM占空比,实现转速控制。在完成串口通讯编程本文采用MSComm控件实现PC机与单片机通讯,数据的发送与接收采用统一通讯协议,这种方法不仅简单,而且实用。本文采用单片机作为下位机来验证上位机控制功能的实现,单片机作为下位机主要负责直流电机的各种参数数据的采集和模数转换,然后通过串行端口把数据发送给上位机,PC机作为上位机主要负责数据的分析处理和显示。

关键词:伺服电机;VC++;串口通讯;PWM调速;PID;

I

The servo motor speed control system design Based on VC++

Abstract

This design's core is under the software of Visual C++6.0, realizes the servo motor control contact surface programming, and completes the PID control algorithm. This control interface has not only succinct practical, but also has the good man-machine exchange part.

The superior machine control interface realizes the function has the electrical machinery's start, electrical machinery's stop, electrical machinery's main story, electrical machinery's reverse, the speed value assigns, Curve demonstration and so on. After completing the PID algorithm simultaneously after-crops PID the adjustment the PWM current output.In completing serial port communication programming this article to use MSComm to control to realize PC machine and the monolithic integrated circuit communication, the data transmission and the receive uses the unified communication protocol, in this method is not only simple but also practical. In this article uses the monolithic integrated circuit to confirm the superior machine control function as the lower position machine realization, Monolithic integrated circuit as lower position machine primary cognizance direct current machine's each kind of parameter data gathering and with A/D conversion. Then through the serial port the data transmission for the superior machine, PC machine takes the superior machine primary cognizance data the analysis processing and the demonstration.

Key word: Servo electrical machinery; VC++; Serial port communication; PWM control speed; PID;

II

目 录

摘 要 ......................................................................................................................................... I Abstract ...................................................................................................................................... II 第一章 引言 .............................................................................................................................. 1

1.1 设计的目的 ................................................................................................................. 1 1.2 设计的意义 ................................................................................................................. 2 第二章 电机调速 ...................................................................................................................... 3

2.1 直流伺服电机简介 ..................................................................................................... 3 2.2 直流伺服电机调速 ..................................................................................................... 5

2.2.1 V-M调速系统 ................................................................................................. 5 2.2.2 PWM调速系统 ............................................................................................... 7

第三章 GUI设计 .................................................................................................................... 10

3.1 主界面 ....................................................................................................................... 10 3.2 实时曲线 ................................................................................................................... 12 3.3 按钮图标的实现 ....................................................................................................... 13 第四章 串口通讯 .................................................................................................................... 17

4.1 PC机与单片机通信方式 .......................................................................................... 17 4.2 VC实现串口通讯的四种方式 .................................................................................. 19

4.2.1 基于Windows API通信函数 ...................................................................... 19 4.2.2 利用端口函数直接操作............................................................................... 21 4.2.3 基于MSComm控件 .................................................................................... 22 4.2.4 基于CSerial类 ............................................................................................ 23

III

4.2.5 四种实现方式的分析 .................................................................................. 24 4.3 串口编程 ................................................................................................................... 25

4.3.1 插入MSCOMM控件 .................................................................................. 25 4.3.2 设置属性 ...................................................................................................... 25 4.3.3 设置串口 ...................................................................................................... 26 4.3.4 数据类型的处理 .......................................................................................... 29 4.4 帧格式定义 ............................................................................................................... 29 4.5 发送数据帧 ............................................................................................................... 30 4.6 接收数据帧 ............................................................................................................... 30

4.6.1 检测接收缓冲区数据 .................................................................................. 30 4.6.2 从接收缓冲区取出数据............................................................................... 32

第五章 PID算法 ..................................................................................................................... 33

5.1 PID算法简介 ............................................................................................................. 33 5.2 参数调整一般规则 ................................................................................................... 34 5.3 PID算法在VC中的实现 ......................................................................................... 34 结 论 ........................................................................................................................................ 37 参考文献 .................................................................................................................................. 38 附录 程序 ................................................................................................................................ 40 致 谢 ........................................................................................................................................ 63

IV

第一章 引言

在生产实践的各个领域,有大量的生产机械要求在不同的场合,用不同的速度来进行工作,以提高生产率和保证产品的质量,如机床、轧钢机、造纸机、纺织机械等。在工程实践中,有很多生产机械要求在一定的范围内进行速度的平滑调节,并且要求有良好的静、动态性能。所以如何能充分发挥生产机械效能,提高生产率,是电机转速控制系统首先要解决的问题。

1.1 设计的目的

随着微电子技术的发展,应用上位机实现现场的采集与控制越来越被大家所重视,上位机提供良好的人机界面,使现场监控更直接、简洁可靠和稳定。应用上位机实现现场监控,主要是通过上位机本身配置的串行口,通过串行通讯技术,控制和管理若干以单片机。由于上位机界面的应用给现场监控提供的极大的方便,所以上位机控制界面的设计也成为工业控制的重点内容。在Visual C++6.0界面下完成上位机的编程具有自己独特的优势,当VB成功推出后,microsoft又将C++包装成为了面向windows的visual C++(以下简称VC)。从VC1.0到VC6.0,每一个版本的推出都激动人心。VC借助传统的C/C++的美名加上microsoft这个金字招牌,使广大的程序员和专业编程人员纷纷投靠在VC的大旗下[16]。

伺服系统在机械制造行业中用得最多最广泛,各种高性能机床运动部件的速度控制运动轨迹控制、位置控制,都是依靠各种伺服系统控制的。它们不仅能完成转动控制、直线运动控制,而且能依靠多套伺服系统的配合,完成复杂的空间曲线运动的控制,如仿型机床的控制、机器人手臂关节的运动控制等等。它们可以完成的运动控制精度高、速度快、远非一般人工操作所能达到。电动机是伺服系统的重要执行元件,又称为执行电动机。在自动控制系统中,其任务是将输入的电信号转换为转角或转速,以带动控制

1

对象。因此,对伺服系统的控制关键所在就是对伺服电机的控制。

伺服电动机一般分为直流伺服电机和交流伺服电机,对于直流伺服电机具有响应快、低速平稳性好、调速范围宽等优点,常用于实现精密调速和位置控制的随动系统中,在工业、国防和民用等领域内得到广泛应用[13] 。而对于交流伺服电机,虽然控制精度较高,但控制较复杂,驱动器参数需要现场调整PID参数整定,需要更多的连线[18]。所以本文主要介绍直流伺服电机转速的控制。

1.2 设计的意义

工业控制领域,往往需要组成上位机—下位机系统,上位机一般为PC机,下位机一般为单片机系统[1]。上位机主要完成数据的采集、显示、以及数据的给定等随着,单片机做为下位机本主要负责直流电机的各种参数数据的采集和和模数转换。

利用VC++编程上位机控制界面可以解决很多工业控制中出现的难题,传统的上位机控制界面参数给定更改比较麻烦,需要浪费大量时间从新更改程序。而VC++的上位机控制界面就很容易实现,只需对程序中设置的参数加以更换就可以,特别是上位机一般都是计算机,随着计算机技术的发展,计算机的功能也越来越强大,很多复杂算法很容易实现,也为工业的中出现的复杂算法提供方便。在更变复杂算法时,由VC++编程上位机控制界面也十分方便。本文在完成控制伺服电机速度过程中介绍了PID算法的编程过程。

2

第二章 电机调速

2.1 直流伺服电机简介

直流伺服电机是将输入的直流电信号转换成机械角位移或角速度信号的装置。直流伺服电机具有良好的启动、制动和调速性能,可以再较宽的范围内实现平滑无极的调速,因而适应于调速性能较高的场合[4]。

(1)直流电动机的结构;

①定子。定子磁极磁场由定子的磁极产生。根据产生磁场的方式,直流伺服电动机可分为永磁式和他激式。永磁式磁极由永磁材料制成,他激式磁极由冲压硅钢片叠压而成,外绕线圈通以直流电流便产生恒定磁场。

②转子。又称为电枢,由硅钢片叠压而成,表面嵌有线圈,通以直流电时,在定子磁场作用下产生带动负载旋转的电磁转矩。

③电刷与换向片。为使所产生的电磁转矩保持恒定方向,转子能沿固定方向均匀的连续旋转,电刷与外加直流电源相接,换向片与电枢导体相接。

(2)直流电机的分类;

一般直流电机按结构、用途、容量的大小等分类。但从运行的观点来看,按励磁方式分类更用意义,因为除了少量微型电机的磁极是永磁铁外,绝大多数电机的磁场都是在磁极绕组中通以直流电流而建立的。因此都是通常都是按励磁绕组的连接方式(即按励磁方式)对直流电机进行分类。

直流电机按其励磁绕组与电枢绕组连接方式的不同,可①他励;②并励;③串励;④复励等四种,如图2.1

3

U1I1MII1MU1IUII1MUI2U1(a)I2(b)MI2(d)(c)

图2.1直流电机励磁方式图

图2.1(a)为他励直流电机,其特点是励磁绕组接在独立大励磁电源上,而与电枢的绕组无关,图2.1(b)为并励直流电机,其特点是励磁绕组与电枢绕组并联,这种励磁绕组的匝数较多,导线较细,图2.1(c)为串励直流电机,其特点是励磁绕组与电枢绕组串联,电枢电流就是励磁电流,励磁绕组匝数少、导线较粗。图2.1(d)为复励直流电机,其特点是在主磁极上装有两套励磁绕组,一套是与电枢绕组并联时并励绕组,另一套与电枢绕组串联是串励绕组[20]。

(3)直流伺服电机工作原理;

直流伺服电动机的工作原理与一般直流电动机的工作原理是完全相同,如图2.2所示。他激直流电机转子上的载流导体(即电枢绕组),在定子磁场中受到电磁转矩M的作用,使电机转子旋转。由直流电机的基本原理分析得到转速值如2.1式所示:

n?u?IaRa (2.1) ken──电枢的转速,r/min; u──电枢电压; Ia ──电机电枢电流;

-RfIfIaRa-uf+u+ Ra──电枢电阻; ke──电势系数(ke=Ceφ)。 图2.2直流电机原理图

4

2.2 直流伺服电机调速

直流伺服电机的调速有三种方法:

(1)改变电枢电压U:由额定电压向下调低,转速也由额定转速向下调低,调速范围大。

(2)改变磁通量Φ(即改变ke):改变激磁回路的电阻可改变Φ。由于激磁回路电感大,电气时间常数大,调速快速性差,转速只能由额定转速向上调高。

(3)在电枢回路中串联调节电阻。转速只能调低,铜耗大,不经济。直流伺服电机通常采用调压调速,通常改变电枢电压调速是直流调速系统采用的主要方法。

改变电枢电压调速是直流调速系统采用的主要方法,调节电枢供电电压或者改变励磁磁通,都需要有专门的可控直流电源,常用的可控直流电源有以下三种:

(1)旋转变流机组。用交流电动机和直流发电机组成机组,以获得可调的直流电压。 (2)静止可控整流器。用静止的可控整流器,如汞弧整流器和晶闸管整流装置,产生可调的直流电压。

(3)直流斩波器或脉宽调制变换器。用恒定直流电源或不可控整流电源供电,利用直流斩波或脉宽调制的方法产生可调的直流平均电压。

下面分别对各种可控直流电源以及由它供电的直流调速系统作概括性介绍。 2.2.1 V-M调速系统

1957年,晶闸管问世,它是一种大功率半导体可控整流元件,俗称可控硅整流元件,简称“可控硅”,20世纪60年代起就已生产出成套的晶闸管整流装置。晶闸管问世以后,变流技术出现了根本性的变革。目前,采用晶闸管整流供电的直流电动机调速系统(即晶闸管-电动机调速系统,简称V-M系统,又称静止Ward-Leonard系统)已经成为直流调速系统的主要形式。图2.3所示是V-M系统的原理框图,图中V是晶闸管可控整流器,它可以是任意一种整流电路,通过调节触发装置GT的控制电压来移动触发脉冲的

5

相位,从而改变整流输出电压平均值Ud,实现电动机的平滑调速。和旋转变流机组及离子拖动变流相比,晶闸管整流不仅在经济性和可靠性上都有很大提高,而且在技术性能上显示出很大的优越性。晶闸管可控整流器的功率放大倍数大约在

,控制功率

小,有利于微电子技术引入到强电领域;在控制作用的快速性上也大大提高,有利于改善系统的动态性能[5]。但是,晶闸管整流器也有它的缺点,主要表现在以下方面:

(1)晶闸管一般是单向导电元件,晶闸管整流器的电流是不允许反向的,这给电动机实现可逆运行造成困难。必须实现四象限可逆运行时,只好采用开关切换或正、反两组全控型整流电路,构成V-M可逆调速系统,后者所用变流设备要增多一倍。

(2)晶闸管元件对于过电压、过电流以及过高的du/dt和di/dt十分敏感,其中任一指标超过允许值都可能在很短时间内元件损坏,因此必须有可靠的保护装置和符合要求的散热条件,而且在选择元件时还应保留足够的余量,以保证晶闸管装置的可靠运行。

(3)晶闸管的控制原理决定了只能滞后触发,因此,晶闸管可控制整流器对交流电源来说相当于一个感性负载,吸取滞后的无功电流,因此功率因素低,特别是在深调速状态,即系统在较低速运行时,晶闸管的导通角很小,使得系统的功率因素很低,并产生较大的高次谐波电流,引起电网电压波形畸变,殃及附近的用电设备。如果采用晶闸管整流装置的调速系统在电网中所占容量比重较大,将造成所谓的“电力公害。为此,应采取相应的无功补偿、滤波和高次谐波的抑制措施。

(4)晶闸管整流装置的输出电压是脉动的,而且脉波数总是有限的。如果主电路电感不是非常大,则输出电流总存在连续和断续两种情况,因而机械特性也有连续和断续两段,连续段特性比较硬,基本上还是直线;断续段特性则很软,而且呈现出显著的非线性。

6

~+L~-+UdM-GTV图2.3晶闸管电机调速系统原理图(V-M)

2.2.2 PWM调速系统

直流斩波器又称直流调压器,是利用开关器件来实现通断控制,将直流电源电压断续加到负载上,通过通、断时间的变化来改变负载上的直流电压平均值,将固定电压的直流电源变成平均值可调的直流电源,亦称直流-直流变换器。它具有效率高、体积小、重量轻、成本低等优点,现广泛应用于地铁、电力机车、城市无轨电车以及电瓶搬运车等电力牵引设备的变速拖动中。

图2.4为直流斩波器的原理电路和输出电压波型,(a)图中VT代表开关器件。当开关VT接通时,电源电压US。加到电动机上;当VT断开时,直流电源与电动机断开,电动机电枢端电压为零。如此反复,得电枢端电压波形如图2.4(b)所示。

+VTLUsVD+UtMUsUd-Tt(a)图2.4 直流斩波器的原理图

(b)

这样,电动机电枢端电压的平均值如式2.2所示: Ud?

1TtUdt?US?kUS (2.2) S?0TT7

式中,T-开关器件的通断周期;

输出电压平均值Ud可以通过改变占空比k,即通过改变开关器件导通或关断时间来调节,常用的改变输出平均电压的调制方法有以下三种:

(1)脉冲宽度调制(pulse width modulation,简称PWM)。开关器件的通断周期T保持不变,只改变器件每次导通的时间t,也就是脉冲周期不变,只改变脉冲的宽度,即定频调宽。

(2)脉冲频率调制(pulse frequency modulation,简称PFW)。开关器件每次导通的时间t不变,只改变通断周期T或开关频率f,也就是只改变开关的关断时间,即定宽调频,称为调频。

(3)两点式控制。开关器件的通断周期T和导通时间t均可变,即调宽调频,亦可称为混合调制。当负载电流或电压低于某一最小值时,使开关器件导通;当电流或电压高于某一最大值时,使开关器件关断。导通和关断的时间以及通断周期都是不确定的。 构成直流斩波器的开关器件过去用得较多的是普通晶闸管和逆导晶闸管,它们本身没有自关断的能力,必须有附加的关断电路,增加了装置的体积和复杂性,增加了损耗,而且由它们组成的斩波器开关频率低,输出电流脉动较大,调速范围有限。自20世纪70年代以来,电力电子器件迅速发展,研制并生产了多种既能控制其导通又能控制其关断的全控型器件,如门极可关断晶闸管(GTO)、电力电子晶体管(GTR)、电力场效应管(P-MOSFET)、绝缘栅双极型晶体管(IGBT)等,这些全控型器件性能优良,由它们构成的脉宽调制直流调速系统(简称PWM调速系统)近年来在中小功率直流传动中得到了迅猛的发展,与V-M调速相比,PWM调速系统有以下优点:

①采用全控型器件的PWM调速系统,其脉宽调制电路的开关频率高,一般在几kHz,因此系统的频带宽,响应速度快,动态抗扰能力强。

②由于开关频率高,仅靠电动机电枢电感的滤波作用就可以获得脉动很小的直流电

8

流,电枢电流容易连续,系统的低速性能好,稳速精度高,调速范围宽,同时电动机的损耗和发热都较小。

③PWM系统中,主回路的电力电子器件工作在开关状态,损耗小,装置效率高,而且对交流电网的影响小,没有晶闸管整流器对电网的“污染”,功率因数高,效率高。

④主电路所需的功率元件少,线路简单,控制方便。所以本文采用脉冲宽度调制的方法来调试电机的速度。

脉宽调速系统的主要电路采用脉宽调制式变换器,简称PWM变换器。PWM变换器有不可逆和可逆两类,可逆变换器又有双极式、单极式和受限单极式等多种电路, 本文采用不可逆PWM变换器,其原理简单容易实现[19]。

9

第三章 GUI设计

3.1 主界面

上位机界面主要完成PID试调、速度给定、速度显示、曲线显示等功能。可实现的软件有VC、Delphi、VB、CAI、Labview。本文是用Visual C++6.0来完成上位机界面的设计;如图3.1所示:

图3.1 电机转速控制界面图

Visual C++6.0作为可视化编程软件具有独特优势。是目前综合性最高、最强大,也是最为复杂的Windows 应用程序开发软件。Visual C++是一个功能强大的可视化软件开发工具。自1993年Microsoft公司推出Visual C++1.0后,随着其新版本的不断问世,Visual C++已成为专业程序员进行软件开发的首选工具。

首先,要了解VC++6.0则必须先了解C/C++语言。在计算机领域,C/C++语言相信

10

一定无人不知、无人不晓。C语言最初主要用于unix系统,大多数unix系统的程序都是用C编写的。借助早期unix系统的流行和C本身的优秀品质,C语言从此名扬天下、威震四方。而后,伴随着面向对象概念的提出和应用,形成了C++语言,AT&T公司于1985年正式推出了C++1.0版,它是c语言面向对象的扩充。C++1.0增加了类和实例、单继承、重载、虚函数、友员、内联等面向对象的程序设计机制。1989年推出的C++2.0版和1993年推出的C++3.0版分别增加了支持多继承、保护接口、模板和异常等。C++被公认为“最好的面向对象的编程语言,是学习面向对象编程技术人员较好的启蒙语言。

Visual C++6.0不仅是一个C++编译器,而且是一个基于Windows操作系统的可视化集成开发环境。Visual C++6.0由许多组件组成,包括编辑器、调试器以及程序向导AppWizard、类向导Class Wizard等开发工具。 这些组件通过一个名为Developer Studio的组件集成为和谐的开发环境[17]。

VC++是微软公司开发的C++语言开发环境,VC的特点是微软公司做了一个自己独有的类库MFC,里面封装了绝大多数的API函数,使得WINDOWS程序的开发变的高效和易于理解,如果用API直接开发WINDOWS程序的话,将会是一件非常烦琐的工作,WINDOWS对资源的管理是非常严格的这与DOS可直接用中断处理程序和I/O指令操作硬件端口是截然相反的;所以这个MFC就是VC和其他公司出产的编译器的最大区别了,当然了别的公司也有自己封装API的类库比如DELPHI等,不过由于微软公司掌握着WINDOWS内核的全部秘密,所以他们的编译器与其他公司的相比有其独到的优势。

主界面控件参数设置首先打开VC++6.0集成开发环境,选择菜单项File/New,在出现的对话框中选中Projects标签中的MFC AppWizard(exe),然后在Project Name框中填入MyCOMM(可根据需要命名),之后点OK按钮。在接着出现的对话框中选中Dialog Based项,然后点NEXT按钮。以下的各对话框都按照缺省设置,这样即可生成一个基于对话框

11

的应用程序。在资源编程器中会出现其对话框模板。在对话框中添加相应应用的控件,再对参数进行属性设置,如设置ID,设置后的界面如图3.2所示:

图3.2伺服电机控制界面图

3.2 实时曲线

在伺服电机转速控制过程中,需要采集电机的实际转速,并实时显示到控制界面上,这样可以轻松的观察的速度曲线的实时变化。实现的方法为在工程中添加曲线资源,然后自Dlg中添加代码:

GetDlgItem(IDC_OSCOPE)->GetWindowRect(rect) ; ScreenToClient(rect) ; //适应picture控件的大小 创建画图工具

m_quxian.Create(WS_VISIBLE | WS_CHILD, rect, this) ; 初始化画图工具

12

m_quxian.SetRange(0, 200, 0) ; //设置纵坐标以及小数位数 m_quxian.SetYUnits(\电机转速(转/秒)\ //设置y轴标题 m_quxian.SetXUnits(\时间轴\ //设置x轴标题 m_quxian.SetBackgroundColor(RGB(0, 0, 64)) ; //背景色 m_quxian.SetGridColor(RGB(192, 192, 255)) ; //坐标颜色 m_quxian.SetPlotColor(RGB(255, 255, 255)) ; // 曲线颜色

当串口调通后可以收发数据时,便可以接收下位机的速度给定时,可以实时显示速度曲线,如图3.3所示。

图3.3速度曲线显示图

3.3 按钮图标的实现

按钮图标是通过CButtonST这个类实现的,CButtonST是目前最强大功能最全的CButton派生类,CButtonST类主要包括BtnST.h、BtnST.cpp、BCMenu.h和BCMenu.cpp四个文件,在VC++的Workspace —> FileView中导入以上的四个文件之后,然后在SdtAfx.h文件中添加合适的include语句,如#include \。

下面介绍应用CButtonST可实现下面6种效果; (1)在按钮上加入Icon,使Icon和文字同时显示;

13

假设按钮ID为IDC_BUTTON1; ①添加成员变量 CButtonST m_btn; ②添加Icon资源,设其ID设为IDI_ICON1; ③在OnInitDialog函数中初始化按钮; m_btn.SubclassDlgItem(IDC_BUTTON1,this); m_btn.SetIcon(IDI_ICON1);

m_btn.SetFlat(FALSE); 根据Icon的大小调整按钮的大小。 (2)显示平面按钮;

只需要在效果一的基础上把m_btn.SetFlat(FALSE);语句去掉。 (3)使按钮上的图标可变; 假设按钮ID为IDC_BUTTON1; ① 添加成员变量CButtonST m_btn;

② 添加两个Icon资源,ID设为IDI_ICON1和IDI_ICON2; IDI_ICON1是普通状态的图标,IDI_ICON2是按下时的图标; ③ 在OnInitDialog函数中初始化按钮; m_btn.SubclassDlgItem(IDC_BUTTON1,this); m_btn.SetIcon(IDI_ICON2,IDI_ICON1); (4)设置按钮在不同状态下的底色和文字颜色; 假设按钮ID为IDC_BUTTON1; ① 添加成员变量 CButtonST m_btn; ② 添加Icon资源,设其ID设为IDI_ICON1; ③ 在OnInitDialog函数中初始化按钮;

m_btn.SubclassDlgItem(IDC_BUTTON1,this); m_btn.SetIcon(IDI_ICON1);

14

m_btn.SetColor(CButtonST::BTNST_COLOR_BK_OUT, RGB(208,208,208)); m_btn.SetColor(CButtonST::BTNST_COLOR_BK_FOCUS,RGB(208,208,208)); m_btn.OffsetColor(CButtonST::BTNST_COLOR_BK_IN, 30);

SetColor函数和OffsetColor函数的第一个参数表示按钮的各个状态,前景色就是文字的颜色,它们的取值表示:

BTNST_COLOR_BK_IN; //鼠标放在按钮内时的背景色 BTNST_COLOR_FG_IN; //鼠标放在按钮内时的前景色 BTNST_COLOR_BK_OUT; //普通状态时的背景色 BTNST_COLOR_FG_OUT; //普通状态时的前景色 BTNST_COLOR_BK_FOCUS; //按钮被按下后的背景色

BTNST_COLOR_FG_FOCUS; //按钮被按下后的前景色实例:演示程序中的48×48 icon 按钮、Zip按钮等。

(5)设置图标和文字的位置; 假设按钮ID为IDC_BUTTON1; ①添加成员变量 CButtonST m_btn; ②添加Icon资源,ID设为IDI_ICON1; ③在OnInitDialog函数中初始化按钮; m_btn.SubclassDlgItem(IDC_BUTTON1,this); m_btn.SetIcon(IDI_ICON1);

m_btn.SetAlign(CButtonST::ST_ALIGN_VERT);

SetAlign函数的第一个参数表示位置信息,缺省情况下,文字在图标右边

ST_ALIGN_HORIZ; // 文字在右 ST_ALIGN_VERT; //文字在下

15

ST_ALIGN_HORIZ_RIGHT; // 文字在左 (6)根据Icon的形状设置按钮的形状; 假设按钮ID为IDC_BUTTON1; ①添加成员变量 CButtonST m_btn; ②添加Icon资源,设其ID设为IDI_ICON1; ③在OnInitDialog函数中初始化按钮; m_btn.SubclassDlgItem(IDC_BUTTON1,this); m_btn.SetIcon(IDI_ICON1); m_btn.DrawBorder(FALSE);

m_btn.SetColor(CButtonST::BTNST_COLOR_BK_OUT, RGB(208,208,208)); m_btn.SetColor(CButtonST::BTNST_COLOR_BK_IN, RGB(208,208,208)); m_btn.SetColor(CButtonST::BTNST_COLOR_BK_FOCUS, RGB(208,208,208));

为达到最好效果,请根据Icon的大小调整按钮的大小。并根据实际情况设置背景颜色,请事先将按钮的文字去掉。

16

第四章 串口通讯

4.1 PC机与单片机通信方式

PC机与单片机通讯通常采用两种方式:并行通信和串行通信。并行通信是指代发送的数据各位同时传送,串行通信则是数据一位一位的按顺序传送。并行通信虽然传输效率高,由于所需硬件设计复杂,不适于长距离通信,所以一般只适用于要求实时性强。传送速率较高的测控系统中,实用面较窄;相比之下,串行通信简单易实现,传输距离较长,所以已被广泛应用于各种工控系统中[2]。

串行通行分为同步通行和异步通信2种方式。同步通信是指通过在每个数据块开始时的同步字符来实现收/发双方同步的一种数据传送方法,常用于信息量大,速度要求高的场合;异步通信则规定了标准的字符数据传输格式,即每一帧信息由起始位、数据位、奇偶校验位和停止位组成[3]。由于有冗余位,所以传送效率不高,常用于信息量不大,速度较低的场合。在计算机测控系统中,由于串行接口的标准化,一般采用异步串行通信方式,以提高其通用性。

PC机与下位机通讯采用标准通讯端口RS-232。目前RS-232是PC机与通信工业中应用最广泛的一种串行接口,而对于单片机与PC机的串行通信,可以进一步简化接法,只需要三根线:RxD (接收)、TxD(发送)、GND(信号地,收发双方信号共地,即信号地线接在一起),这种接法俗称‘零Modem接法”。RS—232传送的是单端信号,即取RxD及TxD对GND的电压值为信号电平,电压+5V—+15V表示逻辑0,电压-15V—-5V表示逻辑1,称之为EIA电平。而单片机一般采用TTl和COMS兼容电平,所以进行机与单片机的串行通信时,要进行EIA电平与TTL电平之间的转换。实施起来很简单,只要在机串口和单片机串口之间再接上一个电平转换芯片就可以了[12]。可使用相应的专用芯片进行电平转化,如MAX232[11]。如图4.1

17

89C51UART1TXDRXDGND10915MAX23278PC机串行端口TXDRXDGND 图4.1PC机与单片机连接图

RS—232C是美国电子工业协会(EIA)于1962年制定并于1968年最后一次修改而

成的一种串行通信标准,它既是一个物理接口标准,也是一个电气标准。RS—232C接口采用25根线,实际使用时,除去接到设备外壳的一根安全地线以外,真正用于异步串行通信的只有9根线(其余空着没定义),也正是因为如此,PC机一般都具有一个或几个针RS—232C串行接口,分别称为 COM1 和 COM2。

(1)由于RS232接口标准出现较早,难免有不足之处,主要有以下四点: ①接口的信号电平值较高,易损坏接口电路的芯片,又因为与TTL 电平不兼容故需使用电平转换电路方能与TTL电路连接。

②传输速率较低,在异步传输时,波特率为20Kbps;因此在“南方的老树51CPLD开发板”中,综合程序波特率只能采用19200,也是这个原因。

③接口使用一根信号线和一根信号返回线而构成共地的传输形式, 这种共地传输容易产生共模干扰,所以抗噪声干扰性弱。

④传输距离有限,最大传输距离标准值为50英尺,实际上也只能用在50米左右[10]。 (2)针对RS232接口的不足,于是就不断出现了一些新的接口标准,RS-485就是其中之一,它具有以下特点:

①RS-485的电气特性:逻辑“1”以两线间的电压差为+(2—6) V表示;逻辑“0”以两线间的电压差为-(2—6)V表示。接口信号电平比RS-232降低了,就不易损坏接口电路的芯片, 且该电平与TTL电平兼容,可方便与TTL 电路连接。

18

② RS-485的数据最高传输速率为10Mbps 。

③RS-485接口是采用平衡驱动器和差分接收器的组合,抗共模干能力增强,即抗噪声干扰性好。

④ RS-485接口的最大传输距离标准值为4000英尺,实际上可达 3000米,另外RS-232接口在总线上只允许连接1个收发器, 即单站能力。而RS-485接口在总线上是允许连接多达128个收发器。即具有多站能力,这样用户可以利用单一的RS-485接口建立设备网络。

[6]

4.2 VC实现串口通讯的四种方式

4.2.1 基于Windows API通信函数

与通信有关的Windows API函数共有26个,但主要有关的有CreateFile() 用 “comn”(n为串口号)作为文件名就可以打开串口;ReadFile() 读串口;WriteFile() 写串口;CloseHandle() 关闭串口句柄。初始化时应注意CreateFile()函数中串口共享方式应设为0,串口为不可共享设备,其它与一般文件读写类似,以下给出API实现的源代码。

(1)发送例程

声明全局变HANDLE m_hIDComDev;OVERLAPPED m_OverlappedRead; void CSerialAPIView::OnSend() //发送数据 {

char szMessage[20] = “data”; DWORD dwBytesWritten;

for (int i=0; i<sizeof(szMessage); i++) {

WriteFile(m_hIDComDev,(LPSTR)&szMessage[i],1,&dwBytesWritten,&m_Overlapped

19

Write);

if (WaitForSingleObject(m_OverlapperWrite, hEvent, 1000))dwBytesWritten = 0; else {

GentOverlappedResult(m_hIDComDev,&m_OverlappedWrite,&dwBytesWritten, FALSE);

m_OverlappedWrite. Offset += dwBytesWritten; }

dwBytesWritten++; }

(2)接收例程

DCB ComDcb; //设备控制块 HANDLE hCom;

hCom = CreateFile (\ NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if (hCom==INVALID_HANDLE_VALUE) { AfxMessageBox(\无法打开串行口\ else {

COMMTIMEOUTS CommTimeOuts ; SetCommMask(hCom, EV_RXCHAR ) ;

SetupComm(hCom, 4096, 4096 ) ; //设置收发缓冲区尺寸为4K

PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT | PURGE_TXCLEAR|

20

PURGE_RXCLEAR ) ; 清收发缓冲区;

(3)以下初始化CommTimeOuts和设置超时参数 ; CommTimeOuts.ReadIntervalTimeout = 0×FFFFFFFF ; CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ; CommTimeOuts.ReadTotalTimeoutConstant = 4000 ; CommTimeOuts.WriteTotalTimeoutMultiplier = 0; CommTimeOuts.WriteTotalTimeoutConstant = 4000 ;

SetCommTimeouts(hCom, &CommTimeOuts ); //设置超时参数 ComDcb.DCBlength = sizeof( DCB ) ;

GetCommState( hCom, &ComDcb ) ; //获取当前参数 ComDcb.BaudRate =9600; //波特率 ComDcb.ByteSize = 8; //数据位

ComDcb.Parity = 0; //校验 0~4=no, odd, even, mark, space SetCommState(hCom, &ComDcb ) ; }

(4)设置新的通信参数; DWORD dRead,dReadNum; unsigned char buff [200];

dRead=ReadFile(hCom, buff, 100, &dReadNum, NULL); //接收100个字符, dReadNu为实际接收字节数 ;

4.2.2 利用端口函数直接操作

这种方式主要是采用两个端口函数_inp(), _outp()实现对串口的读写,其中读端口函

21

数的原型为: int _inp(unsigned shot port) ;该函数从端口读取一个字节,端口号为0—65535;写端口的函数原型为: int _outp(unsigned shot port, int databyte) 该函数向指定端口写入一个字节。 不同的计算机串口地址可能不一样,通过向串口的控制及收发寄存器进行读写,可以实现灵活的串口通信功能,一般涉及具体的硬件电路讨论比较复杂。 4.2.3 基于MSComm控件

MSCOMM控件,即Microsoft Communication Control,是Microsoft为简化Windows下串行通信编程而提供的ActiveX控件。它提供了一系列标准通信命令的使用接口,利用它可以建立与串口的连接,并可以通过串口连接到其他通信设备(如调制解调器),发出命令,交换数据以及监视和响应串行连接中发生的事件和错误。MSCOMM控件可用于创建电话拨号程序、串口通信程序和功能完备的终端程序,MSCOMM控件提供了两种处理通信的方式[7]。

(1)事件驱动方式。

当通信事件发生时,MSCOMM控件会触发OnComm事件,调用者可以捕获该事件,通过检查其CommEvent属性便可确认发生的是哪种事件或错误,从而进行相应的处理。这种方法的优点是响应及时、可靠性高。

(2)查询方式。

在程序的每个关键功能之后,可以通过检查CommEvent属性的值来查询事件和错误。如果应用程序较小,这种方法可能更可取。例如,如果写一个简单的电话拨号程序,则没有必要每接收1个字符都产生事件,因为惟一等待接收的字符是调制解调器的\确定\响应[14]。

(3) MSComm 控件有很多重要的属性, 其中常用的是如下几个:

①CommPort 设置并返回通信口号, 缺省值为COM1, 可设置1~ 16 个。 ② SetStrings 设置并返回波特率、奇偶校验、数据位、停止位的字符串。其中波特

22

率的范围为300~ 19 200 b/s。

③ PortOpen 设置并返回通信口的状态, 同时用来打开和关闭通信口。

④ InputLen 决定每次Input 读入的字符个数,缺省为0, 表示读取接收缓冲区的全部内容。

⑤ Input 读入并清除接收缓冲区的字符。

⑥ InBufferCount 返回接收缓冲区已接收的字符数, 通过置0可清除接收缓冲区。 ⑦ Output 将发送的字符串或数组写到发送缓冲区。

⑧ InputMode 定义Input 属性获得数据的方式: 0 为文本; 1 为二进制[9] 4.2.4 基于CSerial类

Cserial 是由MuMega Technologies公司提供的一个免费的VC++类,可方便地实现串行通信。以下为该类定义的说明部分:

class CSerial { public: CSerial(); ~CSerial();

BOOL Open( int nPort = 2, int nBaud = 9600 ); BOOL Close( void ); int ReadData( void *, int ); int SendData( const char *, int ); int ReadDataWaiting( void );

BOOL IsOpened( void ){ return( m_bOpened ); } protected:

23

BOOL WriteCommByte( unsigned char ); HANDLE m_hIDComDev;

OVERLAPPED m_OverlappedRead, m_OverlappedWrite; BOOL m_bOpened; }

(1)串行通信类Cserial 成员函数简介;

① CSerial::Cserial是类构造函数,不带参数,负责初始化所有类成员变量。 ② CSerial:: Open这个成员函数打开通信端口。带两个参数,第一个是埠号,有效值是1到4,第二个参数是波特率,返回一个布尔量。

③ CSerial:: Close函数关闭通信端口。类析构函数调用这个函数,所以可不用调用这个函数。

④ CSerial:: SendData函数把数据从一个缓冲区写到串行端口。它所带的第一个参数是缓冲区指针,其中包含要被发送的资料;这个函数返回已写到端口的实际字节数。

⑤ CSerial:: ReadDataWaiting函数返回等待在通信端口缓冲区中的数据,不带参数。 ⑥ CSerial:: ReadData函数从端口接收缓冲区读入数据。第一个参数是void*缓冲区指针,资料将被放入该缓冲区;第二个参数是个整数值,给出缓冲区的大小。 4.2.5 四种实现方式的分析

第一种Windows API的通信函数使用面较广,但由于比较复杂,专业化程度较高,使用较困难;第二种通信函数 _ jnp、_ outp需要了解硬件电路结构原理;第三种方法使用的MSComm通信控件提供了标准的事件处理方式,并通过属性的方法提供了COM口参数的设置,较为容易地解决了串口;第四种方法需使用第三方提供的CSerial类,使用受限制,应用范围不广;本文采用第三种方式。

24

4.3 串口编程

在VC++6.0中,用APPWizard可以生成三种应用程序:单文档(SDI)、多文档(MDI)和基于对话框的应用程序。为了说明问题和省去不必要的细节,下面以基于对话框的应用程序为例。

4.3.1 插入MSCOMM控件

选择菜单项Project/Add to project/Components and Controls…,在弹出的对话框中选择Registered ActiveX Controls文件夹下的Microsoft Communications Control,version6.0,然后按下Insert按钮,接着会弹出一个对话框,提示生成的类名及文件名,按OK按钮即可实现控件的插入[8]。这时在对话框的控件工具栏 上会多出一个电话机模样的控件图标,Workspace的Classview中也多了一个类CMSComm。

此时即可将MSCOMM控件加入到对话框模板,加入方法与其他控件一样。然后还要在对话框类中相应加入一个成员变量,此处我们将其命名为m_comm。加入方法为:首先,在对话框模板中,用鼠标右键点击该控件,选择ClassWizard,在出现的对话框的Member Variables标签的Control Ids项下,选中IDC_MSCOMM1。然后,按Add Variable…按钮,在出现的对话框的Member Variable Name项中输入m_comm。最后,按OK按钮即可[16]。 4.3.2 设置属性

可以在两个地方对控件的属性进行设置:

(1)对话框资源编辑器中。在对话框模板上,用右键单击MSCOMM控件,然后选择Properties…菜单项,最后便可设置各项属性。此处只对以下几处进行改动,其他接受缺省设置:Rthershold:1,InputLen:1,DTREnable:不选,InputMode:1-Binary。

(2)对话框类的OnInitDialog()函数中;下面是以上设置的函数实现: BOOL CMyCOMMDlg::OnlnitDialog( )

25

{

CDialog::OnlnitDialog( );

m_comm.SetCommPort(1);//使用串口1

m_comm.SetSettings(\,N,8,1\;//波特率为9600,无奇偶校验,8位数据位,

1位停止位

m_comm.SetRThreshold(10); //每接收10个字符就触发1次接收事件 m_comm.SetSThreshold(0); //不触发发送事件

m_comm.SetInputLen(10); //每次读操作从缓冲区中取10个字符 m_comm.SetInputMode(1); //二进制数据传输形式 m_comm.SetPortOpen(TRUE); //打开串口 return TRUE;

}

4.3.3 设置串口

通过设置命令可以实现对串口参数的设置操作,包括串口的通讯端口的选择、波特率、波特率、校验位、停止位的选择等,通过对这些参数的设置可以改变通讯的方式。在执行参数设置响应函数前首先进行初始化程序,初始化程序如下:

if(m_COM.GetPortOpen()) {

m_COM.SetPortOpen(FALSE);

m_COM.SetSettings(\//波特率9600,无校验,8个数据位,1个停止位m_COM.SetInputMode(1); //1:表示以二进制方式检取数据

m_COM.SetRThreshold(1); //参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件

26

m_COM.SetInputLen(0); //设置当前接收区数据长度为0 m_COM.GetInput(); }

在初始化完毕后,在串口通讯界面上会看到初始后的结果,串口通讯的波特率是9600、校验位none、数据位8、停止位1。如果需要更改设置参数可以执行SettingChanged()//先预读缓冲区以清除残留数据

函数,该程序流程图对应的程序如下:

void SettingChanged() {

//设置波特率

if (m_ctrlCOMName.SetCurSel(m_ctrlCOMName.GetCurSel()) == CB_ERR || m_ctrlBuad.SetCurSel(m_ctrlBuad.GetCurSel()) == CB_ERR || m_ctrlDataBits.SetCurSel(m_ctrlDataBits.GetCurSel()) == CB_ERR || m_ctrlParity.SetCurSel(m_ctrlParity.GetCurSel()) == CB_ERR || m_ctrlStopBits.SetCurSel(m_ctrlStopBits.GetCurSel()) == CB_ERR) { ::AfxMessageBox(\ return ;

}

if (m_COM.GetPortOpen()) {

this->UpdateData(TRUE);

//读取内容

if (m_szCOMName.IsEmpty() ||m_zsParity.IsEmpty() ||

m_szDataBits.IsEmpty() ||m_szStopBits.IsEmpty() || m_szBaud.IsEmpty())

{ ::AfxMessageBox(\请设置好通信参数\ return ;

}

//设置校验位 CString szSetting; szSetting = m_szBaud + \

CString parity;

27

if (m_zsParity == \

parity = \parity = \parity = \parity = \parity = \

else if (m_zsParity == \else if (m_zsParity == \else if (m_zsParity == \else if (m_zsParity == \else

{ ::AfxMessageBox(\请设置好校验方式\ }

parity = \return ;

szSetting += parity + \szSetting += m_szDataBits + \szSetting += m_szStopBits; //选择串口通讯方式

if (m_szCOMName == \ { } else { }

m_COM.SetPortOpen(TRUE); m_szStatus = \打开\this->UpdateData(FALSE); m_COM.SetSettings(szSetting);

28

m_szCOMName == \m_COM.SetPortOpen(FALSE); if (m_szCOMName == \ if (m_szCOMName == \

//先关闭

m_COM.SetCommPort(1); m_COM.SetCommPort(2);

::AfxMessageBox(\端口不对\return ;

//打开串口

m_COM.SetInputMode(1); m_COM.SetRThreshold(1); m_COM.SetSThreshold(1); m_COM.GetInput(); }

4.3.4 数据类型的处理

{ }

this->UpdateData(TRUE); } else

//1:表示以二进制方式检取数据 //为1表示有一个字符引发一个事件

//参数1表示每当串口接收缓冲 //先预读缓冲区以清除残留数据

m_COM.SetInputLen(0);//设置当前接收区数据长度为0

//读取内容

MSCOMM可以两种不同的形式接收数据,即以文本形式和以二进制形式。用MSCOMM控件进行字符数据传输的实例很多。不过几乎所有以单片机为核心的测量系统所得到的原始数据都是二进制形式的,所以,以二进制形式传输数据将是最为直接而又简洁的办法。不仅如此,由于MSCOMM控件在文本形式下,其传输的是宽字符格式的字符,要想得到有用信息,还要额外处理。

4.4 帧格式定义

电机控制由串口实现,串口通讯协议帧所要完成的功能有发送启动、正传、反转、停止等命令数据帧;发送PID参数数据帧;并根据反馈信号使用有效的控制策略使电机达到所需的转速。通讯协议定义如下帧格式:

帧头(2字节)帧长(1字节) 命令字(1字节) 数据(N字节)校验和(1字节) 其中:

帧 头 —— 0x55 0xAA

帧 长 —— 命令字 + 数据区的长度; 命令字 —— 0xEA :电机启动命令;

29

0xEB :电机停止命令 ; 0xEC :电机正转命令;

0xED :电机反转命令; 0XEE :读取转速值命令;

数据区 —— 2字节比例系数,2字节积分系数,2字节微分系数,2字节转速值 校验和 —— 从命令字开始到数据区结束所有字节的算术和的反码,取低字节。

4.5 发送数据帧

当想发送数据给下位机时,首先调用发送函数comsend(CString m_sendms),如果串口已经打开,发送函数可以直接把数据从发送数据框送入发送缓冲区。清空按钮可以直接清空发送或接收缓冲区数据。

发送电机启动命令帧,调用发送函数发送0x55 0xAA 0x02 0xEA0x34数据帧给单片机,当数据接收后校验无误后可以实现电机启动,校验错误则从新发送。

同理停止按钮调用发送函数发送0x55 0xAA 0x02 0xEB 0xA4数据帧给单片机,当数据接收后校验无误后可以使电机制动;正传按钮发送0x55 0xAA 0x02 0xEC 0x56,实现电机正传;反转按钮发送0x55 0xAA 0x02 0xED 0x63实现电机反转。

4.6 接收数据帧

4.6.1 检测接收缓冲区数据

当数据进入接收数据缓冲区时,先检验帧头是否为真,如果为真则接收,否则放弃;当数据帧接收完毕后,再进行和校验,校验成功后则这组数据帧成功的被接受。

void rcvdata_proc() { unsigned char i,j,k;

if(gb_StartRcv == FALSE) {

30

检测帧头 0x55 0xAA LEN;

i=(gi_ucGetPtr-2)&(MaxRcvByte_C-1); j=(gi_ucGetPtr-1)&(MaxRcvByte_C-1); { i=gi_ucGetPtr; }

// 指向0x55 // 指向0xAA

if((ga_ucRcvBuf[i]==0x55)&&(ga_ucRcvBuf[j]==0xAA))

if(ga_ucRcvBuf[i]<= (MaxRcvByte_C-1)); { }

gb_StartRcv=TRUE;

gc_ucDataLen=ga_ucRcvBuf[i];

gi_ucStartPtr=(gi_ucGetPtr+1)&(MaxRcvByte_C-1);

gi_ucEndPtr= (gi_ucGetPtr + gc_ucDataLen+1)&(MaxRcvByte_C-1);

帧头正确,启动数据区接收;

gi_ucGetPtr=(gi_ucGetPtr+1)&(MaxRcvBy te_C-1);

} else {

开始接收数据处理

if(gi_ucGetPtr==gi_ucEndPtr) {

gb_StartRcv=FALSE; j=gi_ucStartPtr; k= 0;

for(i=0;i

k +=ga_ucRcvBuf[j]; }

k +=ga_ucRcvBuf[j]; if( k == 0xFF) {

31

数据帧接收完

计算CS

j=(j+1)&(MaxRcvByte_C-1);

取校验和

数据校验正确

}

4.6.2 从接收缓冲区取出数据

当数据校验无误后,开始读取缓冲区数据; {

CString temp1=receive(); //调用接收数据函数

if ((int)::strlen(temp1)==8) //收到数据为4个字节 { int i;

temptemp=\ }

gi_ucGetPtr=(gi_ucGetPtr+1)&(MaxRcvByte_C-1);

}

gb_DataOK=TRUE;

}

for(i=4;i<=5;i++)

temptemp+=temp1[i]; //提取第三个字节(电机状态)

32

UpdateData(FALSE); //显示电机状态 } else {

temptemp=\temptemp=temp1; if(temptemp==\

m_zhuanxiang=\停止\}

m_szRecvData+=temp1; m_suduzhi+=temp1;

temptemp=\ for(i=6;i<=7;i++)

temptemp+=temp1[i];//提取后两个字节(转速)

UpdateData(FALSE); //更新编辑框内容

第五章 PID算法

5.1 PID算法简介

首先必须明确PID算法是基于反馈的。一般情况下,这个反馈就是速度传感器返回给单片机当前电机的转速。简单的说,就是用这个反馈跟预设值进行比较,如果转速偏大,就减小电机两端的电压;相反,则增加电机两端的电压。

顾名思义,P指是比例(Proportion),I指是积分(Integral),D指微分(Differential)。在电机调速系统中,输入信号为正,要求电机正转时,反馈信号也为正(PID算法时,误差=输入-反馈),同时电机转速越高,反馈信号越大。要想搞懂PID算法的原理, P,I,D各自的含义及控制规律:

比例P:比例项部分其实就是对预设值和反馈值差值的发大倍数。比例P越大时,电机转速回归到输入值的速度将更快,及调节灵敏度就越高。从而,加大P值,可以减少从非稳态到稳态的时间。但是同时也可能造成电机转速在预设值附近振荡的情形,所以又引入积分I解决此问题。

积分I:顾名思义,积分项部分其实就是对预设值和反馈值之间的差值在时间上进行累加。当差值不是很大时,为了不引起振荡。可以先让电机按原转速继续运行。当时要将这个差值用积分项累加。当这个和累加到一定值时,再一次性进行处理。从而避免了振荡现象的发生。可见,积分项的调节存在明显的滞后。而且I值越大,滞后效果越明显。

微分D:微分项部分其实就是求电机转速的变化率。也就是前后两次差值的差而已。也就是说,微分项是根据差值变化的速率,提前给出一个相应的调节动作。可见微分项的调节是超前的。并且D值越大,超前作用越明显。可以在一定程度上缓冲振荡。比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋

33

势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。

5.2 参数调整一般规则

由各个参数的控制规律可知,比例P使反应变快,微分D使反应提前,积分I使反应滞后。在一定范围内,P,D值越大,调节的效果越好。各个参数的调节原则如下: PID调试一般原则

① 在输出不振荡时,增大比例增益P。 ② 在输出不振荡时,减小积分时间常数Ti。 ③ 输出不振荡时,增大微分时间常数Td。

5.3 PID算法在VC中的实现

算法就是解决一个问题的方法,是程序编写的依据。PID算法是一种过程控制算法,就是对误差信号(采样信号与给定信号的差)通过比例,积分,微分的运算后的结果作为输出控制信号,来控制所要控制的对象。PID算法有个标准公式,已经很成熟了,只要把那个公式变为程序就完成了编程。伺服电机要求在某一时刻能转到某一具体位置,控制精度很高。PID控制算法有位置式和增量式两种,在控制过程中可以通过正负脉冲的数量作为输出控制,不存在误差的积累,可以采用位置式。本文采用增量PID算法,式5.1为增量算法公式。

△u(k)?KP{[e(k)?e(k?1)]?TSTe(k)?D[e(k)?2e(k?1)?e(k?2)]}TITS (5.1)

对增量式PID算法5.1归并后,得式5.2

△u(k)?q0e(k)?q1e(k?1)?qe(k?2) (5.2)

2其中5.1式中的q0、q1、q2分别如下:

34

q0?KP[1?TSTD?] (5.3) TITSTD] (5.4) TSq1??KP[1?2q1?KPTD (5.5) TS从式5.2看出,数字增量式PID算法,只要贮存最近的三个误差采样值e(k)、e(k-1)、e(k-2)就足够了。PID控制流程图如图5.1所示:

进入设置参数kI,kP,kD设置初值 en-2=en-1=0 计算偏差 en=R-yn计算变量q1 q2 q3计算控制增量△un、输出控制Un=Un-1+△Un为下一时刻做准备en-2=en-1,en-1= en返回

图5.1增量PID算法流程图

增量PID算法流程图所对应的程序如下: void proc_PID_Ctrl( ) {

int ideltaSpeed[3],idelat_PWM_Val; unsigned int i,uiT,uiA,uiB,uiC; float f_Kp,f_Ti,f_Td;

35

}

ga_iSpeed[2] = ga_iSpeed[1]; ga_iSpeed[1] = ga_iSpeed[0]; ga_iSpeed[0] = g_iCurSpeed; ga_uiGapTime[1] = ga_uiGapTime[0]; ga_uiGapTime[0] = g_uiDeltaTime;

if(gc_ucGetSpeedCnt==0) {

//已获取了3次速度数据,处理 PID 运算 for(i=0;i<3;i++) {

ideltaSpeed[i] = abs(g_iRotateSpeed) - abs(ga_iSpeed[i]);// 计算出前三次的偏差 }

uiT = (ga_uiGapTime[0] + ga_uiGapTime[1])/2; // 计算出平均采样时间 f_Kp = g_uiKp/g_uiPID_Ratio;

f_Ti = g_uiTi/g_uiPID_Ratio; f_Td = g_uiTd/g_uiPID_Ratio;

uiA = (unsigned int)(f_Kp*(1+uiT/f_Ti+f_Td/uiT)); uiB = (unsigned int)(f_Kp*(1+2*f_Td/uiT)); uiC = (unsigned int)(f_Kp*f_Td/uiT);

idelat_PWM_Val = uiA*ideltaSpeed[0] - uiB*ideltaSpeed[1] + uiC*ideltaSpeed[2]; g_iPWM_Value = g_iPWM_Value + idelat_PWM_Val; // 得到 PWM 输出值 gb_CalOutValue = TRUE;

// 刷新时间间隔数据

// 刷新速度数据

} else { }

gc_ucGetSpeedCnt--;

36

结 论

本文在Windows平台下利用Visual C++6.0这个软件来实现PC机与单片机的数据通讯,从而实现伺服电机转速控制的。在控制伺服电机转速过程中本文采用了增量式PID算法,本文主要完成上位机界面的编程,同时也对直流电机的工作原理、结构、转速控制方案等做了详细介绍。在完成串口通讯编程时本文选择MSComm通信控件,由于它实现起来比较简单,还可以用Windows9xAPI的通信函数;VC的标准 通信函数 _ jnp、_ outp或第三方编程的通信类都可以实现。

上位机机界面编程还有很多软件可以实现,如Delphi、VB、CAI、Labview。单片机做为下位机本主要负责直流电机的各种参数数据的采集和和模数转换,然后通过串行端口把数据发送给上位机,PC机做为上位机主要负责数据的分析处理和显示,本设计主要完成上位机的设计。

37

参考文献

1秦建敏,马福昌,林卫虹,乔斌. PC机与单片机串行通信的多种实现方法[J],微计算机技术 ,2000 ,16(3):20—23

2 曹连民,石键,黄德杰. VC++中实现PC机与单片机的串行通讯[J],山东科技大学学报,2003,22(1):89—99

3 温富光,林永君,罗廷坤. Win2000下PC 机与单片机串行通信技术研究[J],微机发展,2004,14(9):70—71

4 杨旭辉,翁惠辉,肖红毅. 基于P87LPC768单片机的直流伺服电机转速控制[J],自动化技术与应用,2005,24(11):75—77

5于秀娜 . 一种基于单片机构成的伺服电机控制系统[J],科技创业,2008 ,(11):148—149

6 王琳琅,张伯珩,边川平. Windows环境下PC 机与单片机串行通信的实现[J],现代电子技术,2004,(3):81-83

7 马延,王健. PC机与单片机串行通信的实现方法[J],电气时代,2002,(2):46—47 8 马风格,胡书屏,王敏. 用VC++控件实现PC机与单片机之间的串行通信[J],应用技术,2000,(95):41—44

9 赵莉,赵波. 徐华英基于VC++6.0的PC机与单片机串行行通信设计[J],计算机应用,2003,110—113

10 姚成虎,王磊. 怎样进行PC机与单片机的串行通信系统的设计[J],计算机辅助工程,2003,18—21

11 黄成. 用VC++实现单片机与PC机通信[J],漳州师范学院学报,2006,(1):47—50 12 潘策,陈晓南,杨培林. PC机与单片机串行通信的硬件设计[J],现代电子技术,2003,

(15):4—9

38

13彭旭的. 机电控制系统原理及工程应用[M],北京:机械工业出版社,2000 14 张筠莉,刘书智,高守传. Visual C++实践与提高—串口通讯与工程应用篇[M], 北京:中国铁道出版社,2006

15 张海林,杜忠友,姜玉波. Visual C++ 简明教程[M],北京:电子工业出版社,2007 16 姜晓铭. Visual C++6.0程序设计应用短期培训教程[M],北京工业大学出版社,2000 17 王丰,高光金. Visual C++6.0程序设计与实训[M],北京大学出版社,2005 18 施振金. 电机与电气控制[M],北京:人民邮电出版社,2007 19 郁建平. 机电控制技术[M],北京:科学出版社,2000

20 刘岸,李文健.与时俱进伺服电机应对挑[J] .战现代制造与现代驱动,2007,29—30

39

附录 程序

#include \#include \#include \#include\#include\#ifdef _DEBUG

#define new DEBUG_NEW #undef THIS_FILE

static char THIS_FILE[] = __FILE__; #endif

CString temptemp; CString temptemp1;

// CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data

//{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA

// ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected:

virtual void DoDataExchange(CDataExchange* pDX); //}}AFX_VIRTUAL // Implementation protected:

//{{AFX_MSG(CAboutDlg) //}}AFX_MSG

DECLARE_MESSAGE_MAP() };

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)

40

// DDX/DDV support

{

//{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT }

void CAboutDlg::DoDataExchange(CDataExchange* pDX) {

CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP }

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP() // CBbyyqq123Dlg dialog

CBbyyqq123Dlg::CBbyyqq123Dlg(CWnd* pParent /*=NULL*/) : CDialog(CBbyyqq123Dlg::IDD, pParent) {

//{{AFX_DATA_INIT(CBbyyqq123Dlg) m_szBaud = _T(\ m_szCOMName = _T(\ m_szDataBits = _T(\ m_zsParity = _T(\ m_szStopBits = _T(\ m_szStatus = _T(\ m_szSendData = _T(\ m_szRecvData = _T(\ m_zhuanxiang = _T(\ m_setspeed = 0; m_Kd = 1; m_Ki = 2; m_Kp = 1;

41

m_suduzhi = _T(\ //}}AFX_DATA_INIT

// Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); }

void CBbyyqq123Dlg::DoDataExchange(CDataExchange* pDX) {

CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CBbyyqq123Dlg)

DDX_Control(pDX, IDC_STATIC_ICON, m_SetIcon); DDX_Control(pDX, IDC_kuozhan, m_kuozhan); DDX_Control(pDX, IDC_STOP_BIT, m_ctrlStopBits); DDX_Control(pDX, IDC_PARITY, m_ctrlParity); DDX_Control(pDX, IDC_DATA_BIT, m_ctrlDataBits); DDX_Control(pDX, IDC_COM_NAME, m_ctrlCOMName); DDX_Control(pDX, IDC_BAUD, m_ctrlBuad); DDX_CBString(pDX, IDC_BAUD, m_szBaud);

DDX_CBString(pDX, IDC_COM_NAME, m_szCOMName); DDX_CBString(pDX, IDC_DATA_BIT, m_szDataBits); DDX_CBString(pDX, IDC_PARITY, m_zsParity); DDX_CBString(pDX, IDC_STOP_BIT, m_szStopBits); DDX_Control(pDX, IDC_MSCOMM1, m_COM); DDX_Text(pDX, IDC_STATUS, m_szStatus); DDX_Text(pDX, IDC_SEND_DATA, m_szSendData); DDX_Text(pDX, IDC_RECV_DATA, m_szRecvData); DDX_Text(pDX, IDC_zhuanxiang, m_zhuanxiang); DDX_Text(pDX, IDC_shisu, m_setspeed); DDV_MinMaxInt(pDX, m_setspeed, 0, 3000); DDX_Text(pDX, IDC_Dcanshu, m_Kd); DDX_Text(pDX, IDC_Icanshu, m_Ki); DDX_Text(pDX, IDC_Pcanshu, m_Kp); DDX_Text(pDX, IDC_suduzhi, m_suduzhi); //}}AFX_DATA_MAP }

BEGIN_MESSAGE_MAP(CBbyyqq123Dlg, CDialog)

42

//{{AFX_MSG_MAP(CBbyyqq123Dlg) ON_WM_SYSCOMMAND() ON_WM_PAINT()

ON_WM_QUERYDRAGICON()

ON_BN_CLICKED(IDC_OPEN_CLOSE, OnOpenClose) ON_BN_CLICKED(IDC_CLEAR_SEND, OnClearSend) ON_BN_CLICKED(IDC_CLEAR_RECV, OnClearRecv) ON_BN_CLICKED(IDC_SAVE, OnSave) ON_BN_CLICKED(IDC_setspeed, Onsetspeed) ON_BN_CLICKED(IDC_qidong, Onqidong) ON_BN_CLICKED(IDC_tingzhi, Ontingzhi) ON_BN_CLICKED(IDC_zhengzhuan, Onzhengzhuan) ON_BN_CLICKED(IDC_fanzhuan, Onfanzhuan) ON_WM_CLOSE()

ON_BN_CLICKED(IDC_fasong, Onfasong) ON_BN_CLICKED(IDC_zhedie, Onzhedie) ON_BN_CLICKED(IDC_huatu, Onhuatu) //}}AFX_MSG_MAP END_MESSAGE_MAP()

// CBbyyqq123Dlg message handlers BOOL CBbyyqq123Dlg::OnInitDialog() {

CDialog::OnInitDialog();

// Add \

// IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) {

CString strAboutMenu;

strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) {

pSysMenu->AppendMenu(MF_SEPARATOR);

43

}

}

pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE);

// Set big icon

// Set small icon

m_btn.SubclassDlgItem(IDC_zhengzhuan,this); m_btn.SetIcon(IDI_ICON1);

m_btn.SetAlign(CButtonST::ST_ALIGN_VERT); m_btn1.SubclassDlgItem(IDC_fanzhuan,this); m_btn1.SetIcon(IDI_ICON2);

m_btn1.SetAlign(CButtonST::ST_ALIGN_VERT);

m_btn2.SubclassDlgItem(IDC_qidong,this); m_btn2.SetIcon(IDI_ICON23);

m_btn2.SetAlign(CButtonST::ST_ALIGN_VERT); m_btn3.SubclassDlgItem(IDC_tingzhi,this); m_btn3.SetIcon(IDI_ICON4);

m_btn3.SetAlign(CButtonST::ST_ALIGN_VERT); m_btn4.SubclassDlgItem(IDCANCEL,this); m_btn4.SetIcon(IDI_ICON6);

m_btn4.SetAlign(CButtonST::ST_ALIGN_VERT);

m_btn4.SetColor(CButtonST::BTNST_COLOR_BK_OUT, RGB(208,208,208)); m_btn4.SetColor(CButtonST::BTNST_COLOR_BK_FOCUS, RGB(208,208,208)); m_btn4.OffsetColor(CButtonST::BTNST_COLOR_BK_IN, 30);

m_btn5.SubclassDlgItem(IDC_CLEAR_RECV,this); m_btn5.SetIcon(IDI_ICON20);

m_btn5.SetAlign(CButtonST::ST_ALIGN_VERT);

m_btn6.SubclassDlgItem(IDC_SAVE,this); m_btn6.SetIcon(IDI_ICON22);

m_btn6.SetAlign(CButtonST::ST_ALIGN_VERT);

44

m_btn7.SubclassDlgItem(IDC_fasong,this); m_btn7.SetIcon(IDI_ICON19);

m_btn7.SetAlign(CButtonST::ST_ALIGN_VERT);

m_btn8.SubclassDlgItem(IDC_CLEAR_SEND,this); m_btn8.SetIcon(IDI_ICON20);

m_btn8.SetAlign(CButtonST::ST_ALIGN_VERT);

m_btn9.SubclassDlgItem(IDC_zhedie,this); m_btn9.SetIcon(IDI_ICON24);

m_btn9.SetAlign(CButtonST::ST_ALIGN_VERT);

ALIGN_HORIZ );

m_hIconRed = AfxGetApp()->LoadIcon(IDI_ICON_RED);//COM开关指示图标 m_hIconOff = AfxGetApp()->LoadIcon(IDI_ICON_OFF); //设置画图类的大小 CRect rect;

GetDlgItem(IDC_OSCOPE)->GetWindowRect(rect) ; ScreenToClient(rect) ; //适应picture控件的大小 // 创建画图工具

m_quxian.Create(WS_VISIBLE | WS_CHILD, rect, this) ;

// 初始化画图工具

m_quxian.SetRange(0, 200, 0) ; //设置纵坐标以及小数位数 m_quxian.SetYUnits(\电机转速(转/分)\设置y轴标题 m_quxian.SetXUnits(\时间轴\设置x轴标题 m_quxian.SetBackgroundColor(RGB(0, 0, 64)) ;//背景色 m_quxian.SetGridColor(RGB(192, 192, 255)) ; //坐标颜色 m_quxian.SetPlotColor(RGB(255, 255, 255)) ; // 曲线颜色 if(m_COM.GetPortOpen()) { }

m_COM.SetPortOpen(FALSE);

//先预读缓冲区以清除残留数据

45

m_COM.SetSettings(\波特率9600,无校验,8个数据位,1个停止位 Input();

本文来源:https://www.bwwdw.com/article/2u88.html

Top