简易计算器设计msp430C语言.docx
《简易计算器设计msp430C语言.docx》由会员分享,可在线阅读,更多相关《简易计算器设计msp430C语言.docx(31页珍藏版)》请在冰豆网上搜索。
简易计算器设计msp430C语言
简易计算器
摘要…………………………………………………………………………………P3
关键字………………………………………………………………………………P3
一、设计要求………………………………………………………………………P3
二、方案论证与选择………………………………………………………………P3
2.1单片机选择………………………………………………………………P3
2.2LCD显示屏选择…………………………………………………………P3
2.3键盘选择…………………………………………………………………P4
2.4CPU工作方式选择………………………………………………………P4
三、系统实现………………………………………………………………………P4
3.1硬件设计…………………………………………………………………P43.1.1系统框图……………………………………………………………P4
3.1.2盘的电平设计以及与单片机的连接键……………………………P5
3.2.3单片机与显示器的连接……………………………………………P5
3.2软件设计……………………………………………………………………P6
四、作品性能测试与分析…………………………………………………………P10
4.1试性能概览………………………………………………………………P10
4.2误差分析…………………………………………………………………P12
五、参考文献………………………………………………………………………P12
六、附录……………………………………………………………………………P13
6.1计算器功能介绍…………………………………………………………P13
6.2仿真电路图………………………………………………………………P13
6.3元件清单…………………………………………………………………P13
6.4原程序代码………………………………………………………………P14
摘要:
本设计以低功耗单片机MSP430V136T、1602字符型液晶屏和4*4简易键盘为主要器件,来实现加、减、乘、除、开根号、平方、求倒数等运算。
设计中分别采用P1口低4位和P2口低4位与键盘的行列线相连,用于采集中断信号并分析键值;键盘规格为4*4,由于所需的功能键数大于16,因此需要进行按键复用;单片机的P3口连接显示器的D0~D7端,用于输出显示数据或控制命令;选用P4口中的3、4和5口用于实现显示屏的控制功能:
使能、控制/数据选择端、读/写。
本设计的软件部分采用中断方式获取键值,空闲时单片机处于休眠状态,极大地减小了单片机的功耗。
关键字:
计算器、MSP430F2274单片机、LCD1602显示屏、键盘
一、设计要求
基本功能要求:
输入两个2位十进制数,完成+、-、*、/、开方运算;
扩展功能要求:
可在完成基本功能的基础上参照实际计算器完成,功能不限。
二、方案论证与选择
2.1单片机选择
本设计采用的是MSP430F2274单片机。
MSP430是德州公司新开发的一类具有16位总线的带FLASH的单片机,由于其性价比和集成度高,受到广大技术开发人员的青睐.它采用16位的总线,外设和内存统一编址,寻址范围可达64K,还可以外扩展存储器.具有统一的中断管理,具有丰富的片上外围模块,片内有精密硬件乘法器、两个16位定时器、一个14路的12位的模数转换器、一个看门狗、4路P口、16个外部中断、两路USART通信端口、一个比较器、一个DCO内部振荡器和两个外部时钟,支持8M的时钟.由于为FLASH型,则可以在线对单片机进行调试和下载,且JTAG口直接和FET(FLASHEMULATIONTOOL)的相连,不须另外的仿真工具,方便实用,而且,可以在超低功耗模式下工作,对环境和人体的辐射小,测量结果为100mw左右的功耗(电流为14mA左右),可靠性能好,加强电干扰运行不受影响。
在软件方面,MSP430单片机适合C语言开发,具有如下优点:
1、可以大大提高软件开发的工作效率;2、可以提高所设计的程序代码的可靠性、可读性和可移植性;3、设计者可以将注意力更多地集中在充分发挥MSP430的功能上。
2.2LCD显示屏选择
本设计采用的是1602液晶显示屏。
在单片机系统中应用晶液显示器作为输出器件有以下几个优点:
1显示质量高
由于液晶显示器每一个点在收到信号后就一直保持那种色彩和亮度,恒定发光,而不像阴极射线管显示器(CRT)那样需要不断刷新新亮点。
因此,液晶显示器画质高且不会闪烁。
2数字式接口
液晶显示器都是数字式的,和单片机系统的接口更加简单可靠,操作更加方便。
3体积小、重量轻
液晶显示器通过显示屏上的电极控制液晶分子状态来达到显示的目的,在重量上比相同显示面积的传统显示器要轻得多。
4功耗低
相对而言,液晶显示器的功耗主要消耗在其内部的电极和驱动IC上,因而耗电量比其它显示器要少得多。
2.3键盘选择
本设计只需实现+、-、*、/、开根号等基本功能,所以使用4*4的简易键盘即可,对于附加功能,通过简单点的按键复用,即可实现。
2.4CPU工作方式选择
查询方式:
在这种工作方式下,当CPU执行完初始化程序后,便通过不断地查询端口值来判断是否有键按下,若有则跳转去执行相关的键值分析或数值计算及显示程序。
执行完毕,继续回到主程序继续不断查询。
中断方式:
在此工作方式下,CPU无需查询,在执行完中断程序后,通过一条指令使得单片机进入休眠状态。
当有键按下时,则跳转去执行中断程序,执行完毕,继续回到主程序休眠。
本设计选择中断方式。
由于本设计为单任务,中断方式的高效性未能体现,但是通过中断方式,我们可以让单片机在空闲的时候进入休眠状态,这样可以极大地减少其消耗。
三、系统实现
3.1硬件设计
3.1.1系统框图
系统的逻辑框图如下图所示。
3.2.2键盘的电平设计以及与单片机的连接
键盘的每个按键都有它的行值和列值,行值和列值的组合就是识别这个按键的编码。
矩阵的行线和列线分别通过两并行接口和CPU通信。
键盘的一端(列线)通过电阻接VCC,而接地是通过程序输出数字“0”实现的。
键盘处理程序的任务是:
确定有无键按下,判断哪一个键按下,键的功能是什么?
还要消除按键在闭合或断开时的抖动。
两个并行口中,一个输出扫描码,使按键逐行动态接地;另一个并行口输入按键状态,由行扫描值和回馈信号共同形成键编码而识别按键,通过软件,查出该键的功能。
键盘的8个口分别与430单片机的P1和P2口的第四位相连,且P1口输入,P2口输出,二者结合,采用行扫描法确定按键。
其与单片机的具体连接方式如下图所示。
3.2.3单片机与显示器的连接
1602液晶显示屏有16个接口,各口的符号及功能如下表。
在本设计中1号、3号和16号引脚并联后接地,2号和15号脚并联接+5V电平,3号、4号和5号三个控制端口分别与单片机的P4.4、P4.5和P4.3相接,分别实现上表中对应的功能,7号~14号口和单片机的P3.0~P3.7口,以实现数据和命令的传输。
具体连接入下图所示。
3.2软件设计
本设计的软件部分由多个模块组成:
主程序、中断程序、键盘分析程序、单片机初始化程序、LCD初始化程序、延时程序、测试LCD是否忙碌、写入指令数据到LCD、设定显示位置、写入字符显示数据到LCD等。
下面将对这些模块的功能一一讲解,并将画出主要模块的流程图。
主程序:
主程序首先调用一些子程序来完成单片机初始化、显示器初始化、显示器清屏、显示器显示方式的设置、确定起始显示位置等,接着显示‘Welcome’,延时一段时间后清屏,然后打开中断,最后进入休眠状态,等待按键中断。
整个过程的流程图如下图所示。
中断程序:
中断程序的架构如流程图所示,其中运算和显示功能由于篇幅限制,没有在图上给出。
由于本设计中共有7种运算,我们在检测到运算符时用FLAG对其进行标志,在接下来的运算中,即根据标志进行相关计算;显示分为整数显示和实数显示,其中加、减、乘、平方中只涉及整数,而除、开方和求倒数结果为实数,我们的程序将这两类结果分开显示,其中整数显示较简单,这里不再叙述;对于实数的显示我们又分为两类:
开方、除法(包含求倒数)。
开方运算时,我们先用逼近法算出其值,然后进行显示:
将结果乘以10000,设其为d,除以10000得整数部分显示,在显示小数点后,再用d对10000求余,将这个结果在小数点后显示;除法运算时则是先将操作数乘以10000,再进行除法运算,最后再运用上面的显示方法。
键盘分析:
键盘分析程序是利用行扫描法来判断按键。
首先由读P1.0~P1.3的值来判断被按下的列,接着使键盘上某一行线为低电平,而其余行接高电平,然后读取列值,如果列值中有某位为低电平,则表明行列交点处的键被按下;否则扫描下一行,直到扫描完全部的行线为止,最后在确定所按键后返回键值。
整个过程的流程图如下。
单片机初始化程序(voidinot()):
该子程序实现了关开门狗、设定DCO频率、设定各端口输入输出方向、清除中断标志、允许P1.0、P1.1、P1.2、P1.3中断并设置为上升沿中断。
LCD初始化程序(voidlcd_init()):
这段子程序主要实现三个功能。
首先是设置显示模式,即设置为16*2显示,5*7点阵,8位数据接口;接着进行显示开/关及光标设置,本程序中设置为开显示,不显示光标,当读或写一个字符后地址指针加一,且光标加一,写一个字符后整屏显示不移动;最后清除LCD显示的内容。
测试LCD是否忙碌(intlcd_bz()):
该子程序的功能是测试LCD数据端时候忙碌。
首先使得P3口切换成输入模式,然后对P3口的数据进行运算判断P3.7是否为高来确定是否忙碌,最后返回判定值。
写入指令数据到LCD(voidlcd_wcmd(ucharcmd)):
该程序在被调用时首先检测LCD是否忙碌,若不忙则设定LCD的控制方式,即将其设定为写命令的方式,然后将要写的命令输出,再使能端E口输出一个正电平脉冲,即可完成输出。
设定显示位置(voidlcd_pos(ucharpos)):
该程序中调用lcd_wcmd子程序写入数据的起始显示位置。
写入字符显示数据到LCD(voidlcd_wdat(uchardat)):
和上面写指令数据到LCD类似,这里只是将控制方式改成写数据的方式。
四、作品性能测试与分析
4.1测试性能概览
我们分别测试了系统具备的7中计算功能:
加、减、乘、除、开方、平方、求倒数。
测试结果如下图所示。
加法
减法
乘法
除法
开方
平方
求倒数
4.2误差分析
根据测试结果,开根号的运算产生了误差,在本设计中
。
经分析发现,这源于程序的算法问题,程序中采用逼近法进行开方计算,并设定比较精度为0.0001,当计算结果逼近至1.7319时,即满足要求。
若在程序中减小比较精度,则计算结果将更为精确。
五、参考文献
1《MPS430系列单片机C语言程序设计与开发》胡大可编著北京航空航天大学出版社
2《C程序设计(第三版)》谭浩强著清华大学出版社
六、附录
6.1计算器功能介绍
/
Clear
*
-
+
3
6
9
=
2
5
8
0
1
4
7
shift
键盘上各个按键所代表的含义如上图所示,我们将这16个键以行优先的方法从左向右编号为1~16,则5-7、9-12、13-15键为数字键;16键位功能转换键,摁下后运算执行1-4键表格中斜线下方的功能,否则执行斜线上方的功能。
6.2仿真电路图
6.3元件清单
元件清单
器件名称
型号
数量(个)
单片机
MSP430F2274
1
液晶显示器
1602LCD
1
4×4键盘
1
电阻
10K
4
直流电源
5V
1
下载线
1
排线
若干
6.4原程序代码
#include
#include
#defineucharunsignedchar
#defineep_1(P4OUT|=BIT3)
#defineep_0(P4OUT&=~BIT3)
#definers_1(P4OUT|=BIT4)
#definers_0(P4OUT&=~BIT4)
#definerw_1(P4OUT|=BIT5)
#definerw_0(P4OUT&=~BIT5)
doubled4=0,x,y,z;
longinti,k;
intkey,keyz,keyzz,key,f1,key1;
longinta,d1=0,d2=0,d3=0,d5=0,d6,d=0,f=0,i,n,flag=0,flag1=0,flag2=0,flag3=0,flag4=0,flag5=0,flag6=0,flag7=0,flag8=0,h0=11,h1=25,h2=40,h3=0,h4=0,t1=0,t2=0,g;
//...............................................................................
voidlcd_wcmd(uchar);//函数声明
voidlcd_pos(uchar);
voidlcd_wdat(uchar);
voiddelay(intms);
voiddelayl(ints);
intkey_pass();
intinot(void);
//*******************************************************
doublep2(doublex)//开方
{y=sqrt(x);
z=y;
y=y*y;
if(y>x)
{do
{z=z-0.0001;
y=z*z;
}
while(y>x);
returnz;}
elseif(y==x)
returnz;
else//if(y{do
{z=z+0.0001;
y=z*z;
}while(yreturnz;
}
}
//..............................测试LCD忙碌状态................
intinot(void)//初始化
{
WDTCTL=WDTPW+WDTHOLD;//关看门狗
BCSCTL1=CALBC1_16MHZ;//设定DCO为1MHZ
DCOCTL=CALBC1_16MHZ;
P1DIR&=~0x0f;//////寄存器,决定对应口的的输入输出状态
P1DIR|=0xf0;//将P1.4P1.5P1.6P1.7设置为输出方向
P1OUT=0x00;//先输出低电平
P2DIR&=~0x00;
P2DIR|=0xFf;//将P2.0P2.1P2,2P2.3设置为输出方向
P3DIR|=0xff;//P3输出
P3OUT=0x00;
P4DIR|=0xff;//P4输出
P4OUT=0x00;
P1IE=0;
P1IES=0;
P1IFG=0;
P1IE|=BIT0;
P1IES|=BIT0;
P1IE|=BIT1;
P1IES|=BIT1;
P1IE|=BIT2;
P1IES|=BIT2;
P1IE|=BIT3;
P1IES|=BIT3;
return0;
}
voiddelay(intms)//延时可能不要
{while(ms--)
{
for(i=0;i<=16000;i++);
}
}
/*voiddelayl(ints)
{
while(s--)
{
delay(100);
}
}*/
//..............................测试LCD忙碌状态.........................................................................................................
intlcd_bz()
{
P3DIR&=~BIT7;
intresult=0;
rs_0;
rw_1;
ep_1;
result=(result||(P3IN&0x80));
ep_0;
P3DIR|=0xff;
returnresult;
}
//.................................写入指令数据到LCD..............................................................................................................................
voidlcd_wcmd(ucharcmd)
{
while(lcd_bz());
rs_0;
rw_0;
ep_0;
P3OUT=cmd;
ep_1;
ep_0;
delay(5);
}
//..........................设定显示位置........................................................................
voidlcd_pos(ucharpos)
{
lcd_wcmd(pos|0x80);
delay(5);
}
//...........................写入字符显示数据到LCD.........................................................
voidlcd_wdat(uchardat)
{
while(lcd_bz());
rs_1;
rw_0;
ep_0;
P3OUT=dat;
ep_1;
ep_0;
}
//........................LCD初始化设定.............................................
voidlcd_init()
{
lcd_wcmd(0x38);//functionset
delay(10);
lcd_wcmd(0x38);//functionset
delay(10);
lcd_wcmd(0x0c);//displayon/off
delay(10);
lcd_wcmd(0x06);//entrymodeset
delay(10);
lcd_wcmd(0x01);//清除LCD的显示内容
delay(10);
}
//------------------------------------------------------------------
intkey_pass()//键盘分析
{
intkeyz;
intkey;
key=(P1IN&0x0f);
switch(key)
{
case0x0e:
//delay(5);
P2OUT|=BIT0;
if((P1IN&BIT0)==0x01)
delay(10);
if((P1IN&BIT0)==0x01)
{keyz=10;break;}
P2OUT|=BIT1;
if((P1IN&BIT0)==0x01)
delay(10);
if((P1IN&BIT0)==0x01)
{keyz=11;break;}
P2OUT|=BIT2;
if((P1IN&BIT0)==0x01)
delay(10);
if((P1IN&BIT0)==0x01)
{keyz=12;break;}
P2OUT|=BIT5;
if((P1IN&BIT0)==0x01)
delay(10);
if((P1IN&BIT0)==0x01)
{keyz=13;break;}
break;
case0x0d:
P2OUT|=BIT0;
if((P1IN&BIT1)==0x02)
delay(10);
if((P1IN&BIT1)==0x02)
{keyz=3;break;}
P2OUT|=BIT1;
if((P1IN&BIT1)==0x02)
delay(10);
if((P1IN&BIT1)==0x02)
{keyz=6;break;}
P2OUT|=BIT2;
if((P1IN&BIT1)==0x02)
delay(10);
if((P1IN&BIT1)==0x02)
{keyz=9;break;}
P2OUT|=BIT5;
if((P1IN&BIT1)==0x02)
delay(10);
if((P1IN&BIT1)==0x02)
{keyz=15;break;}
break;
case0x0b:
P2OUT|=BIT0;
if((P1IN&BIT2)==0x04)
delay(10);
if((P1IN&BIT2)==0x04)
{keyz=2;break;}
P2OUT|=BIT1;
if((P1IN&BIT2)==0x04)
delay(10);
if((P1IN&BIT2)==0x04)
{keyz=5;break;}
P2OUT|=BIT2;
if((P1IN&BIT2)==0x04)
delay(10);
if((P1IN&BIT2)==0x04)
{keyz=8;break;}
P2OUT|=BIT5;
if((P1IN&BIT2)==0x04)
delay(10);
if((P1IN&BIT2)==0x04)
{keyz=0;break;}
case0x07:
P2OUT|=BIT0;
if((P1IN&BIT3)==0x08)
delay(10);
if((P1IN&BIT3)==0x08)
{keyz=1;break;}
P2OUT|=BIT1;
if((P1IN&BIT3)==0x08)
delay(10);
if((P1IN&BIT3)==0x08)
{keyz=4;break;}
P2OUT|=BIT2;
if((P1IN&BIT3)==0x08)
delay(10);
if