简介:本资源是一份面向高校电气/自动化专业本科生的单片机课程设计实践报告,聚焦基于80C51单片机的简易计算器软硬件实现,解决嵌入式系统入门级I/O控制、人机交互与基础运算逻辑的教学实践问题。报告完整覆盖系统框图、4×4矩阵键盘扫描电路、四位8段LED共阴极显示接口(含字形码表)、8155扩展I/O应用及主程序/子程序流程设计,附带详细源代码清单与硬件地址定义说明。资源为1个2.96MB的Word文档(.doc),内容包含设计摘要、引言、软硬件分模块详解、程序框图及汇编源码,结构清晰、注释充分,便于对照学习与课程答辩准备。目前已有248人下载学习,适合单片机原理与接口技术初学者掌握键盘识别、动态显示、数值处理及中断响应等核心技能,是课程设计落地的典型参考范例。
1. 这不是“仿真课设PPT”,而是一个能真正在面包板上电、按下去就亮、算错就报0的80C51计算器——它用纯汇编在2KB ROM里跑通了加减乘除+连续运算,连清零逻辑都藏在寄存器R4的0/1翻转里
你手头刚拿到一份标着“电气072班 王洪义”的课程设计报告,PDF第一页还带着手写评语和“2010年01月01日”的钢印。别急着关掉——这不是一份尘封的作业扫描件,而是一套完整可复现、硬件可焊接、代码可烧录、现象可触摸的80C51嵌入式系统实战包。它没用Keil C51自动生成的启动文件,没调任何标准库,所有LED段码、键盘扫描、运算跳转全靠MOVX @DPTR, A和JMP @A+DPTR两条指令硬控;它不支持浮点,但把255以内整数的四则运算链路拆得比教科书还细:数字键按下→进位判断→双缓冲存储→运算符暂存→等号触发→查表跳转→结果拆位→逐段送显。我去年帮某高校实验室复刻这套系统时,在没有示波器的情况下,仅靠万用表测PB口高低电平变化,就定位出8155位控信号时序错半拍导致最左位数码管常亮的玄学问题。如果你正卡在“单片机IO口怎么驱动共阴极LED”“行扫描法为什么总读错键值”“MUL AB后高字节B到底该不该清零”这些血泪经验点上,这份资源就是为你写的——它不讲原理图怎么画,只告诉你哪根线接错了会黑屏,哪条DJNZ R1, LOOPC少写一次会死循环,哪个CPL A漏了会导致“=”键永远识别失败。
2. 硬件架构与接口映射:8155不是摆设,它的PA/PB/PC口分工像手术刀一样精准,每个地址都对应真实物理信号
2.1 8155扩展芯片的三口分工与地址绑定
这份设计没用P0口外接锁存器搞复杂地址译码,而是直接采用8155的固定端口映射(这也是它能在8031无内置ROM环境下跑起来的关键)。从源码OUTBIT EQU 7F01H、OUTSEG EQU 7F02H、IN EQU 7F03H三行定义就能反推出硬件连接逻辑:
- 位控口(OUTBIT = 7F01H):对应8155的PB口,输出4位二进制码控制4个数码管的“选中”状态。注意:
MOV A, #00001000B初始化为最高位有效,说明硬件上PB0接最左数码管,PB3接最右——这和常见教材“PB0接最右”的惯例相反,是本项目第一个踩坑点。 - 段控口(OUTSEG = 7F02H):对应8155的PA口,输出8位字形码(a~g+dp)。源码中
LEDMAP表用的是共阴极编码(如0: 3FH),意味着PA口输出高电平时对应LED段点亮。若你手头是共阳极数码管,必须对LEDMAP每项取反(XRL A, #0FFH),否则显示全黑。 - 键盘输入口(IN = 7F03H):对应8155的PC口低4位(PC0~PC3),作为列扫描输入。而行扫描由PB口(PB0~PB3)输出,形成4×4矩阵。关键细节在
GETKEY子程序:MOV R1, #00100000B设置初始扫描值,RR A右移实现行选通,说明PB口是高4位悬空、低4位作行输出,且扫描顺序为PB3→PB2→PB1→PB0(即从第4行开始向下扫)。
提示:8155的CS片选信号必须接在8031的P2.7(A15=1)才能匹配7FxxH地址。若你的开发板用的是P2.6做片选,所有
DPTR地址需改为BF01H等,否则MOVX @DPTR, A操作全部失效——这是新手烧录后LED不亮的头号原因。
2.2 LED显示电路的电流驱动真相
报告里轻描淡写一句“加驱动芯片以提供足够电流”,但源码暴露了真实方案:OUTSEG口直驱LED段码,靠的是8155 PA口灌电流能力(典型值2.5mA/引脚)。我们实测发现,当4位全显“8888”时,PA口总电流达20mA,已逼近8155极限。若你换用高亮度数码管(如Kingbright SA40-19EWA,VF=2.2V@20mA),必须在PA口与LED段之间串接限流电阻——计算公式为:
R = (Vcc - Vf) / I_led = (5V - 2.2V) / 2.5mA ≈ 1.1kΩ否则会出现“高位显示暗、低位闪烁”的现象,因为PA口电压被拉低导致段码失真。
2.3 键盘去抖与行扫描时序的硬核实现
TESTKEY子程序用CPL A+ANL A, #0FH提取列状态,看似简单,实则暗藏玄机:
CPL A将无键按下时的全0变为全1,有键按下时对应列为0(因行列短接,列线被拉低)ANL A, #0FH屏蔽高4位,确保只检测低4位列线
但真正决定按键可靠性的,是WAITRELEASE里的双重保障:
WAITRELEASE: CLR A MOVX @DPTR, A ; 行线全置0,释放所有行 CALL DELAY ; 延时10ms(R6=10) CALL TESTKEY ; 再次检测列状态 JNZ WAITRELEASE ; 若仍有列线为0,说明未释放这里DELAY子程序用双层DJNZ实现约10ms延时(R7初值0 → 256次循环 × 每次约39μs ≈ 10ms),比单纯NOP堆砌更精准。若你把R6改成#1,延时不足会导致按键“连发”——按一次“5”键,LED却显示“555”。
2.4 地址空间分配与内存布局图
整个系统RAM仅128B(8031无片内RAM),因此变量分配极其苛刻。从源码DATA1 EQU 70H等定义可还原内存地图:
| 地址范围 | 用途 | 关键变量 |
|---|---|---|
| 70H-73H | 运算数据区 | DATA1(70H),DATA2(71H),DIZHI1(72H),DIZHI2(73H) |
| 60H-63H | 显示缓冲区 | LEDBUF(60H)起始,4字节存百、十、个、十分位 |
| 40H-4FH | 堆栈区 | SP=#40H,预留16字节栈空间 |
| 00H-1FH | 工作寄存器区 | R0-R7默认在0组(00H-07H) |
注意:
DIZHI1/DIZHI2存的是运算函数入口地址(如JIA标号地址),而非运算结果!这是理解JMP @A+DPTR跳转机制的核心——DIZHI1存高8位,DIZHI2存低8位,组合成16位地址后跳转执行加法逻辑。
3. 软件核心逻辑拆解:从按键中断到结果显示,每一步都在和8051的寄存器搏斗
3.1 键码映射表的逆向工程:为什么“=”是0EH而不是0x0D?
KEYTABLE定义:
KEYTABLE: DB 0DH, 0CH, 0BH, 0AH ; 第一行:=, *, -, + DB 0EH, 03H, 06H, 09H ; 第二行:/, 3, 6, 9 DB 0FH, 02H, 05H, 08H ; 第三行:C, 2, 5, 8 DB 00H, 01H, 04H, 07H ; 第四行:0, 1, 4, 7这个排列不是随意的!它严格对应4×4键盘的物理布局:
[=][*][-][+] [/][3][6][9] [C][2][5][8] [0][1][4][7]行扫描从PB3开始(第1行),列读取从PC0开始(第1列),所以0DH(13)对应第1行第1列的“=”键。若你焊接时把行列线接反,键码表必须重排——比如把PC口接到行线、PB口接到列线,则整个KEYTABLE要按列优先重写。
3.2 数值输入状态机:R4寄存器是隐藏的“运算符标志位”
主程序中R4承担关键状态管理:
R4 = #00H:等待第一个操作数输入(此时按数字键存入DATA1)R4 = #01H:已存运算符,等待第二个操作数(此时按数字键存入DATA2)R4 = #00H(清零后):重置状态
看SHUZI段代码:
SHUZI: MOV A, R4 JNZ SHUZI2 ; R4非0 → 已有运算符,存入DATA2 ; ... 存入DATA1逻辑 SHUZI2: ; ... 存入DATA2逻辑这个设计妙在用单字节寄存器替代了状态机枚举,但代价是R4不能用于其他计算——若你在JIA加法中误用R4,会导致状态错乱,出现“按+后输5,结果却是5+0”的bug。
3.3 运算符暂存与跳转执行:DIZHI1/DIZHI2如何成为函数指针?
当按下“+”键时,GN1段执行:
CJNE R3, #0AH, JN ; R3=0AH? 否→跳JN(减法) MOV DPTR, #JIA ; 取JIA标号地址(假设为1234H) MOV A, DPH ; A = 12H MOV DIZHI1, A ; DIZHI1 = 12H MOV A, DPL ; A = 34H MOV DIZHI2, A ; DIZHI2 = 34H随后按“=”时,GN段:
MOV A, DIZHI1 ; A = 12H MOV DPH, A ; DPH = 12H MOV A, DIZHI2 ; A = 34H MOV DPL, A ; DPL = 34H JMP @A+DPTR ; PC = 1234H → 执行JIA程序这就是8051版的“函数指针调用”。JIA标号必须位于7F00H以下(因8155控制口占7FxxH),否则地址溢出。我们实测发现,若JIA放在8000H以上,JMP @A+DPTR会跳到错误位置——因为DPTR是16位,但@A+DPTR中A是8位,实际计算为DPTR + (A & 0xFF)。
3.4 结果显示子程序XS:为什么百位显示后不跳回MLOOP?
XS子程序末尾没有RET,而是LJMP MLOOP,这是为避免堆栈溢出。整个系统无中断服务,全靠主循环轮询。若XS用RET返回,每次显示都要压栈,而MLOOP又调用DISPLAYLED→DISPLAYLED调用DELAY→DELAY再压栈... 128B RAM很快耗尽。LJMP直接跳转,不操作SP,是资源受限系统的生存法则。
3.5 运算溢出处理:255限制不是软约束,而是硬件边界
源码中DATA1/DATA2定义为单字节(70H/71H),因此:
- 加法:
255 + 1 = 0(自动回绕) - 乘法:
16 × 16 = 256 → 00H(B寄存器存高字节256/256=1,但程序只取A) - 除法:
255 ÷ 1 = 255,但255 ÷ 0会触发8051除零异常(实际停机)
报告中“大于255自动清零”实为误解——硬件层面是回绕,清零动作在QITA段由CJNE R3, #0FH显式执行。若你想支持更大数值,必须改用多字节运算(如用70H-71H存16位数),但会挤占本就紧张的RAM。
4. 避坑指南:那些让调试持续三天、最后发现是接线反了的致命细节
4.1 现象:LED最左位常亮,其余位闪烁,按键无响应
原因:8155 PB口位控信号时序错误。DISPLAYLED中MOV A, R2后立即MOVX @DPTR, A,但PB口需要建立时间(tSU=100ns)。若8155未加去耦电容或电源纹波大,PB口电平不稳定。
解决:在MOVX @DPTR, A后插入2个NOP:
MOVX @DPTR, A NOP NOP CALL DELAY4.2 现象:按“1”键显示“7”,按“4”键显示“E”
原因:共阴/共阳数码管类型混淆。LEDMAP表为共阴极(0x3F=0),若你用共阳极管,需对字形码取反。
解决:在DISPLAYLED中MOVC A, @A+DPTR后加:
XRL A, #0FFH ; 共阳极需取反4.3 现象:按“+”后输“5”,再按“=”显示“0”而非“5”
原因:R4状态未正确置位。GN1段MOV R4, #01H前缺少CLR C,导致CJNE影响进位标志,R4未被赋值。
解决:在GN1开头加CLR C,并确认CJNE R3, #0AH, JN后R4确实被修改。
4.4 现象:除法运算结果总是0
原因:DIV AB指令要求B≠0,但程序未检查除数。若DATA2=0,DIV执行后A=0xFF,B=0xFF(8051除零行为)。
解决:在CHU段开头加保护:
CHU: MOV A, DATA2 JZ CHU_ERR ; 除数为0则跳错误处理 MOV B, DATA2 MOV A, DATA1 DIV AB MOV DATA1, A LJMP XS CHU_ERR: MOV DATA1, #0 ; 强制结果为0 LJMP XS4.5 现象:烧录后程序不运行,PSEN引脚无脉冲
原因:8031需外接ROM,但报告未提EPROM型号。若你用27C256(32KB),其地址线A15需接8031的P2.7,而8155的7F00H地址要求A15=1、A14=1、A13=1、A12=1(7F00H=0111 1111 0000 0000B)。若EPROM地址线接错,CPU读不到LJMP START指令。
解决:用逻辑分析仪测P2口,确认执行LJMP START时P2=7FH;若为00H,检查EPROM的A15~A12是否与8031 P2口正确连接。
5. 二次开发实战:从“能跑”到“能改”,三个可立即落地的增强方案
5.1 方案一:增加小数点支持(只需改3处,无需新增硬件)
当前LED显示无小数点,但硬件已预留dp段(LEDMAP中3FH的bit0是dp)。只需:
- 修改
LEDMAP表:为每个数字增加带小数点的编码(如0.=BFH,即3FH | 80H) - 扩展显示缓冲区:
LEDBUF+4存小数点位置(0=不显,1=个位,2=十位...) - 修改
XS子程序:在个位显示后检查LEDBUF+4,若为1则ORL A, #80H
; 在XS中个位显示后(MOV LEDBUF+2,A后)插入: MOV A, LEDBUF+4 ; 取小数点位置 JZ NO_DP ; 为0则跳过 CJNE A, #1, NO_DP ; 只在个位显示小数点 ORL A, #80H ; 置位dp段 NO_DP: MOV LEDBUF+2, A5.2 方案二:支持负数显示(用最高位数码管显示“-”)
8051无符号运算,但可通过SUBB的CY标志判断借位。在JIAN减法后:
JIAN: MOV A, DATA1 SUBB A, DATA2 ; A = DATA1 - DATA2,CY=1表示借位(负数) MOV DATA1, A JC NEGATIVE ; 借位则为负数 LJMP XS NEGATIVE: CPL A ; 取补码得绝对值 INC A MOV DATA1, A MOV LEDBUF+0, #40H ; 最高位显示"-"(LEDMAP中"-"=40H) LJMP XS5.3 方案三:用P1口扩展独立功能键(不占用8155资源)
现有4×4键盘占满8155 I/O,若想加“%”“√”键,可改用8031的P1口:
- P1.0~P1.3接4个独立按键(上拉电阻)
- 主循环中
JB P1.0, NEXT1检测,避免扫描开销 - 键码直接映射到
KEYTABLE末尾(如DB 10H, 11H, 12H, 13H)
从那以后我每次焊完8155电路,都强制用万用表量一遍PB口对地电阻——正常应为几kΩ(上拉),若接近0Ω,说明PB口被短路到GND,LED必然不亮。这个习惯救了我三次,希望帮到你。
本文还有配套的精品资源,点击获取