news 2026/9/19 3:53:41

8051单片机外设实验详解:并行口、中断、定时器与串口通信实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
8051单片机外设实验详解:并行口、中断、定时器与串口通信实践

简介:8051单片机实验报告来自北京工业大学,面向正在学习单片机原理、汇编语言及嵌入式系统基础的本科生与初学者,可作为课程实验的参考资料。报告以C8051F020单片机为载体,围绕并行I/O口操作、LED走马灯循环移位、数码管0-9显示、中断控制与定时器/计数器应用展开,详细展示了多处修改后的汇编源程序,并逐段解释MOVA、CPL、RRA、RLA、CALL、DJNZ等指令在取反、移位、延时和循环显示中的作用;同时梳理了初始设备设置、数据传送到P3/P5端口、延时子程序循环结构等关键实现细节。实验总结部分还记录了程序修改思路与调试心得,帮助读者快速定位实验报告撰写要点。资源为PDF格式,共1个文件,压缩包大小673KB,内容结构完整,修改部分以粗体标注,便于对照研读。目前已有84人学习下载,适合需要撰写同类实验报告、理解8051汇编编程或复习单片机实验内容的同学参考。

1. 北工大 8051 单片机实验报告:一份把外设讲透的汇编修改记录

这份北京工业大学 8051 单片机实验报告,表面上是三个汇编实验的修改记录,实际上把 C8051F020 这款高速 SoC 型单片机最常用的四块外设——并行口、外部中断、定时器/计数器、串口——完整走了一遍。报告里没有长篇原理,全是改代码的思路:走马灯初值从 07FH 改成 80H 为什么还要补一句 CPL A,0-F 循环改成 0-9 时比较值为什么是 0AH,串口收三个字节为什么要用 R6/R5 搭状态机。对正在用 51 单片机做课设、准备蓝桥杯单片机方向,或者刚在 Proteus 里跑通仿真、想对比真实 C8051F020 实验板差异的人来说,这些改法比单纯看书上的指令表有用得多。下文按实验单元拆开讲,重点放在参数为什么要这样设、改的时候容易漏哪里。

2. 并行口 LED 走马灯:A 寄存器初值、CPL 取反与 RRA/RLA 的循环方向

2.1 初值 80H 与“点亮情况相同”背后的取反逻辑

第一问要求把 A 寄存器初值从 07FH 改为 80H,并保证程序运行后 LED 点亮情况与修改前相同。先看两个数的位模式:

数值bit7bit6bit5bit4bit3bit2bit1bit0P3 输出电平(正逻辑)
07FH01111111除 P3.7 外全高
80H10000000仅 P3.7 为高
CPL A 后01111111与 07FH 完全相同

正逻辑下数据位为 1 表示 LED 点亮,07FH 是“只有最高位灭”,80H 是“只有最高位亮”,两者恰好按位相反,所以修改后的代码在传送前补了一句:

MOV A, #080H ; 赋初始值 CPL A ; 按位取反,80H -> 07FH MOV P3, A

CPL A 是 51 汇编里最直接的按位取反指令,对 A 的 8 个位逐位翻转,一条指令解决初值语义不一致的问题。这里值得记住的不是指令本身,而是改初值时的习惯:先画出目标点灯效果对应的位模式,再决定是直接送还是取反后送。实际工程里 LED 驱动经常是低电平点亮,同样的位模式要配合硬件极性反着写,实验里虽然只用正逻辑,但多一步 CPL 的思路是通用的。

2.2 RRA 与 RLA:走马灯左移和右移的本质

第二问把左循环移位改成右循环移位,代码改动只有一行:RLA换成RRA。这两条指令都是带进位标志 CY 的循环移位,区别在于移动方向:

RRA ; A 内容带进位右移 1 位,bit0 移入 CY,CY 原值移入 bit7 RLA ; A 内容带进位左移 1 位,bit7 移入 CY,CY 原值移入 bit0

