简介:本资源是一份面向高校电气/自动化专业本科生的单片机课程设计报告,聚焦基于80C51单片机的简易计算器软硬件实现,解决嵌入式系统入门级项目开发中I/O扩展、键盘扫描、LED动态显示与基础运算逻辑集成等核心问题。压缩包为1个2.96MB的Word文档(.doc),完整包含系统框图、4×4矩阵键盘与四位8段LED硬件接口电路设计、8155扩展芯片地址分配与端口配置、共阴极字形码表、LED显示/读键/主控三类程序流程图及全部汇编源代码清单,并附有课程设计封面、摘要、引言、软硬件分章节说明与教师评语。目前已有248人学习下载,读者可直接复现该经典MCS-51实践项目,掌握单片机最小系统搭建、外设驱动编程、数值进制转换显示及连续运算状态管理等关键能力,是理解嵌入式底层交互原理的优质教学范例。
1. 基于80C51的单片机计算器:不是仿真Demo,是能焊在洞洞板上跑通加减乘除的真实硬件系统
你手头有一块带8031/80C51核心的最小系统板,没接USB转串口,没装Keil C51的高级调试器,只有万用表、示波器探头、一把电烙铁和一包共阴极数码管——这时候想验证“我真把单片机I/O口驱动能力、键盘消抖逻辑、LED动态扫描时序、BCD码拆分显示、寄存器级运算流程”全吃透了?别翻教材,直接抄这份2010年电气专业课设报告里的源码。它不是用Proteus画出来的漂亮框图,而是实打实跑在8031+8155扩展芯片上的汇编程序:4×4矩阵键盘按下去有明确键码(00H–0FH),四位LED从左到右逐位点亮无鬼影,输入“99+99=”,结果“198”稳稳显示在百位、十位、个位,小数点不亮;输错按“C”清零,所有寄存器归零,缓冲区清空,连堆栈指针SP都手动设回#40H。它受限于8位累加器A和255上限,不支持负数、小数、括号嵌套,但正因如此,每个字节怎么进、怎么出、怎么参与ADD/SUBB/MUL/DIV、怎么被MOVC查表取段码、怎么通过RR A控制位选通,全都裸露在指令流里。如果你刚学完《单片机原理》第3章I/O口结构、第5章定时器/计数器、第7章中断系统,正卡在“知道理论却不敢接线”的临界点——这份资源就是你的第一块真实硬件跳板。它不炫技,不堆功能,但每行汇编背后都有硬件信号在走:PB口输出扫描码时PA口低四位正在读列线电平,OUTBIT地址写0关闭所有数码管瞬间,OUTSEG地址已把‘3FH’送进段控锁存器,延时子程序里R6/R7的初值直接决定你肉眼能否看清闪烁。这不是教你怎么用STC下载器,而是教你用最原始的方式,让单片机真正“活”起来。
2. 硬件接口设计:为什么必须用8155扩展,而不是直接用P1口接键盘+P0口接LED?
2.1 80C51原生I/O口的硬伤:驱动能力与端口复用冲突
80C51的P0口是开漏输出,必须外接上拉电阻才能驱动LED段码(典型电流需5–10mA/段),而P1/P2/P3是准双向口,灌电流能力约20mA,拉电流仅几十μA。若直接用P1口接4×4键盘(8根线)+P0口接4位LED(8段+4位控=12线),会立刻暴露两个致命问题:
- P0口无法同时承担地址总线和I/O功能:当系统未扩展外部ROM/RAM时,P0口作为地址/数据复用总线,需接74LS373锁存器。若强行将其当纯I/O用,必须确保ALE信号被禁用或隔离,否则键盘扫描时P0口电平会被ALE脉冲干扰,导致误判按键。
- P1口驱动LED段码会拖垮键盘扫描精度:P1口每位最大灌电流20mA,4位LED全亮时8段×4位=32段,即使每段只取2mA,总电流达64mA,远超P1口总驱动能力(手册标称40mA)。此时P1口电平被拉低,键盘列线读入时可能无法识别高电平,出现“按了没反应”或“连按多键”。
提示:某高校实验室曾让学生直接P1接键盘、P0接LED,结果70%的板子在连续按键后出现“+”键被识别为“3”,根源就是P0口灌电流过大导致VCC局部跌落,影响P1口输入阈值。
2.2 8155成为刚需:三合一扩展芯片的不可替代性
8155是Intel为MCS-51定制的可编程外围芯片,内部集成256字节RAM、两个可编程定时器/计数器、以及最重要的——22根可配置I/O线(PA0–PA7、PB0–PB7、PC0–PC5)。在本设计中,它被配置为:
- PA口(8位)→ 段控口(OUTSEG, 地址7F02H):输出字形码,驱动LED段。PA口为推挽输出,单路驱动能力达2.6mA,8段全亮时总电流可控。
- PB口(8位)→ 位控口(OUTBIT, 地址7F01H):输出位选信号,控制哪一位数码管点亮。PB口同样推挽,可直接驱动共阴极数码管的位选端(需加限流电阻)。
- PC口低4位 + PB口 → 键盘行列扫描:PB口作为行输出(输出扫描码),PC口低4位作为列输入(读取按键状态)。这种分工彻底解耦了驱动与读取,避免I/O口功能冲突。
关键参数必须手算验证:
- 8155片选地址由P2口高位决定(本设计为7F00H–7FFFH),需确保P2.7=0且P2.6=1(即7F00H对应二进制0111111100000000B),否则8155无法响应。
- PA/PB/PC口方向由8155命令字控制:向7F00H写#03H(二进制00000011B),表示PA口输出、PB口输出、PC口低4位输入(高4位未用)。此命令必须在START段执行,否则后续所有I/O操作失效。
2.3 LED与键盘电路的物理实现细节
LED显示电路(共阴极,4位)
- 段控连接:8155的PA0–PA7分别接LED的a–g段及dp(小数点)。注意字形码表是共阴极定义(如‘0’=3FH=00111111B,a–g段为1时亮),若误用共阳极码会导致全黑或乱码。
- 位控连接:PB0–PB3接4位数码管的公共阴极(COM1–COM4)。PB口输出低电平有效(0=点亮该位),因此位控码初值设为#00001000B(即08H),对应点亮最左边第一位(COM1)。
- 驱动增强:因PA/PB口驱动能力有限,实际电路中PA口后接74LS244(三态缓冲器)增强段码驱动,PB口后接ULN2003(达林顿阵列)增强位控电流(每路500mA)。
键盘电路(4×4行列式)
- 行线连接:8155的PB0–PB3作为行输出,接键盘4行。
- 列线连接:8155的PC0–PC3作为列输入,接键盘4列。PC口需外接10kΩ上拉电阻,确保无按键时读入为高电平(#0FH)。
- 消抖关键:硬件消抖用0.1μF电容并联按键两端,软件消抖靠两次读键间隔≥10ms(DELAY子程序R6/R7初值需校准)。若忽略此点,单次按键可能被识别为3–5次,导致“按一次+键,屏幕上跳+ + + +”。
3. 软件架构解析:从主循环到键码映射,汇编指令如何精准控制硬件时序
3.1 主程序框架:三层嵌套循环的实时性保障
整个系统采用查询方式(非中断),主循环MLOOP严格遵循“显示→检测→处理→再显示”节奏:
MLOOP: CALL DISPLAYLED ; 动态扫描4位LED,耗时约2ms(含延时) CALL TESTKEY ; 检测是否有键按下,耗时<0.1ms JZ MLOOP ; 无键则跳回,持续刷新显示 CALL GETKEY ; 有键则读取键码,耗时约0.5ms LJMP JISUAN ; 进入运算逻辑此结构保证LED始终以>50Hz频率刷新(人眼无闪烁),且按键响应延迟≤5ms(两次MLOOP间隔)。若将DISPLAYLED放在GETKEY之后,会导致按键后屏幕冻结,用户感知为“卡死”。
3.2 键码生成逻辑:行扫描法的寄存器级实现
GETKEY子程序是核心难点,其本质是用PB口逐行输出低电平,用PC口读取列线状态,通过位移运算将行列坐标转为唯一键码:
GETKEY: MOV DPTR, #OUTBIT ; 位控口地址(实际用于行扫描) MOV P2, DPH ; P2口高位送DPH,确保地址有效 MOV R0, #LOW(IN) ; IN=7F03H,取低8位=03H,指向PC口读地址 MOV R1, #00100000B ; 行扫描初值:PB0=0(第0行),其余为1 MOV R2, #4 ; 共4行需扫描 KLOOP: MOV A, R1 ; 取当前行码 CPL A ; 取反:00100000B → 11011111B(仅PB0为0) MOVX @DPTR, A ; 写入PB口,选中第0行 CPL A ; 恢复原值 RR A ; 右移准备下一行:00100000B → 00010000B MOV R1, A ; 更新行码 MOVX A, @R0 ; 从PC口读列状态(如第0行第0列按下,则A=1110B) CPL A ; 取反:1110B → 0001B(低4位) ANL A, #0FH ; 屏蔽高4位,得列号(0–3) JNZ GOON1 ; 若A≠0,说明该行有键按下,跳出扫描 DJNZ R2, KLOOP ; 否则扫描下一行 MOV R2, #0FFH ; 全扫完无键,返回0FFH SJMP EXIT GOON1: MOV R1, A ; 保存列号 MOV A, R2 ; R2存当前行号(0–3) DEC A ; 行号减1(因R2初始为4,循环后为0–3) RL A ; 左移1位:0→0, 1→2, 2→4, 3→6 RL A ; 左移2位:0→0, 1→4, 2→8, 3→12(即行号×4) MOV R2, A ; R2 = 行号×4 MOV A, R1 ; 取列号(0–3) ADD A, R2 ; 键码 = 行号×4 + 列号(0–15) MOV R2, A ; 存键码 EXIT: MOV A, R2 RET关键参数说明:
RL A执行两次等效于A = A × 4,因4×4键盘共16键,键码范围0–15,故用行号×4+列号可唯一映射。CPL A后ANL A, #0FH确保只取低4位,避免列线读入时高4位噪声干扰。- 若不执行
CPL A,读入值为高电平(1),需改为JZ GOON1,逻辑易错。
3.3 键码表与功能键路由:如何让“+”键触发加法子程序?
键码0–15需映射到具体功能,KEYTABLE定义了物理键与逻辑功能的绑定:
KEYTABLE: DB 0DH, 0CH, 0BH, 0AH ; 第0行:=, /, -, + DB 0EH, 03H, 06H, 09H ; 第1行:C, 3, 6, 9 DB 0FH, 02H, 05H, 08H ; 第2行:无定义, 2, 5, 8 DB 00H, 01H, 04H, 07H ; 第3行:0, 1, 4, 7GETKEY返回键码后,MOVC A, @A+DPTR查表得功能码(如键码0AH查得#0AH),再通过CJNE R3, #0AH, FOLLOW判断是否为“+”键。此处必须用CJNE而非CJNE R3, #0AH, NEXT,因CJNE会自动更新CY标志位,后续JC SHUZI可直接用CY判断数字键(键码<0AH)或功能键(键码≥0AH),省去一次比较指令。
4. 运算与显示核心:255上限下的数值处理、BCD拆分与动态扫描陷阱
4.1 数值存储与运算边界:DATA1/DATA2寄存器的生命周期管理
系统用两个字节地址DATA1 EQU 70H、DATA2 EQU 71H存储参与运算的两个操作数,其值域被硬件强制限定在0–255(8位无符号):
- 首次输入数字键:
SHUZI段执行ADD A, DATA1,将键码(0–9)加到DATA1。若DATA1原为99,再按“9”则99+9=108,仍在范围内;但若按“9”三次得999,溢出后A=999 MOD 256=231,显示为“231”而非报错。 - 第二次输入数字键:
SHUZI2段对DATA2操作,逻辑同上。 - 运算触发时机:当第一个操作数存入DATA1后,按“+”键会将
DPTR指向JIA子程序地址,并存入DIZHI1/DIZHI2;此时再按数字键,新值存入DATA2,按“=”才执行JIA。
注意:
JIA子程序中ADD A, DATA2后未检查OV(溢出标志),若99+157=256,A=00H且OV=1,结果错误显示“0”。真实项目中应加入JNB OV, NO_OV跳转,但本课设为简化未实现。
4.2 BCD码拆分显示:XS子程序如何把一个字节变成三位LED
XS子程序将运算结果(存于DATA1)拆分为百位、十位、个位,并送入LEDBUF+0(百位)、LEDBUF+1(十位)、LEDBUF+2(个位):
XS: MOV B, #10 ; 除数10 DIV AB ; A = DATA1 / 10, B = DATA1 % 10(个位) MOV R5, A ; 保存商(十位+百位) MOV A, B ; 个位值 MOV DPTR, #LEDMAP ; 字形码表首地址 MOVC A, @A+DPTR ; 查个位段码 MOV LEDBUF+2, A ; 送个位显示缓冲区 MOV A, R5 ; 取商 CJNE A, #0, NEXT1 ; 商为0?是则跳过十位显示 LJMP MLOOP ; 直接返回主循环 NEXT1: MOV B, #10 DIV AB ; A = 商 / 10(百位), B = 商 % 10(十位) MOV R5, A ; 百位 MOV A, B ; 十位 MOVC A, @A+DPTR ; 查十位段码 MOV LEDBUF+1, A ; 送十位 MOV A, R5 ; 百位 CJNE A, #0, NEXT2 ; 百位为0?是则跳过 LJMP MLOOP NEXT2: MOVC A, @A+DPTR ; 查百位段码 MOV LEDBUF+0, A ; 送百位 LJMP MLOOP关键细节:
DIV AB是单字节除法,A为被除数,B为除数,结果A=商,B=余数。此处用两次DIV实现十进制拆分,比查表法更节省ROM空间。CJNE A, #0, NEXT1后未跟SJMP,因CJNE本身会根据A≠0跳转,A=0时顺序执行LJMP MLOOP,避免百位为0时十位也被跳过。LEDBUF+3未使用,因最大结果255仅需三位,第四位(千位)恒为0,DISPLAYLED中R1=#4但LEDBUF+3内容未初始化,实际显示为随机值(需在START中MOV LEDBUF+3, #0补全)。
4.3 DISPLAYLED动态扫描:位控与段控的时序配合
DISPLAYLED是硬件时序敏感代码,必须确保“位控选中→段码送出→延时→关闭所有位”严格同步:
DISPLAYLED: MOV R0, #LEDBUF ; 显示缓冲区首地址 MOV R1, #4 ; 4位数码管 MOV R2, #00001000B ; 位控码初值:点亮第0位(最左) LOOP: MOV DPTR, #OUTBIT ; 位控口地址 MOV A, #0 ; 清零,关闭所有位 MOVX @DPTR, A MOV A, @R0 ; 取当前位显示数据 MOV DPTR, #OUTSEG ; 段控口地址 MOVX @DPTR, A ; 送段码 MOV DPTR, #OUTBIT ; 位控口 MOV A, R2 ; 当前位控码 MOVX @DPTR, A ; 选中该位 MOV R6, #1 ; 延时参数(需实测调整) CALL DELAY MOV A, R2 ; 取位控码 RR A ; 右移:00001000B → 00000100B(点亮第1位) MOV R2, A ; 更新位控码 INC R0 ; 指向下一位缓冲区 DJNZ R1, LOOP ; 4位未完则循环 RET避坑要点:
MOV A, #0后立即MOVX @DPTR, A关闭所有位,是为防止“位控切换瞬间,前一位段码仍有效”导致重影。若省略此步,会出现“9”显示时右下角多一点(前一位的g段残留)。RR A必须在INC R0之后,否则位控码与缓冲区地址错位,导致“个位数据送到百位显示”。R6初值决定每位点亮时间,若太小(如#0),人眼可见闪烁;若太大(如#10),整体刷新率下降,按键响应变慢。实测#1(约1ms)最佳。
5. 避坑指南:那些让课设挂掉的5个玄学问题与血泪解决方案
5.1 现象:按下任意键,LED显示全灭或乱码
原因:8155未正确初始化,或命令字写错。MOVX @DPTR, A向7F00H写#03H失败,导致PA/PB口方向配置错误。
解决:用示波器测8155的CE(片选)引脚,在MOVX @DPTR, A执行时应有低电平脉冲;若无,检查P2口高位是否为01111111B(7F00H);若有,用万用表测8155的IO/M引脚是否为低电平(地址模式)。
5.2 现象:键盘只能识别某一行的按键(如只识别第0行的0–3)
原因:行扫描码R1初值错误或RR A指令位置不对。R1初始应为#00100000B(PB0=0),若误设为#10000000B(PB7=0),则只扫最后一行。
解决:在KLOOP开头加MOV A, R1后MOV P1, A,用LED灯观察PB口电平变化;若PB0–PB3无依次变低,检查RR A是否在MOV R1, A之前执行。
5.3 现象:输入“12+34=”,结果“46”显示为“64”(十位个位颠倒)
原因:XS子程序中MOV LEDBUF+1, A与MOV LEDBUF+2, A地址写反,或LEDBUF缓冲区定义顺序与LED物理排列不一致。
解决:确认LEDBUF EQU 60H后,LEDBUF+0对应最左百位,LEDBUF+1对应中间十位,LEDBUF+2对应最右个位;若LED物理排布为“个十百千”,则需交换LEDBUF+0与LEDBUF+2的赋值顺序。
5.4 现象:连续按“9”键,显示从“9”→“99”→“999”后变为“231”,但按“=”无反应
原因:GN1段中CJNE R3, #0EH, GN2判断“=”键时,R3值被前面运算修改,或GN2跳转目标地址错误。
解决:在GN1开头加MOV A, R3后CJNE A, #0EH, GN2,避免R3被其他指令覆盖;检查GN2标签是否在GN1之后,且JMP @A+DPTR前DPH/DPL已正确加载DIZHI1/DIZHI2。
5.5 现象:上电后LED显示“0000”,但按键无响应,TESTKEY始终返回0
原因:键盘列线上拉电阻未焊接,或PC口读入时未加CPL A取反,导致ANL A, #0FH后恒为0。
解决:用万用表测PC0–PC3引脚,无按键时应为高电平(≈5V);若有按键,对应列线应为低电平(≈0V)。若全为低电平,检查上拉电阻是否虚焊;若全为高电平,检查CPL A指令是否被注释或删除。
6. 实战验证技巧:用万用表和逻辑分析仪定位硬件-软件耦合故障
6.1 分阶段验证法:把200行汇编拆成4个可测模块
不要一上来就烧录整套程序,按硬件信号流分四步验证:
- 8155通信验证:烧录最小测试程序,向7F00H写#03H,再读7F00H,用万用表测PA0–PA7是否全为高电平(输出模式);向7F02H写#3FH,测PA0–PA6是否为低电平(共阴极段码0)。
- LED动态扫描验证:屏蔽键盘相关代码,只留
DISPLAYLED,LEDBUF+0–+2固定赋值#3FH/#06H/#5BH(显示“012”),用示波器测PB0–PB3,应看到00001000B→00000100B→00000010B→00000001B循环。 - 键盘扫描验证:屏蔽显示,只运行
TESTKEY+GETKEY,用万用表测PC0–PC3,按不同键时应有唯一低电平组合;GETKEY返回值用MOV P1, A输出到P1口,用LED灯观察键码0–15是否正确。 - 运算逻辑验证:
DATA1/DATA2手动赋值,单步执行JIA/JIAN,用MOV P1, A输出结果,确认加减法正确性。
6.2 关键信号时序抓取:用逻辑分析仪看懂“为什么一闪而过”
DISPLAYLED中每位点亮时间极短,肉眼无法判断是否正常。用Saleae Logic 8抓取PB0(位控)和PA0(a段)信号:
- 正常波形:PB0低电平脉宽≈1ms,期间PA0为低电平(a段亮);PB0高电平时PA0为高电平(a段灭)。
- 异常波形:若PB0低电平期间PA0恒为高,则段控口未输出;若PB0高电平时PA0仍为低,则
MOVX @DPTR, A写#0失败,位控未及时关闭。
6.3 电源噪声排查:为什么按键时LED突然变暗?
用示波器AC耦合测VCC引脚,正常纹波<50mV;若按键瞬间出现>200mV尖峰,说明:
- 8155或LED驱动芯片电源滤波电容(100μF+0.1μF)未焊接;
- 键盘PCB走线过长,未加去耦电容。
解决:在8155的VCC引脚就近焊0.1μF陶瓷电容,在LED位控驱动芯片(如ULN2003)VCC脚焊100μF电解电容。
从那以后我每次焊完8155电路,必先测CE、IO/M、RD/WR引脚电平,再用万用表通断档查PCB走线是否连错;每次改DISPLAYLED延时参数,必用示波器抓波形,绝不凭感觉调R6。因为单片机世界里没有“差不多”,PA口输出3FH时a–g段必须全亮,PB口输出00001000B时只有COM1能导通——差一个bit,整个系统就停摆。希望帮到你。
本文还有配套的精品资源,点击获取