8051单片机实验报告全解析:GPIO、定时器、中断与串口通信实践指南
2026/9/19 19:41:49 网站建设 项目流程

简介:这份资源是北京工业大学8051单片机实验报告的PDF文档,适合正在学习单片机原理与汇编语言的本科生,以及需要参考实验报告撰写规范的读者。报告以C8051F020芯片为核心,完整记录了并行数据传送、中断控制、定时器/计数器操作等实验过程,重点涵盖LED循环点亮、数码管0-9显示、延时子程序设计等基础编程训练。内容对汇编指令(MOV、CPL、RR、RL、CALL、DJNZ)的功能和用法做了细致说明,并给出修改源程序前后的对比思路,便于理解单片机I/O口控制与循环逻辑。资源共1个文件,为PDF格式,包体大小673KB,已有84人学习。通过该报告可以快速掌握8051系列单片机从初始化设置到程序调试的完整流程,同时也能借鉴其实验报告目录结构、粗体突出修改部分等规范写法,适合作为课程设计或实验报告的参考模板。

1. 8051单片机实验报告拆开以后,到底是一张什么图

一份北工大8051单片机实验报告.pdf放在桌面上的时候,多数人第一反应是打开找代码、找答案。但真正正确的打开方式是把它当作一份“课程实验地图”:LED跑马灯、按键输入、数码管显示、定时器计数、外部中断、串口通信,这些实验名称看起来各自独立,内里却是一条递进链。前半程在解决“怎么控制一个引脚”,后半程在解决“怎么让单片机自己处理时序而不占CPU”,等到串口实验做完,整颗 8051 的常用外设就被你完整摸过一遍了。

这篇文章按照我平时带课程设计时的思路来写:先解释每个实验为什么要这样做、寄存器和引脚关系在哪,再给出可以直接抄进 Keil 的代码,最后补充参数计算和实验报告里数据该怎么呈现。顺序我会调整为“环境搭建→GPIO→定时中断与串口→调试和数据整理”,每一步的坑都会单独点出来。你不需要一本正经从头读到尾,直接跳到当前正在做的那个实验也没有问题。

2. 准备工作不能跳过的三层:引脚、时钟和下载链路

2.1 引脚图与最小系统:P 口怎么分工决定了代码怎么写

8051 的引脚图在网络上一搜能出来几百张,但实验里高频出现的其实只有一组关系:P0、P1、P2、P3 四个 8 位 IO 口,外加 P3 口的第二功能。P0 口在外部扩展存储器和总线访问时要接上拉电阻,单纯做 LED 控制和按键读取时建议直接串联限流电阻使用,避免电流过大烧引脚;P1 口是纯通用 IO,实验板上跑马灯最常用这一组;P2 口在没有外部扩展时就是普通 IO,但在使用片外 RAM 或访问大容量 Flash 时会变成高 8 位地址线,这条要在工程配置里提前选对。

P3 口是最容易出问题的一组。它的第二功能覆盖了串口收发(P3.0 RXD、P3.1 TXD)、外部中断 0/1(P3.2 INT0、P3.3 INT1)、定时器 0/1 外部计数输入(P3.4 T0、P3.5 T1),当你想要同时使用多个外设时,P3 口的引脚会互相挤占。一个典型的错误是:先拿 P3.2 接了按键,后面做外部中断实验时发现中断信号根本拉不进去,因为有按键电路在强制拉低电平。所以设计实验板原理图时,P3 口全部要预留跳线帽或 0 欧电阻断开的可能。

一个标准的最小系统只需要四样东西:5V 供电、晶振加两个 20~30pF 负载电容、10μF 电容加 10k 电阻组成的复位电路、EA 引脚接 VCC(从片内程序存储器取指)。晶振频率的选择要结合后面的串口实验一起定,如果计划用 9600bps 以上波特率,11.0592MHz 晶振是唯一不用特殊误差补偿的常见选择;只做定时和 LED 的话 12MHz 更省事,因为定时器计数周期刚好是 1μs,计算初值不用除小数点。

引脚组实验用途第二功能/注意事项
P0LED、数码管段选总线模式下需要上拉电阻
P1跑马灯、按键读取纯 IO,无第二功能
P2数码管位选、外扩地址高 8 位有外部扩展时不要当普通 IO 用
P3串口、外部中断、定时器输入第二功能优先级高,要复用必须处理冲突