之所以用带进位的 RRA/RLA 而不是直接移动,是因为走马灯要求“循环”点亮——亮点从最右端移出后要能从最左端重新出现。如果用普通的 RR A / RL A 不带进位,移位后最高位补的是 0,亮点会丢失,点灯效果就不循环了。判断循环方向时也不用死记指令方向,直接看实验板 LED 的物理排列:如果 LED0 接在 P3.0 且最靠左,RLA 让点亮位置从低地址往高地址走,视觉上就是左移。

2.3 加快循环速度:并不是一定要改延时子程序

第三问要求加快 LED 循环移位速度,报告里的改法是直接删除 LOOP 循环里的几条CALL DELAY。原程序为了让一次移位停顿足够明显,在每次 RRA 前连续调用了 7 次延时子程序,DELAY 本身是双循环:

DELAY: MOV R5, #0H ; 外层循环计数,0 实际执行 255 次 D1: MOV R6, #0H ; 内层循环计数 DJNZ R6, $ ; R6 减 1 非 0 则原地跳转 DJNZ R5, D1 RET

DJNZ 是“先减 1,再判 0”的指令,初值 0 会从 255 一直递减到 0,所以内外层各执行 255 次,总循环次数约 255 × 255,再乘上 DJNZ 的机器周期就是单次 DELAY 的时间。报告里删 CALL DELAY 的做法直观,但不适合工程复用——正常做法是直接改 R5、R6 的初值,把延时时间当作参数调。实验报告选删除调用是因为它的延时粒度比较粗,DELAY 一次的时间远大于视觉暂留需要的刷新间隔,删掉两三次调用正好把走马灯速度调到肉眼舒适的区间。这里想强调的排错点是:删除 CALL 行时不要顺手删掉 RRA,否则程序会卡在死循环里只灭不亮,最容易出现在复制粘贴改代码的时候。

2.4 0-9 循环显示:CJNE A,#0AH 的边界比较

第一单元第二题把数码管 0-F 循环显示改成 0-9,关键代码是循环末尾的范围判断:

INCR1 ; 操作数据 R1 增 1 MOV A, R1 CJNE A, #0AH, MAIN ; R1 不等于 10 则继续显示 SJMP INIT ; R1 等于 10 则回到初始值重新循环

CJNE A, #0AH 是比较跳转指令:累加器 A 与立即数 0AH 比较,相等时顺序执行下一条,不相等时跳转到 MAIN。原来显示 0-F 时判断值是 10H,因为 0FH 加 1 进位后是 10H,正好作为“超过显示范围”的边界;改成 0-9 后边界变成 0AH,也就是十进制的 10。这里最常见的错误是写成CJNE A, #09H,以为“显示到 9 就该停”,但 R1 加 1 后先判断再显示,当前值已经是 0AH 了,用 09H 判断会导致 9 被重复显示一次。边界值应该等于“最大有效值加 1”,这是所有循环计数判断的统一规则。

3. 外部中断与定时器/计数器:按键功能对调、方向反转与 00-FF 计数

3.1 INT0/INT1 向量入口与按键功能对调的实施点

第二单元第一题把 KEY2、KEY4 功能对调,本质上不是换按键接线,而是交换两个外部中断的中断职责。C8051F020 的中断系统与标准 8051 兼容,INT0 向量在 0003H,INT1 向量在 0013H:

ORG 0003H LJMP STAR ; INT0 服务程序,启动键 ORG 0013H LJMP STOP ; INT1 服务程序,停止键

功能对调要改的不只这两行跳转,中断使能位、中断标志位、优先级必须同步换。报告里两版代码分别用了MOV IP,#04HMOV IP,#01H,IP 寄存器 bit0 控制 INT0 优先级、bit2 控制 INT1 优先级,对调功能后优先级设置也要跟着换,否则会出现“启动键优先级高于停止键”这种奇怪的交互。建议把中断相关的修改列成一张对照表逐项核对:

配置项修改前修改后
0003H 向量STAR(启动)STOP(停止)
0013H 向量STOP(停止)STAR(启动)
IP 优先级位INT1 高优先级INT0 高优先级
中断标志清除停时清 IE1停时清 IE0

TCON 里设置为#05H,即 IT0=1、IT1=1,两个外部中断都工作在边沿触发模式。实验板的按键一般自带 RC 滤波,边沿触发直接可用;如果自己搭按键电路就要注意,机械抖动在边沿模式下会产生重复中断,需要加消抖或软件去抖延时。

3.2 R4 方向标志:中断服务程序里 CPL 翻转循环方向

第二问“每次停下再启动以后,更改 LED 循环方向”,报告用一个 R4 寄存器存方向标志:0F0H 代表向左循环,0FH 代表向右循环,两个值按位互补。STAR 中断服务程序里翻转方向:

STAR: MOV A, #00H PUSH ACC ; 保护现场 MOV A, R4 CPL A ; 0F0H 按位取反得到 0FH,方向翻转 MOV R4, A POP ACC CLR IE0 ; 清除 INT0 中断标志 RETI

主循环里用两条 CJNE 判断方向,注意它的流程是“相等则顺序执行,不相等则跳转”:

LOOP: CJNE R4, #0F0H, LOOP1 ; R4=0F0H 时顺序执行左移分支 RL A MOV P3, A ... ; 延时、计数判断 LOOP1: CJNE R4, #0FH, LOOP ; R4=0FH 时顺序执行右移分支 RR A MOV P3, A

CPL A 在这里不只是取反,它保证了方向标志在两个合法值之间精确互转,不存在中间状态。很多同学会写INC R4再判断奇偶来翻转,在小程序里没问题,但在这份代码里,CPL 方式更省 ROM,而且不需要额外的清零逻辑。边界点在于 R4 的初值必须提前设置为 0F0H 或 0FH,否则启动后第一次 CJNE 判断会落到错误的循环分支。

3.3 T/C0 方式 2 当计数器用:读 TL0 而不是等溢出

第二题要求把计数范围从 0-F 扩展到 00-FF,用定时器/计数器 0,并在两位数码管上以十进制显示 00-99。报告里的配置方式值得单独拆开看:

MOV TMOD, #00000110B ; T/C0 工作于方式 2,8 位自动重装 MOV TL0, #0 ; 计数初值 0 SETB TR0 ; 启动 T/C0 LOOP: MOV A, TL0 ; 主循环直接读当前计数值 CJNE A, #10H, L1 ; 计数值不等于 16 则正常显示 MOV TL0, #0 ; 计数值等于 16,清零重新计数 MOV A, TL0 L1: MOV B, #0AH DIV AB ; 十六进制转十进制,A=商,B=余数 MOV DP_L, B ; 余数作为个位保存 MOV DP_H, A ; 商作为十位保存 CALL DISPLAY SJMP LOOP

TMOD 的低 4 位配置 T/C0:00000110B中 M1M0=10 对应方式 2(8 位自动重装),C/T=0 表示定时器模式。这里没用传统的“溢出中断 + 中断服务程序”套路,而是主循环不停读 TL0 的值,把定时器当作一个自由运行的脉冲计数器。CJNE A,#10H 意味着每计到 16 个脉冲就清零重计,所以数码管实际显示范围是 0-15;如果要把十进制显示扩展到 99,只需把这个比较值改成 63H(99 的十六进制)。方式 2 下 TH0 通常存放自动重装值,但这份程序不依赖溢出,TL0 的读值就是当前计数,省掉了中断现场的保存和恢复。

3.4 DIV AB 把十六进制拆成十位与个位的原理

从十六进制计数值得到十进制显示的十位和个位,报告用的方法是指南里特别提示的 DIV 指令:

MOV B, #0AH DIV AB ; A 除以 B:商存 A,余数存 B

DIV AB 是 51 指令集里唯一的 8 位除法指令,执行后 A 得到商、B 得到余数。任何一个 0-255 的十六进制数,除以 10 的商必然对应十位,余数对应个位。例如 TL0 读到 63H(十进制 99),A=63H, B=0AH除法后A=09H, B=09H,正好拆出十位 9 和个位 9。这个套路比查表省 ROM,也比连续减法快,是 51 上做 BCD 转换的经典手段。容易踩的坑是 DIV 执行前忘记给 B 赋 0AH,B 里残留上次运算的数,除数和商就全乱;以及结果显示顺序,DIV 后先保存 B 再保存 A,因为 DISPLAY 子程序里会再次调用 DECODE 查表,两个暂存单元 DP_L、DP_H 如果不先存好,后面的调用会把 A、B 冲掉。

4. 串口方式 1 收发:SBUF0、状态机过滤与显示位置交换

4.1 SCON0 与 T/C1:方式 1 的波特率发生链路

第三单元用的是 C8051F020 的 UART0,配置语句集中在主程序开头:

MOV TMOD, #00100000B ; T/C1 工作于方式 2,8 位自动重装 MOV TH1, #TIM_H ; T/C1 重装值高字节 MOV TL1, #TIM_L ; T/C1 计数初值 SETB TR1 ; 启动 T/C1,为串口提供波特率 MOV SCON0, #01010000B ; 串口方式 1,8 位 UART,允许接收 SETB ES ; 串口中断使能 SETB EA ; 总中断使能

SCON0 的 bit6 和 bit5 组合决定串口工作方式,01010000B即 SM0=0、SM1=1,对应方式 1——8 位 UART,一帧数据含 1 位起始位、8 位数据位、1 位停止位。bit4 的 REN=1 表示允许接收,因为需要把主机发来的数据收回来显示。方式 1 的波特率必须由 T/C1 溢出率提供,所以主程序同时配置 T/C1 为方式 2(8 位自动重装),TH1 和 TL1 都预装成 TIM_H/TIM_L。这里要记住一个对应关系:串口发送和接收的节奏由 T/C1 的溢出频率决定,改波特率时先换 TH1,不要改 SCON0 的收发位;调完 TH1 后 TL1 必须同步更新,否则第一次发送用的还是旧计数值。

4.2 发送侧:ADDA,#40H 把键值映射到 ASCII 大写字母

发送任务要求把原来的 1、2、3、4 的 ASCII 码改成 A、B、C、D 的 ASCII 码。报告在查询按键状态拿到键值后,用加法完成 ASCII 偏移:

MOV R7, A ; 键值暂存到 R7 MOV A, #043H ; 发送字符 'C' 的 ASCII 码 MOV SBUF0, A CALL DELAY MOV A, #04FH ; 发送字符 'O' MOV SBUF0, A CALL DELAY MOV A, #04DH ; 发送字符 'M' MOV SBUF0, A CALL DELAY MOV A, #04BH ; 发送字符 'K' MOV SBUF0, A CALL DELAY MOV A, R7 ADD A, #40H ; 键值 1~4 加上 40H 得到 41H~44H,即 'A'~'D' MOV SBUF0, A

这里用到的规律是 ASCII 码表里大写字母与数字键值的偏移关系:键值 1 对应 41H,2 对应 42H,依次类推,所以统一加 40H 就能完成映射。如果用查表法要单独建一张 4 字节的表,加法映射一条指令搞定,缺点是键值不连续或超出范围时会出现乱码。要改回发送数字时,把ADD A,#40H换成ADD A,#30H即可,因为 '0' 的 ASCII 码是 30H。注意每次MOV SBUF0, A后程序用延时等待发送完成,没有检查 TI 标志;在高速波特率下延时可能不够,严谨的做法是在循环里查询 TI。实验报告取延时方案,胜在逻辑简单,调试时不会因为忘记清 TI 卡死。

4.3 接收侧:R6/R5 组成的三字节状态机

