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 ----------------------------------------------
//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的。
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
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
//这个地方的处理很奇妙哦。很好的方法。
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.最后两个是赋值了。
基本的思路是上面的。大家还是要根据不同的芯片来不同处理。
那今天驱动部分我们就介绍完了。关于应用层,明天我们再聊。
//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
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.