2.2 Keil C51 与 IAR 6.3 的工程配置差异

北工大类 8051 课程的工程案例以 Keil 系列为主,近两年也还有同学用 IAR 6.3 for 8051 搭环境。两者在编译上最大的差异是:Keil C51 对 51 的 data/idata/xdata 内存模型封装得更直接,IAR 的代码密度和优化级别控制更细。对于课程实验,我更推荐 Keil C51 做日常编译、IAR 只用来做代码体积对比,理由很简单:课程资料里大部分现成代码是 Keil 风格,后续调试和答疑链条更短。

工程建立过程中,有三处配置必须手动改。第一是 Device 型号,如果选成 AT89C52 而板子上用的是 STC89C52RC,虽然寄存器一致,但 STC-ISP 下载时可能提示芯片 ID 不匹配;第二是 Target 选项卡里的晶振频率,填 11.0592MHz 不填 12MHz,否则延时函数的实际偏差会被放大;第三是 Output 选项卡勾选 Create HEX File,这个不勾,烧录器根本拿不到可写文件。另外,如果选用 STC 系列单片机,还需要在 STC-ISP 软件里设置硬件选项,最常见的是选择“12T 模式”还是“1T 模式”,这直接决定定时器计数速度和串口波特率误差,后面第 4 章会具体算。

注意:STC 芯片的下载时序本质是冷启动进 ISP 引导区。下载时点完“下载/编程”按钮后,必须给目标板断电再重新上电,否则会一直卡在“正在检测目标单片机”。这个动作是新手在环境搭建阶段卡住最多的地方。

2.3 一个最短可运行的蜂鸣器与 LED 联控模板

在开始复杂实验前,我习惯先做一个最简单的“上电 LED 闪三下”程序,用来验证下载链路和时钟是否正常。以下代码适用于 12MHz 晶振、任意 8051 兼容芯片,编译环境为 Keil C51。

#include <reg52.h> sbit LED = P1^0; sbit BEEP = P1^1; void delay_ms(unsigned int n) { unsigned int i, j; for (i = 0; i < n; i++) for (j = 0; j < 125; j++) ; } void main(void) { unsigned char k; for (k = 0; k < 3; k++) { LED = 0; BEEP = 0; delay_ms(200); LED = 1; BEEP = 1; delay_ms(200); } while (1) ; }

逻辑说明:reg52.h头文件里定义了 AT89C52 的 SFR 和位寻址地址,sbit LED = P1^0相当于给 P1.0 引脚起了别名,后面直接写LED = 0就能让引脚拉低。delay_ms里两层循环是纯软件延时,内层循环j < 125在当前编译默认优化等级下约等于 1ms,这个值在不同 Keil 版本里会有 8% 以内的偏差,但用来点灯判断下载链路是否正常足够。

参数说明:如果程序烧进去 LED 不闪,先量晶振引脚电压和复位脚电平,再看下载器有没有真正识别到芯片,最后考虑 PC 端 USB 转串口驱动是否异常。不要上来就怀疑代码,这个模板和任何外设冲突都没有关系。

3. GPIO 实验的进阶路径:跑马灯、按键消抖和数码管扫描

3.1 跑马灯实验:从一个引脚到八个引脚的时序控制

跑马灯是所有 8051 实验里第一个能产生“程序在跑”感觉的题目,也是学校实验报告里几乎不会缺席的一项。常见的实验要求是:8 个 LED 依次点亮并循环,周期为 1 秒,要求用左移指令实现而不能逐位赋值。这个要求的考察重点不是左移本身,而是 8051 在循环移位时如何处理最高位。

#include <reg52.h> void delay_ms(unsigned int n) { unsigned int i, j; for (i = 0; i < n; i++) for (j = 0; j < 125; j++) ; } void main(void) { unsigned char dat = 0xFE; while (1) { P1 = dat; delay_ms(500); dat = (dat << 1) | 0x01; if (dat == 0xFF) dat = 0xFE; } }

逻辑说明:0xFE的二进制是1111 1110,对应 P1 口只有最低位接地,其余引脚输出高点平,所以只有第 1 个 LED 亮。每次循环把dat左移一位并和0x01做或运算,作用是让新移进来的最低位补 1,保证除了当前位之外的其他 LED 全灭。当移位到dat变成0xFF时说明已经把 8 个 LED 全部走完一轮,重新赋初值0xFE

