Files

15 KiB
Raw Permalink Blame History

tags, create time
tags create time
接口与通讯技术
定时器
计数器
可编程芯片
2026-06-26 23:05

8254 定时计数器

概述

8254(兼容 8253)是 Intel 公司生产的可编程定时/计数器芯片(Programmable Interval Timer, PIT),在全部 4 套试卷中均有出现,其中 方波产生和计数初值计算 是最高频题型。芯片内部包含 3 个独立的 16 位计数器,每个计数器有 6 种工作方式。

[!tip] 考试要点

  • 计数初值计算:计数初值 = CLK频率 / 输出频率
  • 方式 3(方波发生器) 是最常考的工作方式
  • 控制字编写:计数器选择 + 读写格式 + 工作方式 + 数制选择
  • BCD 与二进制 初值装入的区别

正文

1. 内部结构

graph TB
    A["数据总线缓冲器 D0~D7"] --> B["读/写逻辑"]
    B --> C["计数器 0"]
    B --> D["计数器 1"]
    B --> E["计数器 2"]
    C --> F["CLK0, GATE0, OUT0"]
    D --> G["CLK1, GATE1, OUT1"]
    E --> H["CLK2, GATE2, OUT2"]

每个计数器有 3 条信号线:

信号 方向 功能
CLK 输入 时钟输入,每个下降沿使计数值减 1
GATE 输入 门控信号,控制计数的启动/暂停/触发
OUT 输出 根据工作方式输出不同波形

[!question] 思考题 8254 的计数器是递增还是递减计数?计数值减到 0 后会怎样?

答:递减计数。装入初值后每来一个 CLK 下降沿计数值减 1。减到 0 后的行为取决于工作方式——方式 0 停止,方式 2/3 自动重装初值继续计数。

2. 端口地址

8254 占用 4 个端口地址:

A1 A0 选中 说明
0 0 计数器 0 读写计数初值
0 1 计数器 1 读写计数初值
1 0 计数器 2 读写计数初值
1 1 控制寄存器 只写,用于写入控制字

[!note] 每个计数器占用 1 个还是 2 个地址? 每个计数器只占用 1 个端口地址。16 位初值通过同一地址分两次写入(先低字节后高字节)。整个芯片共需 4 个端口地址。

3. 控制字格式

控制字写入控制寄存器(A1A0=11 的端口),格式如下:

D7 D6    D5 D4      D3 D2 D1      D0
[计数器] [读写格式]  [工作方式]    [数制]
 00=计数器0  00=锁存  000=方式0    0=二进制
 01=计数器1  01=仅低字节 001=方式1  1=BCD
 10=计数器2  10=仅高字节 010=方式2
             11=先低后高  011=方式3
                         100=方式4
                         101=方式5

[!important] 锁存命令(D7D6=00)的特殊用途 控制字的 D7D6 位有双重含义:

  • 写入控制寄存器时(A1A0=11):00 表示选择计数器 0 + 设置工作方式
  • 作为锁存命令:00 + D5D4=00 = 锁存当前计数值到计数锁存器

锁存原理:8254 内部每个计数器有两个 16 位寄存器——计数器寄存器(正在计数的值)和计数锁存器(被保护的值)。CPU 读计数值时,当前正在计数的计数器其值会被"冻结"复制到锁存器中,CPU 再从锁存器读出,避免读到中途变化的值。

注意:其他 2 个非活动计数器不受影响,仍保持原锁存值。

[!example] 控制字速算 要求:计数器 0,方式 3(方波),先低后高,二进制

D7D6=00(计数器 0), D5D4=11(先低后高), D3D2D1=011(方式 3), D0=0(二进制)

控制字 = 0011 0110B = 36H

要求:同上但 BCD 计数 → D0=1 → 控制字 = 37H

4. 六种工作方式

[!tip] 考试重点 方式 2 和方式 3 的区别几乎每卷必考!

4.1 方式总结

方式 名称 输出波形 触发 自动重装 典型应用
0 计数结束中断 计数到 0 后 OUT 变高 软件 否 定时中断
1 可编程单稳态 触发后输出 N 个 CLK 宽度的负脉冲 硬件 否 单稳态触发器
2 频率发生器 每 N 个 CLK 输出 1 个 CLK 宽的负脉冲 软件 是 分频器
3 方波发生器 对称方波,占空比约 50% 软件 是 方波产生
4 软件触发选通 计数到 0 后输出 1 个 CLK 宽的负脉冲 软件 否 软件定时选通
5 硬件触发选通 触发后计数到 0 输出 1 个负脉冲 硬件 否 硬件延迟脉冲

[!question] 如何快速区分"软件触发"与"硬件触发"的方式? 看触发信号来源:软件触发的方式(0、2、3、4)在写入计数初值后就自动开始工作;硬件触发的方式(1、5)需要外部 GATE 引脚有有效边沿才能启动。

4.2 GATE 信号在各工作方式下的行为

这是高频考点——考试中经常考查 GATE 对计数的影响:

graph TB
    subgraph "GATE = 1 — 正常工作"
        direction LR
        G1["GATE=1"] --> A["允许计数"]
        A --> A1["方式 0/2/3/4<br/>持续减计数"]
        A --> A2["方式 1/5<br/>等待硬件触发"]
    end
    subgraph "GATE = 0 — 暂停 / 复位"
        direction LR
        G0["GATE=0"] --> B["暂停计数<br/>计数值保持不变"]
        B --> B1["方式 0<br/>OUT保持低"]
        B --> B2["方式 2/3<br/>OUT变高, 停止计数"]
        B --> B3["方式 1/5<br/>无法触发"]
    end
    subgraph "GATE 上升沿"
        direction LR
        RE["GATE ↑"] --> C["重新装入初值<br/>开始计数"]
        C --> C1["所有方式均适用"]
    end

4.3 方式 2 vs 方式 3 对比

graph LR
    subgraph "方式 2 - 频率发生器"
        A2["CLK 输入"] --> B2["计数器 N 分频"]
        B2 --> C2["OUT: 周期性窄脉冲"]
    end
    subgraph "方式 3 - 方波发生器"
        A3["CLK 输入"] --> B3["计数器 N 分频"]
        B3 --> C3["OUT: 对称方波 50%"]
    end
对比项 方式 2 方式 3
输出波形 负脉冲(宽 1 个 CLK) 对称方波
占空比 很小 ≈ 50%
输出频率 CLK / N CLK / N
重装机制 计数到 1 时 OUT 变低,到 0 时重装 计数到 0 时自动重装,交替翻转 OUT

[!example] 为什么同样分频 N,方式 3 的半周期是 ⌈N/2⌉? 方式 3 产生对称方波,OUT 在每个半周期翻转一次。当 N 为偶数时,每个半周期 = N/2;当 N 为奇数时,两个半周期交替为 ⌈N/2⌉ 和 ⌊N/2⌋,整体仍近似 50% 占空比。 例如 N=5:前半周期 3 个 CLK → OUT 翻转,后半周期 2 个 CLK → OUT 再翻转,周期长度仍为 5 个 CLK。

4.4 方式 0 详解(计数结束中断)

方式 0 是理解其他方式的基础,它的行为最直观:

  • 写入初值后:OUT 立即从低变高,计数器开始递减
  • 计数到 0 时:OUT 从高变低,发出中断请求
  • 之后:停止计数,OUT 保持低电平(除非重新写入新初值)
sequenceDiagram
    participant CPU
    participant 8254
    participant OUT

    CPU->>8254: 写入计数初值 N
    Note over 8254: OUT 变高,开始计数
    loop N 个 CLK 下降沿
        8254->>8254: 每次下降沿减 1
    end
    8254->>OUT: OUT 变低 (计数到 0)
    Note over OUT: 发出中断请求

[!question] 方式 0 下 GATE 的作用是什么?

  • GATE=1:正常计数
  • GATE=0:暂停计数,计数值冻结不变
  • GATE 上升沿:重新装入初值并恢复计数
  • 注意方式 0 不会自动重装,要再次触发必须重新写入新的初值。

5. 计数操作机制

5.1 计数器寄存器 vs 计数锁存器

[!important] 每个计数器有两个 16 位寄存器

  • 计数器寄存器(Counter Register):正在执行递减计数,随 CLK 不断变化
  • 计数锁存器(Count Latch Register):保存某一时刻被"冻结"的计数值
  • 读写缓冲器:两端都连接的桥梁,负责 CPU 与内部寄存器的数据交换

关键规则:当 CPU 读取某个计数器的值时,该计数器的当前值会被复制到锁存器中;而其余 2 个计数器不受影响,继续保持原有状态。

flowchart LR
    CPU["CPU 总线"] <--> BR["读写缓冲器"]
    BR <--> CR["计数器寄存器<br/>正在计数"]
    BR <--> CLR["计数锁存器<br/>保护的值"]
    
    CR -.CLK下降沿减1.-> CR
    CPU --读操作--> CLR
    CPU --写控制字+锁存命令--> CLR

5.1 计数过程中改写初值

flowchart TD
    S["正在计数中..."] --> W["CPU 写入新计数初值"]
    W --> R1{"是否在当前\n计数周期内?"}
    R1 -- 是 --> F1["新初值在当前周期\n不生效"]
    F1 --> F2["等当前周期结束后\n才使用新初值"]
    R1 -- 否 / 下一个周期 --> F3["新初值立即生效"]
    F3 --> F4["下次计数使用新值"]

[!warning] 易错点 在计数过程中向正在工作的计数器写入新的计数初值:

  • 不影响当前正在进行的计数——新初值要在当前计数周期结束后才会被采用
  • 这意味如果你想在下次计数就用新值,需要在当前计数即将结束前写入新初值

6. 计数初值计算(核心公式)


计数初值 = \frac{f_{CLK}}{f_{OUT}}

[!example] 试卷真题计算汇总

题目来源 CLK 频率 要求频率 计数初值 控制字
试题 A 2 MHz 2000 Hz 1000 (03E8H) 34H
试题 B 1.193182 MHz 100 Hz 11932 (2EA4H) 36H
试题 B 1 MHz 1 kHz 1000 (03E8H) 36H
试题 C 1.19318 MHz 1 kHz 1193 (04A9H) 36H
试题 D 1 MHz 1 kHz 1000 (03E8H) 36H

6. BCD 计数 vs 二进制计数

项目 二进制(D0=0) BCD(D0=1)
计数范围 0000HFFFFH (065535) 0000~9999
初值装入 直接装入十六进制值 按 BCD 码装入
初值 1000 03E8H 1000 (低字节=00, 高字节=10)

[!warning] BCD 方式装入初值的坑 BCD 方式下初值 11932 的装入:

  • 低字节 = 32H(低 2 位 BCD)
  • 高字节 = 11H(高 2 位 BCD)

而不是 2EA4H!控制字也要从 36H 改为 37H(D0=1)。

7. 初始化编程模板

; 8254 初始化通用模板
; 假设端口地址为 PORT_0 ~ PORT_3

; 步骤 1:写控制字
MOV DX, PORT_3       ; 控制寄存器地址
MOV AL, 36H          ; 控制字:计数器0, 方式3, 先低后高, 二进制
OUT DX, AL

; 步骤 2:写计数初值(先低后高)
MOV DX, PORT_0       ; 计数器 0 地址
MOV AX, 03E8H        ; 计数初值 = 1000
OUT DX, AL           ; 先写低字节 E8H
MOV AL, AH
OUT DX, AL           ; 再写高字节 03H

[!danger] 易错点 写初值时必须 先低后高(与控制字 D5D4=11 对应),而且低字节和高字节要 分两次 OUT 到同一个端口地址。用 OUT DX, AX 一次写 16 位是错误的。

8. 综合应用与考试技巧

8.1 PC 系统定时器实例(经典案例)

PC 机的 Intel 8253(兼容 8254)系统时钟使用著名的 1.193182 MHz 晶体振荡器频率作为 CLK 输入,产生标准 100 Hz 的周期中断:

参数 值
CLK 频率 1.193182 MHz = 1,193,182 Hz
目标频率 100 Hz
计数初值 1,193,182 ÷ 100 = 11,931(取整为 11931 ≈ 11932)
控制字(方式 3,二进制) 36H

这个 100 Hz 中断被操作系统用作时间基准——每秒触发 100 次,每次更新系统时钟滴答数(tick count)。这就是为什么老式 Windows 系统的 GetTickCount() 精度约为 10ms(1/100 s)。

[!question] 如果改用 BCD 方式实现 100 Hz 中断,初值如何写入? BCD 方式下,11931 按四位十进制数字处理:1,1,9,31 → 高字节=11H,低字节=31H(注意不是 BC 码,而是直接按十进制位拆分)。控制字改为 37H。

8.2 串级连接(级联)

当单个 16 位计数器不够用时,可以用两个计数器串级,形成 32 位 或更长的分频:

flowchart LR
    CLK1["CLK1"] --> C1["计数器 0<br/>分频 N1"]
    C1 -- OUT0 驱动 CLK1 --> C2["计数器 1<br/>分频 N2"]
    C2 -- OUT1 --> FINAL["最终输出<br/>总分频 = N1 × N2"]

总分频比 = N₁ × N₂

例如:计数器 0 设 N₁=1000,计数器 1 设 N₂=1000,输入 CLK=1 MHz,则最终输出频率 = 1,000,000 ÷ (1000×1000) = 1 Hz。

8.3 典型考试题解步骤

遇到 8254 计算题时,按以下步骤解题不会遗漏:

flowchart TD
    S["题目给出:<br/>f_CLK, f_OUT, 工作方式"] --> A["Step 1: 计算计数初值<br/>N = f_CLK / f_OUT"]
    A --> B["Step 2: 将 N 转换为十六进制<br/>例:1000 → 03E8H"]
    B --> C["Step 3: 确定控制字<br/>选择计数器 + 读写格式 + 工作方式 + 数制"]
    C --> D["Step 4: 写出初始化汇编代码<br/>先写控制字,再分两次写入初值"]
    D --> E["Step 5: 检查边界情况<br/>BCD方式? 奇数N? 是否需级联?"]

完整示例:用 2 MHz 时钟源产生 2000 Hz 方波,选计数器 0,方式 3,二进制。

步骤 操作 结果
① 计数初值 2,000,000 ÷ 2,000 1000 = 03E8H
② 控制字 计数器0(00)+先低后高(11)+方式3(011)+二进制(0) 36H
③ 端口基址假设 设 PORT_BASE 计数器0→PORT_BASE, 控制寄存器→PORT_BASE+3
④ 初始化代码 见下方 —
; 初始化计数器 0,产生 2000Hz 方波
MOV DX, BASE_ADDR + 3      ; 控制寄存器
MOV AL, 36H                ; 计数器0, 方式3, 先低后高, 二进制
OUT DX, AL

MOV DX, BASE_ADDR          ; 计数器 0
MOV AX, 03E8H              ; 计数初值 1000
OUT DX, AL                 ; 先写低字节 E8H
MOV AL, AH                 ; 移动高字节
OUT DX, AL                 ; 再写高字节 03H

8.4 快速自查清单

做题时逐项确认:

  • 计数初值计算是否正确?(单位统一:MHz↔Hz)
  • 十六进制转换是否正确?
  • 控制字中 D5D4 是否匹配你编写的写入顺序?(若只需写一个字节可用 01/10,但大多数情况用 11)
  • BCD 方式下 D0=1,且初值按十进制位拆分而非十六进制
  • 奇数分频时方式 3 的实际占空比不是精确 50%(但仍可接受)
  • 是否需要串级?(超出 65535 时需要)

9. 性能参数速查

参数 值
最高 CLK 频率(8254-5) 5 MHz
最小访问时间 500 ns
计数范围(二进制) 1 ~ 65536
计数范围(BCD) 1 ~ 9999
计数速率 每个 CLK 下降沿减 1

关联笔记