中国DOS联盟论坛

中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --

联盟域名:www.cn-dos.net  论坛域名:www.cn-dos.net/forum
DOS,代表着自由开放与发展,我们努力起来,学习FreeDOS和Linux的自由开放与GNU精神,共同创造和发展美好的自由与GNU GPL世界吧!

游客:  注册 | 登录 | 命令行 | 搜索 | 上传 | 帮助 »
English/Chinese Fix Translation
作者:
标题: Turbo c3.0下的8253定时器中断 上一主题 | 下一主题
zzq1986
新手上路





积分 8
发帖 3
注册 2010-3-25
状态 离线
『楼 主』:  Turbo c3.0下的8253定时器中断 [已使用 LLM 解释]

刚刚开始接触dos,由于要求工控机的I/O口发送出1K-10K的方波,在windows下无法实现,故改用dos7.1(在论坛下载,十分感谢),准备用8253的定时器来做。
看了几个例子,就是不知道怎么跳进这个8253中断的,目前的测试程序就这么长,请高手解答,帮忙看下缺了什么。
#include“dos.h”
void interrupt intp()
{
enable();
}
void main()
{
disable();
outportb(0x43 ,0x11); //选中8253计数器0,工作于方式0
outportb(0x40, 50); //是设置计数初值为50
enable();
}
LLM 解释

代码问题与关键缺失分析

你的代码存在核心逻辑错误关键步骤缺失,无法触发8253定时器中断。以下是具体问题与技术解释:

1. 8253定时器中断的触发前提(你未满足)

8253的计数器0是系统定时器(IRQ0)的来源,其输出连接到中断控制器8259的IRQ0引脚。要触发中断,必须满足:

  • 计数器0工作在方式2(速率波)方式3(方波)(方式0是“计数结束中断”,仅触发一次,且默认被系统用于时钟中断,不应修改);
  • 计数初值需正确设置(需考虑8253的二进制/BCD编码16位初值的分两次写入);
  • 必须注册中断处理函数到IRQ0对应的中断向量(即中断号0x08);
  • 若修改了计数器0,需确保系统时钟中断的兼容性(否则DOS系统会崩溃,因为DOS依赖IRQ0维护时间片、进程切换等)。

2. 你的代码的具体错误

  • 错误1:工作方式选择错误
    outportb(0x43, 0x11) 表示:计数器0(0x10)、只写低8位(0x01)、方式0(0x00)、二进制编码(0x00)。方式0是“计数到0后输出高电平”,仅触发一次中断,且无法持续产生方波。若要产生1K-10K方波,应选择方式3(方波),控制字应为 0x36(计数器0、16位初值、方式3、二进制编码)。

  • 错误2:未注册中断向量
    你定义了 intp() 中断函数,但未通过 setvect(0x08, intp) 将其注册到IRQ0的中断向量(0x08),因此即使计数器触发中断,CPU也不会执行你的处理函数。

  • 错误3:计数初值设置不完整
    8253的16位初值需分两次写入(低8位→高8位)。你仅写了低8位 outportb(0x40, 50),高8位未设置,导致实际初值为 0x0032(而非你预期的50),且若初值>255会完全错误。

  • 错误4:系统兼容性问题
    直接修改计数器0会覆盖DOS的系统时钟中断,导致系统时间混乱、进程调度失败,最终崩溃。若要产生方波,更合理的选择是使用计数器2(连接到扬声器,IRQ9,不影响系统时钟)。

3. 正确的方波生成方案(基于计数器2,不影响系统)

若目标是通过I/O口(如扬声器)输出方波,应使用8253的计数器2(而非计数器0),因为计数器2的输出连接到扬声器(通过8255的PB1引脚控制开关)。以下是关键步骤:

  1. 设置计数器2为方式3(方波),控制字 0xB6(计数器2、16位初值、方式3、二进制编码);
  2. 写入16位计数初值(计算公式:初值 = 1.19318MHz / 目标频率,1.19318MHz是8253的输入时钟);
  3. 通过8255的PB1引脚(端口0x61)打开扬声器开关。

示例代码(非中断方式,直接控制方波输出)

#include <dos.h>

// 输出指定频率的方波(通过计数器2和扬声器)
void play_square_wave(unsigned int freq) {
    unsigned int count = 1193180 / freq; // 1.19318MHz / 目标频率
    disable();
    // 步骤1:设置计数器2为方式3(方波)
    outportb(0x43, 0xB6); // 0xB6 = 1011 0110 → 计数器2、16位、方式3、二进制
    // 步骤2:写入16位计数初值(低8位→高8位)
    outportb(0x42, count & 0xFF);   // 低8位
    outportb(0x42, (count >> 8) & 0xFF); // 高8位
    // 步骤3:打开扬声器(设置PB1和PB0为1)
    outportb(0x61, inportb(0x61) | 0x03);
    enable();
}

// 关闭方波输出
void stop_square_wave() {
    disable();
    outportb(0x61, inportb(0x61) & 0xFC); // 关闭PB1和PB0
    enable();
}

void main() {
    play_square_wave(1000); // 输出1KHz方波
    delay(1000);            // 持续1秒
    stop_square_wave();     // 停止输出
}

4. 若必须使用中断方式(如控制I/O口输出)

若需通过中断触发I/O口输出(而非直接控制扬声器),应:

  • 使用计数器1(未被系统占用,输出不连接中断,但可通过查询或外部电路触发中断);
  • 或使用计数器2配合8259的IRQ9(需手动将计数器2的输出连接到IRQ9,需硬件支持);
  • 注册中断处理函数时,需保存原中断向量,并在程序结束时恢复(否则会导致系统不稳定)。

当年常见误解的原因

你对“如何跳进8253中断”的困惑,源于当年资料对8253与8259的硬件连接关系描述不清晰,且很多例子未强调“系统定时器(计数器0)不可随意修改”。此外,8253的控制字编码(4位功能字段)和16位初值的写入方式,容易因文档翻译误差或示例代码省略注释而被误解。

补充建议

若你的工控机需要输出1K-10K方波,优先使用计数器2+扬声器的方案(无需中断,简单可靠)。若需通过其他I/O口输出,可在计数器2的中断处理函数中控制I/O口电平翻转(但需注意中断频率与方波频率的关系:方波周期=2×中断周期)。

另外,论坛中后续若有提到“保存原中断向量”“使用方式3”“分两次写初值”的回复,均是正确的关键补充。



2010-3-28 11:00
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复
zzq1986
新手上路





积分 8
发帖 3
注册 2010-3-25
状态 离线
『第 2 楼』:   使用 LLM 解释/回答一下

或者说我求一个turbo c下的利用中断方式控制扬声器的源代码,小弟在此十分感谢


2010-3-28 11:25
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复

请注意:您目前尚未注册或登录,请您注册登录以使用论坛的各项功能,例如发表和回复帖子等。


可打印版本 | 推荐给朋友 | 订阅主题 | 收藏主题



论坛跳转: