软件课程设计报告
MFC实现大整数运算计算器
学生姓名:***
学 号:***
院 系:***
专业班级:通信工程
指导教师姓名:**
完成时间: 20## 年11月 18
一 需求分析
1、设计背景:
在“MFC实现大整数运算计算器”设计中,本人使用的编译平台是VS2010用到了MFC——微软件基础类库(Microsoft Foundation Classes),实际上是微软提供的,用于C++环境下编写应用程序的一个框架和引擎,给C++程序员在Windows上快速开发用的。目前最新版本为9.0(截止20##年11月)。该类库提供一组通用的可重用的类库供开发人员使用。 MFC 应用程序的总体结构通常由开发人员从MFC类派生的几个类和一个CWinApp类对象(应用程序对象)组成。MFC 提供了MFC AppWizard 自动生成框架。
大数运算不仅仅运用在密码学中,还运用在一些物理学研究、生物学,化学等科目中。大数运算,意味着参加的值和计算结果通常是上百位数,上千位数以及更大长度之间的整数运算。例如大家所熟知圆周率π的值,在一般的数值计算中用到圆周率的不须要多大的精度,但在计算一些星球或是星系上的体积面积时便显的误差很大了,这就要求π值计算的精度达到几百万位甚至更高,才能缩小误差。人工计算是远远不行了,而且本身误差也无法估计,所以大数运算的就应运而生了。
2.课题要求
(一)MFC实现大整数运算计算器设计题目的任务以及程序所能达到的功能:
(1)图形化操作界面读入操作数A和B,并正确显示
(2)实现无限位的加法运算 ,加法运算正确,并正确显示结果
(3)实现无限位的加法运算 ,减法运算正确,并正确显示结果
(4)实现无限位的乘法运算,乘法运算正确,并正确显示结果
(5)实现无限位的除法运算,整除运算正确,并正确显示结果
(二)输入的形式:键盘上的数字键输入或者界面数字键输入
输入、输出值的范围为:(0,∞),输出的形式:编辑框输出运算结果。
测试的数据:包括正确的输入和错误的输入及其相应的输出结果
数A:5432198765432109876543210
数B:6543219876543219876543210
加法:11975418641975329753086420
减法:-1111021111111110000000000
乘法:35544070935308921415529646349766514250878997104100
整除:121932631112635269÷987654321=123456789
3.设计目标
A软件名称:MFC实现大整数运算计算器
B软件组成:
C制作平台及相关调试工具:Visual Studio 2010,MFC——微软件基础类库(Microsoft Foundation Classes)
D运行环境:以上测试环境:AMD Athlon(速龙) 64 X2 双核 4800+
内存:DDR2 3G,硬盘:160GB(5400rpm),系统:windows xp
E性能特点:
1 本软件实现一个大数(要求允许绝对值>10128)的计算器图形化程序软件。要求程序读入大数A和B,选择相应的加、减、乘和除法运算符,然后计算精确结果(不能用科学计数法,不四舍五入)并输出到屏幕上,实现了加法,减法,乘法,除法,理论上可以实现无限长度的运算。
2 界面简单明了,操作简便。
二 概要设计
1. 函数调用示意图
如图是整个计算器的流程图2如下:
图2
三 详细设计
3.1 设计步骤
打开Microsoft Visual Studio2010,在文件中点击新建,在弹出框内选择MFC 应用程序工程,输入工程名BigNum及其所在位置,点击确定,如图3-1所示。
图3-1
这样在MFC应用程序下建立了一个基于对话窗口的程序框架,如图3-2所示。
图3-2
图3-2
3.2 界面设计
1、创建控件
在图3-2所示的在资源视图选项卡中打开BigNum资源组,双击IDD_BIGNUM_DIALOG,在右边的窗口中显示出待编辑的对话框。开始摆放控件,包括编辑框和按钮的创建。
表3-1 各按钮和编辑框等对象的属性
完成后界面如图3-3所示。
图3-3
2、连接变量和代码
1) 给编辑框连接变量。在编辑框上单击鼠标右键,在弹出菜单中选择ClassWizard菜单项,弹出MFC ClassWizard对话框,选择成员变量选项卡,如图3-4
图3-4
3、整体程序思路:
调用voidCBIGNUMDlg::GetNum(string&m)函数把编辑框的操作数1获取,当按下“+或-或*或/”按键时,使用标志变量flag记录运算符号,并把之前获得的操作数1传给m1数组,当按下“=”键时,获得操作数2,把操作数2传给m2数组,根据之前的flag变量来调用加、减、乘、除运算函数,算出结果,并把结果输出到编辑框上。在编辑框上已经限制了输入的只有数字才可以显示,所以获得的操作数1和操作数2都是整数。
4、加法运算的算法:BIGNUMDlg.Add();
算法图:
678894676
+ 987679
----------------- 采用a=m1.length();b=m2.length();获取了操作数1和操作数2的长度,max=a>b?a:b;获取了结果m3的长度,用for(),m3[max]=m1[a]+m2[b],以此类推,从低位开始把操作数m1数组,m2数组按位相加,结果保存在m3数组中,当m2或m1的高位为空(即0),直接把不为零的操作数赋值给m3.用for(i=max;;)循环检验,当(m1[i]+m2[i])>9时,m3[i]=m3[i-1]+=m3[i]/10;m3[i]=m3[i]%10;结果把m3中的第一个不为0及其后的数显示在编辑框中。
5、减法运算的算法:BIGNUMDlg.Sub();
减法算法的算法图:
789788688
— 87659
------------------------ 采用a=m1.length();b=m2.length();获取了操作数1和操作数2的长度,max=a>b?a:b;获取了结果m3的长度,
(1)、当m1的长度<m2的长度时,从低位开始用m2[i]减m1[i],当m2[i]小于m1[i],m3[k]=m2[j]+10-m1[i];m2[j-1]-=1;返回"-";
(2)、当m1的长度>m2的长度时,从低位开始用m1[i]减去m2[i],当m1[i]小于m2[i],m3[k]=m2[j]+10-m1[i];m2[j-1]-=1;
(3)、当m1的长度=m2的长度时,从低位开始用m1[i]减去m2[i],当m1=m2时,输出为0,若m2>m1时用m2[i]减m1[i],当m2[i]小于m1[i],m3[k]=m2[j]+10-m1[i];m2[j-1]-=1;若m2<m1时返回"-";用m1[i]减去m2[i],当m1[i]小于m2[i],m3[k]=m2[j]+10-m1[i];m2[j-1]-=1;结果把m3中的第一个不为0即其后的数显示在编辑框中。
6、乘法的算法图:BIGNUMDlg.Mul();
乘法过程
采用a=m1.length();b=m2.length();获取了操作数1和操作数2的长度,max=a>b?a:b;获取了结果m3的长度,乘法的第一步是用for()循环嵌套两个{for();for()}循环第一个for()循环从操作2的低位的一位*操作数1的所有位,把与操作数1对应的位的结果保存在临时数组m4[16,24,20]中,接着第二个for()循环把位数符合(max==k+n)(其中max是操作数1和操作数2的长度最大的数值的2倍,k是一个与max相等的值,但在for()循环中,会把k循环减,而n是操作数2对应的位数,),把临时数组m4+m3的值赋给m3,然后在for()循环下计算下一行的值,按前面的算法,作循环计算,直到把操作数2中的数都乘过,最后,把m3中的第一个不为0的数显示在编辑框中。
7、除法的算法:BIGNUMDlg.Div();
这个除法的算法我自己做了3天了,最后还是把它实现了。采用a=m1.length();b=m2.length();获取了操作数1和操作数2的长度,max=a>b?a:b;获取了结果m3的长度,当a大于等于b时进入循环,首先判断m1和m2的大小,(1)当m1<m2时,设置了标志变量flag1=false,退出while循环,输出结果为0,否则标志变量flag为false;(2)当a=b时,如果m1<m2,设置了标志变量flag=true,直接退出循环,结果输出为0,否则标志变量flag为false;(3)当a>b时,在b后面补(b-a)个0,设置标志变量flag为false。
接下来,用for()循环time1表示结果商的位数,当false为false时,进入循环减,循环减的算法如下例——举例:999999/1111,先将1111*100,然后用999999-111100做循环减,直到所得结果小于111100,用counts计数做减法的次数,然后商的循环的次数为6(999999的长度)-4(1111的长度)+1。结果输出,当(a>=b&&flag==0)时,输出商的结果,即把m3中的第一个不为0的数显示在编辑框中,否则输出为0。
四 调试分析
1 加法调试分析:
通过测试时发现,加法返回的结果与正确的结果不相符,例如:返回的结果是: 45678但是实际的结果为87654 。通过调试发现原来是自己没有将m3进行反转 。具体的代码实现如下:
for(k=0;k<max+1;k++)
m3[k]+=48;
i=0;
while(m3[i]=='0')i++;
mText="";
for(;i<max+1;i++)
mText+=m3[i];
当使用界面进行加法操作时发现无法清空上一次加法的结果,如图:
第一次的操作9+4得到的结果:
第二次时按C 键清空数据,操作4+1 得到的结果是:
通过分析,得知是我在加法中使用了全局静态变量,导致用界面化操作时无法清空m3的值,解决方法就是在返回结果之前将m3 给清空就行了。
减法的调试分析:
通过调用减法的算法,两个相等的数相减(数组操作越界),原来我只是判断了m1>m2和m1<m2是进行减法运算,而没有把2个操作数相等的情况考虑,后来通过再设置一个标志变量flag1来,使得2个操作数相等时算得的结果为0,
具体的代码实现如下:
elseif(flag1==false&&flag==false)
{
for(k=0;k<max+1;k++)
m3[k]+=48;
i=0;
while(m3[i]=='0')i++;
mText="";
mText="-";
for(;i<max+1;i++)
mText+=m3[i];
}
if(flag==true)
mText="0";
乘法的调试分析:
在做乘法时遇到最多的问题便是超出了字符的数值范围,如9*9+9*9就超过了字符的数值范围,此时,判断当(9*9+9*9)>9时,便把高位的值保存在结果的高一位,低位留在本位,不过通过排查,最后把问题给解决了。
for(n=x,k=counts;n<max;n++,k--)
if(max==k+n)
{
m3[k]+=m4[n];
if(m3[k]>10)
{
m3[k-1]+=m3[k]/10;
m3[k]=m3[k]%10;
}
}
五用户使用说明
输入的形式:键盘上的数字键输入或者界面数字键输入
输入、输出值的范围为:(0,∞),输出的形式:编辑框输出运算结果。
六测试结果
1 加法测试结果:
数A:5432198765432109876543210
数B:6543219876543219876543210
结果:11975418641975329753086420
减法:-1111021111111110000000000
乘法:35544070935308921415529646349766514250878997104100
整除:121932631112635269÷987654321=123456789
数A:98765432198765432198765432198765432198765432198765432187654321987654321
数B:987655433213344566677889099988776654433221233443344555666678988776654433
结果:1086420865412109998876654532187542086631986665642109987854333310764308754
2 减法测试结果
数A:5432198765432109876543210
数B:6543219876543219876543210
结果:-1111021111111110000000000
数A:98765432198765432198765432198765432198765432198765432187654321987654321
数B:987655433213344566677889099988776654433221233443344555666678988776654433
结果:-888889990014578023478013556789900111234455790244578012478913556789000112
3 乘法测试结果:
数A:5432198765432109876543210
数B:6543219876543219876543210
结果:35544070935308921415529646349766514250878997104100
4 除法测试结果:
数A:121932631112635269
数B:987654321
结果:123456789
七设计心得体会
A其实这个计算器是我第一次用到了MFC,因为要写界面,且与.NET比较,觉得MFC使用的平台比较多,然后我自己的学习方向是嵌入式方向,自己想学C++,所以自学了MFC,所花费的时间是两周。
B首先是自己在资源视图那里画了一个基本的界面,如图7,接着就要考虑到了如何获取操作数1和操作数2,且限制了编辑框只能输入数字,就写了一个获取操作数的函数,从编辑框获取,想到了当按下按键(+/-/*//)时可以接受第一个操作数,当按下按键(=)时,获取操作数2,接着到加法运算,接着就是写了加法函数,直到调试成功了,再到减法函数,再到乘法函数,最后是除法函数。就是这样一步一步做出来的。
图7
图7
B在做这个计算器的过程中,虽然没有遇到大的问题,了解到基础是很重要的,坚持是很重要的,就像我作除法就做了3天。
C 有时,虽然你的设计思路是对的,但是如何通过代码来实现你的想法却是另外一回事,面对几十个错误时,要有勇气去面对那些错误,而且,我发现,当你发现了错误,离成功就又前进了一步,所以不要害怕错误。当你排除了所有的语法错误以后,还是会有很多逻辑错误,此时就到了调试程序,调试程序会很需要耐心,一步一步地了解程序的运行,不过这个过程是很有趣的。总的说来,平时要多打代码,加深对代码的了解,以及对编程思想的认识,提高自己的逻辑思维,锻炼自己的动手能力,多实践会更好。
D 在实现某个结果时,提供的算法思维是不只一个,所以要注意挑选合适的算法来实现。
E 在做个计算器的过程中,了解其他方面的知识,扩充了自己的知识面。
F 感谢老师的指导,以及同学之间的交流。
第二篇:数字电子钟(计时、校时以及整点报时)数电课程设计报告
公安技术学院
课程设计报告
课程 数字电子技术
题目数字电子钟(计时、校时以及整点报时)数电课程设计报告
年级
专业
学号
学生
任课教师
2014 年 12 月 29 日
目录
一、引言 …………………………… 1
二、方案论证选择....................................2
2.1 设计要求 …………………………………………2
2.2 系统框图 …………………………………………2
2.3 设计过程 …………………………………………2
三、电路仿真与设计…………………….3
3.1所需芯片及芯片管脚图 ………………………….3
3.2时、分、秒显示电路模块设计 ………………….4
3.3校时电路模块设计 ……………………………….7
3.4报时电路模块设计 ……………………………….7
3.5综合电路 ………………………………………….9
四、电路调试及实物照片……………….9
4.1电路调试 …………………………………………..9
4.2实物照片 ………………………………………….10
五、存在的问题………………………….11
六、课程设计心得体会………………….11
附录:
元件清单
参考资料
一、 引言
目前市场上提供的无论是机械钟还是石英钟在晚上无照明的情况下都是不可见的。要知道当前的时间,必须先开灯,故较为不便。现在市场上出现了这样一类的电子钟,它以六只LED数码管来显示时分秒,与传统的以指针显示秒的方式不同,违背了人们传统的习惯与理念,而且这类电子钟一般是采用大型显示器件,适用于银行、车站等公共场所。这种新型的电子钟因其方便、直观的特点也得到了社会的欢迎,在社会上占有相当一部分市场。
数字电子钟是日常生活中常见的一种工具,大到机场等公共场所的时间屏幕,小到我们的手表、闹钟等,而且其报时功能也给人们提供了方便,因此,了解报时电子钟的工作原理是很有必要的,也很有趣,因此我选择了这个题目——整点报时数字钟。
数字电子技术课程的核心内容是时序逻辑电路、组合逻辑电路和触发器,这些也是我们学电子的学生最基本要掌握的知识,通过实践可以加深对课本知识的理解,能够处理一些实际中的情况,因此这次数电课程设计,我选择了数字电子钟这个题目,虽然这在日常生活中很常见,看起来也比较简单,但是其中包含的学问很多。在这个项目中,校时是一个很重要的模块,既要可以正常校时,又不能干扰到时间计数显示模块,而时间显示比较简单,用熟悉的芯片就可以做出来了,老师说过,对芯片等元器件的了解程度等于将军手中可以调动的兵力,掌握了芯片功能,也就掌握了主动权。
这次课程设计的选题——整点报时数字钟,不仅可以加深我对数字电子技术课程的理解,也可以提高自己的动手能力以及实际问题中解决问题的能力,培养对数字电子技术的兴趣。
二、方案论证选择
2.1设计要求
1. 用秒脉冲作信号源,构成数字钟,显示秒、分、时
2. 具有“对时”功能,即时间可以快速预置
3. 具有整点提示功能。一种实现的方法是每到整点时触发“音乐芯片”或每到整点前几秒钟,发出如 “的、的、的、答”声音信号。
2.2系统框图
2.3设计过程
时间显示模块电路可以用3个CD4518作为核心芯片,进行级联,再辅以若干逻辑门,完成进位、置零等功能,CD4518是双十进制计数器,有两个时钟输入端,正好可以满足进位和校时的功能,而不会产生干扰,且有一个置零功能,可以组成六十进制和二十四进制的计数器。
整点报时模块电路用的是555芯片和一块CD4068芯片组成的电路,555芯片可以接成多谐振荡器,提供交变信号使蜂鸣器发出声音,而整点报时的控制可以用CD4068实现,CD4068是8输入与/与非门,
可以在整点之前输出脉冲信号,经过由555芯片组成的多谐振荡器,为其提供一个信号,这样由多谐振荡器输出端可以使蜂鸣器发出“嘀、嘀、嘀”的响声。
秒信号发生器可以用实验箱上的秒脉冲信号代替。
考虑到开关抖动现象,校时模块电路实验实验箱上的按键开关,每输出一个脉冲信号可以改变分个位和十个位,同时考虑到干扰问题,进位接线和校时接线接在不同的时钟输入端。
三、电路仿真与设计
3.1所需芯片及芯片管脚图
CD4518 CD4068
CD4002 CD4011
CD4069 555
3.2时、分、秒显示电路模块设计
整个电路的的核心芯片是CD4518,它是一个双10进制加法计数器,因此只需要三个芯片,进行级联即可实现两个六十进制和一个二十四进制计数器,再加上一些合适的逻辑门,实现置零和进位。
上图是秒显示电路设计图,右边为秒个位,左边为秒十位,秒个位的电路中置零引脚和时钟输入端CP1必须接地,这是因为CMOS的引脚不能悬空,否则会影响实验结果,CP0接秒脉冲信号,考虑到秒个位计数到9的时候必须进位,所以在显示0的同时输出一个进位信号,输出是0000,因此可以用一个或非门,当输出是0000的时候提供一个进位信号至秒十位的时钟输入端,秒十位另一个时钟输入端接地,当秒十位计数器计到5时,在输出为0110时提供一个信号到秒十位计数器的置零端,使其实现0110——0000,即六十进制。
上图是分计数显示电路设计图,原理与秒计数显示电路接近,分个位的时钟输入端接来自秒十位的进位信号,另外一个时钟输入端接校时电路模块,由于设计的过程是分块设计的,因此先将该时钟输入端接地,分十位的原理也是一样的。
下图是时计数显示电路设计图,与分、秒不同的是,这一块是24进制,当时十位为0、1的时候,时个位正常从0—9显示;当时十位为2时,要求时个位的显示是0、1、2、3,然后就回到0,因此在置零这一部分接法不同于分、秒计数显示电路,考虑到当时计数器为23时必须变为00,即当时十位输出为0010、时个位输出为0100时,分别变为0000、0000,因此可用一个与门实现,按如图的接法,并且注意到时十位和时个位都必须置零。
3.3校时电路模块设计
校时模块的设计思路如图所示,使用实验箱上的消除抖动的按键开关,每按一次就输出一个信号,调节分个位和时个位,考虑到实际接线时可能会出现干扰现象,即校时模块中的校时信号会影响到原来的秒十位进位信号和分十位进位信号,所以进位信号输入和校时信号输入接在不同的时钟端,这刚好利用了CD4518有两个时钟输入端的优点。
3.4报时电路模块设计
报时电路模块中的报时信号输出电路如下图所示,用的芯片是CD4068,CD4068是一个8输入与/与非门,与非门的引脚分别接CD4518芯片的对应的输出引脚,使其在59分51秒开始输出报时信号,根据要求将报时信号设计为第51、53、55、57、59秒时输出报时信号,向由555组成的多谐振荡器发送信号。
下图是由555芯片和电阻、电容构成的多谐振荡器,电阻的一端输入经过CD4068的报时信号,使电路导通,从而在555输出端产生一个交变信号使得蜂鸣器发出声音。
3.5综合电路
四、电路调试
4.1电路调试
课程设计的起点在于仿真,使用仿真软件做出来后,还要进行接线,这时候整个电路的成功与否,就在最后的调试了。在这次电路的调试过程中,遇到两个问题:
1、电路模块之间或者接线之间的干扰,导致在计数时数码管上显示的数并不如预期的变化,比如有时候是每计数到11秒时,本该到12秒的时候,秒十位也跟着变化了,变成22秒,在调整小部分接线之后,这个问题也解决了,计数器正常工作。
2、第二个问题也是最关键的问题,关系到校时电路这一部分能否做出来的问题,也就是上面提到的进位信号与校时信号相互干扰问题,由于CD4518的仿真库出错,所以在仿真软件上试验的话可能不准确,而且实际与理论是有差距的,因此只能在实验箱上进行调试。最后才想到CD4518有两个时钟输入端,之前的仿真不行是因为CD4518的仿真出错,于是将进位信号和校时信号接在不同的时钟输入端上,解决了互相干扰的问题,同时也可以正常校时。
经过调试,整个电路的功能实现了。
五、存在问题
完成了课程设计的基本要求,但是这个电子钟是没有实用价值的,现实中不可能用这么多芯片去生产一个普通的电子钟,但这是学习的一种途径。我设计的这个电路在校时时没有一个开关可以使秒的计数暂时停止,而且有的时候秒也是需要调节的,由于材料不足等原因,报时的“音乐”也显得有些单调。
六、心得体会
经过这段时间的课程设计,我学到了许多东西,对课本上的内容的理解加深了印象,同时也学会了一种学习的态度。
理论要联系实践,当然实践也离不开理论,由于对课本的内容还不是很熟悉,所以在做这个课程设计前,我先把课本的重点知识复习了一遍,时序逻辑电路、组合逻辑电路等,然后就是到图书馆查找相应的资料,抱着好几本书就在那里认真地查,查的过程中也看到了很多关于CMOS芯片的应用实例。
理论上的知识搞定了,接下来就是开始设计了。EWB仿真,给我的印象是简洁实用,很多电路都能在上面先进行仿真,不过我这个题目的核心芯片在仿真上面,出现了一些问题,一些管脚的位置和实际的不一样,仿真调试不成功,于是我就想到,按照理论来讲这是没有错的,为了验证清除,我先将电路进行分模块调试,把每一部分都仔细检查了一遍,最终发现了与仿真的不同,接线是一样的,不过在真实的接线中可行,在仿真中却不行,最大一个不同之处就在于校时模块,虽然仿真是那种接法可行,不过在实际接线中我采用了另外一种接法。
这次课程设计也再次让我看到理论与实践的差别和联系,理论固然重要,然而我们要在实践中发现错误,并解决错误,也提高了自己的动手能力和实际解决问题的能力。
一种学习态度:认真、严谨的学习态度。这就是我的另一个收获,不仅仅是做课程设计,无论是做什么研究,都必须要有一种认真严谨的学习态度,比如说,独立思考独立完成,认真接线,仔细检查等,这些都是对我们自身能力的一种培养,在以后的学习甚至工作中,很多东西都只能靠自己去独立思考完成,因此我们也藉此学会了一种独立思考的学习态度。
无论最后的结果是怎样,你参与了,你就肯定有收获。在这几天可以说是废寝忘食的课程设计过程中,我也收获了许多,我仍然记得将课程设计做出来的时候,那种喜悦的心情,是难以形容的。
附录:
元件清单
参考文献:
《数字电子技术》 梁龙学 主编