您可以使用扬声器来保持您的设计简单.
扬声器让您可以播放不同频率的方波,它实际上可以用来重现数字音频,但更复杂.
扬声器只是一个电磁铁,当电流流过它时,它会被拉回,否则它会保持在默认位置.
通过前后移动扬声器,可以产生声波.
扬声器可以手动移动或使用PIT的通道2.
端口61h的位0控制扬声器源(0 =手动,1 = PIT),同一端口的位1是"扬声器使能"位使用PIT(当没有时,扬声器"位置").
这是一个示意图(从此页面)缺少手动驾驶部分:
PIT通过端口40h-43h控制,我们将使用模式3(方波发生器)设置每次分频器的两个字节.
PIT的振荡器工作频率约为1.193180 MHz,分频器用于控制方波的周期.
不处理内部结构:在PIT振荡器的每个刻度处,加载的分频器递减.方波的周期等于PIT将分频器减小到零所需的时间.
产生声音只是用所需的分频器编程PIT并启用扬声器.
稍后,我们需要禁用它.
一种简单的方法是使用每秒18.2次调用的int 1ch.
通过在第一次播放声音时在变量中保存持续时间,通过在int 1ch的每个刻度处递减它并且通过在计数达到零时禁用扬声器,可以控制蜂鸣声的持续时间.
使用int 1ch需要一个setup函数(beep_setup)和一个拆卸函数(beep_teardown).
BITS 16
ORG 100h
__start__:
;Setup
call beep_setup
;Sample beep of ~2sec
mov ax, 2000
mov bx, 36
call beep_play
;Wait for input
xor ax, ax
int 16h
;Tear down
call beep_teardown
mov ax, 4c00h
int 21h
;-------------------------------------------------
;
;Setup the beep ISR
;
beep_setup:
push es
push ax
xor ax, ax
mov es, ax
;Save the original ISR
mov ax, WORD [es: TIMER_INT * 4]
mov WORD [cs:original_timer_isr], ax
mov ax, WORD [es: TIMER_INT * 4 + 2]
mov WORD [cs:original_timer_isr + 2], ax
;Setup the new ISR
cli
mov ax, beep_isr
mov WORD [es: TIMER_INT * 4], ax
mov ax, cs
mov WORD [es: TIMER_INT * 4 + 2], ax
sti
pop ax
pop es
ret
;
;Tear down the beep ISR
;
beep_teardown:
push es
push ax
call beep_stop
xor ax, ax
mov es, ax
;Restore the old ISR
cli
mov ax, WORD [cs:original_timer_isr]
mov WORD [es: TIMER_INT * 4], ax
mov ax, WORD [cs:original_timer_isr + 2]
mov WORD [es: TIMER_INT * 4 + 2], ax
sti
pop ax
pop es
ret
;
;Beep ISR
;
beep_isr:
cmp BYTE [cs:sound_playing], 0
je _bi_end
cmp WORD [cs:sound_counter], 0
je _bi_stop
dec WORD [cs:sound_counter]
jmp _bi_end
_bi_stop:
call beep_stop
_bi_end:
;Chain
jmp FAR [cs:original_timer_isr]
;
;Stop beep
;
beep_stop:
push ax
;Stop the sound
in al, 61h
and al, 0fch ;Clear bit 0 (PIT to speaker) and bit 1 (Speaker enable)
out 61h, al
;Disable countdown
mov BYTE [cs:sound_playing], 0
pop ax
ret
;
;Beep
;
;AX = 1193180 / frequency
;BX = duration in 18.2th of sec
beep_play:
push ax
push dx
mov dx, ax
mov al, 0b6h
out 43h, al
mov ax, dx
out 42h, al
mov al, ah
out 42h, al
;Set the countdown
mov WORD [cs:sound_counter], bx
;Start the sound
in al, 61h
or al, 3h ;Set bit 0 (PIT to speaker) and bit 1 (Speaker enable)
out 61h, al
;Start the countdown
mov BYTE [cs:sound_playing], 1
pop dx
pop ax
ret
;Keep these in the code segment
sound_playing db 0
sound_counter dw 0
original_timer_isr dd 0
TIMER_INT EQU 1ch
Run Code Online (Sandbox Code Playgroud)
特别感谢Sep Roland修复原始代码中的缺陷!
您可以使用beep_play发出蜂鸣声,使用的单位是上述硬件配置的"自然"单位.
如果您的频率和持续时间是固定的,这些单元可以免费简化代码.
在给定的持续时间后,蜂鸣声停止,您可以使用beep_stop它来强制停止.
一次播放多个声音是不可能的(如果不采用PWM技术,甚至无法混合它们).在播放另一个蜂鸣声时
调用beep_play将具有停止当前蜂鸣声并启动新蜂鸣声的效果.