参数说明:延时 500ms 乘以 8 个 LED 正好 4 秒一个完整来回,这个时间在实验报告里写“循环周期 4 秒”,不是每个 LED 的亮灯周期。如果你想做成“单灯回扫”效果,可以在if分支里把方向标志位翻转,用两个方向的移位序列交替执行,这一点可以在报告的拓展部分补充,但主体代码保持左移循环即可。

3.2 按键消抖的正确做法:状态判断比单纯延时更可靠

按键实验是 8051 课程里第一道真正需要“考虑机械特性”的题目。按键按下和松开的瞬间,机械触点会产生 5ms 到 20ms 不等的抖动,如果不做消抖,一次按压可能被单片机识别成多次触发。常见的消抖方案有两种:一种是软件延时 20ms 后重新读取并确认,另一种是采样多次取稳定状态。课程实验里标准做法是前者,因为代码直观、运行逻辑容易解释,但这其中最关键的细节是读取时机。

#include <reg52.h> sbit KEY = P3^2; sbit LED = P1^0; void delay_ms(unsigned int n); void main(void) { unsigned char key_state = 0; unsigned char key_last = 0; while (1) { key_state = KEY; if (key_state == 0 && key_last == 1) { delay_ms(20); if (KEY == 0) { LED = !LED; } } key_last = key_state; } }

逻辑说明:key_last保存上一次读取的按键电平,只有检测到“电平从高到低的下降沿”时才进入消抖确认流程。按下后 20ms 再读一次,如果读到的还是低电平,说明按键确实按下了而不是抖动。松开瞬间检测到上升沿,key_last恢复为 1,下一次按下才能再次触发。这段代码的精妙之处在于“边沿触发”而不是“电平触发”,它保证按住按键不放时 LED 只翻转一次,而不会像直接写if (KEY == 0)那样在按住低速连闪。

参数说明:20ms 是按键消抖的通用下限,如果用的按键手感较硬或有焊锡残留,可以加到 30ms 到 50ms,但注意不要超过按键的物理按下时间,否则按正常速度操作时会丢失按键。实验报告里建议记录一组对比数据:无消抖时按下一次识别出几次脉冲、加 20ms 消抖后识别出几次,这个数据要写进“实验结果与分析”部分。

3.3 数码管动态扫描:段选与位选到底怎么分

数码管实验是 GPIO 实验阶段最难的一项,它要求同时使用两组 IO:一组驱动数码管的 7 段笔画和公共端(段选),另一组驱动多个数码管的公共端或位选端。动态扫描的核心思想是人眼视觉暂留:以 1ms 到 2ms 的间隔轮流点亮每个数码管,让它们在 100Hz 以上的刷新频率下看起来像同时显示。

#include <reg52.h> unsigned char code table[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07}; sbit LSA = P2^2; sbit LSB = P2^3; sbit LSC = P2^4; void delay_ms(unsigned int n); void main(void) { unsigned char i; while (1) { for (i = 0; i < 8; i++) { switch (i) { case 0: LSC = 0; LSB = 0; LSA = 0; break; case 1: LSC = 0; LSB = 0; LSA = 1; break; case 2: LSC = 0; LSB = 1; LSA = 0; break; case 3: LSC = 0; LSB = 1; LSA = 1; break; case 4: LSC = 1; LSB = 0; LSA = 0; break; case 5: LSC = 1; LSB = 0; LSA = 1; break; case 6: LSC = 1; LSB = 1; LSA = 0; break; case 7: LSC = 1; LSB = 1; LSA = 1; break; } P0 = table[i]; delay_ms(1); P0 = 0x00; } } }

逻辑说明:table[]是共阴数码管 0~7 段码的十六进制字模表,0x3F对应数码管显示“0”。LSA/LSB/LSC三根线控制 74HC138 译码器的输入,用来选择当前点亮哪个位,3 根线可以扩展控制 8 个数码管。P0 = table[i]把当前位的段码送到 IO,延时 1ms 让数码管稳定显示,然后P0 = 0x00消隐,防止屏幕上出现位与位之间的串影。

提示:不少学生学习此实验时觉得“只亮了一个数码管”,这是因为刷新频率不够高。延时 1ms 换 8 个管子正好 8ms 一轮,刷新率 125Hz,眼睛已经看不到闪烁。但如果你用 10ms 延时,刷新率降到 12.5Hz,肉眼就会明显感到闪烁,这个现象写在报告里作为“动态扫描刷新率对显示效果的影响”是加分项。

4. 定时器、中断与串口实验:把时间交给硬件去数

4.1 定时器计数初值的计算:11.0592MHz 和 12MHz 差在哪里

多数 8051 课程实验在定时器部分的题目是:“用定时器 0 实现 LED 灯精确延时 50ms 翻转”。这里的“精确”意味着不能再用delay_ms的软件循环,而要用定时器硬件计数,计数溢出时触发一次中断或在查询标志位后翻转 LED。定时器 0 的计数周期由晶振频率和分频模式共同决定。

以 STC89C52 为例,默认工作在 12T 模式下,即 12 个时钟周期计一个数。如果晶振是 12MHz,那么定时器每计一个数耗时 1μs,要定时 50ms 就需要计数 50000 次,所以定时器的初值应为 65536 减去 50000,即 15536,换算成十六进制是0x3CB0。如果晶振换成 11.0592MHz,每计数一次是 12 除以 11.0592MHz,约等于 1.085μs,50ms 需要 46080 次计数,初值为 19456,即0x4C00

晶振频率12T 模式计数周期定时 50ms 需要的计数值初值
12MHz1μs500000x3CB0
11.0592MHz1.085μs460800x4C00
6MHz2μs250000x9E58
#include <reg52.h> sbit LED = P1^0; void timer0_init(void) { TMOD = 0x01; TH0 = 0x4C; TL0 = 0x00; ET0 = 1; EA = 1; TR0 = 1; } void main(void) { timer0_init(); while (1) ; } void timer0_isr(void) interrupt 1 { TH0 = 0x4C; TL0 = 0x00; LED = !LED; }

逻辑说明:TMOD = 0x01把定时器 0 设为 16 位定时模式,TH0/TL0构成一个 16 位计数器,计数溢出后硬件自动把 TR0 的定时中断标志位 TF0 置 1,并向 CPU 发出中断请求。中断服务函数timer0_isr开头必须重新装载初值,因为 16 位定时器溢出后就从 0 开始继续计数,不重装初值的话下一次定时时间会变成 65536 个计数周期,灯的翻转节奏会突然拉长一倍。

参数说明:ET0 = 1使能定时器 0 中断,EA = 1打开总中断开关,TR0 = 1让定时器启动。三个位少一个都不会工作,而且顺序不要调反,虽然实际写在一起也能用,但规范顺序是先关全局中断再配置局部中断,最后启动定时器,这样能避免配置一半时产生意外中断。

4.2 外部中断的触发方式选择与中断优先级

外部中断实验通常要求用 P3.2 引脚上的按键触发中断,控制 LED 状态切换。这里比普通按键实验多出一个关键点:中断触发方式有下降沿触发和低电平触发两种,由 TCON 寄存器中的 IT0/IT1 位决定。下降沿触发适合按键这种短促信号,低电平触发则适合外部低电平持续有效的系统信号。

#include <reg52.h> sbit LED = P1^0; void main(void) { IT0 = 1; EX0 = 1; EA = 1; while (1) ; } void int0_isr(void) interrupt 0 { LED = !LED; }

逻辑说明:IT0 = 1把外部中断 0 设为下降沿触发,EX0 = 1允许外部中断 0 请求,EA = 1开总中断。中断服务函数里简单翻转 LED 即可,不需要再做按键消抖,因为在下降沿触发模式下,硬件只会在引脚电平从高变低的瞬间产生一次请求,抖动可能导致多次触发,但概率远低于电平触发方式,减轻了软件负担。

参数说明:8051 只有两级中断优先级,由 IP 寄存器控制。如果外部中断 0 和定时器 0 同时发生请求,系统按自然优先级响应,定时器 0 的优先级高于外部中断 0。如果你希望外部中断不管什么时候都能立即响应,需要写PX0 = 1把它设为高优先级。这个设置在实际工程中的意义是:让紧急外部事件能抢占耗时较长的定时任务。

4.3 串口通信的波特率配置:为什么必须用定时器 1 方式 2

