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

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

上一主题 下一主题
离线lajilaji110
 

性别:
人妖
发帖
22
金币
23
提示:会员销售的附件,下载积分 = 版块积分 + 销售积分       只看楼主 倒序阅读 使用道具 0楼 发表于: 2015-04-23
— 本帖被 老吴 从 DSP技术 移动到本区(2017-05-12) —
s0?'mC+p  
翻译:shepherd(zqw100@163.com) 4@-tT;$  
原作信息: y5j:+2|I  
题目:Programming the SoundBlaster 16 DSP OOSf<I*>  
作者:by Ethan Brodsky (Version 3.1) _C/|<Ot:  
联系方式:ericbrodsky@psl.wisc.edu &IUA[{o~e  
写作日期:2/10/95 kuH%aM<R  
免责声明 gLv+L]BnhH  
本免责声明正文如下: jum"T\  
笔者声明,由于利用或误用本资料,最终导致的任何经济损失,或必然的/偶然的/其他形式的损失,笔者将不承担任何责任。本文可以被免费发放,但是在发放时请完整地保留本免责声明。 ]AY 4bm  
SB16简介 TRi#  
16位声霸卡(Sound Blaster 16,本文简写为SB16)可以处理FM(Frequency Modulation:频率调幅)和数字声音信号。其中数字信号的处理范围是:从8位5000HZ单声道,到16位44000HZ立体声(译者注:另外一种说法是到48000HZ)。这份常见问题文档,是关于SB16 DSP CT1341芯片数字音频信号的录制和回放的。理所当然的,有关更早声霸卡的编程知识必不可少。 ZwMVFC-d  
译者补充的背景知识: kS-BB[T  
FM合成技术 5?>4I"ne  
它是运用特定的算法来简单模拟真实乐器声音。 其主要特点是电路简单、生产成本低,不需要大容量存储器支持即可模拟出多种声音。由于 FM是靠算法来合成某个声音,因此实现方法过于生硬、效果单一,所生成的声音与真实乐器产生的声音距离很大。很容易让人听出来是“电子音乐”。 uQO5GDuK>  
DSP }gv'r ";  
DSP(Digital Signal Processor,数字信号处理器)是一种内含微处理器的专用芯片,它为当时的高档16位声卡实现180°环绕立体声再现立下了汗马功劳。 #H~55))F  
SB16的I/O端口 .jQx2 O  
SB16的DSP芯片的可编程I/O服务端口地址,其基址是由主板跳线决定的(译者注:现在一般通过BIOS或者应用程序实现)。在SB16芯片中,有16个I/O端口被用作FM合成音乐,音响混合,DSP编程和CD-ROM访问。而下面列出的五个端口被用做DSP编程: s9wzN6re  
2x6h - DSP 复位 FFw(`[A_  
2xAh - DSP 读 e#;43=/Ia  
2xCh - DSP 写(命令/数据),DSP 写缓冲区状态字(第7位) K:U=Y$x  
2xEh - DSP 读缓冲区状态字(第7位), DSP 中断应答 NRx 7S 9W  
2xFh - DSP 16位中断应答 ; pBLmm*F  
译者补充背景资料: XE2Un1i}j1  
端口中的X为基址,可以取1-6,通常情况下取2,即基址是220h。该基址可以由用户在相关配置文件中指定,也可以在程序中自己检测。 |Gz<I  
DSP复位 F `:Q  
在进行DSP编程之前你必须将DSP复位。复位DSP需要按照以下步骤进行: QfEJU8/5d  
1,在复位端口(2X6)写入1 j_rO_m<8  
2,等待3毫秒 `C>h]H(  
3,在复位端口(2X6)写入0 SdlO]y9E  
4,检测读缓冲区端口(2XE)状态,直到第7位为1。 Wmd@%K  
5,检测读数据端口(2XA)状态,直到接受到AA。 R9A:"sJ  
DSP自身初始化通常需要大约100毫秒。经过这段时间以后如果返回的值仍然不是AA,或者没有任何数据返回,说明SB16卡未被安装,或者I/O地址不正确。 @JlT*:Dz  
译者补充知识: uY~mi9E  
这里所说的第7位,是从第0位开始算的。整个复位过程,C语言的参考代码如下: t[!,puZc#  
int RestDSP(int Test) l5w^rj  
{ Lmjd,t  
/* 重置DSP */ J8~hIy6]  
outportb(Test + 0x6, 1); j4i$2ZT'  
delay(3); F4\:9ws  
outportb(Test + 0x6, 0); 'Q E8  
delay(80); /* 延时时间可以酌情调整 */ )2).kL>  
/* 如果重置成功则检查 */ LkJq Bg  
if ((inportb(Test + 0xE) & 0x80 == 0x80) TYuP EVEXZ  
&& (inportb(Test + 0xA) == 0xAA)) _(f@b1O~  
{ l^R:W#*+U  
G_base = Test; O;VqrO  
return 1; 8x1!15Wiz  
} @] .s^ss9_  
else uO1^Q;F  
{ \)28,`  
return 0; 3)VO{Cj!  
} c= 2E/x?  
} 9'p| [?]v  
写DSP +jrx;xwot  
向SB16写一个字节,需要按照以下步骤进行: `P\H{  
1,读写缓冲区状态端口2XC直到第7位被清除 R~oY R,L;  
2,将数值写入写端口2XC eJqx,W5MK]  
读DSP TQeIAy  
由SB16读一个字节,需要按照以下步骤进行: MMa`}wSs  
1,读读缓冲区状态端口2XE直到第7位被设置 O8hx}dOjA  
2,从读端口2XA读出一个字节 \KJTR0EB:>  
DMA控制器编程 *><j(uz!  
DMA(Direct Memory Access,直接内存读取)控制器掌管着I/O设备和内存之间的数据传输,整个过程不需要CPU参与。一个INTEL 8237 DMAC集成电路被用来控制它,而一个IBM兼容机有两个DMA控制器:一个掌管8位另外一个掌管16位。同外部页面寄存器配对的DMA控制器,可以传输大于64KB的数据块。下面是有关I/O端口和必要的关于声卡寄存器的设置: fQ~~%#z1  
*DMA地址和计数寄存器的I/O端口地址 lg-`zV3  
|====================================| # d"M(nt  
| 控制器 | I/O 地址 | 功能 | {!( htg;  
|====================================| !(bYh`Uy  
| DMA 1 | 00 | 通道0地址 | C|H`.|Q  
| 8位 | 01 | 通道0计数 | KUX6n(u  
| 从 | 02 | 通道1地址 |  @B{  
| | 03 | 通道1计数 | 5Zc  
| | 04 | 通道2地址 | Y]R=z*i%  
| | 05 | 通道2计数 | ,FYA*}[  
| | 06 | 通道3地址 | UV%o&tv|<  
| | 07 | 通道3计数 | 5D3&E_S  
|====================================| q:>`|~MX  
| DMA 2 | C0 | 通道4地址 | )`k+Oyvi<  
| 16位 | C2 | 通道4计数 | ~+ae68{p  
| 主 | C4 | 通道5地址 | q:vN3#=^qf  
| | C6 | 通道5计数 | fc:87ZR{K  
| | C8 | 通道6地址 | 6/QWzw.0c  
| | CA | 通道6计数 | w2 (}pz:  
| | CC | 通道7地址 | .nr%c*JUp  
| | CE | 通道7计数 | ?>=vKU5  
|====================================| , -d2wzhW  
*控制寄存器的I/O端口地址 2hntQ1[  
|========================================| 5FJ%"5n&  
| 地址 | 操作 | 功能 | L) _ VdB  
|DMAC1 DMAC2 | | | T8LvdzS  
|========================================| lh0G/8+C  
| 0A D4 | 写 |写单一掩码寄存器 | Rp zuSh  
| 0B D6 | 写 |写模式寄存器 | M9Z9s11{H  
| 0C D8 | 写 |清除翻转字节指示器 | ,9:v2=C_  
|========================================| <6N3()A)%1  
*页寄存器的I/O端口地址 U GOe(JB  
|===============================| UT_t]m  
| 地址 | 功能 | UWCm:eRQ  
|===============================| GYT0zMMf  
| 81 | 8位 DMA 通道 2 页面 | Nde1`W]:  
| 82 | 8位 DMA 通道 3 页面 | ' z^v}~  
| 83 | 8位 DMA 通道 1 页面 | qk&BCkPT  
| 87 | 8位 DMA 通道 0 页面 | VF-[O  
| 89 | 16位 DMA 通道 6 页面 | y(Pv1=e  
| 8A | 16位 DMA 通道 7 页面 | W70BRXe04D  
| 8B | 16位 DMA 通道 5 页面 | &@&^k$du8q  
|===============================| w `M/0.)V  
*模式寄存器的各位含义 $iy(+}  
|===============================| \<=.J`o{  
| 位/值 |功能 | FP6Jf I8  
|===============================| >vfLlYx  
|Bits 7:6 |状态选择 | G~lnX^46"  
| 00 | 查询模式 | A.P*@}9  
| 01 | 单一模式 | K!88 Nox(  
| 10 | 块模式 | KC\W6|NtGj  
| 11 | 级联模式 | \ ]h$8JwV  
|===============================| |R Qa.^.  
| Bit 5 |地址增加/减少位 | zt )WX9  
| 1 | 地址减少 | _ZuI x=!  
| 0 | 地址增加 | p_sqw~)^%  
|===============================| xO 1uHaL  
| Bit 4 |自动初始化设置位 | 6nk.q|n:g  
| 1 | 自动初始化DMA | R<>uCF0  
| 0 | 单一周期DMA | 41XXL$  
|===============================| ?7*J4.  
|Bits 3:2 |位传输 | apm,$Vvjy  
| 00 | 验证 | TkjZI}]2  
| 01 | 写(到内存) | Of$gs-  
| 10 | 读(从内存) | @v\jL+B+m  
| 11 | 非法 | #fe zUU  
| 忽略 | 如果bits 7:6 = 11 | ~!dO2\X+  
|===============================| dC}4Er  
|Bits 1:0 |通道选择 | Fc"+L+h@W  
| 00 | 通道 0 (4) | y{qKb:~wv  
| 01 | 通道 1 (5) | ViG-tb   
| 10 | 通道 2 (6) | }l@7t&T|  
| 11 | 通道 3 (7) | FE?^}VH  
|===============================| EG!):P  
DMAC2用于16位I/O,DMAC1用于8位I/O。开始数据传送的过程很复杂,所以对于使用I/O的DMA传输方式,我将列出详细步骤。 cNuBWLG  
1)计算你的内存缓冲区的绝对线性地址 )0@&pEObm  
LinearAddr := Seg(Ptr^)*16 + Ofs(Ptr^)); }D#[yE,=\  
2)设置适当的掩码位,从而禁用对应声卡的DMA通道 K}Pi"Le@W  
Port[MaskPort] := 1 + (Channel mod 4); }KL( -Ui$  
3)清除翻转字节指示器 [IuF0$w=dj  
Port[ClrBytePtr] := AnyValue; 8J#TP7;  
4)定义DMA传输模式 cX-) ]D  
查询模式下,模式选择位应该被置为00,地址增加/减少位应置0。有关自动初始化模式位的恰当设定,我将在稍后讨论。回放模式下,传输位应置为10;而录音模式应置为01。通道选择位应该依照声卡的实际DMA通道设置。这里要注意,“读”意味着从内存读到声卡,“写”意味着从声卡写到系统内存。 Q\v^3u2;m`  
Port[ModePort] := Mode + (Channel mod 4); c:z<8#A}  
常用到的方式如下:  *}`D2_uP  
48h+通道号 - 单一周期回放 [ U?a %$G>  
58h+通道号 - 自动初始化回放 Ja6PX P]'  
44h+通道号 - 单一周期录制 'WQ<|(:{  
54h+通道号 - 自动初始化录制 $t$YdleIH  
5)写入内存缓冲区的偏移地址,低字节在前,高字节在后。对于16位数据,这个偏移地址应该是WORDS类型,从128KB的页面其实处算起。最早的计算16位参数的方法是:在计算偏移量之前讲线性地址除以2。 c`G~.paY|  
if SixteenBit syLpnNx=  
then * d[sja+  
begin l ilF _ y  
BufOffset := (LinearAddr div 2) mod 65536; qc`UDD5  
Port[BaseAddrPort] := Lo(BufOffset); }>u<,  
Port[BaseAddrPort] := Hi(BufOffset); .1& F p  
end e$@azi1  
else mq~L1< f  
begin 5;-?qcb^w  
BufOffset := LinearAddr mod 65536; CpF&Vy K  
Port[BaseAddrPort] := Lo(BufOffset); |yow(2(F@  
Port[BaseAddrPort] := Hi(BufOffset); .9;wJ9Bw[  
end; at `\7YfQp  
6)写入传输长度,低字节在前,高字节在后。对于8位传输,写入总字节数-1;对于16位传输,写入WORDS-1。 wNm~H  
Port[CountPort] := Lo(TransferLength-1); Tn8GLn  
Port[CountPort] := Hi(TransferLength-1); ,=kQJ|  
7)向DMA页面寄存器写入内存缓冲区页码 .FXn=4l'vV  
Port[PagePort] := LinearAddr div 65536; -%x9^oQwY  
8)清除适当的掩码位,从而允许声卡DMA通道 WH^r M`9  
Port[MaskPort] := DMAChannel mod 4; L>EC^2\  
设置采样频率 %@Ty,d:;=  
同早期的声霸卡不同,SB16使用实际采样频率代替了时间片。在SB16上采用41H和42H的DSP命令来设置采样频率。41H用来设置输出,而42H用来设置输入。我听说在SB16上这两个命令其实做了同样的事情,但是我建议为了同未来的声卡兼容,最好采用指定的指令。设置采样频率的步骤如下: *6e 5T  
1)写入命令字,输出写入41H,输入写入42H \;s mH;m  
2)写入采样频率的高字节,例如22050HZ则写入56H +b]+5!  
3)写入采样频率的低字节,例如22050HZ责写入22H *aF<#m v  
数字音频I/O (GdL(H#IL  
为了记录或者播放音频,你应该采用以下的步骤: 6- @n$5W0  
1)申请一块不大于64KB的物理内存空间 C7[CfcPA  
2)安装一个中断服务程序 )FrXD3 p  
3)设置DMA控制器为后台传输模式 %v(\;&@  
4)设置采样频率 &<sN( ;%0R  
5)向DSP写入I/O端口命令 "xV9$m>  
6)写DSP写入I/O传输模式 &t\KKsUtd  
7)向DSP写入块大小(低/高字节) M _z-~G  
使用DMA的单一周期模式时,在中断中你需要: +wwK#ocw  
1)将DMA控制器设置为下一个数据块 i`1QR@11  
2)将DSP指向下一个数据块 `<L6Q2Y>j  
3)如果有双缓冲,则复制下一个数据块 lU<n Wf  
4)通过读端口对SB进行中断应答,8位是端口2XEH,16位是端口2XFH {s=$.Kg  
5)应答中断完毕,向20H端口写入20H,如果声卡使用的是IRQ8-15,那么你还要向A0H写入20H L@{5:#-  
译者补充背景知识: QDC]g.x  
20H和A0H分别为主片和从片的中断控制器入口地址。IRQ 0-7对应主片,中断号为08H-0FH,;IRQ 8-15对应从片,中断号为70H-7FH。通常情况下IRQ0为定时器中断,IRQ1为键盘中断,IRQ5为声卡中断,RIQ6为键盘中断。用户的可编程中断为IRQ 10,11,12,15,都在从片上,如果使用的话则必须先打开主片的IRQ2。 f`j RLo*L  
DSP命令  ? h$>7|  
D0-暂停由CXH命令引起的8位DMA模式数字音频初始化。可以应用在单一周期回放和自动初始化模式下。 vO)nqtw  
D0-继续由D0H命令引起的8位DMA模式数字音频暂停。可以应用在单一周期回放和自动初始化模式下。 3'WS6B+  
D5-暂停由BXH命令引起的16位DMA模式数字音频初始化。可以应用在单一周期回放和自动初始化模式下。 H[{ch t h  
D6-继续由D5H命令引起的16位DMA模式数字音频暂停。可以应用在单一周期回放和自动初始化模式下。 @"m? #  
D9-在本数据块操作之后,退出16位自动初始化模式的数字音频I/O操作。 =y/VrF.bV  
DA-在本数据块操作之后,退出8位自动初始化模式的数字音频I/O操作。 p&L`C |0  
E1-获得DSP版本号。在送出这个命令之后,DSP将会返回两字节的内容。第一个字节是主版本号,第二个字节是副版本号。一个SB16的DSP版本号应该大于等于4.00,在使用SB16专用命令前需要检查版本号。 5[|MO.CB$  
BX-数字音频I/O的16位DMA模式 U9KnW]O%"  
命令序列:命令,方式,低字节(长度-1),高字节(长度-1) 5"[Qs|VjA6  
命令格式:|=======================================| l }?'U  
|D7 |D6 |D5 |D4 | D3 | D2 | D1 |D0 | Q b5AQf30  
|=======================================| *}\!&Zk"  
| 1 | 0 | 1 | 1 | A/D | A/I |FIFO | 0 | ba3_5 5]  
|=======================================| uL!{xuN  
|0=D/A |0=SC |0=off | vTD`Ja#h  
|1=A/D |1=AI |1=on | .s_wP  
|===================| u=I>DEe@ c  
常用命令: L`ZH.fN  
B8 - 16位单一周期输入 3H%oTgWk  
B0 - 16位单一周期输出 g|PVOY+|^  
BE - 16位自动初始化输入 ~mtL\!vaM  
B6 - 16位自动初始化输出 xOjCF&W  
模式:|=============================================| R0M(e@H~  
|D7 |D6 | D5 | D4 |D3 |D2 |D1 |D0 | _AQ :<0/#  
|=============================================| h!f7/) |[o  
| 0 | 0 | Stereo | Signed | 0 | 0 | 0 | 0 | :_tsS)Q2m  
|=============================================| 5vL]Y)l  
|0=单声道 |0=unsigned | {O6f1LuH  
|1=立体声 |1=signed | :Q\b$=,:  
|=====================| w$7*za2  
CX-数字音频I/O的8位DMA模式 4b8!LzKS  
基本用法与命令BX对应的16位数字音频I/O操作相同 +>oVc\$  
常用命令: Frt_X%  
C8 - 8位单一周期输入 G Cx]VN3 &  
C0 - 8位单一周期输出 oSt-w{ !  
CE - 8位自动初始化输入 8KD7t&H  
C6 - 8位自动初始化输出 74%,v|  
当声卡在需要时不能得到DMA时,系统将采用FIFO(first in first out,先进先出)算法来消除本采样周期内数据的不一致。如果FIFO被禁止,声卡会尝试使用DMA并立即声明:它需要一个采样。如果拥有较高优先级的其他设备占用了DMA,声卡将等待这个采样并且有可能降低采样频率。FIFO允许采样周期浮动而不影响音频质量,并且每当有信号传入DSP时FIFO将被自动清空。 J%3%l5 /  
在单一周期模式下,DSP在不断地改变,清空FIFO时有可能包含还没有来得及输出的数据。为了避免这个问题,在单一周期模式下应该关闭FIFO。 x~}RL-Y2o  
在自动初始化模式下,DSP是不会被改写的,可以打开FIFO以提高音质。 #`/KF_a3\>  
DMA的自动初始化 :JqH.Sqk  
在单一周期DMA模式下,音频会在每个数据块的末尾停下,虽然中断程序可以开始另外一次传输,但是在音频输出中将会有一个停顿。这造成了在数据块之间出现滴答声,降低了音频质量。 4ow)vS(  
在自动初始化模式下,声音输出在数据缓冲区的末尾循环-DMA控制器保持传输同样的内存数据块,而它们是在DMA初始化的时候就决定的。到达数据块末尾时,系统将依照存储的偏移和计数器寄存器,自动初始化当前偏移和计数寄存器,再次开始传递缓冲区数据。 77OH.E|$  
为减少滴答声而被经常采用的方法是,申请一块内存缓冲区并把它分成两个数据块,将DMA控制器设置为整个缓冲区的长度,但是设置SB16为数据块的长度(也就是缓冲区长度的一半)。一个中断对应一个数据块,所以在播放缓冲区的时候会发生两次中断,一次在缓冲区的中间(就是第二个数据块的开始),一次在缓冲区的最后(就是第一个数据块的开始)。中断服务程序会复制数据到刚刚结束的那个数据区,这样数据在声卡需要输出的时候就准备好了。设置单一周期模式DMA传输的顺序和自动初始化模式下是基本一致的,不过是设置第四位的DMA模式寄存器和第三位的DSP命令不同而已。 <!&&Qd-d6H  
自动初始化模式DMA的中断: 4.7ePbk[E  
1)复制下一个数据单位到刚刚结束的那个输出数据块 Gu&?Gn oc  
2)通过读端口对SB进行中断应答,8位是端口2XEH,16位是端口2XFH 8 @!/%"Kt2  
3)应答中断完毕,向20H端口写入20H,如果声卡使用的是IRQ8-15,那么你还要向A0H写入20H )[6H!y5  
立即停止音频: 'u$$scGt  
8位模式下写入DSP命令D0H,16位模式下写入D5H。这样是立即停止,无需调用中断。 JPgV7+{b[  
在当前数据块播放完毕之后停止音频: {3C~cK{  
8位模式下写入DSP命令DAH,16位模式下写入D9H。这样要等到当前数据块的末尾,如果系统没有为结束音频输出后的中断做好准备,这样可能会导致错误。 _ 9Tv*@  
你仍然可以为单一周期模式而调整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 字节
 
上一个 下一个