接收中断服务程序是这份报告里信息量最大的部分,它用 R6 作为通信状态寄存器,逐字节过滤帧头,再用 R5 做数据计数:

RECE: JNB TI, REC1 ; TI=1 是发送中断,清标志后返回 CLR TI RETI REC1: PUSH ACC MOV A, SBUF0 ; 读接收缓冲器 CJNE R6, #0, REC2 ; 状态 0:等待帧头 'C' CJNE A, #43H, REC6 MOV R6, #1 ; 收到 'C',进入状态 1 SJMP REC6 REC2: CJNE R6, #1, REC3 ; 状态 1:等待帧头 'O' CJNE A, #4FH, REC5 MOV R6, #2 SJMP REC6 REC3: CJNE R6, #2, REC4 ; 状态 2:等待帧头 'M' CJNE A, #4DH, REC5 MOV R6, #3 ; 帧头匹配完成,准备收数据 MOV R5, #3 ; 数据计数器置 3 SJMP REC6 REC4: CJNE R6, #3, REC6 ; 状态 3:接收 3 字节有效数据 PUSH ACC MOV A, #REC_BUF0 ADD A, R5 ; 生成当前字节的缓冲地址 MOV R0, A POP ACC MOV @R0, A ; 保存接收数据 DEC R5 CJNE R5, #0, REC6 CALL DISPLAY ; 收满 3 字节,刷新数码管 MOV R6, #0 ; 状态复位,回到等待帧头 REC6: CLR RI POP ACC RETI

状态机的核心思想是:串口是字节流,没有天然报文边界,必须靠帧头字符来同步。这里约定主机先发 C、O、M 三个字符,紧接着发 3 字节有效数据。R6 等于 0、1、2 时分别等待三个帧头,任一字节不匹配就通过 REC5 路径回到无效状态,不会把噪声数据误当成有效帧;R6 等于 3 时进入数据接收阶段,用 R5 从 3 递减到 1 控制存入 REC_BUF0、REC_BUF1、REC_BUF2 三个缓冲区。溢出检查是必须有的:如果主机只发了 2 字节数据就停,R5 不为 0,状态机永远卡在状态 3,后续帧头会被当成数据吞掉。实际使用中建议加一个超时复位,比如主循环里周期性检查 R5 是否长时间不为 0。

4.4 字节显示位置交换:SWAP A 与 ANL #0FH 的位拆分

接收显示要求把第 2、第 3 字节数据的显示位置交换。每个字节在数码管上要拆成高 4 位和低 4 位,分别在两个数码管显示,DISPLAY 子程序用 SWAP 和 ANL 完成拆分:

DISPLAY: MOV A, REC_BUF1 SWAP A ; 高 4 位与低 4 位互换 ANL A, #0FH ; 屏蔽高 4 位,取出原高 4 位数值 CALL DECODE ; 查段码表 MOV DPTR, #NUM4 ; 交换后:第 2 字节高 4 位显示在 NUM4 MOVX @DPTR, A MOV A, REC_BUF1 ANL A, #0FH ; 取出低 4 位 CALL DECODE MOV DPTR, #NUM5 ; 交换后:第 2 字节低 4 位显示在 NUM5 MOVX @DPTR, A MOV A, REC_BUF2 SWAP A ANL A, #0FH CALL DECODE MOV DPTR, #NUM2 ; 交换后:第 3 字节高 4 位显示在 NUM2 MOVX @DPTR, A MOV A, REC_BUF2 ANL A, #0FH CALL DECODE MOV DPTR, #NUM3 ; 交换后:第 3 字节低 4 位显示在 NUM3 MOVX @DPTR, A RET