串口实验是整份实验报告里最有含金量的部分,也是最容易出现“硬件正常代码也对但收发乱码”的部分。8051 的串口工作在模式 1 时,波特率由定时器 1 的溢出率决定,计算公式为波特率等于定时器 1 溢出率除以 32 再乘以 2 的 SMOD 次方。如果一个系统晶振选择了 11.0592MHz,那么把定时器 1 配置为方式 2(8 位自动重装),TH1 取 0xFD,SMOD 取 0,就能得到标准 9600bps。

#include <reg52.h> unsigned char buf; void uart_init(void) { TMOD = 0x20; TH1 = 0xFD; TL1 = 0xFD; TR1 = 1; SM0 = 0; SM1 = 1; REN = 1; EA = 1; ES = 1; } void main(void) { uart_init(); while (1) ; } void uart_isr(void) interrupt 4 { if (RI) { RI = 0; buf = SBUF; SBUF = buf; while (!TI) ; TI = 0; } }

逻辑说明:TMOD = 0x20把定时器 1 配置为方式 2,也就是 8 位自动重装模式,这个模式下 TH1 每次溢出后自动把初值装回 TL1,不需要在中断里手动重装,波特率稳定性远高于方式 1。TH1 = 0xFD的初值来自波特率计算:256 减去 11.0592MHz 除以 12 再除以 32 再除以 9600,结果是 253,也就是 0xFD。SM0 = 0; SM1 = 1把串口设为模式 1,即 10 位异步收发,8 位数据位加 1 位起始位和 1 位停止位。REN = 1打开接收允许开关,ES = 1打开串口中断。

逻辑说明补充:中断服务函数里先判断RI接收中断标志位,置 1 说明收到一个字节,读SBUF就是读出数据,然后SBUF = buf把收到的数据原路发回,实现回显功能。while (!TI)等待发送完成是因为 SBUF 写入后要经过一个完整的移位过程才能发完,TI 置 1 表示发送结束,这时才能写下一个字节。TI = 0是清标志,因为中断标志如果不手动清零,会反复进入中断。

参数说明:如果用 12MHz 晶振跑 9600 波特率,TH1 初值取 0xFD 时实际波特率为 7812.5bps,和 9600 相差约 18%,收发两端必然乱码。所以串口实验对晶振频率的讲究比定时器实验更严格,报告里可以加一行“晶振频率与波特率误差关系”的测试数据,这是拿高分的关键点。

5. 实验数据验证与报告整理的三个实用手段

5.1 把实验现象变成可信的波形和图

实验报告如果只写“LED 正常闪烁”,审阅人既无法判断延时是否准确,也无法判断你真正跑过设备。最常见的做法是用示波器或逻辑分析仪抓波形。LED 翻转时在引脚上量到的是一个方波,方波的周期就是你定时的两倍,如果代码写 50ms 翻转一次,示波器上看到的应该是 100ms 周期。串口实验则抓 TXD 引脚上的波形,波特率 9600 时每位宽度约 104μs,起始位是低电平,可以数出一帧电平变化的次数来判断数据是否正确。

逻辑分析仪是比示波器更适合课程实验的设备,它有多通道并行采集能力,可以同时抓 P3.0 和 P3.1 的收发波形,并用软件自动解析位流为十六进制数。报告中直接给出解析后的收发数据帧对比表,比贴代码可靠得多。

5.2 用数据表代替大段文字描述实验效果

单片机课程实验报告最忌讳重复实验原理,原理书里全都有,老师想看的是你的实验数据和异常分析。整理数据表时,建议按“实验项目、测试条件、理论值、实测值、误差、说明”六列排布。

实验项目测试条件理论值实测值误差说明
软件延时12MHz, 100ms100ms108ms8%与 Keil 优化等级有关
定时器 012MHz, 50ms 初值 0x3CB050ms50.02ms0.04%中断重装造成微小误差
串口发送11.0592MHz, 96009600bps9600bps0%晶振频率精确时无累计误差
按键消抖20ms 确认识别 1 次识别 1 次0%抖动被滤除

最后提醒一个操作性技巧:实验报告里的代码块不要直接截 Keil 的蓝色背景截图,Word 里用等宽字体粘贴纯文本代码,然后把关键代码段单独加高亮,这样做搜索时可以搜到、打印时也清晰。每个实验配一至两张实物接线照片和一张波形截图,这份报告就会显得“可审计”“可复现”,也方便后续复盘时快速定位是硬件问题还是时序问题。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询