//Author:Mercury Xu
//Date:2008-12-08
//Descrption:聲音作為WinCE系統中一個比較基礎的配置大家已經都不會陌生。
// 本文主要是總結下作者在調試WM9715聲卡驅動芯片
// 過程中遇到的一些問題
//OS:WinCE5.0
/
正文
好了。以下是Segment II,跳过来的朋友和读过来的朋友都要准备好了。我们要开始耍WM9715了。
首先我们还是来热身下,热身我们要准备以下两个东西
1.应用层的一些小知识。
2.WM9715 datasheet。
我们先来看第一个,做流式驱动调用的时候我们知道,有一个XXX_IOCONTROL之类的函数一般在最后,我们再WM9715的WAV驱动的地方也有个。 WAV_IOControl。我们在应用层怎么去调用他呢?
其实很简单。大家先看看MSDN的这个文档:
http://msdn.microsoft.com/en-us/library/aa363216(VS.85).aspx
BOOL WINAPI DeviceIoControl(好了。不多说了,看懂了吧。这里再提醒下,case的值一定不能搞错了,搞错了怎么样都是出不了的。
__in HANDLE hDevice, //句柄一个,定义一个HANDLE的句柄,在初始化的时候就好了
__in DWORD dwIoControlCode,//这个一般无关痛痒,Null掉了
__in_opt LPVOID lpInBuffer,//这个就比较重要了,输入数据的接口,要和你定义好的case数值匹配好哦!
__in DWORD nInBufferSize,//这个长度一般都直接用sizeof处理掉了。
__out_opt LPVOID lpOutBuffer,//这个也是比较重要的,返回值,在上一篇文章里面case最后有个比较精彩的程序段,
//给了这个变量值,大家可以回头过去看看。
__in DWORD nOutBufferSize,//这个也直接sizeof了。
__out_opt LPDWORD lpBytesReturned,//这个我们都Null
__inout_opt LPOVERLAPPED lpOverlapped ///这个也NULL了
);
接下来还有个难度稍微大一点点的。我们再上一篇文章应该有看到在try的里面有个很长的结构体,叫PMMDRV_MESSAGE_PARAMS
这个结构体再APP不管你用EVC还是VS2005都是没有给出初始化定义的。所以,接下来我们给出他的定义
- BOOL WAV_IOControl(DWORD dwOpenData,
- DWORD dwCode,
- PBYTE pBufIn,
- DWORD dwLenIn,
- PBYTE pBufOut,
- DWORD dwLenOut,
- PDWORD pdwActualOut)
- You can use pBufIn pass its parameter, and pBufIn is defined with
- typedef struct {
- UINT uDeviceId;
- UINT uMsg;
- DWORD dwUser;
- DWORD dwParam1;
- DWORD dwParam2;
- } MMDRV_MESSAGE_PARAMS, *PMMDRV_MESSAGE_PARAMS;
- and you can use pBufOut to get return value, it is definded with
- (DWORD *)
- (PMMDRV_MESSAGE_PARAMS)pBufIn, (DWORD *)pBufOut
- WRITE REGISTER
- dwCode: IOCTL_WAV_MESSAGE
- uMsg: WODM_WRITE_AC97
- dwParam1: AC97 register's ID, it should be even, please read AC'97 specification
- dwParam2: pass value(write), the value shoud be an 16-bits
- number.
- READ REGISTER
- dwCode: IOCTL_WAV_MESSAGE
- uMsg: WIDM_READ_AC97
- dwParam1: AC97 register's ID, it should be even, please read AC'97 specification
- pBufOut: RETURN VALUE
这下眼前一亮了吧。我顺带将9715的两个东东也带出来了。关于9715的操作我们接下来就开始给大家说说,刺激的地方要开始了。
下面我们开始比较刺激的地方了。看看WM9715的结构以及驱动的地方怎么去处理。
对于芯片的操作,无非就是两个,读寄存器,写寄存器,首先我们看看WM9715的datasheet
http://www.wolfsonmicro.jp/uploads/documents/cn/WM9715L_Rev3.9.pdf
我们先看下他的功能
rel="File-List" href="file:///C:%5CDOCUME%7E1%5CMERCUR%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">
AC’97 Rev 2.2 compatible stereo codec
AC’97 Rev2.2 兼容立体声解码
- DAC SNR 90dB, THD –86dB
- ADC SNR 88dB, THD –88dB
- Variable Rate Audio, supports all WinCE sample rates
多频率音频,支持所有WinCE采样率
- Tone Control, Bass Boost and 3D Enhancement
音调控制,重音控制以及3D环绕控制
• On-chip 45mW headphone driver
片载45毫瓦耳机驱动
• On-chip 400mW mono speaker driver
片载400毫瓦单声道喇叭驱动
• Stereo, mono or differential microphone input
立体声,单声道或者混合麦克风输入
- Automatic Level Control (ALC)
自动等级控制
• Auxiliary mono DAC (ring tone or DC level generation)
辅助单DAC
• Seamless interface to wireless chipset
无线连接接口
• Resistive touchpanel interface
电阻丝触摸板接口
- Supports 4-wire and 5-wire panels
支持4线和5线触摸板
- 12-bit resolution, INL ±3 LSBs (<0.5 pixels)
12位协议,INL ±LSBs (<0.5 pixels)
- X, Y and touch-pressure (Z) measurement
X,Y以及触摸压力测量
- Pen-down detection supported in Sleep Mode
休眠模式下触摸笔下压侦测
• 2 comparator inputs for battery monitoring
2个电源侦测比较输入端口
• Up to 4 auxiliary ADC inputs
4个辅助ADC输入
• 1.8V to 3.6V supplies
1.8V 到 3.6v 电压支持
• 7x7mm QFN
7* 7mm QFN封装
好了,如果你还是不放心,看到
- Variable Rate Audio, supports all WinCE sample rates
多频率音频,支持所有WinCE采样率
这句话你应该可以放心大胆的来玩这颗芯片了。首先,我们需要知道,系统的声音是一个数据音频,所有,系统走的方式是通过I2S来的。这一块一般再BSP包中都会有写好了。大家都不用去修改。直接用就好了
其次,其他的声音输入采用的方式基本上是通过9715的几个音频输入口,比如MIC LINEIN这几个来实现的。首先你要检查下硬件上都是OK,输入一些音频的模拟信号进去,确认已经进入到了芯片里。
最后我们开始来看这个DATASHEET里面的最神奇的图。Audio paths overview。。。大概在14页图片1
这个图太神奇了。。想起了小时玩红白机三国志里面选角色的情景,走线路判断后选人。怀念啊。。。。
这里我们就拿LINEINL这一路来看PIN23蓝色的线走下去。
1.我们先碰到了10H这个寄存器他是控制输入音量的,我们先要使能,然后选择最大音量,这里有个小尝试,+的dB是音量最大啊!!!(众人,地球人都知道。。。)
2.接下来我们看到了个MIXER.不鸟他
3.然后我们看到我们要使能的寄存位了10H:15我们很开心的把它做成1,使能了。
4.headphone mix是个大关,混合了下音以后送出来
5.接下来有个选择器,我们直接过去,上面有个转向器,我们也不用鸟他了02H的转向器
6.最后一个BOSS来了02H首先你要他处于可以放的状态,也就是非MUTE状态,我们仔细一看,02H:7.好了。我们很开心的把它弄成了1.
7.最后的最后我们来弄个音量,也弄个最大音量就好了。
以上就是一套路线打通的方法,其他路线打通的方法大同小异。大家一路跟下去就OK了。
至于如可写寄存器如何读寄存器,下面我们简单说下,因为不同的ARM可能对这个处理的方式不太一样。但是总体上是类似。
上面我们很开心的将一些寄存器位设计为高,我们通过写寄存器的方式实现。下面我给一个参考给大家
- BOOL WriteAC97Reg(USHORT RegIndex, USHORT RegValue)
- {
- ULONG timeout = 0;
- GRABMUTEX();
- IOW_REG_SET(struct ac97Ctrl1Bits, &v_pCodecReg->ac97ctrl1, (RegValue << 16) | ((RegIndex & 0x7f) << 8)); // CODEC_AC97_CMD = (RegValue << 16) | RegIndex;
- IOW_REG_OR(struct ac97Ctrl0Bits, &v_pCodecReg->ac97ctrl0, 0xe000); // CODEC_AC97_CTRL |= 0xe000;
- while ((v_pCodecReg->ac97ctrl0.ac97_write_tagh & AC97_WRITE_SLOT1_VALID) && (timeout++ < TIMEOUT_LOOPS));
- if(timeout >= TIMEOUT_LOOPS)
- {
- RETAILMSG(1, (TEXT("Timeout in write ac97")));
- }
- usWait(5);
- RELEASEMUTEX();
- return (timeout < TIMEOUT_LOOPS);
- }
- // AC97_CONTROL_0 -0X01C R/W
- struct ac97Ctrl0Bits {
- unsigned da_channel :2;// da channel enable, bit1 left channel, bit0 right channel; 1= channel enable;0=channel disable
- unsigned ad_channel :2;// ad channel enable
- unsigned warm_wake :1;// Warm wake codec 1= warm wake 0= exit warm wake-up mode.
- unsigned ac97_start :1;// ac97 interface start 1= ac97 interface start 0= stop
- unsigned rsvd0 :5;// reserved
- unsigned ac97_write_tagh :5;// AC-link output frame tag setting
- unsigned rsvd1 :16;// reserved
- };
- // AC97_STATUS_0 -0X024 R
- struct ac97Status0Bits {
- unsigned ac97_slot_rq :8; // ac97 slot request
- unsigned ac97_read_tagh :8; // ac97 input frame status bit15 codec ready, bit14 solt1 valid, bit13 slot2....
- unsigned rsvd0 :16;
- };
1.IOW_REG_SET和 IOW_REG_OR这两个函数大家根据自己的芯片进行定义就好了,其他都是很常用的。在这里就不多说了。
2. ac97Ctrl1Bits和0是对WM9715你自身ARM控制的一些操作。这个你按照你自己的来就好了。
3.最后两个是赋值了。
基本的思路是上面的。大家还是要根据不同的芯片来不同处理。
那今天驱动部分我们就介绍完了。关于应用层,明天我们再聊。