资讯详情

资讯详情

8254三级分频与8255动态扫描实现智慧交通定距测速

简介这份实验报告文档围绕微机原理与接口技术的智慧交通车辆测速实验展开面向物联网、微机接口课程学习者及需要完成课程实验的学生帮助梳理从测速需求到硬件电路、程序流程与结果分析的完整实践链路。压缩包仅含1个docx文件大小约769KB正文按实验目的、设备环境、设计指标、硬件电路图、程序流程图、方案讨论、结果分析与心得体会等模块组织目录结构清晰。内容结合8254/8253定时计数与8255数码管显示讨论传感器选型、信号处理、中断与定时器配置、车辆测速中的时间测量、数据采集、DOS屏幕与LED数码管同步显示并对理论值和实测值进行误差评估、提出改进点附有团队分工记录。已有644人学习可作为课程实验参考、报告撰写模板与接口技术复习材料。1. 24 秒停在数码管上的那一刻24 秒。实验箱上的两位数码管停在这个数DOS 屏幕上几乎同一时刻打出24,——前后差不到一个扫描周期。为了这一行输出我们在这台实验箱前蹲了三个晚上中间经历过数码管恒定显示 00、计数器读回乱值、DOS 只肯打印一次这种让人怀疑人生的状态。这个智慧交通车辆测速实验本质是把“测速”翻译成“测时间”距离固定测出车辆从 A 点到 B 点走了多少秒速度自然就出来了。翻译完问题就变成一句很朴素的话——怎么用 8254 把 1MHz 时钟逐级分成 1Hz再用 8255 把这个秒数同时喂给数码管和屏幕。下面这些内容写给两类人正在做微机原理与接口技术实验、卡在 8254 和 8255 上的同学以及想搞清楚“定距测时”这套计数思路在物联网感知层到底怎么落地的开发者。方案怎么选、控制字怎么写、哪里最容易出错我按自己踩过的顺序摆出来。2. 两套方案摆在一起两位实时显示和一位锁存显示差在哪动手之前我们争论了一晚上“到底做哪个方案”因为两份参考代码的硬件连线几乎一模一样但程序骨架完全不同。这里把两套方案放在一起对照说清楚差别到底在哪以及为什么最后用的是两位动态扫描那套。2.1 连线一改程序骨架就变了先把公共的部分定下来。8254 和 8255 都挂在片选上Y0 接 8254 的 CSY1 接 8255 的 CS这个不能接反。时钟链路是硬约束实验台给 1MHz 和 2MHz 两路1MHz 进 CLK0OUT0 串到 CLK1OUT1 再串到 CLK2。三个 GATE 里GATE0 和 GATE1 直接拉高电平常年使能只有 GATE2 留给拨码开关控制——因为 GATE2 是门控高电平才允许计数器 2 走动相当于“开始计时”的扳机。两套方案真正的分叉点只有两个。第一个是拨码开关接在 8255 的哪个引脚上方案一取 PC0方案二取 PC7。别小看这一位它直接决定了 8255 控制字的写法——PC0 属于 C 口低四位PC7 属于 C 口高四位方向控制位在控制字里是分开的写错一位就读不到开关状态。第二个分叉是数码管位数方案一驱动两位方案二只驱动一位。数码管这边的接线是共用的8255 的 PA 口接七段数码管的段选信号PB 口接位选信号。也就是说 PA 出 3FH 这样的段码PB 出 00000001B 或 00000010B 决定点亮右边还是左边那一位。提示接线完成后先别急着烧程序。拿万用表打一下 8254 的 CS 引脚和 8255 的 CS 引脚确认分别只在访问 280H283H 和 288H28BH 时才拉低这一步能省掉后面至少两小时的怀疑人生。2.2 四个维度对照显示范围、读数时机、代码量、演示效果两份代码我都跑过差异列在下面这张表里。判断依据不是“哪份代码短”而是“哪份在验收现场更稳”。对比维度方案一两位动态扫描方案二一位锁存显示数码管位数两位一位可显示范围099 秒09 秒超过即溢出拨码开关接入8255 PC08255 PC7计数器 2 初值0000H等价 65536FFFFH时间换算0 减去读回值取 BLFFFFH 减去读回值DOS 输出方式查 ASCII 表后调 2 号功能小于等于 9 加 30H否则加 37H读数时机计数过程中实时刷新开关拨下、停止计数后锁存读取现场演示效果数字持续跳动直观停表读数需要人为操作节奏端口地址这套映射两套方案完全一致我单独列一遍后面看代码时对着查器件端口地址用途8254 计数器 0280H第一级分频1MHz → 1kHz8254 计数器 1281H第二级分频1kHz → 1Hz8254 计数器 2282H计时脉冲计数8254 控制寄存器283H写控制字、发锁存命令8255 A 口288H数码管段码输出8255 B 口289H数码管位选输出8255 C 口28AH拨码开关状态输入8255 控制寄存器28BH写工作方式控制字方案二的优势很明确代码短、逻辑直、调试快一位数码管显示 09 秒示波器上量个 OUT1 频率就知道通没通。它的短板也明确——超过 9 秒就显示不出来而一次完整的拨上拨下操作人手动拨动开关的时间基本都在 10 秒以上很容易溢出再加上它是“先停表、再读数”本质是个秒表不是个实时测速仪。方案一是“边跑边显示”两位数字覆盖 099 秒更贴近实验指导书里“实时显示”的要求。2.3 为什么最后选的是方案一三条理由按权重排。第一条演示需求。实验的最终验收是“拨码开关拨上去数码管和屏幕同步开始跳数”跳的是一个动态变化的数字而不是一个静态定格的结果。方案一的动态扫描天然满足这个需求方案二是停表后才出数中间那一秒空白会让验收老师以为程序卡死了。第二条扩展余量。两位数意味着计时上限 99 秒方案二的一位数只有 9 秒。别觉得 9 秒够用我们那次从拨下开关到松开手光是手指抖了一下就多计了 3 秒直接溢出成乱码当场就得重来。第三条代码复用价值。方案一里那段“除以 10 分离十位个位、查表转段码、动态扫描输出”的子程序是可以原样搬到后面任何需要多位数码管显示的实验里的方案二的单点查表则没有这个复用价值。从学习收益的角度多写的那 40 行汇编是值得的。代价当然也有方案一的调试难度明显更高因为动态扫描牵扯到消隐和延时两个额外变量第一次跑起来大概率是重影或者闪烁。这部分后面单独讲。3. 8254 三级级联把 1MHz 分解成 1Hz 的控制字写法分频这部分是整个实验的地基。地基没打好前面数码管写得再漂亮也是白搭。我见过同学直接把 1MHz 送进计数器 2 当计时脉冲结果一秒不到计数器就跑完了整个 16 位屏幕上全是乱码——这不是程序错是物理上不可能。3.1 三个控制字逐位拆开36H、76H、0B4H 各管什么8254 的控制字是 8 位格式是“SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD”。拆三个用到的值36H 0011 0110B。D7D6 00选计数器 0D5D4 11读写方式先低字节后高字节D4D3D2 011工作方式 3方波发生器D0 0二进制计数。76H 0111 0110B。D7D6 01选计数器 1其余和上面一致也是方式 3、先低后高、二进制。0B4H 1011 0100B。D7D6 10选计数器 2D5D4 11先低后高D4D3D2 010工作方式 2速率发生器/分频器D0 0二进制。方式 3 输出的是方波占空比接近 50%适合做时钟源往下传方式 2 输出的是周期性负脉冲每 N 个输入时钟吐一个正适合当计数器用。这个搭配不是随便定的——第一级和第二级要“产生时钟”第三级要“数时钟”分工刚好对上。注意BCD 位D0写 0 就是二进制计数写 1 是二-十进制计数。这个位一旦写错计数初值的解释方式就全变了。比如初值 1000 在二进制下是 03E8H在 BCD 下才是 1000你要是把 BCD 位设成 1 却送了个 03E8H 进去E 不是合法 BCD 码计数器行为直接不可预期。3.2 初值 1000 为什么必须写两次而且必须低字节在前8254 的计数器是 16 位的但数据总线只有 8 位所以初值得分两次写。控制字里 RW1 RW0 11 的含义就是“先写低字节再写高字节”顺序反了高字节会被当成新的低字节吃进去整个分频系数就错了。1MHz 经过计数器 0 分频 1000 得 1kHz1kHz 再经过计数器 1 分频 1000 得 1Hz。也就是说计数器 1 的输出每来一个下降沿正好过去一秒。这就是全部的时间基准。; ---- 8254 三级级联初始化 ---- MOV DX, L8254_K ; 283H控制寄存器 MOV AL, 36H ; 计数器0先低后高方式3二进制 OUT DX, AL MOV DX, L8254_0 ; 280H MOV AX, 1000 ; 分频系数 10001MHz - 1kHz OUT DX, AL ; 先写低字节 AL MOV AL, AH OUT DX, AL ; 后写高字节 AH MOV DX, L8254_K MOV AL, 76H ; 计数器1先低后高方式3二进制 OUT DX, AL MOV DX, L8254_1 ; 281H MOV AX, 1000 ; 1kHz - 1Hz OUT DX, AL MOV AL, AH OUT DX, AL MOV DX, L8254_K MOV AL, 0B4H ; 计数器2先低后高方式2二进制 OUT DX, AL MOV DX, L8254_2 ; 282H MOV AX, 0 ; 16位计数器写 0 等价于初值 65536 OUT DX, AL MOV AL, AH OUT DX, AL代码里MOV AX, 1000之后先OUT DX, AL再MOV AL, AH再OUT DX, AL这是固定套路别图省事改成先写 AH不然后面读回来的值会非常离谱。计数器 2 的初值写 0 是个容易误解的点8254 在二进制计数模式下写入 0 会被解释为 65536。对我们这个实验来说65536 个 1Hz 脉冲等于 65536 秒远超实际使用范围所以等效于“从一个大数开始往下减”减到多少就是过去了多少秒。3.3 锁存读数和时间换算难点在于计数器 2 一直在减你读它的时候如果低字节读完了、高字节还没读中间它又减了一次读出来的 16 位值就是拼接错位的。8254 给的解法是锁存命令——先往控制寄存器写一个命令字把当前计数值冻结到内部锁存器然后再慢慢分两次读读的过程中计数器照常减但锁存器不跟着变。锁存命令字的格式是 D7D6 选择计数器、D5D4 00 表示锁存。计数器 2 的锁存命令就是 10000000B十六进制 80H。; ---- 锁存并读出计数器 2换算成已经走过的秒数 ---- MOV AL, 10000000B ; 计数器2 锁存命令D5D400 即锁存 MOV DX, L8254_K OUT DX, AL MOV DX, L8254_2 IN AL, DX ; 先读低字节这是硬性顺序 MOV AH, AL ; 暂存到 AH IN AL, DX ; 再读高字节 XCHG AH, AL ; 交换后 AX 里就是完整的 16 位计数值 MOV BX, 0 SUB BX, AX ; 65536 - 当前值 已经走过的脉冲数 MOV TIME, BL ; 取低字节实验时长 0~255 秒足够覆盖SUB BX, AX这一步是整套换算的核心初值是 65536现在读回来的是 AX那么已经走掉的脉冲数就是 65536 减 AX。16 位运算下 0 减 AX 天然等于 65536 减 AX所以MOV BX, 0加SUB BX, AX就完成了。取 BL 是因为实验时长不会超过 255 秒低字节够用如果要做长时间计时这里就得改成保留完整的 16 位并做十进制转换。这段代码还有个隐藏依赖它必须放在拨码开关已经拉高FLAG 1之后执行。FLAG 0 的时候计数器根本没被 GATE2 使能读出来永远是初值换算完就是 0屏幕上一直显示 00 不动。4. 8255 驱动数码管控制字 81H 背后的口分配与动态扫描时序8255 这边看着简单——A 口出段码、B 口出位选好像写个控制字就完事。实际上最容易出问题的地方反而不在这三个输出口而在 C 口那一位输入上。4.1 81H 逐位解读为什么 PC0 必须是输入8255 的控制字 8 位格式是“D7 标志位 | D6 D5 A 组方式 | D4 PA 方向 | D3 PC 高四位方向 | D2 B 组方式 | D1 PB 方向 | D0 PC 低四位方向”。81H 1000 0001B。D7 1表示这是方式选择控制字不是 C 口置位复位D6 D5 00A 组方式 0基本 I/OD4 0PA 输出D3 0PC 高四位输出D2 0B 组方式 0D1 0PB 输出D0 1PC 低四位输入。关键在于 D0 这一位。拨码开关接在 PC0 上PC0 属于 C 口低四位只有 D0 写成 1C 口低四位才被配置成输入。如果这里手滑写成 80HD0 0C 口低四位变成输出此时再执行IN AL, DX读到的不是开关状态而是输出锁存器里的值——不管你怎么拨开关读回来永远是 0程序就永远停在“未开始计时”的状态。这是当年卡了我们最久的一个坑。对比一下方案二用的是 89H 1000 1001BD3 1 让 C 口高四位输入因为它的开关接在 PC7 上。控制字和接线必须成对改改一个不改另一个一定出问题。4.2 动态扫描的时序先消隐再选位最后送段码两位数码管共用一个段码总线靠位选信号轮流点亮利用人眼视觉暂留看起来是同时亮的。这段扫描代码的顺序极其讲究写反了就是重影。正确的动作序列是先把段码口清零消隐此时两位都不亮→ 再送出位选信号决定点哪一位 → 最后送出这一位的段码 → 延时一小段时间保持 → 换下一位重复。; ---- 两位数码管动态扫描 ---- MOV BL, 10 MOV AL, TIME MOV AH, 0 DIV BL ; AL 商AH 余数 MOV TIME_GE, AH ; 余数是个位 MOV AH, 0 DIV BL ; 商再除一次余数即十位 MOV TIME_SHI, AH ; 先查 LED 段码表把数字换成共阴极段码 LEA BX, LED MOV AL, TIME_GE XLAT ; DS:[BXAL] - AL得到个位段码 MOV TIME_GE, AL LEA BX, LED MOV AL, TIME_SHI XLAT MOV TIME_SHI, AL ; --- 个位 --- MOV DX, L8255_A MOV AL, 00H ; 第一步消隐避免上一位残影 OUT DX, AL MOV DX, L8255_B MOV AL, 00000001B ; 第二步选通右侧数码管 OUT DX, AL MOV DX, L8255_A MOV AL, TIME_GE ; 第三步送个位段码 OUT DX, AL CALL DELAY1 ; --- 十位 --- MOV DX, L8255_A MOV AL, 00H ; 同样先消隐 OUT DX, AL MOV DX, L8255_B MOV AL, 00000010B ; 选通左侧数码管 OUT DX, AL MOV DX, L8255_A MOV AL, TIME_SHI OUT DX, AL CALL DELAY1段码表是共阴极编码09 对应 3FH、06H、5BH、4FH、66H、6DH、7DH、07H、7FH、6FH。注意这里面 6 是 7DH、9 是 6FH不是把 8 的 7FH 随便砍一刀段码写错的表现是“数字形状怪但能看出来是几”很容易被忽略。DELAY1里的循环次数要调。延时的作用有两个太长两位数字会肉眼可见地轮流闪太短又会影响下一轮扫描的节奏并且亮度偏暗。我们用MOV CX, 0FFFH加一个LOOP在实验箱上正好换成模拟器或者换一台机器这个数得重调。提示判断延时是不是调对了有个笨办法——把两位都显示 8盯着看五秒。如果看到的是稳定不闪的两个 8就对了如果感觉到有规律的一明一暗延时偏短或偏长微调 CX 初值。4.3 DOS 和数码管同步显示一个 BUF 缓存解决两件事屏幕输出那部分第一版我们是在主循环里无条件调用 DOS 的 2 号功能打印字符。跑起来以后发现两个问题屏幕疯狂滚动根本看不清数码管明显变暗并且开始闪。原因是一样的INT 21H 是个相当重的调用执行时间远大于一次数码管扫描主循环里每扫一次就打印一次等于让慢操作拖住了快操作。数码管是靠高频刷新骗过眼睛的一旦刷新被拖慢视觉暂留就断了。解决办法是加一个 BUF 变量存放上一次打印过的值每次先比较值没变就直接跳过打印。; ---- DOS 屏幕同步输出带变化检测---- CMP FLAG, 0 ; 拨码开关没开启不输出 JZ SHOW_DOS_END MOV AL, TIME CMP BUF, AL ; 和上次打印的值一样就跳过避免刷屏 JZ SHOW_DOS_END MOV BUF, AL ; 更新缓存 ; 分离十位个位查 ASCII 表 MOV BL, 10 MOV AH, 0 DIV BL MOV TIME_GE, AH ; 个位 MOV AH, 0 DIV BL MOV TIME_SHI, AH ; 十位 LEA BX, ASC MOV AL, TIME_SHI XLAT ; 数字 - ASCII MOV DL, AL MOV AH, 02H INT 21H ; 输出十位 LEA BX, ASC MOV AL, TIME_GE XLAT MOV DL, AL MOV AH, 02H INT 21H ; 输出个位 MOV DL, , ; 分隔符和下一轮输出隔开 MOV AH, 02H INT 21H SHOW_DOS_END:这套写法带来一个副作用BUF 的初值必须设成不可能等于实际计时的值我们设成 100十进制这样第一次进循环一定不相等一定会打印一次 0屏幕上有个初始输出看起来更自然。如果把 BUF 初值设成 0第一次就不打印了验收老师会以为屏幕坏了。5. 避坑记录五个把实验卡在 00 上的问题这一章全是我们真真切切卡过的按踩到的顺序排。每一条都按“看到什么现象 → 为什么 → 怎么修”来写照着对症状比自己瞎猜快得多。5.1 数码管恒显示 00拨码开关怎么拨都没反应现象程序编译下载都正常数码管稳定显示 00拨码开关从低拨到高屏幕和数码管都不动。用示波器量 OUT11Hz 方波是有的说明分频通了。原因8255 控制字写成了 80HPC 口低四位被配置成了输出。此时IN AL, DX读的是端口 C 的输出锁存器内容不是外部引脚电平拨码开关状态根本进不来FLAG 永远是 0。解决把控制字改成 81H重新编译。改完第一秒就能看到数字开始跳。这一条我们查了将近两个小时最后是靠单步调试盯着IN之后 AL 的值发现的。5.2 两位数字叠在一起出现重影现象数码管能显示但十位和个位的笔画重叠看起来像两个数字糊在一起。拨得快的时候更明显。原因动态扫描的顺序写反了先送位选再送段码导致新一位的位选信号在旧段码还没撤销时就生效了上一位的段码短暂地串到了这一位上。解决严格按“段码清零 → 送位选 → 送段码 → 延时”的顺序重写扫描子程序。行列顺序不能图省事调换这不是风格问题。5.3 DOS 屏幕只打印一次就不再更新现象屏幕上出现一个数字以后后面的计时变化完全不显示但数码管是正常在跳的。原因BUF 缓存的初值设成了 0第一次比较 TIME 和 BUF 时两者相等直接跳过输出。之后 TIME 每次变化都会更新但初始那一次漏了给人感觉是“卡住了”。解决BUF 初值改成 100 这种不可能出现的值。或者更稳妥的做法是加一个首轮标志位第一次循环强制打印。5.4 计时值偶尔跳变从 0 直接蹦到 200 多现象正常计时到十几秒的时候偶尔会突然跳到一个明显不可能的值然后再恢复正常。原因拨码开关是机械触点拨动瞬间有抖动GATE2 在高电平和低电平之间来回弹了几次计数器 2 被反复使能和停止读数出现异常。另外每次测量结束如果没有重新装载计数器 2 的初值下一次读数就是接着上一次的余数继续也是错的。解决软件侧在 KEY_SCAN 里加简单的多次采样确认连续三次读到同一个状态才认为是真的变化硬件侧在开关和 GATE2 之间加个去抖电容会更干净。测量结束后重新写一次计数器 2 的初值保证每一轮从同一个起点开始。5.5 读计数器 2 永远读到 0 或者离谱的值现象锁存读数那段代码单独跑读回来的 AX 要么是 0要么是 65535 这种看着就像没变过的数。原因有两个都挺常见。一是锁存命令写错把 10000000B 写成了 00000000B 之类控制字没被识别成锁存命令内部锁存器没更新读出来的就是当前实时值两次读之间计数器在减高低字节对不上。二是读完低字节后中间插了别的操作等高字节读的时候计数值已经又变了。解决锁存命令固定写 80H并且保证“写锁存命令 → 读低字节 → 读高字节”这三步之间不插入任何其他端口访问或长耗时操作。如果一定要插入就先关中断。6. 从 0 到 24 秒验收前我会固定走一遍的检查顺序现在说点具体的。这套实验做完之后我形成了一个固定习惯不管代码看起来多正确验收之前一定按下面七个步骤重走一遍全程不跳步。因为硬件实验的坑大多是“代码对、接线错”光看代码是查不出来的。顺序检查项判据1片选接线Y0 接 8254 CSY1 接 8255 CS不能反2时钟链路1M → CLK0OUT0 → CLK1OUT1 → CLK23GATE 电平GATE0、GATE1 接高电平GATE2 接拨码开关48254 独立验证示波器量 OUT1应为稳定 1Hz 方波58255 独立验证PA 全送 3FH两位应同时显示 06开关通路验证PC0 拨到高电平读回值最低位应是 17合并联调拨上开关数码管和 DOS 同步跳数第 4 步和第 5 步是分开做的这一点很关键。很多人喜欢一次把所有代码烧进去出了问题是分频错了还是显示错了完全看不出来只能靠猜这才是真正的黑匣子。先让 8254 单独跑通用示波器确认 1Hz 存在再把 8255 单独跑通确认两位数码管能稳定显示同一个数字两个都独立没问题了再合出问题必然在它们之间的协同上范围一下子缩小到十几行代码。第 6 步是很多人会漏掉的。它其实是在验证 5.1 里说的那个坑有没有复发——IN AL, DX之后AND AL, 00000001B拨到高电平这一位必须是 1。如果这一位读出来永远是 0前面所有工作都白做。最后说个调试习惯。数码管不亮先别改程序先量段码口有没有输出、位选口有没有选中屏幕不打印先别怀疑 DOS 功能先在打印前把值扔进一个内存变量用调试器看一眼它到底是多少。程序员的直觉在硬件实验里经常是错的因为真正的错误往往不在你以为的那一层。注意如果换了一台实验箱端口地址是要重新核对的。我们那次换台后地址没变但片选线接反了症状和 5.1 一模一样只不过原因完全在硬件侧。基础地址 280H 和 288H 这一组是这台机器上的约定不能想当然搬到别的平台上。回过头看这个实验真正值钱的地方不是把 8254 和 8255 的寄存器背下来而是那三天里反复被“现象 — 原因 — 解决”这套流程捶打的经历现象永远只是表象靠猜没戏靠仪器和分步验证才靠得住。从那以后我每次做完一个带硬件的实验都会强制走一遍第 4、5、6 步那种“先拆开独立验证、再合并联调”的流程宁可多花半小时也不想再体验一次盯着 00 发呆一整晚。希望这些踩过的坑能帮你少走一点弯路。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →