第9章 计时器与时序控制系统设计详解

内容分享13小时前发布
0 0 0

第9章 计时器与时序控制系统设计详解

时序控制是计算机系统中的基础功能之一,通过精确的时间计量和控制,可以实现各种定时任务、时钟同步和精准延时等功能。本章将详细讲解8253/8254定时计数器芯片的工作原理、编程方法和实际应用案例,帮助读者全面理解时序控制系统的设计与实现。

9.1 8253/8254时序控制器基础

9.1.1 8253/8254时序控制器的结构与引脚功能

8253/8254是Intel设计的可编程定时器/计数器芯片,广泛应用于各种需要精确时序控制的场合。8254是8253的增强版本,两者在功能上基本兼容,但8254具有更好的可靠性和更高的工作频率。

内部结构
8253/8254内部包含三个独立的16位计数器(通常称为Counter 0、Counter 1和Counter 2),每个计数器都可以独立工作,执行不同的计时任务。

芯片内部主要组件包括:

三个16位计数器数据总线缓冲器读/写控制逻辑控制寄存器

引脚功能
8253/8254采用24引脚DIP封装,主要引脚功能如下:

D₀-D₇:8位双向数据总线,用于CPU与芯片之间的数据传输CLK 0、CLK 1、CLK 2:三个计数器的时钟输入引脚OUT 0、OUT 1、OUT 2:三个计数器的输出引脚GATE 0、GATE 1、GATE 2:三个计数器的门控制引脚A₀、A₁:地址输入线,用于选择内部寄存器CS̅:片选信号,低电平有效RD̅:读信号,低电平有效WR̅:写信号,低电平有效VCC、GND:电源和地线

下面是一段C语言模拟8253/8254内部结构的代码示例:

c



#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
 
// 模拟8254计时器内部结构
typedef struct {
    // 三个16位计数器
    uint16_t counters[3];
    
    // 三个计数器的当前模式
    uint8_t modes[3];
    
    // 三个计数器的初始计数值
    uint16_t initial_counts[3];
    
    // 三个计数器的门控状态
    bool gates[3];
    
    // 三个计数器的输出状态
    bool outputs[3];
    
    // 控制字寄存器
    uint8_t control_register;
    
    // 锁存器状态
    bool latched[3];
    
    // 锁存值
    uint16_t latch_values[3];
    
    // 读写状态(0=未设置, 1=低字节, 2=高字节)
    uint8_t rw_state[3];
} Timer8254;
 
