wince音频驱动

wince音频驱动

发表于: 2009-4-15 09:22    作者: weipeng    来源: 嵌入式产品设计

/
//Author:Mercury Xu
//Date:2008-12-05
//Descrption:聲音作為WinCE系統中一個比較基礎的配置大家已經都不會陌生。
//           本文主要是總結下作者在調試WM9715聲卡驅動芯片
//           過程中遇到的一些問題
//OS:WinCE5.0
/
正文:

最近一直在調試關於WM9715的東東,整體調試下來感覺9715的確是個很好很強大的芯片。下面我們按照我的開發思路來整理下關於9715開發的一些心得體會。

首先我們來看下WinCE的聲音處理部分的一些基礎知識。如果您對這一部分已經爛熟於心了,請跳到下一個segment。如果您對WM971X系列爛熟於心了。。I'm sorry。看我的文实在是浪费您的时间了。

另外。大家可以参考下《Windows CE实用开发技术》之 8.10 驱动程序实例分析,這裡張老師已經將驅動的結構講的非常清晰了。大家可以參考下。張老師的書中所提及的是ES1371,我以下的文章針對芯片為WM9715.關於9715的datasheet,大家可以參考wolfson的官方站上的datasheet。

Segment I: WinCE的声音驱动部分

WinCE的声音驱动其实很清晰的给了我们大家。以samsung24X0系来说。你会在X:\WINCE500\PUBLIC\COMMON\OAK\CSP\ARM\SAMSUNG\S3C2410X\WAVEDEV类似的目录下找到他们。以下为我Tree以后他的文件结构

文件夹 PATH 列表卷序列号码为 000028F4 409E:08D7C:.│  audiosys.h│  devctxt.cpp│  devctxt.h│  hwctxt.cpp│  hwctxt.h│  i2s.cpp│  i2s.h│  input.cpp│  makefile│  midinote.cpp│  midistrm.cpp│  midistrm.h│  mixerdrv.cpp│  mixerdrv.h│  output.cpp│  sources│  strmctxt.cpp│  strmctxt.h│  wavemain.cpp│  wavemain.h│  wavepdd.h│  wfmtmidi.h│ └─obj    └─ARMV4I        └─retail                devctxt.obj                hwctxt.obj                i2s.obj                input.obj                midinote.obj                midistrm.obj                mixerdrv.obj                output.obj                strmctxt.obj                wavemain.obj
很清晰的程序结构。其实很多公司的都是参照这个结构来定制自己声音部分的程式代码。而我们需要注意的这是以下几个文件:
这几个基本上算是做好了对声音驱动部分的所有接口。如果您要基于WinCE5.0开发一些声音控制方面的App.第一个方法您可以对注册表的一些键值进行修改。比如说触摸屏按键音,音频音量等等。另外,你也可以通过操作以上提及到的文件中的一些接口来对声音处理芯片进行控制,已实现我们所要实现的效果。这里我只分析下我说到的几个文件。其他的例如I2S之类的大家可以看一下。(I2S是一种通讯方式,数字音频传输采用这种方式来处理,WM9715就是I2S来传送系统部分的声音。)

我们先看看HWCTXT.h文件中的一些信息,直接了解他的接口部分内容。

class HardwareContext{
public:    HardwareContext();    ~HardwareContext();

void Lock()   {EnterCriticalSection(&m_Lock);}
void Unlock() {LeaveCriticalSection(&m_Lock);}

#DWORD GetNumInputDevices()  {return 1;}
#DWORD GetNumOutputDevices() {return 1;}
#DWORD GetNumMixerDevices()  {return 1;}
    DeviceContext *GetInputDeviceContext(#UINT DeviceId)    {
return &m_InputDeviceContext;    }
    DeviceContext *GetOutputDeviceContext(#UINT DeviceId)    {
return &m_OutputDeviceContext;    }

#BOOL Init(#DWORD Index);                                     
#BOOL Deinit();

void PowerUp();
void PowerDown();

#BOOL StartInputDMA();
#BOOL StartOutputDMA();

void StopInputDMA();
void StopOutputDMA();

void InterruptThread();

#DWORD       GetOutputGain (void);    MMRESULT    SetOutputGain (#DWORD dwVolume);
#DWORD       GetInputGain (void);    MMRESULT    SetInputGain (#DWORD dwVolume);

#BOOL        GetOutputMute (void);    MMRESULT    SetOutputMute (#BOOL fMute);
#BOOL        GetInputMute (void);    MMRESULT    SetInputMute (#BOOL fMute);


protected:
#DWORD m_dwOutputGain;
#DWORD m_dwInputGain;
#BOOL  m_fInputMute;
#BOOL  m_fOutputMute;


#DWORD m_MicrophoneRouting;
#DWORD m_SpeakerRouting;
#DWORD m_InternalRouting;
#DWORD m_MasterOutputGain;

#BOOL InitInterruptThread();

#BOOL InitInputDMA();
#BOOL InitOutputDMA();
#BOOL Codec_channel();
#BOOL InitCodec();

#BOOL MapRegisters();
#BOOL UnmapRegisters();
#BOOL MapDMABuffers();
#BOOL UnmapDMABuffers();

#ULONG TransferInputBuffer(#ULONG NumBuf);
#ULONG TransferOutputBuffer(#ULONG NumBuf);
#ULONG TransferInputBuffers(#DWORD dwDCSR);
#ULONG TransferOutputBuffers(#DWORD dwDCSR);


#DWORD GetInterruptThreadPriority();

#DWORD m_DriverIndex;    CRITICAL_SECTION m_Lock;

#BOOL m_Initialized;
#BOOL m_InPowerHandler;
#DWORD m_dwSysintrOutput;
#DWORD m_dwSysintrInput;
    InputDeviceContext m_InputDeviceContext;    OutputDeviceContext m_OutputDeviceContext;

#PBYTE       m_Input_pbDMA_PAGES[2];
#PBYTE       m_Output_pbDMA_PAGES[2];

#BOOL m_InputDMARunning;
#BOOL m_OutputDMARunning;
#ULONG m_OutBytes[2];
#ULONG m_InBytes[2];

#WORD  m_nOutputVolume;                  // Current HW Playback Volume 

#WORD  m_nInputVolume;                   // Current HW Input (Microphone) Volume 


#HANDLE m_hAudioInterrupt;               // Handle to Audio Interrupt event.

#HANDLE m_hAudioInterruptThread;         // Handle to thread which waits on an audio interrupt event.


//----------------------- Platform specific members ----------------------------------


#DWORD  m_OutputDMAStatus;                   // Output DMA channel's status

#DWORD  m_InputDMAStatus;                    // Input DMA channel's status


#BOOL AudioMute(#DWORD channel, #BOOL bMute);  
//------------------------------------------------------------------------------------

};

void CallInterruptThread(HardwareContext *pHWContext);


//----------------------------------- Helper Functions and Macros ----------------------------------------


//======== Record =========

#define ioRecordPointerLow                      (g_pDMAregs->DIDST1)

#define ioRecordPointerHigh                     (g_pDMAregs->DIDST1)


#define RECORD_DMA_BUFFER_PHYS                  (g_PhysDMABufferAddr.LowPart + 2 * AUDIO_DMA_PAGE_SIZE)


#define AUDIO_RESET_RECORD_POINTER()            {ioRecordPointerLow  = (RECORD_DMA_BUFFER_PHYS);    \
                                                 ioRecordPointerHigh = (RECORD_DMA_BUFFER_PHYS+ AUDIO_DMA_PAGE_SIZE); }

#define AUDIO_IN_CLEAR_INTERRUPTS()             (g_pDMAregs->DCON1 = g_pDMAregs->DCON1)


#define AUDIO_IN_DMA_ENABLE()                   {   g_pDMAregs->DMASKTRIG1 = ENABLE_DMA_CHANNEL; \
                                                    g_pDMAregs->DMASKTRIG1 &= STOP_DMA_TRANSFER; \                                                    g_pIISregs->IISFCON |= ( RECEIVE_FIFO_ACCESS_DMA  | RECEIVE_FIFO_ENABLE);   \                                                    g_pIISregs->IISCON  |= RECEIVE_DMA_REQUEST_ENABLE;   }

#define AUDIO_IN_DMA_DISABLE()                  {   StopI2SClock(); \
                                                    g_pIISregs->IISCON &= ~RECEIVE_DMA_REQUEST_ENABLE;  \                                                    g_pIISregs->IISFCON &= ( RECEIVE_FIFO_ACCESS_DMA  | RECEIVE_FIFO_ENABLE);   \                                                    g_pDMAregs->DMASKTRIG1 |= STOP_DMA_TRANSFER;    }

#define SELECT_AUDIO_DMA_INPUT_BUFFER_A()       (g_pDMAregs->DIDST1 = (int)(g_PhysDMABufferAddr.LowPart+2*AUDIO_DMA_PAGE_SIZE) )

#define SELECT_AUDIO_DMA_INPUT_BUFFER_B()       (g_pDMAregs->DIDST1 = (int)(g_PhysDMABufferAddr.LowPart+3*AUDIO_DMA_PAGE_SIZE) )



//======== Playback =========

#define ioPlaybackPointerLow                    (g_pDMAregs->DISRC2)

