
A44基于51单片机汇编语言LCD12864简易计算器Proteus设计
用51单片机汇编语言做一个简易计算器:4×4矩阵键盘输入数字和运算符,LCD12864液晶显示"数字1:/数字2:/结果"三行,支持加减乘除四则运算。数字在内存里按BCD逐位存放,加法靠ADD加DIV AB取进位,减法先补10再SUBB借位,乘法用MUL AB逐位相乘,除法用长除法一位一位往下除。
一、设计实现功能
本设计功能简介:使用51单片机和汇编语言设计一个简易计算器的proteus仿真,要求以下功能:
1、使用汇编语言编写程序。
2、4×4矩阵键盘输入,0~9数字键、加减乘除键、等号键、清除键。
3、LCD12864液晶显示输入的数字和计算结果。
4、实现8位以内整数的加、减、乘、除四则运算,除法结果带小数。
二、资源汇总
1、仿真文件:Proteus工程,7.8和8.6两个版本都能直接打开。
2、程序文件:main.ASM汇编源码,以及编译好的cx.hex。
3、修改日期小工具:购买资料后赠送,用于修改文件创建日期,下载地址在文章最后。
三、设计介绍
拿到压缩包之后先解压,没有解压软件的直接搜"压缩",下载解压软件后先解压文件,再运行仿真和程序。
图1 Proteus仿真总览