// 初始化8254计时器
void init_timer(Timer8254 *timer) {
    for (int i = 0; i < 3; i++) {
        timer->counters[i] = 0xFFFF; // 默认最大值
        timer->modes[i] = 0;
        timer->initial_counts[i] = 0;
        timer->gates[i] = true;
        timer->outputs[i] = false;
        timer->latched[i] = false;
        timer->latch_values[i] = 0;
        timer->rw_state[i] = 0;
    }
    
    timer->control_register = 0;
    
    printf("8254 定时器已初始化
");
}
 
// 打印计时器状态
void print_timer_status(Timer8254 *timer) {
    printf("
8254 定时器状态:
");
    printf("--------------------
");
    
    for (int i = 0; i < 3; i++) {
        printf("计数器 %d:
", i);
        printf("  当前值: 0x%04X (%u)
", timer->counters[i], timer->counters[i]);
        printf("  工作模式: %d
", timer->modes[i]);
        printf("  初始计数值: 0x%04X (%u)
", timer->initial_counts[i], timer->initial_counts[i]);
        printf("  门控状态: %s
", timer->gates[i] ? "启用" : "禁用");
        printf("  输出状态: %s
", timer->outputs[i] ? "高电平" : "低电平");
        printf("  锁存状态: %s
", timer->latched[i] ? "已锁存" : "未锁存");
        printf("  读写状态: %s
", 
            timer->rw_state[i] == 0 ? "未设置" : 
            timer->rw_state[i] == 1 ? "低字节" : "高字节");
        printf("
");
    }
}
 
int main() {
    Timer8254 timer;
    
    // 初始化计时器
    init_timer(&timer);
    
    // 打印初始状态
    print_timer_status(&timer);
    
    return 0;
}

此代码模拟了8253/8254定时器的内部结构,包含三个16位计数器及相关控制状态。真实硬件中,这些状态会通过芯片内部的晶体管电路实现。

9.1.2 8253/8254定时计数器的运行模式

8253/8254可以工作在6种不同的模式下,每种模式都有特定的计时行为和输出特性。理解这些模式对正确编程和使用定时器至关重要。

模式0:计数终止模式

计数器被设置初始值后,开始向下计数当计数到0时,输出信号变为高电平并保持门控信号(GATE)为低电平时暂停计数典型应用:事件计数、产生延时

模式1:可编程单稳态模式

输出初始为高电平当触发信号(GATE从低到高跳变)到来时,输出变为低电平计数器开始从初始值向下计数,计数到0时输出恢复高电平典型应用:产生可编程宽度的脉冲

模式2:频率发生器模式

产生连续的方波信号计数器从初始值N开始计数,到1时输出短暂低电平脉冲然后自动重装载初始值N继续计数产生周期为N个时钟的方波,占空比接近50%典型应用:产生稳定的频率信号

模式3:方波发生器模式

类似模式2,但产生的是精确50%占空比的方波对于偶数计数值N,输出为高电平N/2个时钟,低电平N/2个时钟对于奇数计数值N,高电平为(N+1)/2个时钟,低电平为(N-1)/2个时钟典型应用:时钟信号生成

模式4:软件触发选通模式

计数器加载后输出保持高电平计数到0时,输出产生一个时钟周期的低电平脉冲不会自动重装载,需要软件重新写入计数值典型应用:精确定时触发

模式5:硬件触发选通模式

类似模式4,但需要硬件触发(GATE从低到高跳变)才开始计数计数到0时,输出产生一个时钟周期的低电平脉冲典型应用:硬件同步的精确定时

下面是一个模拟这些工作模式的C语言函数:

c



#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
 
// 定义工作模式
#define MODE0_INTERRUPT_ON_TERMINAL_COUNT 0
#define MODE1_HARDWARE_RETRIGGERABLE      1
#define MODE2_RATE_GENERATOR              2
#define MODE3_SQUARE_WAVE_GENERATOR       3
#define MODE4_SOFTWARE_TRIGGERED_STROBE   4
#define MODE5_HARDWARE_TRIGGERED_STROBE   5
 
// 模拟8254计时器计数器
typedef struct {
    uint16_t counter;        // 当前计数值
    uint16_t initial_value;  // 初始计数值
    uint8_t mode;            // 工作模式
    bool gate;               // 门控信号状态
    bool output;             // 输出信号状态
    bool counting;           // 是否正在计数
} Counter8254;
 
// 初始化计数器
void init_counter(Counter8254 *counter, uint16_t initial_value, uint8_t mode) {
    counter->counter = initial_value;
    counter->initial_value = initial_value;
    counter->mode = mode;
    counter->gate = true;
    counter->counting = false;
    
    // 根据模式设置初始输出状态
    switch (mode) {
        case MODE0_INTERRUPT_ON_TERMINAL_COUNT:
            counter->output = false;  // 初始为低电平
            break;
        case MODE1_HARDWARE_RETRIGGERABLE:
        case MODE2_RATE_GENERATOR:
        case MODE3_SQUARE_WAVE_GENERATOR:
        case MODE4_SOFTWARE_TRIGGERED_STROBE:
        case MODE5_HARDWARE_TRIGGERED_STROBE:
            counter->output = true;   // 初始为高电平
            break;
        default:
            counter->output = false;
            break;
    }
    
    // 在模式0、2、3、4中,写入初始值后就开始计数
    if (mode == MODE0_INTERRUPT_ON_TERMINAL_COUNT ||
        mode == MODE2_RATE_GENERATOR ||
        mode == MODE3_SQUARE_WAVE_GENERATOR ||
        mode == MODE4_SOFTWARE_TRIGGERED_STROBE) {
        counter->counting = counter->gate;
    }
    
    printf("计数器初始化:模式%d, 初值=%u, 输出=%s
", 
           mode, initial_value, counter->output ? "高" : "低");
}
 
// 设置门控信号
void set_gate(Counter8254 *counter, bool gate) {
    bool prev_gate = counter->gate;
    counter->gate = gate;
    
    // 处理门控变化引起的行为
    switch (counter->mode) {
        case MODE0_INTERRUPT_ON_TERMINAL_COUNT:
        case MODE2_RATE_GENERATOR:
        case MODE3_SQUARE_WAVE_GENERATOR:
            // 门控影响计数状态
            counter->counting = gate;
            break;
        case MODE1_HARDWARE_RETRIGGERABLE:
        case MODE5_HARDWARE_TRIGGERED_STROBE:
            // 门控上升沿触发
            if (!prev_gate && gate) {
                counter->counter = counter->initial_value;
                counter->output = false;  // 输出变为低电平(模式1)
                counter->counting = true;
            }
            break;
        case MODE4_SOFTWARE_TRIGGERED_STROBE:
            // 门控不影响模式4的触发
            break;
    }
    
    printf("门控信号设置为%s
", gate ? "高" : "低");
}
 
// 模拟时钟滴答
void clock_tick(Counter8254 *counter) {
    if (!counter->counting) {
        return;  // 未计数状态
    }
    
    // 根据不同模式处理计数
    switch (counter->mode) {
        case MODE0_INTERRUPT_ON_TERMINAL_COUNT:
            if (counter->counter > 0) {
                counter->counter--;
                if (counter->counter == 0) {
                    counter->output = true;  // 输出变为高电平
                    printf("模式0: 计数终止,输出变为高
");
                }
            }
            break;
            
        case MODE1_HARDWARE_RETRIGGERABLE:
            if (counter->counter > 0) {
                counter->counter--;
                if (counter->counter == 0) {
                    counter->output = true;  // 输出变为高电平
                    counter->counting = false;
                    printf("模式1: 单稳态脉冲结束,输出变为高
");
                }
            }
            break;
            
        case MODE2_RATE_GENERATOR:
            if (counter->counter > 0) {
                counter->counter--;
                if (counter->counter == 0) {
                    counter->output = false;  // 输出产生低电平脉冲
                    printf("模式2: 计数到0,输出低脉冲
");
                    counter->counter = counter->initial_value;  // 自动重装载
                } else {
                    counter->output = true;  // 正常时为高电平
                }
            }
            break;
            
        case MODE3_SQUARE_WAVE_GENERATOR:
            if (counter->counter > 0) {
                counter->counter--;
                
                // 计算占空比切换点
                uint16_t half = counter->initial_value / 2;
                if (counter->initial_value % 2) {  // 奇数
                    if (counter->counter == half) {
                        counter->output = false;  // 切换到低电平
                        printf("模式3: 输出变为低
");
                    } else if (counter->counter == 0) {
                        counter->output = true;   // 切换到高电平
                        counter->counter = counter->initial_value;  // 重装载
                        printf("模式3: 输出变为高,重装载
");
                    }
                } else {  // 偶数
                    if (counter->counter == half) {
                        counter->output = !counter->output;  // 切换电平
                        printf("模式3: 输出翻转为%s
", counter->output ? "高" : "低");
                    }
                    if (counter->counter == 0) {
                        counter->counter = counter->initial_value;  // 重装载
                        printf("模式3: 重装载
");
                    }
                }
            }
            break;
            
        case MODE4_SOFTWARE_TRIGGERED_STROBE:
            if (counter->counter > 0) {
                counter->counter--;
                if (counter->counter == 0) {
                    counter->output = false;  // 输出产生低电平脉冲
                    printf("模式4: 计数到0,输出低脉冲
");
                    // 下一个时钟恢复高电平
                } else {
                    counter->output = true;
                }
            } else {
                counter->output = true;  // 保持高电平
            }
            break;
            
        case MODE5_HARDWARE_TRIGGERED_STROBE:
            if (counter->counter > 0) {
                counter->counter--;
                if (counter->counter == 0) {
                    counter->output = false;  // 输出产生低电平脉冲
                    counter->counting = false;
                    printf("模式5: 计数到0,输出低脉冲
");
                } else {
                    counter->output = true;
                }
            } else {
                counter->output = true;  // 保持高电平
            }
            break;
    }
}
 
// 打印计数器状态
void print_counter_status(Counter8254 *counter) {
    printf("计数值=%u, 输出=%s, 计数状态=%s
",
           counter->counter,
           counter->output ? "高" : "低",
           counter->counting ? "计数中" : "停止");
}
 
// 模拟特定模式运行
void simulate_mode(uint8_t mode, uint16_t initial_value, int ticks) {
    Counter8254 counter;
    
    printf("
=======================================
");
    printf("模拟模式 %d (初值=%u, 时钟周期=%d)
", mode, initial_value, ticks);
    printf("=======================================
");
    
    init_counter(&counter, initial_value, mode);
    
    printf("
初始状态: ");
    print_counter_status(&counter);
    
    // 特定模式的特殊操作
    if (mode == MODE1_HARDWARE_RETRIGGERABLE || 
        mode == MODE5_HARDWARE_TRIGGERED_STROBE) {
        // 需要硬件触发,模拟一次GATE上升沿
        printf("
模拟GATE上升沿触发...
");
        set_gate(&counter, false);
        set_gate(&counter, true);
    }
    
    // 模拟时钟周期
    for (int i = 0; i < ticks; i++) {
        printf("
第%d个时钟周期: ", i + 1);
        clock_tick(&counter);
        print_counter_status(&counter);
        
        // 模式1和模式5的再次触发
        if ((mode == MODE1_HARDWARE_RETRIGGERABLE || 
             mode == MODE5_HARDWARE_TRIGGERED_STROBE) && i == ticks / 2) {
            printf("
模拟中间再次触发...
");
            set_gate(&counter, false);
            set_gate(&counter, true);
        }
    }
}
 
int main() {
    printf("8254定时计数器工作模式模拟
");
    printf("=========================
");
    
    // 模拟各种工作模式
    simulate_mode(MODE0_INTERRUPT_ON_TERMINAL_COUNT, 5, 10);
    simulate_mode(MODE1_HARDWARE_RETRIGGERABLE, 5, 10);
    simulate_mode(MODE2_RATE_GENERATOR, 5, 15);
    simulate_mode(MODE3_SQUARE_WAVE_GENERATOR, 6, 20);  // 偶数
    simulate_mode(MODE3_SQUARE_WAVE_GENERATOR, 5, 20);  // 奇数
    simulate_mode(MODE4_SOFTWARE_TRIGGERED_STROBE, 5, 10);
    simulate_mode(MODE5_HARDWARE_TRIGGERED_STROBE, 5, 10);
    
    return 0;
}

这段代码详细模拟了8253/8254计数器在各种工作模式下的行为。通过运行可以观察不同模式下计数器的计数过程、输出信号的变化以及门控信号的影响。

9.1.3 8253/8254的编程接口与方法

8253/8254通过I/O端口与CPU通信,编程主要涉及控制字的设置和计数值的写入/读取。

端口分配
通常在PC系统中,8253/8254的端口地址分配如下:

端口40h:计数器0端口41h:计数器1端口42h:计数器2端口43h:控制寄存器

控制字格式
控制字是一个8位值,用于配置计数器的工作模式和操作方式:

apache



D7 D6 | D5 D4 | D3 D2 | D1 D0
SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD

SC1,SC0:选择计数器(00=计数器0, 01=计数器1, 10=计数器2, 11=控制命令)RW1,RW0:读/写方式(00=锁存命令, 01=只读/写低字节, 10=只读/写高字节, 11=先低后高字节)M2,M1,M0:工作模式(000=模式0, 001=模式1, 010/110=模式2, 011/111=模式3, 100=模式4, 101=模式5)BCD:计数方式(0=二进制计数, 1=BCD计数)

编程步骤

向控制寄存器写入控制字,配置计数器根据控制字中的读/写方式,向对应计数器写入计数值如需读取计数值,可以直接读取计数器端口或先发送锁存命令

下面是一个完整的8253/8254编程示例:

c



#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
 
// 8254端口地址(模拟)
#define TIMER0_PORT     0x40
#define TIMER1_PORT     0x41
#define TIMER2_PORT     0x42
#define TIMER_CTRL_PORT 0x43
 
// 控制字位定义
#define SC_COUNTER_0     0x00  // 选择计数器0
#define SC_COUNTER_1     0x40  // 选择计数器1
#define SC_COUNTER_2     0x80  // 选择计数器2
#define SC_READ_BACK     0xC0  // 读回命令
 
#define RW_LATCH         0x00  // 锁存命令
#define RW_LSB_ONLY      0x10  // 只读/写低字节
#define RW_MSB_ONLY      0x20  // 只读/写高字节
#define RW_LSB_MSB       0x30  // 先低后高字节
 
#define MODE_0           0x00  // 模式0:计数终止
#define MODE_1           0x02  // 模式1:可编程单稳态
#define MODE_2           0x04  // 模式2:频率发生器
#define MODE_3           0x06  // 模式3:方波发生器
#define MODE_4           0x08  // 模式4:软件触发选通
#define MODE_5           0x0A  // 模式5:硬件触发选通
 
#define COUNT_BINARY     0x00  // 二进制计数
#define COUNT_BCD        0x01  // BCD计数
 
// 模拟的8254状态
typedef struct {
    uint16_t counter_values[3];  // 三个计数器的当前值
    uint8_t control_reg;         // 控制寄存器
    uint8_t latch_status[3];     // 锁存状态(0=未锁存, 1=低字节锁存, 2=高字节锁存)
    uint16_t latch_values[3];    // 锁存的值
} Timer8254State;
 
Timer8254State timer_state = {
    .counter_values = {0xFFFF, 0xFFFF, 0xFFFF},
    .control_reg = 0,
    .latch_status = {0, 0, 0},
    .latch_values = {0, 0, 0}
};
 
// 模拟outb - 向端口写入一个字节
void outb(uint16_t port, uint8_t value) {
    printf("向端口0x%04X写入0x%02X
", port, value);
    
    if (port == TIMER_CTRL_PORT) {
        // 写入控制寄存器
        timer_state.control_reg = value;
        
        // 解析控制字
        uint8_t counter_select = (value >> 6) & 0x03;
        uint8_t rw_mode = (value >> 4) & 0x03;
        uint8_t operation_mode = (value >> 1) & 0x07;
        uint8_t bcd_mode = value & 0x01;
        
        // 处理读回命令
        if (counter_select == 3) {  // 读回命令
            printf("执行读回命令
");
            return;
        }
        
        // 打印解析后的控制字信息
        printf("设置计数器%d:", counter_select);
        
        switch (rw_mode) {
            case 0: printf("锁存操作, "); break;
            case 1: printf("只读/写低字节, "); break;
            case 2: printf("只读/写高字节, "); break;
            case 3: printf("先低后高字节, "); break;
        }
        
        printf("模式%d, ", operation_mode);
        printf("%s计数
", bcd_mode ? "BCD" : "二进制");
        
        // 处理锁存命令
        if (rw_mode == 0) {
            timer_state.latch_values[counter_select] = timer_state.counter_values[counter_select];
            timer_state.latch_status[counter_select] = 1;  // 锁存低字节
            printf("锁存计数器%d当前值: %u (0x%04X)
", 
                counter_select, 
                timer_state.latch_values[counter_select],
                timer_state.latch_values[counter_select]);
        }
    } else if (port >= TIMER0_PORT && port <= TIMER2_PORT) {
        // 写入计数器
        uint8_t counter_num = port - TIMER0_PORT;
        uint8_t rw_mode = (timer_state.control_reg >> 4) & 0x03;
        
        // 根据读/写模式处理
        if (rw_mode == 1) {  // 只写低字节
            timer_state.counter_values[counter_num] = (timer_state.counter_values[counter_num] & 0xFF00) | value;
            printf("设置计数器%d低字节: 0x%02X, 当前值: %u (0x%04X)
", 
                counter_num, value, 
                timer_state.counter_values[counter_num],
                timer_state.counter_values[counter_num]);
        } else if (rw_mode == 2) {  // 只写高字节
            timer_state.counter_values[counter_num] = (timer_state.counter_values[counter_num] & 0x00FF) | (value << 8);
            printf("设置计数器%d高字节: 0x%02X, 当前值: %u (0x%04X)
", 
                counter_num, value, 
                timer_state.counter_values[counter_num],
                timer_state.counter_values[counter_num]);
        } else if (rw_mode == 3) {  // 先低后高字节
            static uint8_t lsb_values[3] = {0, 0, 0};
            static bool expecting_msb[3] = {false, false, false};
            
            if (!expecting_msb[counter_num]) {  // 期望低字节
                lsb_values[counter_num] = value;
                expecting_msb[counter_num] = true;
                printf("设置计数器%d低字节: 0x%02X, 等待高字节
", counter_num, value);
            } else {  // 期望高字节
                timer_state.counter_values[counter_num] = (value << 8) | lsb_values[counter_num];
                expecting_msb[counter_num] = false;
                printf("设置计数器%d高字节: 0x%02X, 当前值: %u (0x%04X)
", 
                    counter_num, value, 
                    timer_state.counter_values[counter_num],
                    timer_state.counter_values[counter_num]);
            }
        }
    }
}
 
// 模拟inb - 从端口读取一个字节
uint8_t inb(uint16_t port) {
    uint8_t value = 0;
    
    if (port >= TIMER0_PORT && port <= TIMER2_PORT) {
        uint8_t counter_num = port - TIMER0_PORT;
        uint8_t rw_mode = (timer_state.control_reg >> 4) & 0x03;
        
        // 检查是否有锁存值
        if (timer_state.latch_status[counter_num]) {
            if (timer_state.latch_status[counter_num] == 1) {  // 读取锁存的低字节
                value = timer_state.latch_values[counter_num] & 0xFF;
                if (rw_mode == 3) {  // 如果是先低后高,更新锁存状态
                    timer_state.latch_status[counter_num] = 2;  // 下次读取高字节
                } else {
                    timer_state.latch_status[counter_num] = 0;  // 锁存完成
                }
            } else if (timer_state.latch_status[counter_num] == 2) {  // 读取锁存的高字节
                value = (timer_state.latch_values[counter_num] >> 8) & 0xFF;
                timer_state.latch_status[counter_num] = 0;  // 锁存完成
            }
        } else {
            // 直接读取计数器值
            if (rw_mode == 1) {  // 只读低字节
                value = timer_state.counter_values[counter_num] & 0xFF;
            } else if (rw_mode == 2) {  // 只读高字节
                value = (timer_state.counter_values[counter_num] >> 8) & 0xFF;
            } else if (rw_mode == 3) {  // 先低后高字节
                static bool read_lsb[3] = {true, true, true};
                
                if (read_lsb[counter_num]) {  // 读取低字节
                    value = timer_state.counter_values[counter_num] & 0xFF;
                    read_lsb[counter_num] = false;
                } else {  // 读取高字节
                    value = (timer_state.counter_values[counter_num] >> 8) & 0xFF;
                    read_lsb[counter_num] = true;
                }
            }
        }
    }
    
    printf("从端口0x%04X读取0x%02X
", port, value);
    return value;
}
 
// 设置计数器
void set_timer_counter(uint8_t counter, uint8_t mode, uint16_t count) {
    uint8_t control_byte = 0;
    
    // 构建控制字
    switch (counter) {
        case 0: control_byte |= SC_COUNTER_0; break;
        case 1: control_byte |= SC_COUNTER_1; break;
        case 2: control_byte |= SC_COUNTER_2; break;
        default: return;  // 无效计数器
    }
    
    control_byte |= RW_LSB_MSB;  // 使用先低后高字节模式
    
    switch (mode) {
        case 0: control_byte |= MODE_0; break;
        case 1: control_byte |= MODE_1; break;
        case 2: control_byte |= MODE_2; break;
        case 3: control_byte |= MODE_3; break;
        case 4: control_byte |= MODE_4; break;
        case 5: control_byte |= MODE_5; break;
        default: return;  // 无效模式
    }
    
    control_byte |= COUNT_BINARY;  // 使用二进制计数
    
    // 写入控制字
    printf("
设置计数器%d为模式%d,计数值%u (0x%04X)
", counter, mode, count, count);
    outb(TIMER_CTRL_PORT, control_byte);
    
    // 写入计数值,先低后高
    uint8_t low_byte = count & 0xFF;
    uint8_t high_byte = (count >> 8) & 0xFF;
    
    outb(TIMER0_PORT + counter, low_byte);
    outb(TIMER0_PORT + counter, high_byte);
}
 
// 读取计数器当前值
uint16_t read_timer_counter(uint8_t counter) {
    if (counter > 2) {
        return 0;  // 无效计数器
    }
    
    // 发送锁存命令
    uint8_t control_byte = (counter << 6) | RW_LATCH;
    printf("
读取计数器%d当前值
", counter);
    outb(TIMER_CTRL_PORT, control_byte);
    
    // 读取当前值
    uint8_t low_byte = inb(TIMER0_PORT + counter);
    uint8_t high_byte = inb(TIMER0_PORT + counter);
    
    uint16_t value = (high_byte << 8) | low_byte;
    printf("计数器%d当前值: %u (0x%04X)
", counter, value, value);
    
    return value;
}
 
// 模拟计数器倒计数
void simulate_counting(uint8_t counter, int ticks) {
    printf("
模拟计数器%d倒计数%d个时钟周期
", counter, ticks);
    
    for (int i = 0; i < ticks; i++) {
        if (timer_state.counter_values[counter] > 0) {
            timer_state.counter_values[counter]--;
        } else {
            // 根据模式可能需要重载
            uint8_t mode = (timer_state.control_reg >> 1) & 0x07;
            if (mode == 2 || mode == 3) {  // 模式2和3会自动重载
                timer_state.counter_values[counter] = 0xFFFF;  // 假设初值
                printf("计数器%d重载
", counter);
            }
        }
    }
}
 
int main() {
    printf("8253/8254计时器编程示例
");
    printf("=====================
");
    
    // 配置计数器0为模式3(方波发生器)
    set_timer_counter(0, 3, 1193);  // PC标准配置:1.193182MHz/1000Hz = 1193
    
    // 配置计数器1为模式2(频率发生器)
    set_timer_counter(1, 2, 2000);
    
    // 配置计数器2为模式0(计数终止)
    set_timer_counter(2, 0, 5000);
    
    // 模拟倒计数
    simulate_counting(0, 500);
    simulate_counting(1, 1000);
    simulate_counting(2, 2000);
    
    // 读取当前计数值
    read_timer_counter(0);
    read_timer_counter(1);
    read_timer_counter(2);
    
    return 0;
}

这个程序模拟了8253/8254定时器的编程接口,包括控制字的构建、计数值的设置和读取,以及基本的计数过程。在实际系统中,我们通过向特定端口读写数据来操作定时器,而这个程序则通过模拟函数展示了这个过程。

9.2 8253/8254在PC系统中的应用

8253/8254定时计数器在PC系统中扮演着至关重要的角色,为操作系统和应用程序提供了基本的时序控制和定时服务。

9.2.1 系统定时中断与周期性更新

在标准的PC系统中,8253/8254的计数器0被设置为模式3(方波发生器模式),产生约18.2Hz(精确值为1193182Hz/65536 ≈ 18.2065Hz)的时钟信号。这个信号连接到8259A中断控制器的IRQ0,生成系统的定时中断(INT 08h)。

操作系统使用这个定时中断来维护系统时钟、实现多任务调度和提供各种定时服务。每个定时中断都会增加系统时间计数器,系统通过这个计数器来获取当前时间。

下面是一个模拟PC系统定时中断处理的C语言程序:

c



#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
 
// 模拟PC系统环境
typedef struct {
    uint32_t ticks;             // 系统启动后的tick数
    uint32_t day_ticks;         // 从午夜开始的tick数
    uint8_t timer_interrupt;    // 定时中断号
    bool timer_active;          // 定时器是否激活
    timer_t timer_id;           // POSIX定时器ID
    struct itimerspec timer_spec;  // 定时器规格
} PCSystem;
 
PCSystem pc_system = {
    .ticks = 0,
    .day_ticks = 0,
    .timer_interrupt = 0x08,   // INT 08h
    .timer_active = false
};
 
// 模拟BIOS数据区
typedef struct {
    uint32_t tick_count;        // 从午夜开始的低字节(0x0046C)
    uint8_t day_rollover;       // 天计数进位标志(0x00470)
    uint8_t timer0_divisor[2];  // Timer 0的分频值(0x00471)
} BIOSDataArea;
 
BIOSDataArea bios_data = {
    .tick_count = 0,
    .day_rollover = 0,
    .timer0_divisor = {0xA9, 0x04}  // 1193(0x04A9)
};
 
// 时间结构
typedef struct {
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    uint16_t millisecond;
} SystemTime;
 
// 模拟定时中断处理程序
void timer_interrupt_handler(void) {
    // 更新系统tick计数
    pc_system.ticks++;
    
    // 更新BIOS数据区的tick计数
    bios_data.tick_count++;
    
    // 检查是否过了一天(1天 = 1573040个tick ≈ 24小时)
    if (bios_data.tick_count >= 1573040) {
        bios_data.tick_count = 0;
        bios_data.day_rollover = 1;
    }
}
 
// 模拟IRQ0中断处理过程
void handle_irq0(void) {
    // 调用定时中断处理程序
    timer_interrupt_handler();
}
 
// POSIX信号处理程序,用于模拟硬件中断
void timer_signal_handler(int signum) {
    if (signum == SIGALRM) {
        handle_irq0();
    }
}
 
// 启动系统定时器
void start_system_timer(void) {
    if (pc_system.timer_active) {
        return;  // 已经启动
    }
    
    // 设置POSIX信号处理
    struct sigaction sa;
    sa.sa_handler = timer_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, NULL);
    
    // 创建POSIX定时器
    if (timer_create(CLOCK_REALTIME, NULL, &pc_system.timer_id) == -1) {
        perror("timer_create");
        return;
    }
    
    // 设置为约18.2Hz
    pc_system.timer_spec.it_interval.tv_sec = 0;
    pc_system.timer_spec.it_interval.tv_nsec = 55000000;  // 约18.2Hz
    pc_system.timer_spec.it_value.tv_sec = 0;
    pc_system.timer_spec.it_value.tv_nsec = 55000000;
    
    if (timer_settime(pc_system.timer_id, 0, &pc_system.timer_spec, NULL) == -1) {
        perror("timer_settime");
        return;
    }
    
    pc_system.timer_active = true;
    printf("系统定时器已启动 (18.2Hz)
");
}
 
// 停止系统定时器
void stop_system_timer(void) {
    if (!pc_system.timer_active) {
        return;  // 已经停止
    }
    
    // 停止POSIX定时器
    pc_system.timer_spec.it_interval.tv_sec = 0;
    pc_system.timer_spec.it_interval.tv_nsec = 0;
    pc_system.timer_spec.it_value.tv_sec = 0;
    pc_system.timer_spec.it_value.tv_nsec = 0;
    
    if (timer_settime(pc_system.timer_id, 0, &pc_system.timer_spec, NULL) == -1) {
        perror("timer_settime");
        return;
    }
    
    pc_system.timer_active = false;
    printf("系统定时器已停止
");
}
 
// 获取当前系统时间
SystemTime get_system_time(void) {
    SystemTime time;
    uint32_t total_seconds = pc_system.ticks / 18;  // 近似每秒18个tick
    
    time.hour = (total_seconds / 3600) % 24;
    time.minute = (total_seconds / 60) % 60;
    time.second = total_seconds % 60;
    time.millisecond = (pc_system.ticks % 18) * (1000 / 18);  // 每个tick约55ms
    
    return time;
}
 
// 打印系统状态
void print_system_status(void) {
    SystemTime time = get_system_time();
    
    printf("
系统状态:
");
    printf("Ticks: %u
", pc_system.ticks);
    printf("BIOS Tick计数: %u
", bios_data.tick_count);
    printf("天计数进位: %s
", bios_data.day_rollover ? "是" : "否");
    printf("系统时间: %02d:%02d:%02d.%03d
", 
           time.hour, time.minute, time.second, time.millisecond);
}
 
int main() {
    printf("PC系统定时中断模拟
");
    printf("===============

");
    
    // 显示Timer 0的分频值
    uint16_t divisor = (bios_data.timer0_divisor[1] << 8) | bios_data.timer0_divisor[0];
    printf("Timer 0分频值: %u
", divisor);
    printf("时钟频率: 1193182 Hz / %u = %.2f Hz
", 
           divisor, 1193182.0 / divisor);
    
    // 启动系统定时器
    start_system_timer();
    
    // 运行一段时间并监视系统状态
    for (int i = 0; i < 10; i++) {
        sleep(1);  // 休眠1秒
        print_system_status();
    }
    
    // 停止系统定时器
    stop_system_timer();
    
    return 0;
}

这个程序模拟了PC系统中的定时中断机制,包括18.2Hz的系统时钟、BIOS数据区的tick计数以及基于这些tick的系统时间计算。在实际的PC系统中,这些功能由BIOS和操作系统协同完成。

9.2.2 音频产生与扬声器控制

在早期的PC系统中,计数器2通常连接到PC扬声器,用于产生简单的音频信号。通过将计数器设置为模式3(方波发生器)并选择适当的分频值,可以生成不同频率的方波,驱动扬声器发出不同音调的声音。

PC系统中的扬声器控制端口(61h)用于控制扬声器的开关:

位0:计时器通道2门控(Timer 2 Gate)位1:扬声器数据(Speaker Data)位2:控制扬声器(通道1和2的输出)

下面是一个模拟PC扬声器控制的C语言程序:

c



#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
 
// 8254端口定义
#define TIMER2_PORT     0x42
#define TIMER_CTRL_PORT 0x43
 
// 扬声器控制端口
#define SPEAKER_PORT    0x61
 
// 模拟I/O端口读写函数
void outb(uint16_t port, uint8_t value);
uint8_t inb(uint16_t port);
 
// 模拟的系统状态
typedef struct {
    uint16_t timer2_counter;   // 计数器2的当前值
    uint8_t speaker_control;   // 扬声器控制端口的值
    bool speaker_enabled;      // 扬声器是否启用
} SystemState;
 
SystemState system_state = {
    .timer2_counter = 0,
    .speaker_control = 0,
    .speaker_enabled = false
};
 
// 设置扬声器频率
void set_speaker_frequency(uint16_t frequency) {
    if (frequency == 0) {
        printf("错误: 频率不能为0
");
        return;
    }
    
    // 计算分频值: 1193182 / frequency
    uint16_t divisor = 1193182 / frequency;
    
    printf("设置扬声器频率: %u Hz (分频值: %u)
", frequency, divisor);
    
    // 设置计数器2为模式3(方波发生器)
    outb(TIMER_CTRL_PORT, 0xB6);  // 10 11 011 0 = 选择计数器2,先低后高字节,模式3,二进制计数
    
    // 写入分频值
    outb(TIMER2_PORT, divisor & 0xFF);          // 低字节
    outb(TIMER2_PORT, (divisor >> 8) & 0xFF);   // 高字节
    
    // 保存到模拟状态
    system_state.timer2_counter = divisor;
}
 
// 启用扬声器
void enable_speaker(void) {
    // 读取当前扬声器控制值
    uint8_t speaker_value = inb(SPEAKER_PORT);
    
    // 设置位0和位1以启用扬声器
    speaker_value |= 0x03;
    
    // 写回控制端口
    outb(SPEAKER_PORT, speaker_value);
    
    printf("扬声器已启用
");
    system_state.speaker_enabled = true;
}
 
// 禁用扬声器
void disable_speaker(void) {
    // 读取当前扬声器控制值
    uint8_t speaker_value = inb(SPEAKER_PORT);
    
    // 清除位0和位1以禁用扬声器
    speaker_value &= 0xFC;
    
    // 写回控制端口
    outb(SPEAKER_PORT, speaker_value);
    
    printf("扬声器已禁用
");
    system_state.speaker_enabled = false;
}
 
// 播放一个音调
void play_tone(uint16_t frequency, int duration_ms) {
    printf("
播放音调: %u Hz, 持续 %d ms
", frequency, duration_ms);
    
    // 设置频率
    set_speaker_frequency(frequency);
    
    // 启用扬声器
    enable_speaker();
    
    // 模拟延时
    printf("播放中");
    for (int i = 0; i < duration_ms / 100; i++) {
        printf(".");
        fflush(stdout);
        usleep(100000);  // 100ms
    }
    printf("
");
    
    // 禁用扬声器
    disable_speaker();
}
 
// 播放一个简单的旋律
void play_melody(void) {
    printf("
播放简单旋律
");
    printf("=============
");
    
    // 简单的音符频率表
    const uint16_t C4 = 262;   // 中央C
    const uint16_t D4 = 294;
    const uint16_t E4 = 330;
    const uint16_t F4 = 349;
    const uint16_t G4 = 392;
    const uint16_t A4 = 440;   // 标准A
    const uint16_t B4 = 494;
    const uint16_t C5 = 523;   // 高八度C
    
    // 播放《小星星》旋律
    play_tone(C4, 500);  // 闪
    play_tone(C4, 500);  // 闪
    play_tone(G4, 500);  // 亮
    play_tone(G4, 500);  // 亮
    play_tone(A4, 500);  // 小
    play_tone(A4, 500);  // 星
    play_tone(G4, 1000); // 星
    
    play_tone(F4, 500);  // 眨
    play_tone(F4, 500);  // 眨
    play_tone(E4, 500);  // 眼
    play_tone(E4, 500);  // 睛
    play_tone(D4, 500);  // 放
    play_tone(D4, 500);  // 光
    play_tone(C4, 1000); // 芒
    
    printf("
旋律播放完成
");
}
 
// 模拟outb - 向端口写入一个字节
void outb(uint16_t port, uint8_t value) {
    printf("向端口0x%04X写入0x%02X
", port, value);
    
    // 模拟端口行为
    switch (port) {
        case TIMER_CTRL_PORT:
            // 模拟控制寄存器
            break;
            
        case TIMER2_PORT:
            // 模拟计数器2
            static bool expecting_high_byte = false;
            static uint8_t low_byte = 0;
            
            if (!expecting_high_byte) {
                low_byte = value;
                expecting_high_byte = true;
            } else {
                system_state.timer2_counter = (value << 8) | low_byte;
                expecting_high_byte = false;
            }
            break;
            
        case SPEAKER_PORT:
            // 模拟扬声器控制端口
            system_state.speaker_control = value;
            system_state.speaker_enabled = (value & 0x03) == 0x03;
            break;
    }
}
 
// 模拟inb - 从端口读取一个字节
uint8_t inb(uint16_t port) {
    uint8_t value = 0;
    
    // 模拟端口行为
    switch (port) {
        case SPEAKER_PORT:
            value = system_state.speaker_control;
            break;
    }
    
    printf("从端口0x%04X读取0x%02X
", port, value);
    return value;
}
 
// 打印系统状态
void print_system_status(void) {
    printf("
系统状态:
");
    printf("Timer 2计数值: %u
", system_state.timer2_counter);
    printf("频率: %.2f Hz
", system_state.timer2_counter ? 1193182.0 / system_state.timer2_counter : 0);
    printf("扬声器控制值: 0x%02X
", system_state.speaker_control);
    printf("扬声器状态: %s
", system_state.speaker_enabled ? "启用" : "禁用");
}
 
int main() {
    printf("PC扬声器控制模拟
");
    printf("==============

");
    
    // 初始状态
    print_system_status();
    
    // 测试基本操作
    set_speaker_frequency(1000);  // 1kHz
    print_system_status();
    
    enable_speaker();
    print_system_status();
    
    printf("
模拟播放1kHz音调3秒...
");
    for (int i = 0; i < 30; i++) {
        printf(".");
        fflush(stdout);
        usleep(100000);  // 100ms
    }
    printf("
");
    
    disable_speaker();
    print_system_status();
    
    // 播放旋律
    play_melody();
    
    return 0;
}

这个程序模拟了PC系统中使用8254计数器2和扬声器控制端口来生成音频的过程。它展示了如何设置不同频率、启用/禁用扬声器以及播放简单的旋律。在实际的PC中,这种方法曾被广泛用于游戏和简单的声音提示。

9.2.3 可编程延时与时间测量

8253/8254定时器还可以用于实现精确的时间延迟和测量。通过将计数器设置为特定模式(如模式0或模式2)并加载适当的计数值,可以实现从微秒到秒级的各种延时。

下面是一个使用8254实现精确延时的C语言示例:

c



#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <unistd.h>
 
// 8254端口定义
#define TIMER0_PORT     0x40
#define TIMER1_PORT     0x41
#define TIMER2_PORT     0x42
#define TIMER_CTRL_PORT 0x43
 
// 模拟I/O端口读写函数
void outb(uint16_t port, uint8_t value) {
    // 在实际系统中,这会写入硬件端口
    printf("写端口0x%04X: 0x%02X
", port, value);
}
 
uint8_t inb(uint16_t port) {
    // 在实际系统中,这会从硬件端口读取
    uint8_t value = 0;
    printf("读端口0x%04X: 0x%02X
", port, value);
    return value;
}
 
// 使用Timer 1实现微秒级延时
void delay_us(uint32_t microseconds) {
    if (microseconds == 0) return;
    
    // 确保延时不超过定时器能处理的范围
    if (microseconds > 54925) {
        printf("警告: 延时太长,将被截断
");
        microseconds = 54925;  // 最大约55ms(65535/1.193182MHz)
    }
    
    // 计算计数值
    uint16_t count = (uint16_t)((uint32_t)microseconds * 1193182 / 1000000);
    
    printf("延时%u微秒,计数值=%u
", microseconds, count);
    
    // 配置Timer 1为模式0(计数到0中断)
    outb(TIMER_CTRL_PORT, 0x30);  // 00 11 000 0 = 选择计数器0,读写低高字节,模式0,二进制
    
    // 写入计数值
    outb(TIMER1_PORT, count & 0xFF);          // 低字节
    outb(TIMER1_PORT, (count >> 8) & 0xFF);   // 高字节
    
    // 在实际系统中,我们会等待OUT引脚变为高电平
    // 这里我们使用usleep来模拟延时
    usleep(microseconds);
    
    printf("延时完成
");
}
 
// 使用Timer 1测量事件执行时间
uint32_t measure_execution_time(void (*function)(void)) {
    // 配置Timer 1为模式0(计数到0中断)
    outb(TIMER_CTRL_PORT, 0x30);  // 00 11 000 0 = 选择计数器0,读写低高字节,模式0,二进制
    
    // 加载最大计数值
    outb(TIMER1_PORT, 0xFF);  // 低字节
    outb(TIMER1_PORT, 0xFF);  // 高字节
    
    // 执行被测量函数
    function();
    
    // 读取计数器当前值
    outb(TIMER_CTRL_PORT, 0x00);  // 锁存计数器0
    uint8_t low_byte = inb(TIMER1_PORT);
    uint8_t high_byte = inb(TIMER1_PORT);
    
    // 计算已经过去的计数
    uint16_t counts_elapsed = 0xFFFF - ((high_byte << 8) | low_byte);
    
    // 转换为微秒
    uint32_t microseconds = (uint32_t)((uint64_t)counts_elapsed * 1000000 / 1193182);
    
    return microseconds;
}
 
// 测试函数:执行一些消耗CPU时间的操作
void test_function(void) {
    printf("执行测试函数...
");
    
    // 模拟一些工作
    volatile int sum = 0;
    for (int i = 0; i < 1000000; i++) {
        sum += i;
    }
    
    printf("测试函数完成, 结果: %d
", sum);
}
 
// 使用Timer 2实现可变周期脉冲
void generate_pulse(uint16_t frequency, int duration_ms) {
    printf("
生成频率为%uHz的脉冲,持续%dms
", frequency, duration_ms);
    
    // 计算分频值
    uint16_t divisor = 1193182 / frequency;
    
    // 配置Timer 2为模式3(方波发生器)
    outb(TIMER_CTRL_PORT, 0xB6);  // 10 11 011 0 = 选择计数器2,读写低高字节,模式3,二进制
    
    // 写入分频值
    outb(TIMER2_PORT, divisor & 0xFF);          // 低字节
    outb(TIMER2_PORT, (divisor >> 8) & 0xFF);   // 高字节
    
    // 模拟脉冲生成
    printf("生成脉冲中");
    for (int i = 0; i < duration_ms / 100; i++) {
        printf(".");
        fflush(stdout);
        usleep(100000);  // 100ms
    }
    printf("
完成
");
}
 
// 使用时间测量和延时功能实现精确定时循环
void precise_timing_loop(int iterations, int interval_ms) {
    printf("
开始精确定时循环:%d次迭代,间隔%dms
", iterations, interval_ms);
    
    for (int i = 0; i < iterations; i++) {
        // 记录循环开始时间
        struct timespec start_time;
        clock_gettime(CLOCK_MONOTONIC, &start_time);
        
        // 执行一些操作
        printf("迭代 %d: 执行操作
", i + 1);
        
        // 计算剩余延时时间
        struct timespec current_time;
        clock_gettime(CLOCK_MONOTONIC, &current_time);
        
        int64_t elapsed_ns = (current_time.tv_sec - start_time.tv_sec) * 1000000000 +
                            (current_time.tv_nsec - start_time.tv_nsec);
        
        int64_t target_ns = (int64_t)interval_ms * 1000000;
        int64_t remaining_ns = target_ns - elapsed_ns;
        
        if (remaining_ns > 0) {
            // 使用延时函数等待剩余时间
            int remaining_us = remaining_ns / 1000;
            printf("等待剩余时间: %d微秒
", remaining_us);
            
            // 在实际系统中会使用8254定时器等待
            usleep(remaining_us);
        } else {
            printf("警告: 操作耗时超过间隔!
");
        }
    }
    
    printf("定时循环完成
");
}
 
int main() {
    printf("8254定时器延时与时间测量示例
");
    printf("=======================

");
    
    // 测试微秒级延时
    printf("测试微秒级延时:
");
    printf("--------------
");
    delay_us(1000);   // 1ms
    delay_us(10000);  // 10ms
    delay_us(50000);  // 50ms
    
    // 测试执行时间测量
    printf("
测试执行时间测量:
");
    printf("----------------
");
    uint32_t execution_time = measure_execution_time(test_function);
    printf("函数执行时间: %u微秒 (%.2f毫秒)
", execution_time, execution_time / 1000.0);
    
    // 测试脉冲生成
    printf("
测试脉冲生成:
");
    printf("------------
");
    generate_pulse(100, 2000);   // 100Hz, 2秒
    generate_pulse(1000, 1000);  // 1kHz, 1秒
    
    // 测试精确定时循环
    printf("
测试精确定时循环:
");
    printf("----------------
");
    precise_timing_loop(5, 500);  // 5次,间隔500ms
    
    return 0;
}

这个程序演示了如何使用8254定时器实现微秒级精确延时、测量执行时间、生成特定频率的脉冲以及实现精确的定时循环。在实际的PC系统中,这些技术被广泛用于各种需要精确时序控制的应用,如硬件驱动、实时控制系统和高精度计时。

9.3 定时计数器的扩展应用

除了基本的时钟信号生成和时间测量功能外,8253/8254定时计数器还可以用于更多创新性的应用。通过组合多个计数器或与其他硬件配合,可以实现更复杂的时序控制功能。

9.3.1 多通道同步定时控制

通过同时使用8253/8254的多个计数器,可以实现多通道同步定时控制,适用于需要精确协调多个设备或动作的场景。

下面是一个多通道同步定时控制的示例程序:

c



#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <time.h>
 
// 8254端口定义
#define TIMER0_PORT     0x40
#define TIMER1_PORT     0x41
#define TIMER2_PORT     0x42
#define TIMER_CTRL_PORT 0x43
 
// 模拟I/O端口读写函数
void outb(uint16_t port, uint8_t value) {
    printf("写端口0x%04X: 0x%02X
", port, value);
}
 
uint8_t inb(uint16_t port) {
    static uint8_t counter_values[3] = {0, 0, 0};
    static int counter_index = 0;
    
    uint8_t value = 0;
    
    if (port >= TIMER0_PORT && port <= TIMER2_PORT) {
        counter_index = port - TIMER0_PORT;
        value = counter_values[counter_index];
    }
    
    printf("读端口0x%04X: 0x%02X
", port, value);
    return value;
}
 
// 定义多通道控制器
typedef struct {
    uint16_t periods[3];           // 三个通道的周期(计数值)
    uint16_t counters[3];          // 三个通道的当前计数值
    bool outputs[3];               // 三个通道的输出状态
    bool active[3];                // 三个通道的激活状态
    uint32_t trigger_time_us[3];   // 三个通道的触发时间(微秒)
    void (*callbacks[3])(void);    // 三个通道的回调函数
} MultiChannelTimer;
 
// 全局实例
MultiChannelTimer timer_controller = {
    .periods = {0, 0, 0},
    .counters = {0, 0, 0},
    .outputs = {false, false, false},
    .active = {false, false, false},
    .trigger_time_us = {0, 0, 0},
    .callbacks = {NULL, NULL, NULL}
};
 
// 获取当前时间(微秒)
uint64_t get_current_time_us(void) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (uint64_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
}
 
// 配置单个计时器通道
void configure_timer_channel(uint8_t channel, uint16_t frequency, void (*callback)(void)) {
    if (channel > 2) {
        printf("错误: 无效的通道号%d
", channel);
        return;
    }
    
    printf("
配置通道%d: 频率=%uHz
", channel, frequency);
    
    // 计算分频值
    uint16_t divisor = frequency > 0 ? 1193182 / frequency : 0;
    
    // 保存配置
    timer_controller.periods[channel] = divisor;
    timer_controller.counters[channel] = divisor;
    timer_controller.outputs[channel] = false;
    timer_controller.active[channel] = (frequency > 0);
    timer_controller.callbacks[channel] = callback;
    
    // 实际硬件配置
    uint8_t command = (channel << 6) | 0x34;  // xx 11 010 0 = 选择通道, 读写低高字节, 模式2(频率发生器), 二进制
    outb(TIMER_CTRL_PORT, command);
    
    outb(TIMER0_PORT + channel, divisor & 0xFF);          // 低字节
    outb(TIMER0_PORT + channel, (divisor >> 8) & 0xFF);   // 高字节
    
    printf("通道%d已配置: 分频值=%u
", channel, divisor);
}
 
// 启动所有通道的同步操作
void start_synchronized_timers(void) {
    printf("
启动同步定时器
");
    
    // 记录启动时间
    uint64_t start_time = get_current_time_us();
    
    // 设置所有通道的触发时间
    for (int i = 0; i < 3; i++) {
        if (timer_controller.active[i]) {
            timer_controller.counters[i] = timer_controller.periods[i];
            timer_controller.outputs[i] = false;
            timer_controller.trigger_time_us[i] = start_time;
            
            printf("通道%d已启动
", i);
        }
    }
}
 
// 停止所有通道
void stop_all_timers(void) {
    printf("
停止所有定时器
");
    
    for (int i = 0; i < 3; i++) {
        timer_controller.active[i] = false;
        printf("通道%d已停止
", i);
    }
}
 
// 模拟定时器滴答
void simulate_timer_tick(void) {
    uint64_t current_time = get_current_time_us();
    
    for (int i = 0; i < 3; i++) {
        if (!timer_controller.active[i] || timer_controller.periods[i] == 0) {
            continue;
        }
        
        // 计算自上次触发以来经过的时间
        uint64_t elapsed_us = current_time - timer_controller.trigger_time_us[i];
        
        // 计算对应的时钟滴答数(假设每个滴答是1.193182MHz的周期)
        uint64_t ticks = elapsed_us * 1193182 / 1000000;
        
        // 更新计数器
        if (ticks > 0) {
            uint16_t ticks_to_apply = (ticks > 0xFFFF) ? 0xFFFF : (uint16_t)ticks;
            
            if (ticks_to_apply >= timer_controller.counters[i]) {
                // 计数器到达零,触发事件
                timer_controller.outputs[i] = !timer_controller.outputs[i];
                
                // 调用回调函数
                if (timer_controller.callbacks[i] != NULL) {
                    printf("通道%d触发回调
", i);
                    timer_controller.callbacks[i]();
                }
                
                // 重装载计数器
                uint16_t overflows = ticks_to_apply / timer_controller.periods[i];
                uint16_t remainder = ticks_to_apply % timer_controller.periods[i];
                
                if (overflows % 2 == 1) {
                    // 奇数次溢出,输出状态翻转
                    timer_controller.outputs[i] = !timer_controller.outputs[i];
                }
                
                timer_controller.counters[i] = timer_controller.periods[i] - remainder;
                
                // 更新触发时间
                timer_controller.trigger_time_us[i] = current_time - 
                    remainder * 1000000 / 1193182;
            } else {
                // 更新计数器
                timer_controller.counters[i] -= ticks_to_apply;
                
                // 更新触发时间
                timer_controller.trigger_time_us[i] = current_time;
            }
        }
    }
}
 
// 打印定时器状态
void print_timer_status(void) {
    printf("
定时器状态:
");
    printf("----------
");
    
    for (int i = 0; i < 3; i++) {
        printf("通道%d: ", i);
        printf("激活=%s, ", timer_controller.active[i] ? "是" : "否");
        printf("周期=%u, ", timer_controller.periods[i]);
        printf("计数值=%u, ", timer_controller.counters[i]);
        printf("输出=%s
", timer_controller.outputs[i] ? "高" : "低");
    }
}
 
// 各通道的回调函数
void channel0_callback(void) {
    printf("通道0事件: 系统时钟滴答
");
}
 
void channel1_callback(void) {
    printf("通道1事件: DRAM刷新请求
");
}
 
void channel2_callback(void) {
    printf("通道2事件: 扬声器输出
");
}
 
// 进行事件循环,处理同步定时器事件
void event_loop(int duration_ms) {
    printf("
开始事件循环 (%dms)
", duration_ms);
    printf("------------------
");
    
    uint64_t start_time = get_current_time_us();
    uint64_t end_time = start_time + duration_ms * 1000;
    
    while (get_current_time_us() < end_time) {
        // 模拟定时器滴答
        simulate_timer_tick();
        
        // 每100ms打印一次状态
        static uint64_t last_print_time = 0;
        uint64_t current_time = get_current_time_us();
        
        if (current_time - last_print_time >= 100000) {
            print_timer_status();
            last_print_time = current_time;
        }
        
        // 降低CPU使用率
        usleep(10000);  // 10ms
    }
    
    printf("
事件循环结束
");
}
 
int main() {
    printf("多通道同步定时控制示例
");
    printf("==================

");
    
    // 配置三个通道
    configure_timer_channel(0, 100, channel0_callback);    // 100Hz - 系统时钟
    configure_timer_channel(1, 1000, channel1_callback);   // 1kHz - DRAM刷新
    configure_timer_channel(2, 440, channel2_callback);    // 440Hz - 标准A音调
    
    // 打印初始状态
    print_timer_status();
    
    // 启动同步操作
    start_synchronized_timers();
    
    // 运行事件循环
    event_loop(2000);  // 运行2秒
    
    // 停止所有通道
    stop_all_timers();
    
    // 打印最终状态
    print_timer_status();
    
    return 0;
}

这个程序演示了如何使用8254的三个通道实现同步定时控制。每个通道可以配置不同的频率和回调函数,但它们的操作是同步协调的。这种方法在需要多个定时信号协同工作的场景(如多轴控制系统、多通道数据采集等)中非常有用。

9.3.2 高精度PWM信号生成

通过特殊配置8253/8254计数器,可以生成具有可变占空比的PWM(脉宽调制)信号,用于电机控制、LED亮度调节等应用。

下面是一个使用8254生成PWM信号的示例程序:

c



#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <math.h>
 
// 8254端口定义
#define TIMER0_PORT     0x40
#define TIMER1_PORT     0x41
#define TIMER2_PORT     0x42
#define TIMER_CTRL_PORT 0x43
 
// 模拟I/O端口读写函数
void outb(uint16_t port, uint8_t value) {
    printf("写端口0x%04X: 0x%02X
", port, value);
}
 
uint8_t inb(uint16_t port) {
    uint8_t value = 0;
    printf("读端口0x%04X: 0x%02X
", port, value);
    return value;
}
 
// PWM控制器结构
typedef struct {
    uint8_t channel;             // 使用的8254通道
    uint16_t period;             // PWM周期(计数值)
    uint16_t duty_cycle;         // 占空比(计数值)
    uint8_t duty_percent;        // 占空比百分比
    bool output_state;           // 当前输出状态
    bool active;                 // 是否活动
} PWMController;
 
// 全局PWM控制器
PWMController pwm_controller = {
    .channel = 2,                // 默认使用通道2
    .period = 0,
    .duty_cycle = 0,
    .duty_percent = 0,
    .output_state = false,
    .active = false
};
 
// 初始化PWM控制器
void init_pwm_controller(uint8_t channel, uint16_t frequency) {
    if (channel > 2) {
        printf("错误: 无效的通道号%d
", channel);
        return;
    }
    
    printf("
初始化PWM控制器: 通道=%d, 频率=%dHz
", channel, frequency);
    
    // 保存通道
    pwm_controller.channel = channel;
    
    // 计算周期值
    pwm_controller.period = frequency > 0 ? 1193182 / frequency : 0;
    pwm_controller.duty_cycle = 0;
    pwm_controller.duty_percent = 0;
    pwm_controller.output_state = false;
    pwm_controller.active = false;
    
    printf("初始化完成: 周期值=%d
", pwm_controller.period);
}
 
// 生成PWM信号,使用两种方法实现:
// 方法1:使用模式3方波,通过快速重新加载不同的计数值来改变占空比
void generate_pwm_mode3(uint8_t duty_percent) {
    if (duty_percent > 100) {
        duty_percent = 100;
    }
    
    // 保存占空比设置
    pwm_controller.duty_percent = duty_percent;
    
    // 计算占空比对应的计数值
    pwm_controller.duty_cycle = (uint16_t)((uint32_t)pwm_controller.period * duty_percent / 100);
    
    printf("
设置PWM占空比: %d%% (计数值=%d/%d)
", 
        duty_percent, pwm_controller.duty_cycle, pwm_controller.period);
    
    // 配置通道为模式3(方波发生器)
    uint8_t command = (pwm_controller.channel << 6) | 0x36;  // xx 11 011 0
    outb(TIMER_CTRL_PORT, command);
    
    // 设置计数器的值
    uint16_t count_value;
    
    if (duty_percent == 0) {
        count_value = 1;  // 特殊情况:0%占空比
    } else if (duty_percent == 100) {
        count_value = 2;  // 特殊情况:100%占空比
    } else {
        count_value = pwm_controller.duty_cycle;
    }
    
    outb(TIMER0_PORT + pwm_controller.channel, count_value & 0xFF);
    outb(TIMER0_PORT + pwm_controller.channel, (count_value >> 8) & 0xFF);
    
    pwm_controller.active = true;
    pwm_controller.output_state = (duty_percent > 50);
    
    printf("PWM信号已启动 (模式3)
");
}
 
// 方法2:使用模式0和外部中断实现更精确的PWM
void simulate_pwm_mode0(uint8_t duty_percent, int duration_ms) {
    if (duty_percent > 100) {
        duty_percent = 100;
    }
    
    // 保存占空比设置
    pwm_controller.duty_percent = duty_percent;
    
    // 计算占空比对应的计数值
    pwm_controller.duty_cycle = (uint16_t)((uint32_t)pwm_controller.period * duty_percent / 100);
    
    printf("
模拟PWM占空比: %d%% (计数值=%d/%d), 持续%dms
", 
        duty_percent, pwm_controller.duty_cycle, pwm_controller.period, duration_ms);
    
    // 对于完全开/关的情况,简化处理
    if (duty_percent == 0) {
        printf("输出恒定低电平
");
        pwm_controller.output_state = false;
        return;
    } else if (duty_percent == 100) {
        printf("输出恒定高电平
");
        pwm_controller.output_state = true;
        return;
    }
    
    // 模拟PWM循环
    int cycles = duration_ms / ((pwm_controller.period * 1000) / 1193182);
    printf("模拟%d个PWM周期
", cycles);
    
    pwm_controller.active = true;
    
    for (int i = 0; i < cycles; i++) {
        // 高电平部分
        pwm_controller.output_state = true;
        printf("周期%d: 输出高电平 (%d个单位)
", i + 1, pwm_controller.duty_cycle);
        
        // 模拟高电平持续时间
        usleep(pwm_controller.duty_cycle * 1000000 / 1193182);
        
        // 低电平部分
        pwm_controller.output_state = false;
        printf("周期%d: 输出低电平 (%d个单位)
", i + 1, pwm_controller.period - pwm_controller.duty_cycle);
        
        // 模拟低电平持续时间
        usleep((pwm_controller.period - pwm_controller.duty_cycle) * 1000000 / 1193182);
    }
    
    pwm_controller.active = false;
    printf("PWM模拟结束
");
}
 
// 生成渐变PWM效果
void generate_fade_effect(int cycles, int step_ms) {
    printf("
生成PWM渐变效果: %d个循环, 步长%dms
", cycles, step_ms);
    
    for (int cycle = 0; cycle < cycles; cycle++) {
        printf("
循环 %d/%d:
", cycle + 1, cycles);
        
        // 渐亮
        printf("渐亮过程:
");
        for (int duty = 0; duty <= 100; duty += 5) {
            simulate_pwm_mode0(duty, step_ms);
        }
        
        // 渐暗
        printf("渐暗过程:
");
        for (int duty = 100; duty >= 0; duty -= 5) {
            simulate_pwm_mode0(duty, step_ms);
        }
    }
    
    printf("
渐变效果完成
");
}
 
// 生成特殊的呼吸灯效果
void generate_breathing_effect(int duration_seconds) {
    printf("
生成呼吸灯效果: %d秒
", duration_seconds);
    
    // 模拟时间
    int steps = duration_seconds * 10;  // 每秒10个采样点
    
    for (int i = 0; i < steps; i++) {
        // 使用正弦函数生成平滑的呼吸效果
        double position = (double)i / steps * 2.0 * M_PI;
        double sin_value = sin(position);
        int duty = (int)((sin_value + 1.0) * 50);  // 0-100范围
        
        printf("时间点 %d/%d: 占空比 %d%%
", i + 1, steps, duty);
        
        // 生成对应占空比的PWM
        simulate_pwm_mode0(duty, 100);  // 100ms每步
    }
    
    printf("
呼吸灯效果完成
");
}
 
// 模拟电机控制
void simulate_motor_control(void) {
    printf("
模拟电机控制:
");
    printf("-----------
");
    
    printf("
1. 缓慢启动
");
    for (int speed = 0; speed <= 100; speed += 10) {
        printf("电机速度: %d%%
", speed);
        simulate_pwm_mode0(speed, 500);  // 500ms每步
    }
    
    printf("
2. 全速运行
");
    simulate_pwm_mode0(100, 2000);  // 2秒全速
    
    printf("
3. 缓慢减速
");
    for (int speed = 100; speed >= 0; speed -= 10) {
        printf("电机速度: %d%%
", speed);
        simulate_pwm_mode0(speed, 500);  // 500ms每步
    }
    
    printf("
电机控制模拟完成
");
}
 
int main() {
    printf("8254高精度PWM信号生成示例
");
    printf("======================

");
    
    // 初始化PWM控制器
    init_pwm_controller(2, 100);  // 通道2, 100Hz PWM
    
    // 测试不同占空比
    printf("
测试不同占空比:
");
    printf("-------------
");
    
    simulate_pwm_mode0(25, 1000);   // 25%占空比,持续1秒
    simulate_pwm_mode0(50, 1000);   // 50%占空比,持续1秒
    simulate_pwm_mode0(75, 1000);   // 75%占空比,持续1秒
    
    // 测试渐变效果
    generate_fade_effect(2, 100);  // 2个循环,每步100ms
    
    // 测试呼吸灯效果
    generate_breathing_effect(5);  // 5秒
    
    // 模拟电机控制
    simulate_motor_control();
    
    return 0;
}

这个程序演示了如何使用8254定时器生成PWM信号,并通过控制占空比来实现各种效果,如LED亮度渐变、呼吸灯和电机速度控制。通过改变占空比,可以精确控制输出功率,这在各种控制应用中非常有用。

9.3.3 频率计数与事件计时

8253/8254还可以用作频率计或事件计数器,测量外部信号的频率或计数事件的发生次数。

下面是一个使用8254实现频率计数和事件计时的示例程序:

c



#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
 
// 8254端口定义
#define TIMER0_PORT     0x40
#define TIMER1_PORT     0x41
#define TIMER2_PORT     0x42
#define TIMER_CTRL_PORT 0x43
 
// 模拟I/O端口读写函数
void outb(uint16_t port, uint8_t value) {
    printf("写端口0x%04X: 0x%02X
", port, value);
}
 
uint8_t inb(uint16_t port) {
    // 模拟返回随机值
    uint8_t value = rand() & 0xFF;
    printf("读端口0x%04X: 0x%02X
", port, value);
    return value;
}
 
// 模拟频率计结构
typedef struct {
    uint16_t gate_time_ms;      // 门控时间(毫秒)
    uint32_t reference_count;    // 参考计数器值
    uint32_t input_count;        // 输入信号计数
    double frequency;            // 计算的频率
} FrequencyCounter;
 
// 全局频率计
FrequencyCounter freq_counter = {
    .gate_time_ms = 1000,       // 默认门控时间1秒
    .reference_count = 0,
    .input_count = 0,
    .frequency = 0.0
};
 
// 配置频率计
void configure_frequency_counter(uint16_t gate_time_ms) {
    printf("
配置频率计: 门控时间=%dms
", gate_time_ms);
    
    freq_counter.gate_time_ms = gate_time_ms;
    
    // 配置计数器0作为参考定时器
    outb(TIMER_CTRL_PORT, 0x34);  // 00 11 010 0 = 计数器0,先低后高字节,模式2,二进制
    
    // 使用最大值作为计数值(用于参考计时)
    outb(TIMER0_PORT, 0xFF);
    outb(TIMER0_PORT, 0xFF);
    
    // 配置计数器1作为事件计数器
    outb(TIMER_CTRL_PORT, 0x74);  // 01 11 010 0 = 计数器1,先低后高字节,模式2,二进制
    
    // 使用最大值作为计数值
    outb(TIMER1_PORT, 0xFF);
    outb(TIMER1_PORT, 0xFF);
    
    printf("频率计配置完成
");
}
 
// 读取计数器值
uint16_t read_counter(uint8_t counter) {
    // 锁存计数器的当前值
    outb(TIMER_CTRL_PORT, 0x00 | (counter << 6));
    
    // 读取计数值
    uint8_t low = inb(TIMER0_PORT + counter);
    uint8_t high = inb(TIMER0_PORT + counter);
    
    return (high << 8) | low;
}
 
// 测量频率
double measure_frequency(uint32_t input_signal_hz) {
    printf("
开始频率测量: 输入信号=%uHz
", input_signal_hz);
    
    // 在实际系统中,我们会读取计数器的初始值,然后等待门控时间,再读取最终值
    // 这里我们使用模拟数据
    
    // 计算参考计数器在门控时间内的变化
    uint32_t reference_ticks = (uint32_t)(1193182UL * freq_counter.gate_time_ms / 1000);
    freq_counter.reference_count = reference_ticks;
    
    // 计算输入信号在门控时间内的计数
    freq_counter.input_count = (uint32_t)(input_signal_hz * freq_counter.gate_time_ms / 1000);
    
    // 计算频率: 输入计数 * (1193182 / 参考计数)
    freq_counter.frequency = (double)freq_counter.input_count * 1193182.0 / reference_ticks;
    
    printf("测量结果:
");
    printf("参考计数: %u
", freq_counter.reference_count);
    printf("输入计数: %u
", freq_counter.input_count);
    printf("计算频率: %.2f Hz
", freq_counter.frequency);
    
    return freq_counter.frequency;
}
 
// 事件计数器结构
typedef struct {
    uint8_t counter;            // 使用的计数器
    uint16_t initial_value;     // 初始计数值
    uint16_t current_value;     // 当前计数值
    uint32_t total_events;      // 总事件数
    bool active;                // 是否活动
} EventCounter;
 
// 全局事件计数器
EventCounter event_counter = {
    .counter = 2,              // 使用计数器2
    .initial_value = 0,
    .current_value = 0,
    .total_events = 0,
    .active = false
};
 
// 初始化事件计数器
void init_event_counter(uint8_t counter, uint16_t preset) {
    if (counter > 2) {
        printf("错误: 无效的计数器编号%d
", counter);
        return;
    }
    
    printf("
初始化事件计数器: 计数器=%d, 预设值=%d
", counter, preset);
    
    event_counter.counter = counter;
    event_counter.initial_value = preset;
    event_counter.current_value = preset;
    event_counter.total_events = 0;
    event_counter.active = false;
    
    // 配置计数器为模式0(计数器模式)
    outb(TIMER_CTRL_PORT, (counter << 6) | 0x30);  // xx 11 000 0 = 计数器x, 先低后高, 模式0, 二进制
    
    // 设置初始值
    outb(TIMER0_PORT + counter, preset & 0xFF);
    outb(TIMER0_PORT + counter, (preset >> 8) & 0xFF);
    
    printf("事件计数器已初始化
");
}
 
// 开始事件计数
void start_event_counting(void) {
    printf("
开始事件计数
");
    
    // 重置计数器到初始值
    event_counter.current_value = event_counter.initial_value;
    
    // 写入初始值
    outb(TIMER0_PORT + event_counter.counter, event_counter.initial_value & 0xFF);
    outb(TIMER0_PORT + event_counter.counter, (event_counter.initial_value >> 8) & 0xFF);
    
    event_counter.active = true;
    printf("事件计数已开始
");
}
 
// 停止事件计数并获取结果
uint32_t stop_event_counting(void) {
    if (!event_counter.active) {
        printf("
错误: 事件计数器未启动
");
        return 0;
    }
    
    printf("
停止事件计数
");
    
    // 读取当前计数值
    uint16_t final_value = read_counter(event_counter.counter);
    
    // 计算发生的事件数
    uint32_t events = event_counter.initial_value - final_value;
    event_counter.total_events += events;
    event_counter.current_value = final_value;
    event_counter.active = false;
    
    printf("计数结果:
");
    printf("初始值: %u
", event_counter.initial_value);
    printf("最终值: %u
", final_value);
    printf("本次事件数: %u
", events);
    printf("总事件数: %u
", event_counter.total_events);
    
    return events;
}
 
// 模拟产生事件
void simulate_events(int event_count, int interval_ms) {
    printf("
模拟%d个事件,间隔%dms
", event_count, interval_ms);
    
    for (int i = 0; i < event_count; i++) {
        printf("事件 %d/%d
", i + 1, event_count);
        
        // 模拟事件发生导致计数器递减
        if (event_counter.current_value > 0) {
            event_counter.current_value--;
        }
        
        usleep(interval_ms * 1000);
    }
}
 
// 测量时间间隔
uint32_t measure_time_interval(void (*function)(void), const char *name) {
    printf("
测量时间间隔: %s
", name);
    
    // 配置计数器0为模式0(计数器模式)
    outb(TIMER_CTRL_PORT, 0x30);  // 00 11 000 0 = 计数器0, 先低后高, 模式0, 二进制
    
    // 设置最大初始值
    outb(TIMER0_PORT, 0xFF);
    outb(TIMER0_PORT, 0xFF);
    
    // 读取初始值
    uint16_t start_value = read_counter(0);
    
    // 执行被测量函数
    function();
    
    // 读取最终值
    uint16_t end_value = read_counter(0);
    
    // 计算时间间隔(微秒)
    uint32_t counts = start_value - end_value;
    uint32_t microseconds = (uint32_t)((uint64_t)counts * 1000000 / 1193182);
    
    printf("时间测量结果:
");
    printf("初始计数: %u
", start_value);
    printf("最终计数: %u
", end_value);
    printf("计数差: %u
", counts);
    printf("时间间隔: %u微秒 (%.2f毫秒)
", microseconds, microseconds / 1000.0);
    
    return microseconds;
}
 
// 用于测量的测试函数
void test_function_short(void) {
    printf("执行短耗时操作...
");
    usleep(50000);  // 50ms
}
 
void test_function_long(void) {
    printf("执行长耗时操作...
");
    usleep(500000);  // 500ms
}
 
int main() {
    printf("8254频率计数与事件计时示例
");
    printf("========================

");
    
    // 初始化随机数生成器
    srand(time(NULL));
    
    // 1. 频率计测量
    configure_frequency_counter(1000);  // 1秒门控时间
    
    // 测量不同频率
    measure_frequency(1000);    // 1kHz
    measure_frequency(10000);   // 10kHz
    measure_frequency(100000);  // 100kHz
    
    // 2. 事件计数
    init_event_counter(2, 100);  // 计数器2,初始值100
    
    // 开始计数
    start_event_counting();
    
    // 模拟事件发生
    simulate_events(50, 20);  // 50个事件,间隔20ms
    
    // 停止计数并获取结果
    stop_event_counting();
    
    // 再次启动计数器,累计更多事件
    start_event_counting();
    simulate_events(25, 40);  // 25个事件,间隔40ms
    stop_event_counting();
    
    // 3. 时间间隔测量
    measure_time_interval(test_function_short, "短操作");
    measure_time_interval(test_function_long, "长操作");
    
    return 0;
}

这个程序演示了如何使用8254定时器实现频率测量、事件计数和时间间隔测量。通过这些功能,可以测量外部信号的频率、计数事件的发生次数以及测量操作的执行时间,这在许多测试、测量和控制应用中都非常有用。

习题9

简述8253/8254定时计数器的基本原理和主要功能。

8253/8254有哪几种工作模式?请解释每种模式的特点和应用场景。

在PC系统中,8253/8254的三个计数器分别有什么典型用途?

写一个C语言函数,配置8254的计数器0为方波发生器模式,频率为1kHz。

如何使用8254实现精确的毫秒级延时?给出实现代码。

简述如何使用8254产生可变占空比的PWM信号,并说明其应用场景。

在PC系统中,系统时钟是多少Hz?这个频率是如何通过8254配置产生的?

设计一个使用8254计数器的频率计,能够测量1Hz-1MHz范围内的输入信号频率。

© 版权声明

相关文章

暂无评论

none
暂无评论...