#define ioPlaybackPointerHigh                   (g_pDMAregs->DISRC2)


#define AUDIO_RESET_PLAYBACK_POINTER()          {ioPlaybackPointerLow  = (g_PhysDMABufferAddr.LowPart); \
                                                 ioPlaybackPointerHigh = (g_PhysDMABufferAddr.LowPart + AUDIO_DMA_PAGE_SIZE); }   

#define AUDIO_OUT_CLEAR_INTERRUPTS()            (g_pDMAregs->DCON2 = g_pDMAregs->DCON2)


#define AUDIO_OUT_DMA_ENABLE()                  {   StartI2SClock(); \
                                                    g_pDMAregs->DMASKTRIG2 |= ENABLE_DMA_CHANNEL;  }

#define AUDIO_OUT_DMA_DISABLE()                 { StopI2SClock();  g_pDMAregs->DMASKTRIG2 &= ~ENABLE_DMA_CHANNEL; }


#define SELECT_AUDIO_DMA_OUTPUT_BUFFER_A()      (g_pDMAregs->DISRC2 = (int)(g_PhysDMABufferAddr.LowPart) )

#define SELECT_AUDIO_DMA_OUTPUT_BUFFER_B()      (g_pDMAregs->DISRC2 = (int)(g_PhysDMABufferAddr.LowPart+AUDIO_DMA_PAGE_SIZE) )


//------------------------------------------ Externs ----------------------------------------------

我也来说两句 查看全部回复 最新回复
好了,到這裡大家估計已經基本上暈了,我來給大家刪除點東西。大家就能看的非常清晰


class HardwareContext 

public: 
   

//通用接口部分





protected: 

//保护接口部分





    //----------------------- Platform specific members ---------------------------------- 
//成员

    //------------------------------------------------------------------------------------ 


}; 


void CallInterruptThread(HardwareContext *pHWContext); 




//----------------------------------- Helper Functions and Macros ---------------------------------------- 


//======== Record ========= 


//------------------------------------------ Externs ---------------------------------------------- 
extern HardwareContext *g_pHWContext;
好了。通用接口部分我们可以自由使用,接口再wavemain.cpp中都已经给我们做好了流式驱动通用的接口。大家只要简单的了解下格式就可以很容易的去使用他。

现在,我们准备自由发挥了。在自由发挥前,我们再做个热身运动,看一看wavemain.cpp.


#BOOL CALLBACK DllMain(#HANDLE hDLL, 
                      #DWORD dwReason, 
                      #LPVOID lpvReserved) 

    switch (dwReason) { 
        case DLL_PROCESS_ATTACH : 
            DEBUGREGISTER((#HINSTANCE)hDLL); 
            DisableThreadLibraryCalls((#HMODULE) hDLL); 
            break; 

        case DLL_PROCESS_DETACH : 
            break; 

        case DLL_THREAD_DETACH : 
            break; 

        case DLL_THREAD_ATTACH : 
            break; 

        default : 
            break; 
    } 
    return TRUE; 



// ----------------------------------------------------------------------------- 
// 
// @doc     WDEV_EXT 
// 
// @topic   WAV Device Interface | Implements the WAVEDEV.DLL device 
//          interface. These functions are required for the device to 
//          be loaded by DEVICE.EXE. 
// 
// @xref                          <nl> 
//          <f WAV_Init>,         <nl> 
//          <f WAV_Deinit>,       <nl> 
//          <f WAV_Open>,         <nl> 
//          <f WAV_Close>,        <nl> 
//          <f WAV_Read>,         <nl> 
//          <f WAV_Write>,        <nl> 
//          <f WAV_Seek>,         <nl> 
//          <f WAV_PowerUp>,      <nl> 
//          <f WAV_PowerDown>,    <nl> 
//          <f WAV_IOControl>     <nl> 
// 
// ----------------------------------------------------------------------------- 
// 
// @doc     WDEV_EXT 
// 
--------- 
extern "C" #DWORD WAV_Init(#DWORD Index) 

    return((#DWORD)HardwareContext::CreateHWContext(Index)); 




extern "C" #BOOL WAV_Deinit(#DWORD dwData) 

    return(g_pHWContext->Deinit()); 




extern "C" PDWORD WAV_Open( #DWORD dwData, 
              #DWORD dwAccess, 
              #DWORD dwShareMode) 

    // allocate and return handle context to efficiently verify caller trust level 
    return new #DWORD(NULL); // assume untrusted. Can't tell for sure until WAV_IoControl 




extern "C" #BOOL WAV_Close(PDWORD pdwData) 

    // we trust the device manager to give us a valid context to free. 
    delete pdwData; 
    return(TRUE); 




extern "C" #DWORD WAV_Read(#DWORD dwData, 
               #LPVOID pBuf, 
               #DWORD Len) 

    // Return length read 
    return(0); 




extern "C" #DWORD WAV_Write(#DWORD dwData, 
                #LPCVOID pBuf, 
                #DWORD Len) 

    // return number of bytes written (or -1 for error) 
    return(0); 




extern "C" #DWORD WAV_Seek(#DWORD dwData, 
               #long pos, 
               #DWORD type) 

    // return an error 
    return((#DWORD)-1); 




extern "C" #VOID WAV_PowerUp(#VOID) 

    g_pHWContext->PowerUp(); 
    return; 




extern "C" #VOID WAV_PowerDown(#VOID) 

    g_pHWContext->PowerDown(); 
    return; 



#BOOL HandleWaveMessage(PMMDRV_MESSAGE_PARAMS pParams, #DWORD *pdwResult) 

    //  set the error co de to be no error first 
    SetLastError(MMSYSERR_NOERROR); 

    #UINT uMsg = pParams->uMsg; 
    #UINT uDeviceId = pParams->uDeviceId; 
    #DWORD dwParam1 = pParams->dwParam1; 
    #DWORD dwParam2 = pParams->dwParam2; 
    #DWORD dwUser   = pParams->dwUser; 
    StreamContext *pStreamContext = (StreamContext *)dwUser; 

    #DWORD dwRet; 

    g_pHWContext->Lock(); 
    switch (uMsg) 
    { 
        //很多个CASE,我们就省略掉了,大家自己看看找到自己要用的            

        default: 
        dwRet  = MMSYSERR_NOTSUPPORTED; 
    } 
    g_pHWContext->Unlock(); 

    // Pass the return co de back via pBufOut 
    //这个地方的处理很奇妙哦。很好的方法。

    if (pdwResult) 
    { 
        *pdwResult = dwRet; 
    } 

    return(TRUE); 




extern "C" #BOOL WAV_IOControl(PDWORD  pdwOpenData, 
                   #DWORD  dwCode, 
                   #PBYTE  pBufIn, 
                   #DWORD  dwLenIn, 
                   #PBYTE  pBufOut, 
                   #DWORD  dwLenOut, 
                   PDWORD pdwActualOut) 


    // check caller trust. if context hasn't been initialized, load from CeGetCallerTrust. 
    if (*pdwOpenData != OEM_CERTIFY_TRUST) { 
        if (OEM_CERTIFY_TRUST != (*pdwOpenData = CeGetCallerTrust())) { 
            PRINTMSG(ZONE_WARN, (TEXT("WAV_IoControl: untrusted process\r\n"))); 
            SetLastError(ERROR_ACCESS_DENIED); 
            return FALSE; 
        } 
    } 
//以下为无敌的出错处理部分,非常指的我们学习的。


    _try 
    { 
        switch (dwCode) 
        { 
        case IOCTL_MIX_MESSAGE: 
                        return HandleMixerMessage((PMMDRV_MESSAGE_PARAMS)pBufIn, (#DWORD *)pBufOut); 

        case IOCTL_WAV_MESSAGE: 
            return HandleWaveMessage((PMMDRV_MESSAGE_PARAMS)pBufIn, (#DWORD *)pBufOut); 
        } 

    } 
    _except (GetExceptionCode() == STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) 
    { 
        RETAILMSG(1, (TEXT("EXCEPTION IN WAV_IOControl!!!!\r\n"))); 
        SetLastError(E_FAIL); 
    }
以上的部分就是我们做APP程序最终的接口部分了。我们会用到WAV_IOControl作为最后的app调用的入口来对那些看的眼花缭乱的case进行调用操作。关于CASE和case所对应的函数就是针对不同的芯片处理的不同功能,针对不同的芯片有不同的处理函数实现,这个需要大家静下心来仔细阅读芯片的datasheet了。

下一篇我们就来一起看看WM9715这个传说中的芯片。很牛X的。
/
//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(
__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了
);

好了。不多说了,看懂了吧。这里再提醒下,case的值一定不能搞错了,搞错了怎么样都是出不了的。

接下来还有个难度稍微大一点点的。我们再上一篇文章应该有看到在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



我们先看下他的功能

st1\:*{beha vior:url(#ieooui) } 
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.最后两个是赋值了。

基本的思路是上面的。大家还是要根据不同的芯片来不同处理。
那今天驱动部分我们就介绍完了。关于应用层,明天我们再聊。

嵌入式产品设计 | 交流论坛 | 快捷面板 | 站点地图 | 友情链接 | 空间列表 | 站点存档 | 联系我们

Powered by 6.0.1 UC © 2001-2008 Comsenz Inc. 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值