硬件分三块:LCD12864数据口接P0,控制脚接P2,键盘接P1。LCD12864是并行接口,16个引脚挂上去就行:
| 信号 | 引脚 | 作用 |
| D0~D7 | P0.0~P0.7 | 8位数据总线 |
| E | P2.0 | 使能,下降沿锁存 |
| RW | P2.1 | 读/写选择,本设计只写不读 |
| RS | P2.2 | 0选指令、1选数据 |
| CSB | P2.3 | 右半屏片选 |
| CSA | P2.4 | 左半屏片选 |
12864屏幕内部是左右两片64×64的驱动IC,程序里用writeleft和writeleft2两个子程序分别往左半屏和右半屏写字,本质是拉低CSA或CSB再送数据。P0口外接了一个10K排阻上拉,因为P0内部没有上拉电阻,不加上拉屏上什么都不显示。
上电后先LCALL INT初始化液晶,再LCALL CLEAR清屏,然后往指定行列写固定字符"数字1:"、"数字2:"、"结果"。字符不是调字库,是程序里存好的字模:
`asm
mov column3,#0
mov pagef3,#0
mov codes,#0 ;字模编号0 = "数字1:"
lcall writeleft2
mov column,#40
mov pagef,#2
mov codes,#15 ;字模编号15 = "结果"
lcall writeleft`
pagef是页地址(屏幕纵向8像素为1页),column是列地址,12864每页64列,所以列地址超过63就要换到另一片半屏。这套"列地址+页地址+字模编号"的写法比直接算指令字节直观,改界面只要改这三个数。
键盘是标准的4×4接法,接在P1口,P1.0~P1.3接行、P1.4~P1.7接列。扫描没有用查表,而是用一根"跑零线":
`asm
dd: mov r0,#0f7h ;11110111B,P1.3先拉低
mov r1,#0 ;键值计数器
cc: mov a,r0
mov p1,a
mov a,p1
mov r2,a ;原始读回值存r2
setb c
mov r3,#4
bb: rlc a ;逐位左移找0
jnc aa
ee: inc r1
djnz r3,bb
mov a,r0
setb c
rrc a ;0往右移一位,轮下一行
mov r0,a
jc cc`
0F7H是P1.3为0、其余为1,读回P1时哪一位变0就说明哪一行哪一列接通。RLC配合进位位C一路数下来,落到第几位R1就加几次,最后R1正好是0~15的键值——按下第3行第2列,R1就是9。松开判断用xrl a,r2比较前后两次读值,相等才算稳定按下,等于软件消抖。
键值直接当功能码用:
| 键值 | 功能 | 键值 | 功能 |
| 0~9 | 数字0~9 | 13 | 除号÷ |
| 10 | 加号+ | 14 | 等号= |
| 11 | 减号- | 15 | 清除/复位 |
| 12 | 乘号× |
按下运算符时INC 51H累计次数:加一次是加法、减两次是减法、乘三次是乘法、除四次是除法,等号按下时按51H的值分派到四段运算程序。这个写法省掉了独立的运算符变量,代价是四种运算的判断必须严格按顺序写,中间漏一个INC就会跳到错误的运算分支。
数字的存放是这套程序最有意思的地方。8位BCD不放在连续单元里,而是故意隔开:
| 位数 | 数字1 | 数字2 |
| 个位 | 40H | 60H |
| 十位 | 41H | 71H |
| 百位 | 42H | 72H |
| 千位 | 44H | 74H |
| 万位 | 45H | 75H |
| 十万位 | 46H | 76H |
| 百万位 | 48H | 78H |
| 千万位 | 49H | 79H |
跳开43H、47H和73H、77H,是为了避开中断和显示子程序用的工作单元,防止算到一半数字被别的程序改掉。这也是51汇编里很典型的手工分配内存思路。
每按一个数字键,先把已输入的数字整体左移一位(高位往高处搬),再把新键值填到个位。以输入第4位为例:
`asm
AA3: CJNE A,#3,AA5
MOV 42H,41H ;原十位→百位
MOV 41H,40H ;原个位→十位
MOV 40H,R1 ;新键值→个位
LCALL A0
LCALL A1
LCALL A2
LCALL A3
INC 50H`
50H记当前已经输入了几位,A0~A3是刷新显示的子程序,只重画变动的位,不用整屏刷新,所以按键响应很快。
加法是逐位带进位串起来的,最低位加完用DIV AB取商当进位:
`asm
MOV A,40H
ADD A,60H
MOV B,#10
DIV AB ;A=进位,B=本位结果
MOV 40H,B
ADD A,41H ;上一次的进位加本位
ADD A,71H
MOV B,#10
DIV AB
MOV 41H,B`
因为BCD每位不超过9,两个数相加最大18,加进位最大19,除以10的商只可能是0或1,正好当进位用。八位一路加下去,最后49H再往上的进位存到0CEH,结果超过8位时靠它显示最高位。
减法反过来,先给被减数补位再减:个位补10,其余位补9,然后SUBB带借位减,DIV AB的商就是借位:
`asm
MOV A,40H
ADD A,#10 ;个位预补10
MOV 40H,A
MOV A,41H
ADD A,#9 ;高位预补9
MOV 41H,A
CLR C
MOV A,40H
SUBB A,60H
MOV B,#10
DIV AB
MOV 40H,B`
补9而不是补10,是因为高位本身还要借1给低位,补9等于"补10再减1",跟借位正好抵掉。这个技巧在51的BCD减法里用得很多,比逐位判断大小再借位省事。
乘法用MUL AB逐位乘,乘积的十位存进暂存单元91H,累加到下一位:
`asm
MOV B,40H
MOV A,60H
MUL AB
MOV B,#10
DIV AB
MOV 3AH,B ;本位结果
MOV 91H,A ;进位暂存
MOV A,60H
MOV B,41H
MUL AB
MOV B,#10
ADD A,91H ;加上一次的进位
DIV AB
MOV 3BH,B`
除法是长除法:从最高位开始,取一位除以除数得到商,余数乘10再拉下一位继续除,结果里既有整数部分也有小数部分(69H、6FH、4FH…对应十分位、百分位)。所以这个计算器除不尽时能显示小数,不是直接取整。
结果算完以后,从最高位49H往下找第一个非零位,找到哪一位就跳到对应的显示位置:
`asm
MOV A,49H
CJNE A,#0,CC1
MOV A,48H
CJNE A,#0,CC2
MOV A,46H
CJNE A,#0,CC3
...`
A00~A08这九个子程序唯一的区别就是column的值(56、48、40…每次减8),一个字模占8列,所以定位到第几位就把列地址设成几倍。这样前导零不会被显示出来,1+2显示"3"而不是"00000003"。
特别注意: 屏幕全黑或者全白,先查P0的上拉排阻有没有接,P0没有内部上拉,悬空时送什么都是乱码;只亮半屏,检查CSA、CSB两个片选,12864是左右两片64×64,写右半屏时CSB要拉低;按键没反应,看P1的接法,行和列接反了扫出来的键值全乱;按键连加好几下,是消抖没做,松开判断要比较前后两次读值;结果显示成乱码,先看数字是不是被写进了43H、47H这些被程序占用的单元;减法出负数,这段程序只处理大减小,被减数小于减数时结果会错,是算法本身的限制,不是bug;除法结果小数位不对,检查余数有没有乘10再拉下一位。
四、日期修改
资料中提供修改日期的小工具,购买资料赠送修改文件创建日期的小工具,三个下载地址都在文章最后。
五、资料下载
付费后获取下载链接,直接下载到电脑上,然后解压即可!每个链接前面都配了二维码,手机扫码可以直接跳转。
/

