单片机课程设计——基于C51简易计算器

单片机双字节十六进制减法实验设计

                     摘要

   本设计是基于51系列的单片机进行的双字节十六进制减法设计,可以完成计算器的键盘输入,进行加、减、3位无符号数字的简单运算,并在LED上相应的显示结果。

    设计过程在硬件与软件方面进行同步设计。硬件方面从功能考虑,首先选择内部存储资源丰富的AT89C51单片机,输入采用5个键盘。显示采用3位7段共阴极LED动态显示。软件方面从分析计算器功能、流程图设计,再到程序的编写进行系统设计。编程语言方面从程序总体设计以及高效性和功能性对C语言和汇编语言进行比较分析,针对计算器四则运算算法特别是乘法和除法运算的实现,最终选用KEIL公司的μVision3软件,采用汇编语言进行编程,并用proteus仿真。

                           引言

    十六进制减法计算器的原理与设计是单片机课程设计课题中的一个。在完成理论学习和必要的实验后,我们掌握了单片机的基本原理以及编程和各种基本功能的应用,但对单片机的硬件实际应用设计和单片机完整的用户程序设计还不清楚,实际动手能力不够,因此对该课程进行一次课程设计是有必要的。

单片机课程设计既要让学生巩固课本学到的理论,还要让学生学习单片机硬件电路设计和用户程序设计,使所学的知识更深一层的理解,十进制加法计算器原理与硬软件的课程设计主要是通过学生独立设计方案并自己动手用计算机电路设计软件,编写和调试,最后仿真用户程序,来加深对单片机的认识,充分发挥学生的个人创新能力,并提高学生对单片机的兴趣,同时学习查阅资料、参考资料的方法。

关键词:单片机、计算器、AT89C51芯片、汇编语言、数码管、加减  

  

目录

摘 要  ..........................................01

引 言  ..........................................01

一、  设计任务和要求.............................

1、1  设计要求

1、2  性能指标

1、3  设计方案的确定

二、  单片机简要原理.............................

2、1  AT89C51的介绍

2、2  单片机最小系统

2、3  七段共阴极数码管

三、  硬件设计...................................

3、1  键盘电路的设计

3、2  显示电路的设计

四、  软件设计...................................

    4、1  系统设计

4、2  显示电路的设计

五、  调试与仿真.................................

5、1  Keil C51单片机软件开发系统

5、2  proteus的操作

六、 心得体会....................................

参考文献.........................................

附录1  系统硬件电路图............................

附录2  程序清单..................................

一、  设计任务和要求

1.1 设计要求

本次课程设计,我选择的课题是单片机十进制加法计算器软硬件设计 ,设计任务为:

设计一键盘显示装置,键盘上除需定义16个十六进制数字键外还要相应的功能键,其它键不定义无响应。利用此系统可分别可输入十六进制被减数与减数,实现两数相减并将结果以十六进制形式显示出来。(扩展:多位16进制数相减)

1.2 性能指标

      本课程设计的十六进制减法,计算结果全为整数,计算结果溢出结果不显示。

      1 、减法:三位减法,计算结果若小于零溢出不显

 3、有显示差的功能

1.3设计方案的确定

按照1.1的设计要求,本课题需要使用数码管显示和扩展1*5键盘,由于AT89C51芯片的I口不够多,而且为了硬件电路设计的简单化,故选择串行动态显示和用P1口扩展1*5键盘,扩展的1*5键盘定义十个数字键,5个功能键,使用串行动态显示显示运算结果。

主程序进行初始化,采用行列扫描进行查表得出键值,每次按键后调用显示子程序。

二、 单片机简要原理

在该课程设计中,主要用到一个AT89C51芯片和串接的共阴数码管。作为该设计的主要部分,下面将对它们的原理及功能做详细介绍和说明。

2.1  AT89C51的介绍:

             图一  AT89C51外形结构和引脚分布图

芯片AT89C51的外形结构和引脚图如图一所示。AT89C51是一种带4K字节闪烁可编程可擦除只读存储器(FPEROM—Falsh Programmable and Erasable Read Only Memory)的低电压,高性能CMOS8位微处理器,俗称单片机。该器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。由于将多功能8位CPU和闪烁存储器组合在单个芯片中,ATMEL的AT89C51是一种高效微控制器,为很多嵌入式控制系统提供了一种灵活性高且价廉的方案。

2.2  单片机最小系统

    单片机最小系统就是支持主芯片正常工作的最小部分,包括主控芯片、复位电路和晶振电路。

   (一) 复位电路

 

                           图二  复位电路

     本设计采用上电与手动复位电路,电阻分别选取100和10K,电容选取10uF,系统一上电,芯片就复位,或者中途按按键也可以进行复位。

(二)  晶振电路

                      图三    晶振电路

晶振电路是单片机的心脏,它用于产生单片机工作所需要的时钟信号。单片机的晶振选取11.0592MHz,晶振旁电容选取30pF。

2.3   七段共阴极数码管

     图四  七段共阴数码管

图为七段共阴数码管的引脚图,从左到右数码管的段码分别为a,b,c,d,e,f,g和小数点dp,低电平时点亮,最右边为位选端。

三、  硬件设计

简易数字计算器系统硬件设计主要包括:显示电路以及其他辅助电路。下面分别进行设计。

                            

3.1 显示电路的设计

当系统需要显示少量数据时,采用LED数码管进行显示是一种经济实用的方法。数码管显示有静态显示和动态显示两种方法。为了减少端口的使用,故选择动态显示。 电路如下图六所示:

   

                         图六   三位数码管的显示电路

四、  软件设计

在十进制加法计算器的软件规划要求下,简易计算器的程序主要包括以下功能模块:(1)    主模块,为系统的初始化。(2)   显示与读键模块,分为判键程序段、运算操作子程序、显示子程序等部分;

4.1      系统设计

(一)  系统模块图

                        图六  系统模块图

   此系统包括输入、运算和显示模块,由单片机控制。其中通过输入键盘模块将数字0~9和运算符号“+”、“-”、“*”、“/”输入单片机进行运算;运算模块分别根据输入的运算符进行加减乘除运算;显示模块将运算后的数值通过动态扫描使之在数码管上输出。

(二)  系统总流程图

主程序主要是用来进行初始化的,调用其他子程序,清空各个标志位,清空缓存区,读取键码,判断功能,在LED上作出回应,主程序流程图如图六所示。

(1)数字送显示缓冲程序设计

简易计算器所显示的数值最大位三位。要显示数值,先判断数值大小和位数,如果是超过三位或大于255,将不显示数字。可重新输入数字,再次计算。

(2)运算程序的设计

首先初始化参数,送LED三位显示“0”,其它位不显示。然后扫描键盘看是否有键输入,若有,读取键码。判断键码是数字键、清零键还是功能键,是数值键则送LED显示并保存数值,是清零键则做清零处理,是功能键则又判断是“=”还是运算键,若是“=”则计算最后结果并送LED显示,若是运算键则保存相对运算程序的首地址。

    

                           图七   主程序流程图

4.2  显示与按键设计

(一) LED显示程序设计

    LED显示器由七段发光二极管组成,排列成8字形状,因此也称为七段LED显示器。为了显示数字或符号,要为LED显示器提供代码,即字形代码。七段发光二极管,再加上一个小数点位,共计8段,因此提供的字形代码的长度正好是一个字节。简易计算器用到的数字0~9的共阴极字形代码如下表:

                     表一    共阴极数码管段码对照表

(二)  读键子程序设计

为了实现键盘的数据输入功能和命令处理功能,每个键都有其处理子程序,为此每个键都对应一个码——键码。为了得到被按键的键码,现使用行扫描法识别按键。其程序框图如图八:

读键程序使用的是反转法读键,不管键盘矩阵的规模大小,均进行两次读键。第一次所有列线均输出低电平,从所有读入键盘信息(行信息);第二次所有行线均输出低电平,从所有行线读入键盘信息(列信息)。将两次读键信息进行组合就可以得到按键的特征编码,然后通过查表得到按键的顺序编码。将各特征编码按希望的顺序排成一张表,然后用当前读得的特征码来查表。当表中有该特征码时,它的位置就是对应的顺序编码;当表中没有该特征码时,说明这是一个没有定义的键码,与没有按键(0FFH)同等看待。

                    图八   计算键值子程序流程图

五、  调试与仿真

下面用KEIL uVision3与 proteus仿真软件介绍十进制加法计算器的仿真与调试。

5.1 Keil C51单片机软件开发系统

(一) 系统的整体结构

C51工具包的整体结构中,其中uVision是C51 for Windows的集成开发环境(IDE),可以完成编辑、编译、连接、调试、仿真等整个开发流程。开发人员可用IDE本身或其它编辑器编辑C或汇编源文件。然后分别由C51编译器编译生成目标文件(.OBJ)。目标文件可由LIB51创建生成库文件,也可以与库文件一起经L51连接定位生成绝对目标文件(.ABS)。ABS文件由OH51转换成标准的Hex文件,以供调试器dScope51或tScope51使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器如EPROM中。

(二) 采用KEIL 开发的89c51单片机应用程序步骤:

1. 在uVision 集成开发环境中创建新项目(Project),扩展文件名为.UV2,并为该项目选定合适的单片机CPU器件(本设计采用ATMEL 公司下的AT89C51)

    2. 用uVision 的文本编辑器编写源文件,可以是汇编文件(.ASM),也可以使C语言文件(扩展名.C),并将该文件添加到项目中去。一个项目文件可以包含多个文件,除了源程序文件外,还可以是库文件、头文件或文本说明文件。

 3. 通过uVision 3 的相关选择项,配置编译环境、连接定位器以及Debug调试器的功能。

 4. 对项目中的源文件进行编译连接,生成绝对目标代码和可选的HEX文件,如果出现编译连接错误则返回到第2步,修改源文件中的错误后重构整个项目。

 5. 对没有语法错误的程序进行仿真调试,调试成功后将HEX文件写入到单片机应用系统的ROM中。

5.2  proteus的操作

 (一)硬件电路图的接法操作

1.放置选择(删除)元器件

2.移动元器件

3.缩放视图

4.连接导线

5.仿真,调试

 (二) 单片机系统PROTEUS设计与仿真过程

Proteus强大的单片机系统设计与仿真功能,使它可成为单片机系统应用开发和改进手段之一。全部过程都是在计算机上通过Proteus来完成的。其过程一般也可分为三步:

1.在ISIS平台上进行单片机系统电路设计、选择元器件、接插件、连接电路和电气检测等,简称Proteus电路设计。

2.在Keil平台上进行单片机系统程序设计、编辑、汇编编译、代码级调试,最后生成目标代码文件(*.hex)。简称Proteus源程序设计和生成目标代码文件。

3.在ISIS平台上将目标代码文件加载到单片机系统中,并实现单片机系统的实时交互、协同仿真。它在相当程度上反映了实际单片机系统的运行情况。简称Proteus仿真。

(三) Proteus中课程设计的仿真结果

在Proteus中将硬件电路全部接好以后,将Keil中生成的.hex文件导入到单片机中,点击开始仿真按钮,电路开始仿真。可以观察到:数码管显示“000”;分别依次按下按键“5”、“+”、“6”和“=”,可以看到数码管显示“11”;也可以分别调试其它功能键,结果发现调试结果与预期的理论值相吻合,即本次课设已成功。

总的仿真原理电路图见附录1图九。

六、心得体会

    为期两周的单片机课程设计终于结束了,通过紧张的工作,完成了我们的设计任务-十进制加法计算器。总的来说,这次课程设计是比较成功的。当然,这其中也经历了许多坎坷,但是在我的坚持不懈下,在老师的细心指导下,在同学们的热情帮助下,最终克服了种种困难,取得了成功。

    刚开始接到这个计算器的课程设计任务时,因为以前做过类似的题目,于是在脑海中初步构建了编写程序的一些控制程序。但是由于缺乏编写大量程序的经验,不能如行云流水般的将全部的各部分代码写出,于是去网上查找相关资料,了解计算器的输入控制原理、运算处理以及显示的原理。了解之后自己尝试编写程序,在此过程中,其中键盘扫描和动态扫描显示扫描程序困扰了我很久,经过三四天的辛苦工作,终于初步把所需要的程序编好了,于是就用Keil uversion3进行仿真,在仿真期间也发现了许多错误,基本上都是平日容易犯的错误,比如忘记了子程序标号、死循环程序、标点符号的漏写等。经过反复的编译差错,仿真编译通过后,于是开始在Proteus中连硬件电路,全部接完电路之后将Keil生成的.Hex文件导入进行仿真,发现软件与硬件不能够对应协调工作,于是分别对软件和硬件进行检查,经过反复的仿真调试,并且在同学的帮助和自己对每个子程序进行仿真观察下,终于在课设快结束时成功的调试出结果了。这就是我这一周课设的经过,看似简单,过程却曲折艰辛。

    通过这次课程设计,我进一步加深了对电子自动控制的了解。并进一步熟练了对Keil和Proteus软件的操作。通过与同学探讨和请教老师,终于把问题都解决了,并加深了对计算器工作的原理的了解。同时也掌握了做课程设计的一般流程,为以后的设计积累了一定的经验。做课程设计时,先查阅相关知识,把原理吃透,确定一个大的设计方向,在按照这个方向分模块的把要实现的功能用流程图的形式展示。学会了怎么样去制定计划,怎么样去实现这个计划,并掌握了在执行过程中怎么样去克服心理上的不良情绪。总之,通过这次的设计,进一步了解了单片微型计算机及应用原理,收获很大,对软件编程、排错调试、查阅资料等方面得到较全面的锻炼和提高。

    同时通过本次课程设计的学习,掌握了一种系统的研究方法,可以进行一些简单的编程,我还深深的体会到设计课的重要性和目的性所在。为今后的学习和实践打下了良好的基础。此次课设还巩固和综合运用所学过的原理知识,从而提高自己的实际动手能力和独立思考的能力。

                    参考文献

