乐者为王

Do one thing, and do it well.

C语言的扬声器发声程序

计算机的主板通常装有8253/8254定时与计数器芯片和8255可编程并行接口芯片,由它们组成的硬件电路可用来产生扬声器的声音。目前的计算机由于采用了超大规模集成电路,因而看不到这些芯片,它们均集成在外围电路芯片中。以下是扬声器的电路图:

speaker-sound

使用程序对这些电路编程可以控制声音的长短和音调的高低。在扬声器电路中,定时器的频率决定了扬声器发音的频率,所以可通过设定定时器电路的频率来使扬声器发出不同的声音。对定时器电路进行频率设定时,首先对其命令寄存器端口地址0x43写命令来选择定时器的通道,接着向计数寄存器端口地址0x42发送频率计数值,先送低8位,后送高8位。通过这两步使定时器电路产生一系列的方波信号,此信号能否推动扬声器发音,还要看由8255产生的送数信号和门控信号是否为1,而它们也是可以编程的,端口地址为0x61。以下是完整的实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <dos.h>
#include <conio.h>

#define FREQ 1193180.0

unsigned long getticks()
{
    union REGS regs;

    regs.h.ah = 0x00;
    int86(0x1a, ®s, ®s);
    return ((unsigned long)regs.x.cx << 16) + regs.x.dx;
}

void beep(int tone)
{
    unsigned long ticks;
    unsigned char bits;
    unsigned short count;

    count = FREQ / tone;
    outportb(0x43, 0xb6);    /* 选择定时器的某个通道 */
    outportb(0x42, (unsigned char)(count & 0xff));    /* 发声频率计数值的低8位 */
    outportb(0x42, (unsigned char)(count >> 8));    /* 发声频率计数值的高8位 */

    ticks = getticks();
    while (ticks == getticks())
        ;

    /*
     * 当8255的PB端口的第0位和第1位为1时,表示允许发声;
     * 为0时,表示禁止发声。
     */
    bits = inportb(0x61);
    outportb(0x61, bits | 3);    /* 允许发声 */
    ticks += 2;
    while (getticks() < ticks)
        ;
    outportb(0x61, bits & 0xfc);    /* 禁止发声 */
}

void main()
{
    do
    {
        beep(1046.50);
    } while (!kbhit());
}

Comments