vault backup: 2026-06-26 23:43:44

This commit is contained in:
2026-06-26 23:43:44 +08:00
parent b5ab951ca8
commit 9a4c642854
4 changed files with 915 additions and 173 deletions
@@ -76,6 +76,15 @@ D7 D6 D5 D4 D3 D2 D1 D0
101=方式5
```
> [!important] 锁存命令(D7D6=00)的特殊用途
> 控制字的 D7D6 位有双重含义:
> - **写入控制寄存器时**(A1A0=11):`00` 表示选择计数器 0 + 设置工作方式
> - **作为锁存命令**:`00` + `D5D4=00` = **锁存当前计数值**到计数锁存器
>
> 锁存原理:8254 内部每个计数器有两个 16 位寄存器——**计数器寄存器**(正在计数的值)和**计数锁存器**(被保护的值)。CPU 读计数值时,当前正在计数的计数器其值会被"冻结"复制到锁存器中,CPU 再从锁存器读出,避免读到中途变化的值。
>
> 注意:其他 2 个非活动计数器不受影响,仍保持原锁存值。
> [!example] 控制字速算
> **要求**:计数器 0,方式 3(方波),先低后高,二进制
>
@@ -90,6 +99,8 @@ D7 D6 D5 D4 D3 D2 D1 D0
> [!tip] 考试重点
> 方式 2 和方式 3 的区别几乎每卷必考!
#### 4.1 方式总结
| 方式 | 名称 | 输出波形 | 触发 | 自动重装 | 典型应用 |
|:----:|------|----------|------|:--------:|----------|
| 0 | 计数结束中断 | 计数到 0 后 OUT 变高 | 软件 | 否 | 定时中断 |
@@ -99,7 +110,36 @@ D7 D6 D5 D4 D3 D2 D1 D0
| 4 | 软件触发选通 | 计数到 0 后输出 1 个 CLK 宽的负脉冲 | 软件 | 否 | 软件定时选通 |
| 5 | 硬件触发选通 | 触发后计数到 0 输出 1 个负脉冲 | 硬件 | 否 | 硬件延迟脉冲 |
#### 方式 2 vs 方式 3 对比
> [!question] 如何快速区分"软件触发"与"硬件触发"的方式?
> 看触发信号来源:**软件触发**的方式(0、2、3、4)在写入计数初值后就自动开始工作;**硬件触发**的方式(1、5)需要外部 GATE 引脚有有效边沿才能启动。
#### 4.2 GATE 信号在各工作方式下的行为
这是高频考点——考试中经常考查 GATE 对计数的影响:
```mermaid
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 对比
```mermaid
graph LR
@@ -120,12 +160,79 @@ graph LR
| 输出频率 | CLK / N | CLK / N |
| 重装机制 | 计数到 1 时 OUT 变低,到 0 时重装 | 计数到 0 时自动重装,交替翻转 OUT |
> [!question] GATE 引脚在方式 2 和方式 3 中的作用?
> - GATE=0:暂停计数,OUT 输出高电平
> - GATE 上升沿:重新装入初值,开始计数
> - GATE=1:允许计数正常进行
> [!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。
### 5. 计数初值计算(核心公式)
#### 4.4 方式 0 详解(计数结束中断)
方式 0 是理解其他方式的基础,它的行为最直观:
- **写入初值后**:OUT 立即从低变高,计数器开始递减
- **计数到 0 时**:OUT 从高变低,发出中断请求
- **之后**:停止计数,OUT 保持低电平(除非重新写入新初值)
```mermaid
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 个计数器不受影响,继续保持原有状态。
```mermaid
flowchart LR
CPU["CPU 总线"] <--> BR["读写缓冲器"]
BR <--> CR["计数器寄存器<br/>正在计数"]
BR <--> CLR["计数锁存器<br/>保护的值"]
CR -.CLK下降沿减1.-> CR
CPU --读操作--> CLR
CPU --写控制字+锁存命令--> CLR
```
#### 5.1 计数过程中改写初值
```mermaid
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}}
@@ -178,6 +285,95 @@ 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 位** 或更长的分频:
```mermaid
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 计算题时,按以下步骤解题不会遗漏:
```mermaid
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 |
| ④ 初始化代码 | 见下方 | — |
```asm
; 初始化计数器 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 |
## 关联笔记
- [[8255A可编程并行接口]]