论坛风格切换切换到宽版
发帖 回复
返回列表  提醒:不能用迅雷等P2P下载,否则下载失败标(二级)的板块,需二级才能下载,没二级不要购买,下载不了
  • 1599阅读
  • 5回复

[技术文章]16位声霸卡的DSP编程 [复制链接]

上一主题 下一主题
离线lajilaji110
 

性别:
人妖
发帖
22
金币
23
提示:会员销售的附件,下载积分 = 版块积分 + 销售积分       只看楼主 倒序阅读 使用道具 0楼 发表于: 2015-04-23
— 本帖被 老吴 从 DSP技术 移动到本区(2017-05-12) —
,nPnH1vb  
翻译:shepherd(zqw100@163.com) 0QrRG$<4X  
原作信息: ;G"!y<F  
题目:Programming the SoundBlaster 16 DSP O~ x{p,s U  
作者:by Ethan Brodsky (Version 3.1) p}wysVB  
联系方式:ericbrodsky@psl.wisc.edu c5O8,sT  
写作日期:2/10/95 F* Yx1vj  
免责声明 Ju;^^  
本免责声明正文如下: Ep')@7^n  
笔者声明,由于利用或误用本资料,最终导致的任何经济损失,或必然的/偶然的/其他形式的损失,笔者将不承担任何责任。本文可以被免费发放,但是在发放时请完整地保留本免责声明。 Zc&pJP+M'U  
SB16简介 ?G]yU  
16位声霸卡(Sound Blaster 16,本文简写为SB16)可以处理FM(Frequency Modulation:频率调幅)和数字声音信号。其中数字信号的处理范围是:从8位5000HZ单声道,到16位44000HZ立体声(译者注:另外一种说法是到48000HZ)。这份常见问题文档,是关于SB16 DSP CT1341芯片数字音频信号的录制和回放的。理所当然的,有关更早声霸卡的编程知识必不可少。 NxK.q)tj6  
译者补充的背景知识: ?hIDyM  
FM合成技术 9Q\B1Q  
它是运用特定的算法来简单模拟真实乐器声音。 其主要特点是电路简单、生产成本低,不需要大容量存储器支持即可模拟出多种声音。由于 FM是靠算法来合成某个声音,因此实现方法过于生硬、效果单一,所生成的声音与真实乐器产生的声音距离很大。很容易让人听出来是“电子音乐”。 N#R8ez`  
DSP 1 un!  
DSP(Digital Signal Processor,数字信号处理器)是一种内含微处理器的专用芯片,它为当时的高档16位声卡实现180°环绕立体声再现立下了汗马功劳。 '9<8<d7?  
SB16的I/O端口 CR#-!_=4  
SB16的DSP芯片的可编程I/O服务端口地址,其基址是由主板跳线决定的(译者注:现在一般通过BIOS或者应用程序实现)。在SB16芯片中,有16个I/O端口被用作FM合成音乐,音响混合,DSP编程和CD-ROM访问。而下面列出的五个端口被用做DSP编程: uOKdb6]r6  
2x6h - DSP 复位 $I ,Np)i  
2xAh - DSP 读 LHA :frC  
2xCh - DSP 写(命令/数据),DSP 写缓冲区状态字(第7位) #Wb4*  
2xEh - DSP 读缓冲区状态字(第7位), DSP 中断应答 BI!EmA  
2xFh - DSP 16位中断应答 07ppq?,y  
译者补充背景资料: s(L!]d.S$y  
端口中的X为基址,可以取1-6,通常情况下取2,即基址是220h。该基址可以由用户在相关配置文件中指定,也可以在程序中自己检测。 g0;6}n  
DSP复位 &Fk|"f+  
在进行DSP编程之前你必须将DSP复位。复位DSP需要按照以下步骤进行: :inVwc  
1,在复位端口(2X6)写入1 tBsvi%F  
2,等待3毫秒 EgzdRB\Cf  
3,在复位端口(2X6)写入0 B LZ<"npn  
4,检测读缓冲区端口(2XE)状态,直到第7位为1。 Lo}/k}3Sx  
5,检测读数据端口(2XA)状态,直到接受到AA。 QNXS.!\P  
DSP自身初始化通常需要大约100毫秒。经过这段时间以后如果返回的值仍然不是AA,或者没有任何数据返回,说明SB16卡未被安装,或者I/O地址不正确。 /&c>*4)  
译者补充知识: X>]<rEh  
这里所说的第7位,是从第0位开始算的。整个复位过程,C语言的参考代码如下: .&>3nu  
int RestDSP(int Test) 1h)K3cC  
{ hOdU%  
/* 重置DSP */ $"0`2C  
outportb(Test + 0x6, 1); YXdo&'Q<qX  
delay(3); >.XXB 5a  
outportb(Test + 0x6, 0); ?TmVLny  
delay(80); /* 延时时间可以酌情调整 */ {q;_Dd  
/* 如果重置成功则检查 */ 5fmQ+2A C1  
if ((inportb(Test + 0xE) & 0x80 == 0x80) 87+u` ~  
&& (inportb(Test + 0xA) == 0xAA)) KyqP@ {  
{ <si cldz  
G_base = Test; 'S=eW_ 0/  
return 1; _C'VC#Sy  
} 8'v:26   
else ._.Qf<7  
{ '3E25BsL  
return 0; $lUz!m jG  
} 9wdX#=I  
} lJS3*x#H  
写DSP a%/x  
向SB16写一个字节,需要按照以下步骤进行: *WWDwY@!u  
1,读写缓冲区状态端口2XC直到第7位被清除 G('UF1F  
2,将数值写入写端口2XC b%VZPKA;  
读DSP Q} g"pl  
由SB16读一个字节,需要按照以下步骤进行: G=kW4rAk  
1,读读缓冲区状态端口2XE直到第7位被设置 /c7jL4oD  
2,从读端口2XA读出一个字节 vsYbR3O  
DMA控制器编程 pp >F)A0v  
DMA(Direct Memory Access,直接内存读取)控制器掌管着I/O设备和内存之间的数据传输,整个过程不需要CPU参与。一个INTEL 8237 DMAC集成电路被用来控制它,而一个IBM兼容机有两个DMA控制器:一个掌管8位另外一个掌管16位。同外部页面寄存器配对的DMA控制器,可以传输大于64KB的数据块。下面是有关I/O端口和必要的关于声卡寄存器的设置: 0]GenT"   
*DMA地址和计数寄存器的I/O端口地址 d zV2;  
|====================================| R+Ug;r-[  
| 控制器 | I/O 地址 | 功能 | UHIXy#+o5  
|====================================| ydE}.0zN  
| DMA 1 | 00 | 通道0地址 | zIU6bMMT3u  
| 8位 | 01 | 通道0计数 | #X?E#^6?E  
| 从 | 02 | 通道1地址 | <DEu]-'>  
| | 03 | 通道1计数 | LftGA7uGJ)  
| | 04 | 通道2地址 | e_1L J  
| | 05 | 通道2计数 | :G5O_T$  
| | 06 | 通道3地址 | iU# "G" &  
| | 07 | 通道3计数 | ^r{N^  
|====================================| 5/HkhT yj  
| DMA 2 | C0 | 通道4地址 | 81)i>]  
| 16位 | C2 | 通道4计数 | j`MK\*qmz  
| 主 | C4 | 通道5地址 | >;fn,9w  
| | C6 | 通道5计数 | Hig.` P  
| | C8 | 通道6地址 | J d,9<m $  
| | CA | 通道6计数 | RXO5p d  
| | CC | 通道7地址 | Z07n>|WF-  
| | CE | 通道7计数 | ')(U<5y)  
|====================================| q3GkfgY  
*控制寄存器的I/O端口地址 ;i ?R+T  
|========================================| q 'uGB fE.  
| 地址 | 操作 | 功能 | t&_X{!1X"w  
|DMAC1 DMAC2 | | | y%<CkgZS  
|========================================| V2'5doo  
| 0A D4 | 写 |写单一掩码寄存器 | n6C!5zq7U  
| 0B D6 | 写 |写模式寄存器 | 6 8Vxy  
| 0C D8 | 写 |清除翻转字节指示器 | 65rf=*kz:  
|========================================| r<Q0zKW!jN  
*页寄存器的I/O端口地址 Qzv&  
|===============================| nrbP3sf*  
| 地址 | 功能 | ( F4c0  
|===============================| $JiypX^DOP  
| 81 | 8位 DMA 通道 2 页面 | [|(=15;  
| 82 | 8位 DMA 通道 3 页面 | #E_<}o  
| 83 | 8位 DMA 通道 1 页面 |  C8} ;,  
| 87 | 8位 DMA 通道 0 页面 | fC$@m_-KD  
| 89 | 16位 DMA 通道 6 页面 | Lw<.QMN%f  
| 8A | 16位 DMA 通道 7 页面 | CKC5S^Mx  
| 8B | 16位 DMA 通道 5 页面 | OLqynY  
|===============================| yI%q3lB}^  
*模式寄存器的各位含义 | S'mF6Y  
|===============================| f#gV>.P;h\  
| 位/值 |功能 | y'O<*~C(X  
|===============================| WzBr1 ea{I  
|Bits 7:6 |状态选择 | ciFqj3JS  
| 00 | 查询模式 | S[N9/2  
| 01 | 单一模式 | Epm8S}6K  
| 10 | 块模式 | !mUO/6Q hq  
| 11 | 级联模式 | BteeQ&A|~  
|===============================| J_9[ x mM  
| Bit 5 |地址增加/减少位 | vD(:?M  
| 1 | 地址减少 | 8U!$()^?  
| 0 | 地址增加 | Ms-)S7tMz  
|===============================| SEH[6W3  
| Bit 4 |自动初始化设置位 | b|\dHi2F T  
| 1 | 自动初始化DMA | f{P?|8u  
| 0 | 单一周期DMA | 9q\_UbF  
|===============================| -TS,~`O  
|Bits 3:2 |位传输 | i4&V+h"  
| 00 | 验证 | O9AFQ)u   
| 01 | 写(到内存) | tjx|;m7  
| 10 | 读(从内存) | 1pjx8*!B  
| 11 | 非法 | 1X9J[5|ll  
| 忽略 | 如果bits 7:6 = 11 | u^W!$OfZpp  
|===============================| %.HLO.A  
|Bits 1:0 |通道选择 | )UyJ.!Fly  
| 00 | 通道 0 (4) | &2I8!Ia  
| 01 | 通道 1 (5) | {uJ"%  
| 10 | 通道 2 (6) | Ty7)j]b"zl  
| 11 | 通道 3 (7) | l+X\>,  
|===============================| s^Xs*T@~h  
DMAC2用于16位I/O,DMAC1用于8位I/O。开始数据传送的过程很复杂,所以对于使用I/O的DMA传输方式,我将列出详细步骤。 Z$zX%w  
1)计算你的内存缓冲区的绝对线性地址 r`< x@,  
LinearAddr := Seg(Ptr^)*16 + Ofs(Ptr^)); +[4y)y`  
2)设置适当的掩码位,从而禁用对应声卡的DMA通道 xC}'"``s  
Port[MaskPort] := 1 + (Channel mod 4); U} w@,6  
3)清除翻转字节指示器 wc&D[M]-/  
Port[ClrBytePtr] := AnyValue; {SD%{  
4)定义DMA传输模式 ,Z}ST|$u  
查询模式下,模式选择位应该被置为00,地址增加/减少位应置0。有关自动初始化模式位的恰当设定,我将在稍后讨论。回放模式下,传输位应置为10;而录音模式应置为01。通道选择位应该依照声卡的实际DMA通道设置。这里要注意,“读”意味着从内存读到声卡,“写”意味着从声卡写到系统内存。 r|i)  
Port[ModePort] := Mode + (Channel mod 4); ^66OzT8A  
常用到的方式如下: *kcc]*6@s  
48h+通道号 - 单一周期回放 N>1d]DrQR  
58h+通道号 - 自动初始化回放 aIh} j,  
44h+通道号 - 单一周期录制 @.`k2lxGd~  
54h+通道号 - 自动初始化录制 !YZKa-  
5)写入内存缓冲区的偏移地址,低字节在前,高字节在后。对于16位数据,这个偏移地址应该是WORDS类型,从128KB的页面其实处算起。最早的计算16位参数的方法是:在计算偏移量之前讲线性地址除以2。 *zW]IQ'A  
if SixteenBit 5u3KL A  
then (]PH2<3t  
begin #zBqj;p  
BufOffset := (LinearAddr div 2) mod 65536; D0z[h(m  
Port[BaseAddrPort] := Lo(BufOffset); ^YB2E*  
Port[BaseAddrPort] := Hi(BufOffset); 5fYWuc9}z  
end q- 0q:  
else ~$hR:I1  
begin iSg0X8J)  
BufOffset := LinearAddr mod 65536; $: |`DCC  
Port[BaseAddrPort] := Lo(BufOffset); Ge7B%p8  
Port[BaseAddrPort] := Hi(BufOffset); Hi*|f!,H?  
end; eKZS_Qd  
6)写入传输长度,低字节在前,高字节在后。对于8位传输,写入总字节数-1;对于16位传输,写入WORDS-1。 AlkHf]oB  
Port[CountPort] := Lo(TransferLength-1); 5)5yH bS  
Port[CountPort] := Hi(TransferLength-1); &xGpbJG  
7)向DMA页面寄存器写入内存缓冲区页码 %b2Hm9r+  
Port[PagePort] := LinearAddr div 65536; w~Nat7nD  
8)清除适当的掩码位,从而允许声卡DMA通道 nHRk2l|  
Port[MaskPort] := DMAChannel mod 4; xEeHQ7J  
设置采样频率 S.q0L  
同早期的声霸卡不同,SB16使用实际采样频率代替了时间片。在SB16上采用41H和42H的DSP命令来设置采样频率。41H用来设置输出,而42H用来设置输入。我听说在SB16上这两个命令其实做了同样的事情,但是我建议为了同未来的声卡兼容,最好采用指定的指令。设置采样频率的步骤如下: $Sa7N%D  
1)写入命令字,输出写入41H,输入写入42H Ih4$MG6QC  
2)写入采样频率的高字节,例如22050HZ则写入56H ~t ZB1+%)  
3)写入采样频率的低字节,例如22050HZ责写入22H "fUNrhCx  
数字音频I/O 6a_U[-a9;  
为了记录或者播放音频,你应该采用以下的步骤: MUGoW;}v )  
1)申请一块不大于64KB的物理内存空间 }[h]z7e2S  
2)安装一个中断服务程序 g1.u1}  
3)设置DMA控制器为后台传输模式 lnLy"f"zV  
4)设置采样频率 99CK [G  
5)向DSP写入I/O端口命令 FK`:eP{  
6)写DSP写入I/O传输模式 c\&;Xr  
7)向DSP写入块大小(低/高字节) iHK.hs;  
使用DMA的单一周期模式时,在中断中你需要: FK94CI  
1)将DMA控制器设置为下一个数据块 MI(;0   
2)将DSP指向下一个数据块  + #E?)  
3)如果有双缓冲,则复制下一个数据块 a|.IAxJ  
4)通过读端口对SB进行中断应答,8位是端口2XEH,16位是端口2XFH @^  *62  
5)应答中断完毕,向20H端口写入20H,如果声卡使用的是IRQ8-15,那么你还要向A0H写入20H ,2+d+Zuh  
译者补充背景知识: MNb9~kM  
20H和A0H分别为主片和从片的中断控制器入口地址。IRQ 0-7对应主片,中断号为08H-0FH,;IRQ 8-15对应从片,中断号为70H-7FH。通常情况下IRQ0为定时器中断,IRQ1为键盘中断,IRQ5为声卡中断,RIQ6为键盘中断。用户的可编程中断为IRQ 10,11,12,15,都在从片上,如果使用的话则必须先打开主片的IRQ2。 xXa4t4gR  
DSP命令 e6 x#4YH  
D0-暂停由CXH命令引起的8位DMA模式数字音频初始化。可以应用在单一周期回放和自动初始化模式下。 mH4u@aQ}  
D0-继续由D0H命令引起的8位DMA模式数字音频暂停。可以应用在单一周期回放和自动初始化模式下。 q-uzu!  
D5-暂停由BXH命令引起的16位DMA模式数字音频初始化。可以应用在单一周期回放和自动初始化模式下。 TW70z]B  
D6-继续由D5H命令引起的16位DMA模式数字音频暂停。可以应用在单一周期回放和自动初始化模式下。 $xq04ejJ  
D9-在本数据块操作之后,退出16位自动初始化模式的数字音频I/O操作。 4VwMl)8ic  
DA-在本数据块操作之后,退出8位自动初始化模式的数字音频I/O操作。 \Q1&w2mw  
E1-获得DSP版本号。在送出这个命令之后,DSP将会返回两字节的内容。第一个字节是主版本号,第二个字节是副版本号。一个SB16的DSP版本号应该大于等于4.00,在使用SB16专用命令前需要检查版本号。 D .LR-Z  
BX-数字音频I/O的16位DMA模式 VmN7a6a  
命令序列:命令,方式,低字节(长度-1),高字节(长度-1) "PO8Q  
命令格式:|=======================================| D6+3f #k6  
|D7 |D6 |D5 |D4 | D3 | D2 | D1 |D0 | yNn=r;FZQ  
|=======================================| !E_|Zp]up  
| 1 | 0 | 1 | 1 | A/D | A/I |FIFO | 0 | R5(([C1  
|=======================================| $`ptSR  
|0=D/A |0=SC |0=off | @TX@78fWz=  
|1=A/D |1=AI |1=on | 75R#gQ]EV  
|===================| Il(o[Q>jJ3  
常用命令: p@uHzu7  
B8 - 16位单一周期输入 n:) [ %on  
B0 - 16位单一周期输出 N:k>V4oE  
BE - 16位自动初始化输入 5U4V_*V  
B6 - 16位自动初始化输出 nvXjW@)`  
模式:|=============================================| kR^h@@'F"  
|D7 |D6 | D5 | D4 |D3 |D2 |D1 |D0 | jw {B8<@s  
|=============================================| Az8ZA~Op=  
| 0 | 0 | Stereo | Signed | 0 | 0 | 0 | 0 | o$Nhx_F  
|=============================================| Mx`';z8~  
|0=单声道 |0=unsigned | &FT`z"^  
|1=立体声 |1=signed | F ]Zg  
|=====================| "J2q|@.  
CX-数字音频I/O的8位DMA模式 YM'4=BlJHv  
基本用法与命令BX对应的16位数字音频I/O操作相同 x9a\~XL>a  
常用命令: s@7hoU-+  
C8 - 8位单一周期输入 =BE!  
C0 - 8位单一周期输出 US"g>WLwJ  
CE - 8位自动初始化输入 fDfph7[)  
C6 - 8位自动初始化输出 svl!"tMXl  
当声卡在需要时不能得到DMA时,系统将采用FIFO(first in first out,先进先出)算法来消除本采样周期内数据的不一致。如果FIFO被禁止,声卡会尝试使用DMA并立即声明:它需要一个采样。如果拥有较高优先级的其他设备占用了DMA,声卡将等待这个采样并且有可能降低采样频率。FIFO允许采样周期浮动而不影响音频质量,并且每当有信号传入DSP时FIFO将被自动清空。 "-dA\,G  
在单一周期模式下,DSP在不断地改变,清空FIFO时有可能包含还没有来得及输出的数据。为了避免这个问题,在单一周期模式下应该关闭FIFO。 TNA7(<"fV|  
在自动初始化模式下,DSP是不会被改写的,可以打开FIFO以提高音质。 3o?eUwI}  
DMA的自动初始化 >dm9 YfQ  
在单一周期DMA模式下,音频会在每个数据块的末尾停下,虽然中断程序可以开始另外一次传输,但是在音频输出中将会有一个停顿。这造成了在数据块之间出现滴答声,降低了音频质量。 eI2HTFyT  
在自动初始化模式下,声音输出在数据缓冲区的末尾循环-DMA控制器保持传输同样的内存数据块,而它们是在DMA初始化的时候就决定的。到达数据块末尾时,系统将依照存储的偏移和计数器寄存器,自动初始化当前偏移和计数寄存器,再次开始传递缓冲区数据。 #{J~ km/  
为减少滴答声而被经常采用的方法是,申请一块内存缓冲区并把它分成两个数据块,将DMA控制器设置为整个缓冲区的长度,但是设置SB16为数据块的长度(也就是缓冲区长度的一半)。一个中断对应一个数据块,所以在播放缓冲区的时候会发生两次中断,一次在缓冲区的中间(就是第二个数据块的开始),一次在缓冲区的最后(就是第一个数据块的开始)。中断服务程序会复制数据到刚刚结束的那个数据区,这样数据在声卡需要输出的时候就准备好了。设置单一周期模式DMA传输的顺序和自动初始化模式下是基本一致的,不过是设置第四位的DMA模式寄存器和第三位的DSP命令不同而已。 nK?S2/o#A  
自动初始化模式DMA的中断: $,U/,XA {E  
1)复制下一个数据单位到刚刚结束的那个输出数据块 GN! R<9  
2)通过读端口对SB进行中断应答,8位是端口2XEH,16位是端口2XFH YW/V}C'>  
3)应答中断完毕,向20H端口写入20H,如果声卡使用的是IRQ8-15,那么你还要向A0H写入20H PQSmBTs.  
立即停止音频: Ry]9n.y  
8位模式下写入DSP命令D0H,16位模式下写入D5H。这样是立即停止,无需调用中断。 S2*sh2-&6  
在当前数据块播放完毕之后停止音频: Ew| Z<(  
8位模式下写入DSP命令DAH,16位模式下写入D9H。这样要等到当前数据块的末尾,如果系统没有为结束音频输出后的中断做好准备,这样可能会导致错误。 @P75f5p}<  
你仍然可以为单一周期模式而调整DSP,达到结束自动初始化模式的目的,声卡将在下一次中断后由A/I模式转变为S/C模式。然后它将继续在指定的长度上播放或录音,产生中断,停止。这会让你在数据结束之后正确的关闭输出音频,而不用担心剩余的DMA缓冲区被静音填满。这项技术或许对你有用,但是我建议你使用立即停止的命令,除非别的方法更合乎你的要求。


评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

一般

差劲
离线zbmba

性别:
帅哥
发帖
355
金币
322
提示:会员销售的附件,下载积分 = 版块积分 + 销售积分       只看该作者 1楼 发表于: 2015-05-14
这个不错,很好


离线sky小5

性别:
人妖
发帖
405
金币
17
提示:会员销售的附件,下载积分 = 版块积分 + 销售积分       只看该作者 2楼 发表于: 2015-06-29
不错


离线sky小5

性别:
人妖
发帖
405
金币
17
提示:会员销售的附件,下载积分 = 版块积分 + 销售积分       只看该作者 3楼 发表于: 2015-07-02
不错



性别:
人妖
发帖
593
金币
476
提示:会员销售的附件,下载积分 = 版块积分 + 销售积分       只看该作者 4楼 发表于: 2017-04-29
DSP编程好东西



性别:
人妖
发帖
593
金币
476
提示:会员销售的附件,下载积分 = 版块积分 + 销售积分       只看该作者 5楼 发表于: 2017-05-07
DSP学习下


快速回复
限150 字节
 
上一个 下一个