SWAP A 一次就把累加器的高低半字节交换,ANL A,#0FH 再屏蔽掉高位,两次操作配合正好取出一个字节的高 4 位;取低 4 位只需要 ANL 一次。这个组合比循环移位拆位快得多,也是所有 LED 数码管显示程序里最常用的技巧。交换位置前,第 2 字节显示在 NUM2/NUM3,第 3 字节显示在 NUM4/NUM5;交换后交叉映射,但拆分算法完全不用动,只改 DPTR 指向的显示地址。这里需要留意 C8051F020 的数码管接口是挂在外部数据存储器空间的 MOVX 访问方式,地址 7F80H、7F90H、7FA0H、7FB0H 分别对应实验板上编号从低到高的数码管锁存器,而不是 P0-P3 端口。

5. 共阳极段码表的复用验证:从 Proteus 到实验板的三个核对点

拿到段码表DB 0C0H,0F9H,0A4H,0B0H,099H,092H,082H,0F8H,080H,090H,088H,083H,0C6H,0A1H,086H,08EH时,第一件事是判断数码管的公共端极性。0C0H 的二进制是 1100 0000,低 6 位全为 0,如果 0 代表点亮,那么 a-f 段亮、g 段和 dp 灭,正好是数字 0;0F9H 的二进制是 1111 1001,只有 b、c 两段为 0,对应数字 1。由此可以断定实验板用的是共阳极数码管,低电平点亮。换到共阴极板子时,这段数据要全部按位取反,或者直接在代码里对查表结果 CPL 一次。

在 Proteus 里做仿真验证时,C8051F020 的模型支持 P0-P3 端口,但报告里出现的 P5 和 7F80H 地址是实验板级的译码映射,纯芯片上不存在这组端口,仿真时要用 AT89C52 加外置锁存器复现,或者直接跳过数码管部分只看串口收发的逻辑。三个核对点建议按顺序做:第一,段码表第一位 0C0H,确认共阳极才能往下查;第二,TMOD 的值同时影响两个定时器,#00000110B只改了 T/C0,确认低 4 位而非整体;第三,串口帧的 C、O、M 三个帧头字符必须与发送端完全一致,包括大小写,否则接收状态机永远停在 REC5 的错误恢复路径上。按键扫描里RR A连续执行 4 次,是把 P2 端口的高位按键位移动到 CY 标志位再判断;如果实验板改了按键接线,这个移位次数必须跟着改,否则按 KEY1 会识别成 KEY4。最后验证 DELAY 延时是否满足串口发送节奏时,可以数一下 DELAY 的双循环次数:R5、R6 初值都为 0,各执行 255 次,总循环约 65025 次,在默认系统时钟下级数约几毫秒,发送 6 字节前分别调用一次,刚好保证前一字节移出 SBUF0 后下一字节再写入。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 3:48:00

Spring Boot+Spark气象数据分析毕设全流程实战指南

我最近在毕设资源库里翻项目,发现各种“Spring Boot Spark”的组合标题特别多,比如“基于Spark的西南天气数据的分析与应用”,几乎每隔几页就能看到一次。这个现象背后其实有很实际的原因:Spring Boot负责业务接口,Sp…

作者头像 李华
网站建设 2026/9/19 3:47:58

底座 WeKnora 管知识,TaoToken 替 ReAct Agent 管 Token

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 3:47:41

技术选型实战:从约束倒推架构,Electron+Agent案例复盘

1. 从业务约束倒推技术栈:没有最好的技术,只有最匹配的组合做技术选型最怕什么?不是候选方案不够多,而是评审会上所有人都在聊“这个框架性能好”“那个中间件社区火”,却没人先说清楚:我们的业务到底要解决…

作者头像 李华
网站建设 2026/9/19 3:47:05

搜索性能评估四锚点:数据规模、查询特征、SLA与运维水位

1. 为什么“比ES快5倍”这个说法本身就需要被拆解“推荐一个比ES快5倍的搜索引擎”——这句话在技术社区里像一枚扔进水塘的石子,涟漪一圈圈扩散,但很少有人蹲下来捞起那块石头,看看它到底是什么质地。我从2014年开始做搜索相关系统&#xff…

作者头像 李华