[1]   周航慈.单片机应用程序设计技术(修订版).北京航空大学出版社

[2]  张志良.单片机原理与控制技术.第2版. 机械工业出版社

[3]  康华光.电子技术基础(第5版). 高等教育出版社出版,2006

[4]  蒋力培.单片微机系统实用教程(第1版).机械工业出版社

附录1  系统硬件电路图

                                   图九   总电路原理图

附录2  程序清单

# include<reg51.h>

typedef unsigned char uint8;

typedef unsigned int  uint16;

sbit p20 = P2^0;

sbit p21 = P2^1;

sbit p22 = P2^2;

sbit p23 = P2^3;

sbit keyin1 = P2^4;

sbit keyin2 = P2^5;

sbit keyin3 = P2^6;

sbit keyin4 = P2^7;

sbit keyin5 = P1^0;

code uint8 number[] = {0x3f,0x06,0x5b,0x4f,

                                                  0x66,0x6d,0x7d,0x07,

                                                  0x7f,0x6f,0x77,0x7c,

                                                  0x58,0x5e,0x79,0x71};

uint8 a[5],b[2];

uint8 num1,num2;

uint8 n = 0, m = 0, g = 0, h = 0;

void delay(uint16 zms)

{

    uint16 i,j;

         for(i = zms ; i > 0 ; i--)

                   for(j = 110 ; j > 0 ; j--);

}

void refresh()

{

   static uint8 j = 0;

  

   switch(j)

   {

       case 0:p20 = 0; p21 = 1; p22 = 1; p23 = 1;P0 = number[a[0]]; j ++;delay(1); break;

       case 1:p20 = 1; p21 = 0; p22 = 1; p23 = 1;P0 = number[a[1]]; j ++; delay(1);break;

       case 2:p20 = 1; p21 = 1; p22 = 0; p23 = 1;P0 = number[a[2]]; j ++; delay(1);break;

       case 3:p20 = 1; p21 = 1; p22 = 1; p23 = 0;P0 = number[a[3]]; j++; delay(1); break;

       default:p20 = 1; p21 = 1; p22 = 1; p23 = 1;j = 0;delay(1);break;

   } 

}

void refresh_1()

{

   static uint8 k = 0;

   switch(k)

   {

       case 0:p20 = 0; p21 = 1; p22 = 1; p23 = 1;P0 = number[b[1]]; k ++;delay(1); break;

            case 1:p20 = 1; p21 = 0; p22 = 1; p23 = 1;P0 = number[b[0]]; k ++;delay(1); break;

            default:p20 = 1; p21 = 1; p22 = 1; p23 = 1;k = 0; break;

   }

}

main()

{

          P0 = 0x3f;

         while(1)

         {

           

             if(!keyin1)

                   {

                      delay(5);

                      if(!keyin1)

                      {

                         if(n >= 15)

                                 n = 0;

                              else

                                 n ++;

                              a[0] = n;

                              while(!keyin1);

                      }  

                   }

                   if(!keyin2)

                   {

                      delay(5);

                      if(!keyin2)

                      {

                         if(m >= 15)

                                 m = 0;

                              else

                                 m ++;

                              a[1] = m;

                             

                              while(!keyin2);

                      }  

                   }

                   if(!keyin3)

                   {

                      delay(5);

                      if(!keyin3)

                      {

                         if(g >= 15)

                                 g = 0;

                              else

                                 g ++;

                              a[2] = g;

                             

                              while(!keyin3);

                      }  

                   }

                   if(!keyin4)

                   {

                      delay(5);

                      if(!keyin4)

                      {

                         if(h >= 15)

                                 h = 0;

                              else

                                 h ++;

                              a[3] = h;

                              while(!keyin4);

                           

                      }  

                   }

        

                   if(!keyin5)

                   {

                     

                           

                            num1 = a[0]*16 + a[1];

                            num2 = a[2]*16 + a[3];

                            a[4] = num1 - num2;

                            b[0] = a[4]%16;

                            refresh_1();

                            delay(10);

                  

                            b[1] = a[4]/16;

                           

                            refresh_1();

                            delay(10);

                            }

                    refresh();

                  

         }

}

   

相关推荐