简介:围绕Intel 8255A可编程并行接口芯片整理的基础实验报告,面向学习微机接口与汇编语言程序设计的学生,服务于方式0下I/O扩展、端口编程和实验报告撰写等场景。文档以LED静态显示、开关数显示和七段数码管显示三个任务为主线,串联芯片内部结构、A/B/C端口分组、控制字设置及硬件连线思路,并给出程序流程图与带详细注释的汇编源码,便于对照GX-8000实验系统复现实验、理解输入输出过程。压缩包仅含1个docx文件,大小约141KB,排版紧凑,适合直接查阅与二次整理。相关资源已有809人学习,内容还记录实验现象、结论及问题分析,可供课程实验参考、报告框架借鉴和调试排错时比对,帮助读者从原理、连线到程序实现形成较完整的认识。
1. 一块板子通电全亮之后,微机接口 8255A 实验报告该从哪写起
实验台上那块板子一上电,LED 整排全亮,拨码开关拨来拨去没反应,微机接口 8255A 实验报告的第三页就卡在这儿——数据填不出来,因为压根没读到数。这个场面在接口课里太常见:8255A 的线不难接,难的是你没法判断问题出在地址、控制字还是握手时序上,于是只能一遍遍重烧程序,最后抄一份别人的现象。
这篇按一线调试的顺序讲:先把方式选择字、位控字、A1A0 寻址这三件事定死,再落到基地址确认、初始化代码、拨码开关与 LED 的第一次闭环,然后做流水灯、4×4 矩阵键盘、双机并行传送三个实验项,最后给五条踩坑记录和一套让报告数据站得住的验证方法。适合正在做微机接口实验、要把现象写成结论的人,也适合带实验、需要快速定位板子问题的人。
2. 控制字先行:8255A 三个端口的方向、寻址和工作方式怎么定下来
2.1 方式选择字与 C 口位控字:D7 写错一位,整块板子都在装死
8255A 的控制口(A1A0 = 11)收两种字节,靠最高位 D7 区分。D7 = 1 是方式选择字,决定 A 组(A 口 + C 口高四位)、B 组(B 口 + C 口低四位)走哪种方式、每个端口是进还是出;D7 = 0 是 C 口位控字,只对 C 口某一位做置位或复位,不改方向。
| 位 | 方式选择字(D7=1)含义 |
|---|---|
| D7 | 固定为 1,标志本字节是方式选择字 |
| D6 D5 | A 组方式:00 方式 0,01 方式 1,1x 方式 2 |
| D4 | A 口方向:1 输入,0 输出 |
| D3 | C 口高四位(PC7~PC4):1 输入,0 输出 |
| D2 | B 组方式:0 方式 0,1 方式 1 |
| D1 | B 口方向:1 输入,0 输出 |
| D0 | C 口低四位(PC3~PC0):1 输入,0 输出 |
| 位 | 位控字(D7=0)含义 |
|---|---|
| D6 D5 D4 | 无关位,习惯写 0 |
| D3 D2 D1 | 选位:000~111 对应 PC0~PC7 |
| D0 | 1 置位(该引脚输出高),0 复位(输出低) |
常见的全输出字是 0x80(1000 0000),全输入字是 0x9B(1001 1011),A 口输入其余输出的组合字是 0x90(1001 0000)。这三个字节建议直接背下来,实验现场没时间现推。
血泪经验在这里:某同学想写全输出,手滑写成 0x08(0000 1000)。D7 = 0,芯片把它当位控字解析——D3D2D1 = 100 选中 PC4,D0 = 0 做复位。现象是"初始化执行完 LED 一个都不亮,只有 C 口某一根线被拉低",然后开始怀疑接线、怀疑芯片坏、怀疑电源。写控制字之前把 D7 单独圈出来看一眼,能省掉半小时。
还有一条容易忽略:位控字在方式 1、方式 2 下对已被握手信号占用的引脚无效。方式 1 输入时 PC4 是 /STB、PC5 是 IBF,你再用位控字去动它们,芯片不理你。所以位控字最好只用在纯方式 0 的场景,比如单独点亮一个指示灯、给外设发一个窄脉冲。
2.2 端口寻址与片选:A1A0 选中谁,/CS 决定这块芯片在不在总线上
8255A 有 40 根引脚,实验台上真正要你自己接的其实就那几组:数据线 D0~D7 挂系统数据总线,A1A0 接地址总线低两位,/CS 接译码输出,/RD 与 /WR 接系统读写信号,RESET 接复位。
A1A0 的组合决定访问哪个寄存器,这是死规定:
| A1 | A0 | 选中对象 |
|---|---|---|
| 0 | 0 | A 口(PA0~PA7) |
| 0 | 1 | B 口(PB0~PB7) |
| 1 | 0 | C 口(PC0~PC7) |
| 1 | 1 | 控制字寄存器(只写,读不回来) |
所以只要知道基地址,四个端口地址就是基地址 +0、+1、+2、+3。基地址由 /CS 的译码逻辑决定,实验台上见得多的是三种:
- 线选法:高位地址里挑一根直接当 /CS。接法最省事,但地址不唯一,同一根地址线拉高就选中,别名一大堆,写代码时容易和别的外设打架。
- 部分译码:几根地址线经门电路组合,地址范围比线选法干净,但仍可能留有空隙。
- 全译码:三—八译码器把高位地址全解出来,一个模块吃一个输出,地址唯一、互不干扰,实验箱上带地址开关设定的多半是这种。
写实验报告时,"端口地址确定"这一段不能只写一句"基地址为 XXX"。要写清楚:/CS 由哪几根地址线译出、A1A0 怎么接、为什么这四个地址是唯一的或者是存在别名的。这一段恰恰是评分时最能看出你有没有真动手的地方。
还有一个硬件细节必须提:RESET 是高电平有效,复位后三个端口全部回到方式 0 输入,控制寄存器清零,输出锁存器清零。有的实验板复位电路上电容偏大,上电后 RESET 拉高的时间比你想象的长,程序跑起来的时候芯片还没退出复位——这也是"程序没错但没反应"的经典来源,后面避坑章会细说。
2.3 方式 0 / 1 / 2 的取舍:什么时候必须动用握手信号
三种方式不是难度递进,而是用途不同。
| 方式 | 可用端口 | 关键信号 | 典型场景 |
|---|---|---|---|
| 方式 0 | A、B、C 任意分组 | 无握手,纯读写 | LED、拨码开关、数码管、继电器 |
| 方式 1 | A 口、B 口 | 输入:/STB、IBF、INTR;输出:/OBF、/ACK、INTR | 打印机接口、键盘、双机并行传送 |
| 方式 2 | 仅 A 口 | 双向:/STB、IBF、/OBF、/ACK、INTR | 双向总线、双机对传 |
方式 0 是大多数实验项的默认选择,因为不需要对方配合,写进去就出来。但它有个隐含前提:外设的速度得跟得上 CPU,或者你根本不在乎丢数据。拨码开关和 LED 满足这个前提,键盘扫描勉强满足,一旦对方是另一台机器或者一个有自己节奏的设备,就必须换方式 1。
方式 1 的价值全在握手:发送方把数据放到端口上,拉低 /OBF 告诉对方"有新数据",对方取走后回一个 /ACK,/OBF 变高,如果中断允许位开着,INTR 拉高通知 CPU 可以送下一个字节。接收方向同理,/STB 是对方告诉你"数据到了",IBF 表示缓冲区满。这套机制的实质是把"什么时候能写"这个问题从"我猜对方应该读完了"变成"芯片告诉我可以写了"。
方式 2 只有 A 口能做,本质是方式 1 输入加方式 1 输出的合体,五根 C 口引脚全被占用:PC7 /OBF、PC6 /ACK、PC5 IBF、PC4 /STB、PC3 INTR。做双机对传时用它最省引脚,但 C 口就剩 PC2~PC0 三根可自由支配,B 口通常配成方式 0 打辅助。
写报告时"工作方式选择依据"这一段,别写成"因为题目要求用方式 1"。要写成:外设是主动还是被动、有没有自己的时序、CPU 能不能容忍忙等、需不需要中断。这三句话一写,评的人就知道你不是照着指导书抄的。
3. 从地址到第一行能跑的代码:初始化、回读与第一次闭环
3.1 基地址怎么定:三种译码方式与一次回读验证
拿到一块不熟悉的板子,第一步不是写业务代码,是确认基地址。手册上写了基地址最好,没写就得自己验。
这里有一个非常实用的技巧:控制口写不进去也读不出来,但 C 口的输出锁存器是能读回来的。8255A 的读操作规则是——该端口配置为输出时,读回的是输出锁存器的内容;配置为输入时,读的是引脚电平。利用这一条,可以写一个只用四行代码的探测器:
/* 位控字回读法:向候选基地址写位控字,再读 C 口看对应位是否跟随 */ int probe_base(unsigned int base) { unsigned int ctrl = base + 3; /* 控制口 */ unsigned int portc = base + 2; /* C 口 */ outportb(ctrl, 0x80); /* 全输出,先把 C 口锁存器定成输出 */ outportb(ctrl, 0x0F); /* 位控字:PC7 置 1 */ if ((inportb(portc) & 0x80) == 0) return 0; /* 没跟随,地址不对 */ outportb(ctrl, 0x0E); /* 位控字:PC7 复位 */ if ((inportb(portc) & 0x80) != 0) return 0; /* 没能拉低,也不对 */ return 1; /* 两次都对,这个基地址基本可信 */ }逻辑说明:先写 0x80 把 C 口定成输出,再写 0x0F(D3D2D1 = 111 选中 PC7,D0 = 1 置位),读 C 口看 D7 是否为 1;再写 0x0E(D0 = 0 复位)看是否变 0。两次都跟随,说明 /CS、A1A0、/WR、/RD 这一整条链路是通的。
参数说明:0x0F 和 0x0E 只差最低位,前者置位、后者复位,这是位控字最直接的用法。判断时用按位与& 0x80只取 D7,其他位不管——因为 C 口高四位此刻还可能是别的状态,整字节比较会误判。
探测范围按常见的实验卡地址段扫,比如 0x280、0x288、0x300、0x340 这一带,步长 4 逐个试。别扫全部 64K 端口,一是慢,二是可能碰到系统正在用的地址,那属于自找麻烦。
3.2 初始化代码:C 版与汇编版,逐位对一遍
基地址确定后,第一段要写的代码就是初始化。下面这段是 A 口输出、B 口输出、C 口全输出的方式 0 配置:
#include <conio.h> #define BASE 0x280 /* 换成你实测确认的基地址 */ #define PA (BASE + 0) #define PB (BASE + 1) #define PC (BASE + 2) #define CTRL (BASE + 3) void init_8255(void) { outportb(CTRL, 0x80); /* 1000 0000: 方式0, A/B/C 三组全部输出 */ }参数说明:0x80 展开是 D7 = 1(方式选择字)、D6D5 = 00(A 组方式 0)、D4 = 0(A 口输出)、D3 = 0(PC 高四位输出)、D2 = 0(B 组方式 0)、D1 = 0(B 口输出)、D0 = 0(PC 低四位输出)。八个位一个都不能错,写之前拿纸对一遍最保险。
如果 A 口接拨码开关要读、B 口接 LED 要写,控制字改成 0x90:D4 = 1 让 A 口输入,其余不变。这个改动只在一位上,但现象天差地别——A 口配成输出时你读回来的永远是上次写进去的锁存器值,拨码开关怎么拨都读不出变化,很多人到这一步开始拆线,其实只是方向位错了。
同样的事在汇编里更直观,因为二进制可以直接写:
; 假设基地址 280H,控制口 283H MOV DX, 0283H ; DX 指向控制寄存器 MOV AL, 10010000B ; A口输入(0x90), B口输出, C口高输出低输出 OUT DX, AL ; 写入方式选择字 MOV DX, 0280H ; 指向 A 口 IN AL, DX ; 读拨码开关状态到 AL汇编版的优势是端口地址和二进制控制字都摆在明面上,调试时改一位不用回编译器里翻宏定义。做实验报告的话,两版都贴上去,再配一句"两版控制字一致,C 版用宏、汇编用二进制立即数",比只贴一版更有说服力。
初始化之后建议立刻做一件事:读一次 C 口并把原始值打印出来。全输出配置下读回的应该是 0x00(复位后锁存器清零),如果读到 0xFF 或者跳变值,说明接线有问题或者芯片处于复位状态,这时候别往下写业务代码。
3.3 第一次闭环:拨码开关进、LED 出
闭环的意思是"输入能影响输出",这是实验报告里第一个能拿来当证据的现象。做法是 A 口接 8 位拨码开关,B 口接 8 个 LED,控制字写 0x90,然后循环读 A 口写 B 口:
void loop_through(void) { unsigned char v; while (!kbhit()) { /* 按键退出,避免死循环 */ v = (unsigned char)inportb(PA); /* 读拨码开关 */ outportb(PB, v); /* 原样送 LED 显示 */ } }逻辑说明:这段代码没有任何算法,价值全在"原样透传"四个字上——它把输入链路和输出链路同时验证了。如果 LED 显示和开关位置一一对应,说明地址、控制字、接线三件事全对,后面的实验项才有基础。
参数说明:拨码开关常见是共地接法,开关拨到 ON 时该位接地读 0,OFF 时通过上拉电阻读 1,所以显示是反的。写报告时别急着在代码里取反,先在报告里写清楚"实测 ON 对应 0,OFF 对应 1",再说明代码用v = ~v或直接按反相解读。这个细节看着小,其实是判断你有没有真看波形、真读数据的依据。
如果这一步 LED 全亮但开关拨动无变化,问题基本在两个地方:A 口方向位没置 1,或者开关那侧的公共端没接地。前者查控制字,后者拿万用表量一下开关公共端和板子地是不是通的,一分钟能定位。
4. 三个实验项的完整做法:流水灯、矩阵键盘与双机并行传送
4.1 流水灯:延时阈值与整字节输出
流水灯是方式 0 输出的标准练习,核心只有两件事:位怎么移、延时给多少。
void delay_ms(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) for (j = 0; j < 1000; j++) /* 空循环,具体次数按主频调 */ ; } void flow_led(void) { unsigned char pat = 0x01; int i; for (i = 0; i < 8; i++) { outportb(PA, pat); /* 整字节一次写出,不做读改写 */ delay_ms(150); pat = (unsigned char)(pat << 1); } outportb(PA, 0x00); /* 收尾清零,避免残留一个灯 */ }参数说明:150 ms 是个经验值。低于 50 ms 时人眼视觉暂留会把相邻两三个灯叠在一起,看起来像"全亮在闪";高于 500 ms 又像卡顿,不像流水。做报告时可以给个对比表,分别用 30 ms、150 ms、600 ms 各跑一遍,把观察结果写进去,这比只写"延时约 150 ms"有内容得多。
逻辑说明:pat << 1移到最高位后会变成 0,所以循环固定八次,别写成 while 死等。下面接第二个板子的灯时,注意 A 口和 B 口都要单独写一遍,别指望一个口写两次。
一个容易被忽略的点:整字节一次写出,不要在端口上做"读—改—写"。有些同学为了只改一位,先inportb(PA)再改一位再写回去,如果端口配成输出,读回的是锁存器,逻辑上没问题,但在真实时序里这中间会有一个短暂的中间态,接示波器能看到毛刺。实验报告里如果要写"输出无毛刺",这就是必须交代的细节。
4.2 4×4 矩阵键盘:C 口高四位出行、低四位回读
矩阵键盘用 C 口做最省事:PC7~PC4 四根做行输出,PC3~PC0 四根做列输入。控制字要拆开 C 口高四位和低四位的方向:D3 = 0(高四位输出)、D0 = 1(低四位输入),配 A、B 都输出,得到 0x81(1000 0001)。
/* 返回 0xFF 表示无键;否则高四位为行号,低四位为列掩码 */ unsigned char scan_key(void) { unsigned char row, val; outportb(CTRL, 0x81); /* PC7~PC4 输出(行), PC3~PC0 输入(列) */ for (row = 0; row < 4; row++) { /* 逐行拉低:只让当前行为 0,其余行保持 1 */ outportb(PC, (unsigned char)(~(0x10 << row)) & 0xF0); val = (unsigned char)(inportb(PC) & 0x0F); /* 只取列线 */ if (val != 0x0F) { /* 有列被拉低 */ return (unsigned char)((row << 4) | val); } } return 0xFF; }逻辑说明:0x10 << row依次得到 0x10、0x20、0x40、0x80,取反后与 0xF0 相与,得到"当前行为 0、其余行为 1"的行扫描码,比如 row = 1 时是 0xD0。列线平时被上拉为高(0x0F),某一行拉低后如果该行上有键按下,对应列被拉低,读回值就不再是 0x0F。
参数说明:为什么只写高四位?因为低四位配成了输入,往 C 口写数据时会写整个锁存器,但输入引脚读的是外部电平,写进去的值不影响读取,用& 0xF0把低四位清掉只是让代码意图更清楚。val != 0x0F这个判断是"有任意一列变低",不是"哪一列变低",返回值里带了列掩码,上层再查表映射成 0~9、A~F 就行。
实际跑之前必须加消抖。按键机械触点抖动时间在 5~20 ms 量级,扫描周期如果只有几十微秒,一次按下会被识别成十几次。常见做法是连续两次扫描结果一致才确认,或者检测到按键后延时 10~20 ms 再读一次。报告里把"未消抖时的误触发次数"和"消抖后的次数"做成两行数据,说服力立刻不一样。
4.3 双机并行传送:方式 1 的握手时序怎么对
两块板子通过 8255A 对传,是接口课里最能体现"接口"两个字的实验。做法是一侧 A 口配方式 1 输出,另一侧 A 口配方式 1 输入,八根数据线对接,握手线交叉接。
输出侧引脚分配:PC7 = /OBF(输出缓冲满,低有效),PC6 = /ACK(应答输入,低有效),PC3 = INTR(中断请求,高有效)。控制字为 A 组方式 1 输出:D7 = 1、D6D5 = 01、D4 = 0(输出)、D3 无关、D2 = 0(B 组方式 0)、D1 = 0、D0 = 0,即 0xA0 一类的组合,具体低四位按 B 组和 C 口低位的用途再定。
输入侧引脚分配:PC4 = /STB(选通输入,低有效),PC5 = IBF(输入缓冲满,高有效),PC3 = INTR。
一次完整传送的时序是这样的:
| 步骤 | 发送侧动作 | 接收侧动作 | 信号变化 |
|---|---|---|---|
| 1 | CPU 写 A 口 | — | 数据上线,/OBF 变低 |
| 2 | — | 检测到 /STB 有效 | 数据打入输入锁存器,IBF 变高 |
| 3 | — | CPU 读 A 口 | IBF 变低,/STB 回高 |
| 4 | 收到 /ACK | — | /OBF 变高,允许写下一个字节 |
写代码时最省事的版本是不用中断,纯查询:
/* 发送一个字节:等上一次的 /OBF 释放后再写 */ void send_byte(unsigned char data) { unsigned char st; do { st = (unsigned char)inportb(PC); /* 读 C 口拿状态 */ } while ((st & 0x80) == 0); /* PC7 即 /OBF,等它变高 */ outportb(PA, data); }逻辑说明:读 C 口拿到的是状态字,其中包含了 /OBF、IBF、INTR 这些握手信号的当前电平,所以查询式发送就是反复读状态、等 /OBF 释放。参数说明:不同教材对状态字各位的排布描述可能略有出入,实验前务必对照手上的器件手册把"哪一位对应 /OBF"核一遍,别照着记忆里的位序写——这是最容易出错又最难查的一处。
如果要用中断,多出来三件事:INTR 要接到中断控制器的某个输入上、C 口位控字要把对应 INTE 位置起来(方式 1 下 INTE 用位控字操作,但它不对应实际引脚,只控制内部触发器)、中断服务程序结尾要发中断结束命令。这三件事少一件,现象都是"数据能传但中断不进",很容易误判成硬件坏。
5. 避坑与排查:那些让实验报告数据对不上的地方
5.1 地址与复位类:写进去像石沉大海
现象一:初始化执行完,LED 全亮或者全灭,拨开关、改控制字都没反应。
原因通常有两个。一是基地址算错,比如把控制口写成 BASE + 4 或者把 A 口和 C 口搞反,写进去的字节落到了别的寄存器上。二是 RESET 还没释放——有些板子的复位电路上电后高电平持续时间偏长,程序跑得比复位快,芯片还在复位态,三个端口全是方式 0 输入,写什么都不生效。
解决:先用 3.1 那个位控字回读法确认地址,能跟随就说明地址对;再在初始化前面加几百毫秒延时,或者在程序里先读一次 C 口判断是否全 0。报告里把"加延时前后"的现象都记下来,是很实在的一条数据。
现象二:程序换了一台机器或换了个插槽,同样的代码不工作了。
原因是线选法或部分译码留下的地址别名。同一块板子在别的机器上被别的设备抢了地址,或者系统的地址译码把它映射到了另一段。解决:优先用全译码的板子;只能用线选法时,在报告里写清地址别名的范围,并在代码注释里标注"该地址非唯一"。
5.2 方向位与中断类:数据对了但中断不响
现象三:拨码开关怎么拨都读不出变化,读回值永远等于上次写出去的值。
原因是该端口被配成了输出。8255A 读输出口读的是输出锁存器,不是引脚,所以外部电平变化根本反映不出来。解决:查控制字里对应的方向位有没有置 1,A 口是 D4、B 口是 D1、C 口高四位是 D3、低四位是 D0。改完之后先读一次原始值打印出来,确认极性再往下写逻辑。
现象四:方式 1 数据能传,但中断死活进不去。
三个原因按概率排:INTR 没接到中断控制器的输入引脚;方式 1 的 INTE 内部触发器没通过位控字打开;中断服务程序没发中断结束命令,第一次进了以后就再也不进了。解决:先把程序改成状态查询(轮询 IBF 或 /OBF),确认数据通路没问题,再切回中断。这样能把"数据链路问题"和"中断配置问题"分开,不然两个变量一起动,永远定位不了。
5.3 时序与电气类:偶尔对、偶尔错
现象五:双机传送时大部分数据对,偶尔丢一两个字节,重跑一次丢的位置还不一样。
原因是发送侧没等 /OBF 释放就写了下一个字节,把上一个还没被取走的数据覆盖掉了;另一类原因是两块板子没共地,电平参考不一致,握手线偶发误判。解决:发送前加状态查询,确认 /OBF 已经变高再写;硬件上把两侧地线可靠连起来,握手线尽量短。这类问题最典型的特征是"实验室里跑十次对九次",报告里如果有这种描述,基本可以判定是时序余量不够,而不是逻辑错。
还有一条不算踩坑但值得写进报告:延时函数用空循环实现时,实际延时随主频变化很大。同一段代码在实验箱和 PC 上跑,流水灯速度能差好几倍。写报告时把编译环境、主频、延时实现方式交代清楚,别人复现才不会对不上。
6. 让报告数据站得住:从引脚级自检到现象表
实验报告最容易失分的地方不是结论错,是数据没有可追溯的来源。想让每一行现象都能自证,我一般按"从引脚往上"的顺序做验证,先证明单根线对,再证明字节对,最后才谈功能。
第一步永远是引脚级自检:用位控字把 C 口八根线逐根置位、复位,每根都量一次电平或者看一次 LED。
| 验证项 | 操作方法 | 期望现象 | 失败指向 |
|---|---|---|---|
| 单引脚输出 | 写位控字 0x01/0x00 动 PC0,依次到 0x0F/0x0E 动 PC7 | 对应引脚逐根变高再变低 | 该引脚接线或负载 |
| 端口方向 | 写 0x80 后读 C 口,再写 0x9B 后读 C 口 | 前者读回锁存器,后者读引脚 | 控制字方向位 |
| 端口寻址 | 分别写 A、B、C 三个口 | 三组外设各自响应,不串 | A1A0 接线 |
| 握手链路 | 发送侧写一字节,量 /OBF 与 /ACK | /OBF 先低后高,/ACK 期间有低脉冲 | 握手线交叉或未共地 |
第二步是字节级验证:控制字写完立刻回读 C 口打印原始值,把"写进去的字节"和"读回来的字节"并排列在报告里。全输出时读回应为 0x00,如果读到别的那就是方向位或者接线有问题。这一步花不了两分钟,但它是区分"我调通了"和"我抄对了"的分水岭。
第三步才是功能验证:流水灯的每个相位停留时间、键盘每个键的扫描码、双机传送的字节数和误码数,都要有具体数字。做键盘实验时把未消抖和消抖后的误触发次数分别记一行,做双机实验时把连发 1000 字节的接收计数记一行,这些数字比任何文字描述都硬。
最后说一个我自己的习惯:每次实验前先在纸上把控制字按位写一遍,标上每一位为什么取这个值,写完再敲代码。这个动作看着笨,但它把"控制字写错"这类最没技术含量又最耗时间的错误挡在了动手之前。接口实验里真正难的是时序和边界,别让低级错误吃掉你本就不多的实验课时。
希望帮到你。
本文还有配套的精品资源,点击获取