1. 课程论文题目
设计一个模拟汽车控制系统,用简单逻辑电平控制车的档位(前进1,前进2,前进3,后退),用步进电机的转速模拟车的速度(若步进电机不能用,也可以用流水灯的刷新方向和速度,或数码管显示的数值来模拟车的速度),用A/D转换器模拟车的油门大小。用Visual C++ 6.0(推荐使用)编程,并设计程序界面。
2. 程序设计思路(包括硬件连接结构)
本实验用到的芯片主要是一款8255接口芯片,利用其完成串行信号与并行信号的转换,步进电机的驱动,流水灯的显示,数码管显示等。由于多次用到8255接口芯片,下面首先结合我掌握的资料,介绍一下8255接口芯片。
(1)芯片简介
8255A是通用的8位并行输入/输出接口芯片,使用灵活,功能强大,是应用最广的典型可编程并行接口芯片。共有40个引脚,采用双列直插式封装,各引脚功能如下:与微处理器连接的信号线 D7~D0:数据线,三态双向8位,与系统的数据总线相连。 CS:片选信号,低电平有效。 WR:写信号,低电平有效。 RD:读信号,低电平有效。 A1,A0:端口地址选择信号。用于选择8255A的3个数据端口和一个控制端口。 8255A与外部设备连接的信号线 PA7~PA0:A口数据线。 PB7~PB0:B口数据线。 PC7~PC0:C口数据线
引脚图如下:
(2)流水灯控制平台:
在学校的试验平台上LED灯的端口地址是0xc860,通过控制LED输入电平的高低来控制LED灯的明暗,而电平的高低是通过向端口写书据来体现的,例如向端口写入1就代表输入的是高电平而0是低电平。因而要实现流水灯的各种功能,只需控制各端口电平的高低也就是对端口写入的数据的不同。灯的闪烁的实现就是在LED灯的全明与全暗之间设置一个延迟,得到闪烁的效果,从左至右或从右至左的变化是通过将初始值进行左移位或右移位即可。
(3)、小键盘控制
本次课程设计中我们用小键盘控制油门,具体做法在下面的数据结构与算法设计方面会得到体现。
(4)、设计思想
在以前的上机实验中,我们分别做过流水灯和步进电机的实验,都成果的完成了,因此这次课程设计是对以前实验的一次综合。对于此次实验概况:都是有手动控制,分别是汽车档位由LED控制开关来控制,总共设置为0—4五个档位,汽车油门大小有小键盘控制,小汽车的行驶速度由步进电机的转速以及流水灯的闪烁速度来模拟,倒档时表现为反方向转动(此处流水灯因为闪烁方向不明显,所以不好分辨)。
3.功能模块图
4.数据结构设计
由于本次微机接口课程设计对算法的要求不高,所以在涉及算法与数据结构的领域并没有特别的。主要是开了些数组。
1.小键盘控制小车
在课设时,试验台上的小键盘是4*4的,行号+列号=索引值,具体索引键盘上的16个位置。
每一个键可以由行列共同索引,所以结合墙威老师给的小键盘参考代码,我们开辟了如下数组:
unsigned char pc0_3_out[]={0x01,0x02,0x04,0x08};
unsigned char pc4_7_in[]={0x10,0x20,0x40,0x80};
上述数组通过行列组合的优势在于可以快速索引到所输入的控制信息。在响应键盘时时间复杂度为O(n^2),空间复杂度为O(1)。
5.算法设计
1.控制系统
(1).小键盘响应算法
关于小键盘的响应,我们组用到的是双重循环结合开辟的索引数组找到用户输入的命令信息。伪代码如下:
For(行从0到4)
{
For(列从0到4)
如果找到用户输入的命令字,记录行列下表Col,Row
}
根据行列算出具体命令。
(2).步进电机算法
依据步进电机的基本原理,
_outp(0x0c803,0x88);//初始化步进电机,B组0方式、PB口输出、PC口输入。 _outp(0x0c801,0x05); _outp(0x0c801,0x15); _outp(0x0c801,0x14); _outp(0x0c801,0x54); _outp(0x0c801,0x50); _outp(0x0c801,0x51); _outp(0x0c801,0x41); _outp(0x0c801,0x45);
以双八拍方式AB→ABC→BC→BCD→CD→CDA→DA→DAB控制步进电机工作。
通过循环控制步进电机反复执行,由并行接口8255控制。
(3)、七段数码管显示算法
根据油门和档位模拟出小车行驶的速度,然后结合前几次做过的七段数码管内在结构和外在特性,在七段数码管显示显示速度,由并行接口8255控制。
2.MFC界面
关于界面,由于之前有些MFC编程经验,也知道动画的制作原理。于是在卡通动画中截了几张小车行驶的连续图,然后结合具体的速度重载自己的定时器函数,最终达到动画效果。
6.程序关键代码
源码1 Mqc类:模拟汽车控制系统关键代码 /************************************************* *功能:“确定”功能响应函数 * *************************************************/ void Mqc::OnOk() { CSliderCtrl*pSlide=(CSliderCtrl*)GetDlgItem(IDC_YOUMENDAXIAO); pSlide->SetRange(0,255); pSlide->SetPos(0); _outp(0x0c803,0x0e); SetTimer(1000,youmen,NULL); } /************************************************ * *功能:”取消“功能响应函数 * *************************************************/ void Mqc::OnCancel() { CDialog::OnCancel(); } /************************************************ * *功能:滑动框响应函数 * *************************************************/ void Mqc::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if(pScrollBar->GetDlgCtrlID()==IDC_YOUMENDAXIAO) { CSliderCtrl*pSlide=(CSliderCtrl*)pScrollBar; CString strText; strText.Format("%d",pSlide->GetPos()); SetDlgItemText(IDC_YOUMEN,strText); } CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } /************************************************ * *功能:小键盘响应函数,根据键盘输入控制小车油门 * *************************************************/ unsigned char pc0_3_out[]={0x01,0x02,0x04,0x08}; unsigned char pc4_7_in[]={0x10,0x20,0x40,0x80}; char scan_key16() { int i,j,key1; int in_i,in_j; unsigned char find; unsigned char pc_in,pc_in4_7; find=0; for(i=0;i<4;i++) { _outp(0x0c802,pc0_3_out[i]); pc_in=_inp(0x0c802); pc_in4_7=pc_in & 0xf0; for(j=0;j<4;j++) { if(pc4_7_in[j]==pc_in4_7) { find=1; in_j=j; in_i=i; } } } if(!find) return 0; key1=in_j*4+in_i; if(key1<9) (key1)++; else if(key1==9) (key1=0); return key1; } /************************************************ * *功能:重载定时器函数,完成人机交互显示 * *************************************************/ void Mqc::OnTimer(UINT nIDEvent) { if(i==0) i=1; else if(i==1) i=0; UpdateData(true); if(nIDEvent==1000) { CSliderCtrl*pSlide=(CSliderCtrl*)GetDlgItem(IDC_YOUMENDAXIAO); _outp(0x0c803,0x88); _outp(0x0c802,0x00); if(_kbhit()) { _getch(); } m_youmen=(scan_key16())*30; pSlide->SetPos(m_youmen); m_daxiao=m_youmen; UpdateData(false); //显示油门大小 if(m_youmen>=200) { if(i==1) _outp(0x0c860,0x0e); _outp(0x43,0xb6); _outp(0x42,19); _outp(0x42,83); int PB; PB=_inp(0x61); _outp(0x61,0xfc); PB=PB|0x3; _outp(0x61,PB); Sleep(1000); PB=PB&0xfc; _outp(0x61,PB); } m_daxiao=pSlide->GetPos(); UpdateData(false); if(_inp(0x0c860)<=4) { m_dangwei=_inp(0x0c860); // 读档位 UpdateData(false); } if(m_dangwei==0) { // 倒车敬示 if(i==1) _outp(0x0c860,0xaa); else _outp(0x0c860,0x55); } switch(count) { case 0:_outp(0x0c801,0x05);break; case 1:_outp(0x0c801,0x15);break; case 2:_outp(0x0c801,0x14);break; case 3:_outp(0x0c801,0x54);break; case 4:_outp(0x0c801,0x50);break; case 5:_outp(0x0c801,0x51);break; case 6:_outp(0x0c801,0x41);break; case 7:_outp(0x0c801,0x45);break; default:break; } if(m_dangwei!=0) { if(count!=7)count++; else count=0; } if(m_dangwei==0) { if(count!=0)count--; else count=7; } if(m_dangwei==0)youmen=1000; // 控制电机延时 else youmen=5+1000/m_dangwei-m_youmen; if(m_dangwei==0) { m_EDITg+=-10000*1.0/youmen; m_Speed=-10000*1.0/youmen; Direction=false; } else { Direction=true; m_EDITg+=10000*1.0/youmen; m_Speed=10000*1.0/youmen; } m_Time+=1; UpdateData(false); SetTimer(1000,youmen,NULL); } CDialog::OnTimer(nIDEvent); } /************************************************ * *功能:“显示”响应函数,转入小车动画界面 * *************************************************/ void Mqc::OnShowbutton() { CCarShowDlg *dlg=new CCarShowDlg ; dlg->m_Speed=m_Speed; dlg->Direction=Direction; dlg->DoModal(); } 源码2 CarShowDlg类:MFC小车行驶动画关键代码 /************************************************ * *功能:对话框初始化函数 * *************************************************/ BOOL CCarShowDlg::OnInitDialog() { nowOne=1; CDialog::OnInitDialog(); SetTimer(1, 1.0/m_Speed*1000, NULL); return true; } /************************************************ * *功能:重绘对话框背景,模拟小车行驶动画 * *************************************************/ void CCarShowDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); 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; dc.DrawIcon(x, y, m_hIcon);// 绘制图标 } else { CPaintDC dc(this); CRect rc; GetClientRect(&rc); CDC dcMem; dcMem.CreateCompatibleDC(&dc); CBitmap bmpBackground; if(nowOne==1) { if(Direction) nowOne++; else nowOne--; bmpBackground.LoadBitmap(IDB_BITMAP1); } else if(nowOne==2) { if(Direction) nowOne++; else nowOne--; bmpBackground.LoadBitmap(IDB_BITMAP2); } else if(nowOne==3) { if(Direction) nowOne++; else nowOne--; bmpBackground.LoadBitmap(IDB_BITMAP3); } else if(nowOne==4) { if(Direction) nowOne++; else nowOne--; bmpBackground.LoadBitmap(IDB_BITMAP4); } else if(nowOne==5) { if(Direction) nowOne++; else nowOne--; bmpBackground.LoadBitmap(IDB_BITMAP5); } else if(nowOne==6) { if(Direction) nowOne++; else nowOne--; bmpBackground.LoadBitmap(IDB_BITMAP6); } else if(nowOne==7) { nowOne=1; bmpBackground.LoadBitmap(IDB_BITMAP1); } else if(nowOne==0) { nowOne=6; bmpBackground.LoadBitmap(IDB_BITMAP6); } BITMAP bitmap; bmpBackground.GetBitmap(&bitmap); CBitmap* pbmpPri = dcMem.SelectObject(&bmpBackground); dc.StretchBlt(0,0,rc.Width(), rc.Height(), &dcMem,0,0,bitmap.bmWidth, bitmap.bmHeight, SRCCOPY); } } /************************************************ * *功能:重载定时器 * *************************************************/ void CCarShowDlg::OnTimer(UINT nIDEvent) { Invalidate(); CDialog::OnTimer(nIDEvent); }
7.程序运行结果
(1)基于MFC创建的主窗口界面:
对进入该界面,点击“确定”按钮后,进行小车的行驶模拟,在这过程中,还有一个功能是不可见的,就是当油门大小超过200,会发出警报声。对于界面上的“显示”按钮,是我们小组做的一个模拟小车行驶的一个动画界面按钮。
(3)、动画界面
8.编程中遇到的困难及解决方法
1.本次课设最大的问题莫过于好的实验箱不多,我们去得很早找到了一台之前用过的被证明是好的实验台,可是做了一会发现步进电机控制的转盘不转,于是我和其他3个组员到处找,开机连电路然后检测,但最终没有找到完全可用的实验箱,只好找了一个A/D接口坏掉的替代品,这也导致最终我们只选择小键盘实现小车的控制。
2.对于芯片不熟,在使用芯片的时候还要翻看书上的介绍以及例题。但是在这个过程中,我通过带上自己的电脑上网查阅相关芯片说明,查看前几次实验程序并加以总结以及回过头仔细看书深入了解了一些芯片,掌握了初始化、写入命令字等操作,并对芯片的内部结构、运行原理有了一定的认识。
3.MFC界面的设计。老师本次实验给了我们很大的发挥空间—没有强制要求我们要具体做出什么效果,另一方面导致刚开始我们组不到做些什么,但经过一阵气氛活跃的组内讨论,我们确定下来最终在界面上完成什么功能、营造什么效果。
4.由于连不上网,很多资料只能通过手机查找,带来一定的不便,导致我们进度比较慢,本可以1天完成的任务硬是拖到第二天上午,且给老师检查的时候没能做出MFC模拟小车的动画效果。下午的时候我们在原来的基础上加以改进,添加了MFC动画。
5.小键盘的使用。刚开始使用小键盘时由于没看电路图,即使墙威老师事前给过代码,也没能看懂,毕竟我们之前的使用的C语言不用涉及具体到硬件层面的东西。通过问了梁老师,结合着电路图基本摸清小键盘的使用方法。
6.事先规划程序流程。由于之前听老师说本次课设比较简单,所以没有事先画流程图,导致我们在具体编程的时候只能想到一点写一点,类似大爆发模式,导致刚开始有些逻辑混乱,程序条理不清。后来我们索性推倒重来,指定组内专人设计了程序流程图,然后有条不紊地开始编程,效果不错。建议以后在编程的时候无论课题如何,事先明确大致方向和流程。
7.模仿与创新。由于本次实验台上有些源码,刚开始我们准备直接在原有基础上随便改改,但进行到具体控制系统时发现有些地方并不能直接照抄,如果只是改改又会导致自己开发出的程序结构混乱,于是我们索性在看懂源有代码的基础上自己重写,加进组内成员的点子,编程出真正属于自己的东西。
9.总结心得及良好建议
为期两天的微机接口课程设计很快过去了。在这两天里,我根据微机接口课程上的知识结合前几次做过的实验,在多次修改的基础上,完成了自己的小车控制系统设计。下面我谈谈我对微机接口课程以及微机接口课程设计的理解。
接口(interface),顾名思义,就是微处理器与“外部世界”的连接电路,是CPU与外界进行信息交换的中转站。之所以在计算机系统中引入微机接口概念,是因为信息类型和信号电平的匹配问题,(信号线的功能、逻辑、时序),速度的匹配问题,提高了CPU的效率,使外设的发展不受限制(硬件结构不依赖CPU)。由以上可以总结出微机接口的主要功能:接受、解释和执行CPU命令的功能;返回外设状态的功能;数据的缓冲功能;信号的转换功能;设备选择(端口寻址)功能;数据宽度与数据格式转换功能;中断管理功能;可编程功能。我们的微机接口课本就是围绕上面的内容展开的。个人感觉微机接口是门博大精深的课程,设计的学科很多,有计算机组成原理、计算机体系结构、汇编语言、C语言等。
在学习微机接口理论课程以及进行课设时,我认为应该做到两点:掌握硬件基本结构和原理,自主编程完成书上例题和课程实验包括最后的课程设计。总结来说,就是要做到理论结合实际。在课程设计以及之前的几个实验过程中我是深有体会的。最开始在做基本I/O实验时,由于之前在课下没有认真看过书,导致对I/O基本原理不熟,本该很简单的实验做了较长时间。后来再做之后的CMOS实时钟实验、步进电机的驱动等实验时又遇到过已经掌握基本原理和芯片结构图,但一时无法编程出正确程序的问题,总的原因是理论偏离了实际。有时在逻辑上完全正确的代码就是在实验台上得不到正确结果,在一番思考尝试之后我选择在需要与外设读写交换信息的地方加上了几条延时指令,结果问题就迎刃而解了。我得出的结论是CPU的执行速度是很快的,但外设的速度是相对有限的,在二者进行数据交换的时候需要适当缓冲,有时缓冲作用通过特定的锁存器、缓冲器来实现,有时则需要我们自己人为调整,比如尝试适当加些延时指令,当然这需要在确实有需要的地方添加,不能随意乱加导致程序效率降低,即要在功能与效率之间折中,这种折中思想在计算机体系结构以及程序设计中是经常用到的。
关于本课程的建议,首先,我真诚的希望学校能更新一下实验台。虽然大家最终都做了,但有些组是在别人做完之后才找到好的机器;有的恐怕是在没有找到好的试验台之后索性放弃了最后用别人的试验箱请老师检查。
然后,我认为学好微机接口应该做到理论实际相结合。不能一味只看书,轻视具体编程体验,撇开实际空谈理论,最终一无所获;也不能完全脱离理论,在实验原理一知半解的情况下就动手连接电路然后敲上相应代码,好比囫囵吞枣。微机接口课以及微机接口课程设计有着独有的特点,初学者要处理好理论与实际、硬件与软件等的关系。既要掌握书上的基本原理,又要运用所学的汇编语言以及C语言去获得相应的编程体验。
最后,就是要注意合作。在课程设计过程中,梁老师有意安排3个人一组(由于试验箱有限,我们组最终有4个人,我、潘传军、张映红、郭庆艳)。其实本次课设较其他课设简单很多,我想老师可能是有意培养我们的合作意识。毕竟将来走进企事业单位碰到的问题很可能是个人无法解决的,这就需要与同事合作。合作是门艺术,合作的目的是要做到1+1>=2,双方互利共赢,而不是两败俱伤。在本次课程设计过程中,我们组也积极响应梁老师的要求,各自分工,虽然没有强制要求组员具体做事么事,但每个人是分了工的,大家在分工中合作。最终我们在多次更改程序的情况下,依然在第2天上午成功完成任务。在此感谢我的组员。
源代码下载地址 http://download.csdn.net/detail/xj2419174554/6845793
上述可能有很多错误或不足的地方,欢迎大牛斧正!