一、课程设计题目:
直流电机转速测量与控制系统实验。
二、课程设计目的:
1.了解以微机为核心的闭环控制系统的组成原理。掌握电机转速闭环控制系统的构成方法。 2.了解霍尔器件的工作原理:电机转速的测量与控制的基本原理。掌握PWM调速原理和应用
方法。
3.掌握控制系统的设计与调试方法,提高分析问题和解决问题能力。
三、课程设计的内容:
设计一个对直流电机转速测量与转速控制的闭环控制系统。微机控制中心在监控界面上设置电机转速。电机转速测量利用霍尔传感器电路产生转速脉冲,定时/计数电路通过脉冲计数获得转速参量。电机转速调整采用PWM(脉宽调节)方法,控制中心采样到电机转速参量,算得转速值同预定转速设置值进行比较,若不相同,则调整控制转速脉冲的占空比,来达到调速的目的。(占空比=脉冲宽度/脉冲周期)
四、系统功能要求与设计要求:
1.系统监控界面设计:
监控系统具有转速参数设置窗口、采样的电机转速数据显示窗口、转速动态曲线显示窗口及强行干预系统运行的按钮功能或相应功能选择菜单。
2.监控程序设计要求:
a) 监控程序用查询方式获取转速数据。
b) 监控程序用中断方式获取转速数据。
3.硬件设计要求:
充分利用现有实验系统资源设计一个性能较好的直流电机转速闭环控制系统。利用带锁存的I/O接口电路(如 8255,74LS273,D/A-DA0832)输出控制电机转速的脉冲。采样转速用霍尔传感器件提供电机转速脉冲。利用定时/计数电路对电机转速脉冲计数。微机可从定时/计数电路中获得电机转速数值,并产生控制电机转速的PWM脉冲。
五、 设计详情 :
1)闭环控制系统原理图
电机转速测量与控制闭环系统基本功能图
2)电机控制及转速测量原理图
3)操作步骤
直流电机在控制脉冲作用下转动,电机转盘上的永久磁铁随之旋转,霍尔传感器件 3101T受磁场的影响,从端口OUT输出脉冲信号,电机旋转一圈,霍尔传感器输出一个脉冲,脉冲频率于电机转速成正比。通过测出脉冲信号的频率(单位时间脉冲个数)就可以计算出电机的转速。
测量转速时,需要一个定时器,设定时间为T,还需要一个计数器,将霍尔传感器的(OUT端)输出脉冲引导计数器的输入端。电机转动时,同时启动定时器和计数器,当定时器定时时间T到时,停止计数器的脉冲计数,并读出计数器的计数值S(即:时间T内的转数),可以计算出主流电机的转速 R=S/T。
直流电机转速调整:
首先确定控制脉冲占空比的调整方式,一种是设定正(或负)脉冲宽度,不断调整负脉(或正)冲宽度实现转速,当转速高于设定转速时,加大负脉冲(或减小正脉冲)的宽度。另一种是同时调整正负脉冲的宽度实现直流电机转速调整。
在调整了脉冲占空比后,同时启动定时器和计数器,进行转速测量,直流电机转速调整与测量交叉进行。
A.编制利用带锁存功能I/O端口(如 8255,74LS273,D/A-DA0832)输出控制电机转速的PWM脉冲程序
B. 编制利用定时器/计数器测量电机转速程序
C.A)程序和B)程序合并,实现电机转速测量与控制
D.编制系统监控界面的程序
E.电机转速测量与闭环控制系统的连调。
4)课程设计试验环境:
1.微机一台 (Pentium 4)
微机接口技术实验箱 一个
ISA – PCI转接卡 一块
连接电缆 一条
万用表 一块
微机接口技术实验讲义 一本
导线、剥线钳等
2.软件环境:
Windows XP 平台
Visual C++ 6.0 编译器
六、实验结果:
我们在完成了实验的基本要求基础上,还完成了实验的全部三个附加要求。详情如下:
1)界面截图
2)测试数据
设定转速 | 2600r/m | 2100r/m | 1800r/s | 1200r/m | 600r/m | 300r/m |
实测转速 | 40r/s | 37r/s | 30r/s | 19r/s | 11r/s | 0 |
调整时间 | ∞ | 0 | 15s | 8s | 20s | ∞ |
误差 | ∞ | 5.71% | 0.0% | 5% | 10% | ∞ |
实际观察情况如:调节快慢,波动大小等 | 设定速度超过了额定最大速度,无法平衡,因此该数据为坏值。 | 最接近设定速度,并且刚启动就稳定。 | 电机刚启动后,立即达到最大速度,等待一段时间后,下降到设定速度。 | 电机刚启动后,立即达到最大速度,等待一段时间后,下降到设定速度。 | 电机刚启动后,立即达到最大速度,等待一段时间后,下降到设定速度。 | 由于速度太小,电机收到阻尼,产生震荡,无法测出实际速度。 |
这次实验的课题相对而言比较大,做起来相对困难,不过10个人一组,大家齐心协力,团体的智慧是非常强大的!我们组抽到了最难的实验——直流电机转速测量与控制系统设计与实现。
拿到这个题目,我们首先读懂实验要求,分析8255,8253等器件的用处以及电路的连接方法;然后呢,选择实验箱,测试重要器件的好坏,确保器件是可用的。之后大部分时间是软件的改写,因为这个实验的软件实在难写,甚至看懂都比较困难。我们仿照计算机上原有的程序,把我们的想法加进去,完善了一些细节,美化了界面。最后综合在一起进行测试,因为我们采取电脑计数的方式,实验前害怕误差有点大,最后的实验结果挺准的。
实验过程中,我负责器件的测试及软件的改进。我看了将近10遍的代码,看懂了画图方法、程序结构。但是还有一些不懂的,比如某些MFC函数,器件接口的进制转换有些复杂,从中还是可以学到很多知识的。
实验过程中,大家在一起交流学习,共同为一个课题而努力,这是平时很难得的机会。增进了友谊,促进了学习。很希望大学期间可以多有一些这样的课程设计!
八、实验附录:
实验代码:
#include "stdafx.h"
#include "kkk.h"
#include "kkkDlg.h"
#include "PCI9052Dll.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// 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); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{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()
/////////////////////////////////////////////////////////////////////////////
// CKkkDlg dialog
CKkkDlg::CKkkDlg(CWnd* pParent /*=NULL*/)
: CDialog(CKkkDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CKkkDlg)
m_str = _T("");
m_set_speed = 30;
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CKkkDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CKkkDlg)
DDX_Text(pDX, IDC_EDIT_SPEED, m_str);
DDX_Text(pDX, IDC_EDIT_SET_SPEED, m_set_speed);
DDV_MinMaxInt(pDX, m_set_speed, 0, 2400);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CKkkDlg, CDialog)
//{{AFX_MSG_MAP(CKkkDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, OnSpeed_Add)
ON_BN_CLICKED(IDC_BUTTON2, OnSpeed_Min)
ON_WM_TIMER()
ON_BN_CLICKED(IDC_BUTTON_ADD, OnButtonAdd)
ON_BN_CLICKED(IDC_BUTTON4, OnStop)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CKkkDlg message handlers
BOOL CKkkDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// 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);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//this->SetTimer(1235,1000,0);
OpenDevice();
GetIoBase(&usIoAddr1);
data1=0x50;
OutByte(usIoAddr1+0x08+3,data1);
data1=0xff;
OutByte(usIoAddr1+0x08+1,data1);
return TRUE; // return TRUE unless you set the focus to a control
}
void CKkkDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
extern int data[100];
extern int index_data;
extern int index_x;
void CKkkDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
CClientDC dc(this);
//x
dc.MoveTo(0,300);
dc.LineTo(700,300);
dc.MoveTo(685,295);
dc.LineTo(700,300);
dc.MoveTo(685,305);
dc.LineTo(700,300);
//y
dc.MoveTo(20,0);
dc.LineTo(20,300);
dc.MoveTo(15,20);
dc.LineTo(20,0);
dc.MoveTo(25,20);
dc.LineTo(20,0);
//xxxxx
CPen pen;
pen.CreatePen(PS_SOLID,2,RGB(0,0,255));
CPen *oldpen=dc.SelectObject(&pen);
dc.MoveTo(0,300-m_set_speed*5);
dc.LineTo(700,300-m_set_speed*5);
dc.SelectObject(oldpen);
//draw data
CPen pen1;
pen1.CreatePen(PS_SOLID,2,RGB(255,0,0));
oldpen=dc.SelectObject(&pen1);
index_data=0;
for(int i=0;i dc.MoveTo(index_data+20,300-data[i]*5); dc.LineTo(index_data+10+20,300-data[i+1]*5); index_data+=10; } dc.SelectObject(oldpen); //font.DeleteObject(); } } // The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR CKkkDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } #define Num 1024 int data[100]; int index_x=0; int index_data=0; int count=0; int flag=Num/2; int last=0; bool IsStop=0; int timeLast=0; void CKkkDlg::OnSpeed_Add() { // TODO: Add your control notification handler code here if(flag CString str; str.Format("%f",flag*1.0/Num); SetDlgItemText(IDC_EDIT_PWM,str); } void CKkkDlg::OnSpeed_Min() { // TODO: Add your control notification handler code here if(flag>=10) flag-=10; CString str; str.Format("%f",flag*1.0/Num); SetDlgItemText(IDC_EDIT_PWM,str); } void CKkkDlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default //read 8253 InByte(usIoAddr1+0x09,&Idata1); int temp=255-Idata1; data1=0xff; OutByte(usIoAddr1+0x08+1,data1); UpdateData(); while (temp==m_set_speed) goto loop; if(temp>m_set_speed+1) { if(flag>=20) { flag-=20; } } else if(temp if(flag flag+=20; } } loop: CString str; str.Format("%d",temp); SetDlgItemText(IDC_EDIT_SPEED,str); //CString str; str.Format("%f",flag*1.0/Num); SetDlgItemText(IDC_EDIT_PWM,str); if(IsStop) this->KillTimer(1235); //drawing //CClientDC dc(this); data[index_x]=temp; index_x++; if(index_x>70) index_x=0; // timeLast++; // str.Format("%d:%d timeLast/60,timeLast%60); // SetDlgItemText(IDC_EDIT_TIME,str); double t=(temp-m_set_speed)*1.0/m_set_speed; if(t>0.1) { SetDlgItemText(IDC_EDIT_ERROR,"ERROR"); timeLast++; str.Format("%d:%d timeLast/60,timeLast%60); SetDlgItemText(IDC_EDIT_TIME,str); } else { SetDlgItemText(IDC_EDIT_ERROR,"Right"); } this->Invalidate(); CDialog::OnTimer(nIDEvent); } void delay(int t) { for(int i=0;i<10;i++) for(int j;j unsigned long _stdcall ChangeEdit(void* p) { //ASSERT(0 > 1); return 1; unsigned short usIoAddr; //unsigned short Addr; unsigned char data; GetIoBase(&usIoAddr); data=0x82; OutByte(usIoAddr+0x03,data); while(1) { count++; //delay(1); if(count>=Num) count=0; if(count //pa0=1; //data=0x82; OutByte(usIoAddr,0x01); } else { //pa0=0; OutByte(usIoAddr,0x00); } if(IsStop) break; } return 0; } void CKkkDlg::OnButtonAdd() { // TODO: Add your control notification handler code here //HANDLE hl; unsigned long pID = 11; CreateThread( 0, 0, &ChangeEdit, 0, 0, &pID); IsStop=0; this->SetTimer(1235,1000,0); timeLast=0; } void CKkkDlg::OnStop() { // TODO: Add your control notification handler code here IsStop=1; }