实 验 报 告
直流电机转速测量与控制系统设计与实现
评 语: 教 师: 年 月 日 成 绩
班 级: 学 号: 姓 名: 实验地点: 实验时间:
直流电机转速测量与控制系统设计与实现
一、 应用系统设计方案设计目的:
了解以微机为核心的闭环控制系统的组成原理。掌握电机转速闭环控制系统的构成方法。
了解霍尔器件的工作原理:电机转速的测量与控制的基本原理。掌握PWM调速原理和应用方法。
掌握控制系统的设计与调试方法,提高分析问题和解决问题能力。
二、 课程设计的内容:
设计一个对直流电机转速测量与转速控制的闭环控制系统。微机控制中心在监控界面上设置电机转速。电机转速测量利用霍尔传感器电路产生转速脉冲,定时/计数电路通过脉冲计数获得转速参量。电机转速调整采用PWM(脉宽调节)方法,控制中心采样到电机转速参量,算得转速值同预定转速设置值进行比较,若不相同,则调整控制转速脉冲的占空比,来达到调速的目的。(占空比=脉冲宽度/脉冲周期)
三、 系统功能要求与设计要求:
1) 系统监控界面设计:
监控系统具有转速参数设置窗口、采样的电机转速数据显示窗
口、转速动态曲线显示窗口及强行干预系统运行的按钮功能或相应功能选择菜单。
2) 监控程序设计要求:
监控程序用查询方式获取转速数据。 监控程序用中断方式获取转速数据。 3) 硬件设计要求:
充分利用现有实验系统资源设计一个性能较好的直流电机转速闭环控制系统。利用带锁存的I/O接口电路(如 8255,74LS273,D/A-DA0832)输出控制电机转速的脉冲。采样转速用霍尔传感器件提供电机转速脉冲。利用定时/计数电路对电机转速脉冲计数。微机可从定时/计数电路中获得电机转速数值,并产生控制电机转速的PWM脉冲。
四、 设计详情 :
1)闭环控制系统原理图
图一 电机转速测量与控制闭环系统基本功能图
2)电机控制及转速测量原理图
图二 电机控制及转速测量原理图
3)硬件连线图
图三 硬件连线图
4)操作步骤
直流电机在控制脉冲作用下转动,电机转盘上的永久磁铁随之旋转,霍尔传感器件 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.电机转速测量与闭环控制系统的连调。
五、 课程设计试验环境:
a) 实验仪器
微机一台 (Pentium 4) 微机接口技术实验箱 一个 ISA – PCI转接卡 一块 连接电缆 一条 万用表 一块 微机接口技术实验讲义 一本 导线、剥线钳等 b) 软件环境:
Windows XP 平台和Visual C++ 6.0 编译器
六、 实验主要代码
// ReaderDlg.cpp : implementation file #include #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif int iospeed; //设置转速 int rate; //实际转速 int times = 0; //运行时间 double key = 1.0; //占空比 int normal = 0; //异常判断 int stop = 0; ///////////////////////////////////////////////////////////////////////////// // CReaderDlg dialog CReaderDlg::CReaderDlg(CWnd* pParent /*=NULL*/) : CDialog(CReaderDlg::IDD, pParent) { //{{AFX_DATA_INIT(CReaderDlg) m_edit6 = _T(\"\"); m_edit4 = _T(\"\"); m_edit5 = _T(\"\"); m_speed = 0; m_edit3 = _T(\"\"); //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CReaderDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CReaderDlg) DDX_Text(pDX, IDC_EDIT6, m_edit6); DDX_Text(pDX, IDC_EDIT4, m_edit4); DDX_Text(pDX, IDC_EDIT5, m_edit5); DDX_Text(pDX, IDC_EDIT1, m_speed); DDV_MinMaxInt(pDX, m_speed, 0, 2500); DDX_Text(pDX, IDC_EDIT3, m_edit3); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CReaderDlg, CDialog) //{{AFX_MSG_MAP(CReaderDlg) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON1, OnButton1) ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1) ON_BN_CLICKED(IDC_BUTTON2, OnButton2) ON_BN_CLICKED(IDC_BUTTON3, OnButton3) ON_EN_CHANGE(IDC_EDIT3, OnChangeEdit3) ON_EN_CHANGE(IDC_EDIT6, OnChangeEdit6) ON_EN_CHANGE(IDC_EDIT4, OnChangeEdit4) ON_EN_CHANGE(IDC_EDIT5, OnChangeEdit5) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CReaderDlg message handlers BOOL CReaderDlg::OnInitDialog() { CDialog::OnInitDialog(); // 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 return TRUE; // return TRUE unless you set the focus to a control } // 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. void CReaderDlg::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(); } } // The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR CReaderDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } void CReaderDlg::OnButton1() //启动按钮 { // TODO: Add your control notification handler code here //初始化 unsigned short iobase,addr; OpenDevice(); GetIoBase(&iobase); //得到基地址 addr=iobase+3; //8255控制字 OutByte(addr,0x8a); addr = iobase + 11; //8253计数器0控制字 OutByte(addr , 0x32); addr = iobase + 8; //计数器0初始化 OutByte(addr,0x09); OutByte(addr,0x3d); addr = iobase +11 ; //8253计数器1、2控制字 OutByte(addr,0x52); OutByte(addr,0xa0); addr = iobase + 9 ; //计数器1初始化 OutByte(addr,100); //初始值为100 addr = iobase + 10 ; //计数器2初始化 OutByte(addr,0x01); addr = iobase +2; //PC口置1 OutByte(addr,0xff); OutByte(iobase,0x01); //PA0置1,电机以最高速度转动 //闭环调节系统 char a11,a12; //辅助变量,调节占空比 int num = 1; //用来判断是否计时到1秒 unsigned char speed; unsigned char tt; //判断PB口状态,辅助计时 while(stop == 0) { Sleep(1); //转速 if(num == 1) { times++; //运行时间 CReaderDlg::OnChangeEdit3(); //显示运行时间 addr=iobase+9; //计时器1初始化 OutByte(addr, 100); addr=iobase+2; //PC口控制计数器0、1的GATE,同步计数 OutByte(addr, 0x00); OutByte(addr, 0xff); num = 0; } addr=iobase+1; //读PB状态,判断是否计时超过1秒 InByte(addr,&tt); if(tt==1) //时间到,进入转速计算语句 { addr=iobase+2; //PC口置零,GATE失效,停止计数 OutByte(addr,0x00); addr=iobase+9; //读计数器1的计数值 InByte(addr,&speed); if((int)speed<0) speed=0; rate = 60*(100-(int)speed); //转速计算 CReaderDlg::OnChangeEdit6(); //显示转速 num = 1; if(rate > iospeed && key >= 0.1) //修改占空比 key = key-0.1; else if(rate < iospeed && key <= 0.9) key = key+0.1; CReaderDlg::OnChangeEdit4(); //显示占空比 if(abs(rate-iospeed)/iospeed > 0.1) //判断正常/异常 } } normal = 0; else normal = 1; //异常 //正常 a11=(char)(100*key); //电机驱动脉冲高电平持续时间 OutByte(iobase, 0x01); addr=iobase+10; OutByte(addr, 0); OutByte(addr, a11); unsigned char a; int b; while(1) //高电平倒计时 { addr=iobase+2; InByte(addr,&a); b=(int)a; if((b & 0x10)) break; } a12=(char)(100*(1-key)); //电机驱动脉冲低电平持续时间 OutByte(iobase, 0x00); addr=iobase+10; OutByte(addr, 0); OutByte(addr, a12); while(1) //低电平倒计时 { addr=iobase+2; InByte(addr,&a); b=(int)a; if((b & 0x10)) break; } } //CloseDevice(); void CReaderDlg::OnChangeEdit1() { // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CDialog::OnInitDialog() // function and call CRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here } void CReaderDlg::OnButton2() //停止按钮 { stop = 1; // TODO: Add your control notification handler code here unsigned short iobase; OpenDevice(); GetIoBase(&iobase); OutByte(iobase, 0x00); //置驱动脉冲无效 MessageBox(\"电机已停止工作!\"); CloseDevice(); } void CReaderDlg::OnChangeEdit2() { // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CDialog::OnInitDialog() // function and call CRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here } void CReaderDlg::OnButton3() //设置转速确认按钮 { // TODO: Add your control notification handler code here UpdateData(); //m_speed.GetDlgItemInt(iospeed); iospeed = m_speed; } void CReaderDlg::OnChangeEdit3() //显示运行时间 { // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CDialog::OnInitDialog() // function and call CRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here CString temp; temp.Format(\"%d\ m_edit3 = temp; //m_edit3=times; UpdateData(false); UpdateWindow(); } void CReaderDlg::OnChangeEdit6() //显示实测转速 { // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CDialog::OnInitDialog() // function and call CRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here CString temp; temp.Format(\"%d\ m_edit6 = temp; UpdateData(false); UpdateWindow(); } void CReaderDlg::OnChangeEdit4() //显示占空比 { // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CDialog::OnInitDialog() // function and call CRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here CString temp; temp.Format(\"%lf\ m_edit4 = temp; UpdateData(false); UpdateWindow(); } void CReaderDlg::OnChangeEdit5() //显示异常状况 { } // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CDialog::OnInitDialog() // function and call CRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here CString temp; if(normal == 1) temp.Format(\"正常\"); else temp.Format(\"异常\"); m_edit4 = temp; UpdateData(false); UpdateWindow(); 七、 系统测试结果 八、课程设计中遇到的问题及解决办法 这次实验的课题相对于其他三个而言,难度比较大,做起来相对困难,由于实验器材的限制,我们两个组合成了一个组,一共十个人。大家齐心协力,团体的智慧是非常强大的! 拿到这个题目,我们首先读懂实验要求。然后根据实验的要求对人员进行了分工,一部分同学分析8255,8253等器件的用处以及电路的连接方法,一部分同学选择实验箱,测试重要器件的好坏,确保器件以及芯片是可用的。硬件相对于软件比较简单,大部分的组员是参与软件的编写。软件耗时耗力,我们综合了大家的智慧于一体,经过了艰苦的奋斗,终于在最后一天将代码写好了。连接硬件进行综合测试。 实验的不足之处还有很多,在老师的指导下,我们已经意识到了。比如可视界面不够精确,没有相应的坐标标识,在改变数值的时候,曲线不能及时的反馈出来。我们以后的软件设计中一定会注意这些方面的问题。 实验过程中,大家在一起交流学习,共同为一个课题而努力,这是平时很难得的机会。增进了友谊,促进了学习。很希望大学期间可以多有一些这样的课程设计! 谢谢老师的耐心指导! 因篇幅问题不能全部显示,请点此查看更多更全内容