FM驱动中实现打开FM,音频驱动自动切换音频的方法:
一:声明一个事件句柄:
static HANDLE g_hEventFMOpen = NULL;
二:编写创建事件函数:
BOOL FMR_CreatEvent()
{
DWORD dwRet;
g_hEventFMOpen = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("FMWAVOUT_EVENT"));
if(g_hEventFMOpen == NULL)
{
g_hEventFMOpen = CreateEvent(NULL, FALSE, FALSE, TEXT
("FMWAVOUT_EVENT"));
if(NULL == g_hEventFMOpen)
{
dwRet = GetLastError();
RETAILMSG(ZONE_ERROR, (TEXT("FMR_KT0801_Init:Open
FMWAVOUT_EVENT fail,ERROR:%d /r/n"),dwRet));
return FALSE;
}
}
return TRUE;
}
三:在打开FM驱动时,同时执行步骤二创建的事件函数:
if (FMR_I2C_Init())
{
RETAILMSG(ZONE_ERROR,(TEXT("<FMR>
FMR_KT0801_Init failed!/r/n")));
}
MapVirtualAddress();
FMR_CreatEvent();
//上电,复位,GPIO初始化
if(FMR_KT0801_Power_Set(1))
{
g_FMSTATE=1; //g_FMSTATE==1 FM POWER ON
FM TX
//FMR_SendMessageId(1);//masked by lqm.
}
四:在FM模块工作时,通过SetEventData()和SetEvent()函数通知音频驱动,FM驱动已经打开:
if((pArg->dwFreqBuffer>=OMITFREQUENT_MIN) & (pArg->dwFreqBuffer<=OMITFREQUENT_MAX))
{
RETAILMSG(1, (TEXT("<FMR> Set KT0801_Frequent:%d!/r/n"),pArg-
>dwFreqBuffer));
FMR_KT0801_I2C_Write(0x00,(USHORT)(pArg->dwFreqBuffer & 0xff));
FMR_KT0801_I2C_Write(0x01,(USHORT)((pArg->dwFreqBuffer>>8) & 0x7 |
0xC0));// PGA Gain:0dB
FMR_KT0801_I2C_Write(0x02,0x40);//清掉CHSEL[0],使用
step=100K.RFGAIN:112.5dBuV
FMR_KT0801_I2C_Write(0x13,0x84);
FMR_KT0801_I2C_Write(0x0B,0x00);//打开PA
// 通知音频驱动,FM已经打开
SetEventData(g_hEventFMOpen,1);
SetEvent(g_hEventFMOpen);
}
else
{
RETAILMSG(1, (TEXT("<FMR> Not support Frequent:%d!/r/n"),pArg-
>dwFreqBuffer));
FMR_KT0801_I2C_Write(0x0B,0x20);//关闭PA
FMR_KT0801_Power_Set(FALSE);
// 通知音频驱动,FM已经关闭
SetEventData(g_hEventFMOpen,0);
SetEvent(g_hEventFMOpen);
FMR_KT0801_Deinit();
return TRUE;
}
五:在关闭FM模块时,通过SetEventData()和SetEvent()函数通知音频驱动,FM驱动已经关闭:
在IOCTL的IOCTL_FMR_KT0801_POWERSTATE_SET中,代码如下:
if((0 == *pBufOut)&&(1==g_FMSTATE))
{
//断电。
FMR_KT0801_I2C_Write(0x0B,0x20);//关闭PA
FMR_KT0801_Power_Set(0);
// 通知音频驱动,FM已经关闭
SetEventData(g_hEventFMOpen,0);
SetEvent(g_hEventFMOpen);
FMR_KT0801_Deinit();
g_FMSTATE=0; //g_FMSTATE==0; FM POWER OFF FM
TX
//FMR_SendMessageId(0);//masked by lqm.
}
六:在音频驱动中定义一个事件的句柄:
在Hwctxt.h的前面,添加事件句柄如下:
HANDLE g_hEventFMOpen;
在Hwctxt.h的前面,添加线程句柄如下:
HANDLE g_hThreadFMOpen;
七:在音频驱动中编写FM线程函数:
void FMWavOutThread(HardwareContext *pHWContext)
{
DWORD dwret;
DWORD dwFMdata = 0;
RETAILMSG(1, (_T("FMWavOutThread start!/r/n")));
HardwareContext *pHWContexttmp = pHWContext;
while(TRUE)
{
dwret = WaitForSingleObject(pHWContexttmp->g_hEventFMOpen, INFINITE);
if(dwret == WAIT_OBJECT_0)
{
dwFMdata = GetEventData(pHWContexttmp->g_hEventFMOpen);
if(dwFMdata == 1)
{
RETAILMSG(1, (_T("FMWavOutThread:Open FM /r/n")));
pHWContexttmp->g_WavOutChannel = WAV_FM;
pHWContexttmp->SetOutChannel(pHWContexttmp-
>g_WavOutChannel);
}
else if(dwFMdata == 0)
{
RETAILMSG(1, (_T("FMWavOutThread:Close FM /r/n")));
pHWContexttmp->g_WavOutChannel = WAV_UNKNOW;
SetEvent(pHWContexttmp->g_hEventHpDet);
}
}
}
}
上面程序中,WaitForSingleObject()函数等待FM驱动发送事件,检测到FM驱动打开时,即切换
音频通道为WAV_FM;检测到FM驱动关闭时,将音频通道切换到WAV_UNKNOW状态,同时将耳机侦测
事件置1,这时该事件会检测是耳机状态还是喇叭状态,再一次切换声音通道。
八:在音频初始化函数BOOL HardwareContext::Initialize(DWORD Index)中,创建事件和线程
:
g_hEventFMOpen = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("FMWAVOUT_EVENT"));
if(g_hEventFMOpen == NULL)
{
g_hEventFMOpen = CreateEvent(NULL, FALSE, FALSE, TEXT
("FMWAVOUT_EVENT"));
if(NULL == g_hEventFMOpen)
{
RETAILMSG(1, (_T(" %s() : CreateEvent() g_hEventFMOpen event
Failed /n/r"), _T(__FUNCTION__)));
return FALSE;
}
}
g_hThreadFMOpen = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) FMWavOutThread,
this, 0, NULL);
if (g_hThreadFMOpen == NULL )
{
RETAILMSG(1, (_T(" %s() : CreateThread() FM open thread Failed /n/r"), _T
(__FUNCTION__)));
return FALSE;
}
整个FM切换过程至此为止,也就结束了。我们将上面用到的函数总结一下:
CreateEvent();
SetEvent();
SetEventData();
GetEventData();
OpenEvent();
CreateThread();
WaitForSingleObject();
a:CreateEvent()
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES
lpEventAttributes,
BOOL bManualReset,
BOOL InitialState,
LPTSTR lpName
);
lpEventAttributes:必须是NULL.
bManualReset:为1时为手动复位,为0时为自动复位。
InitialState:为1时初始化为有信号,为0时初始化为无信号。
lpName:指向一个以0结束的字符串。为0时创建一个没有名字的事件。
b:SetEvent()
BOOL SetEvent(
HANDLE hEvent
);
该函数设置相应的事件对象为有信号。
返回为非0表示设置成功。
CreateEvent函数返回hEvent这个句柄。
当CreateEvent函数中设置为手动复位时,在SetEvent之后,对应的事件对象仍然为有信号,直
到执行ResetEvent函数之后,方变为无信号。任意数目的等待线程或者并发线程,当指定释放。
当CreateEvent函数中设置为自动复位时,在SetEvent之后,对应的事件对象会自动设置为无信
号,同时等待的线程就被释放。如果SetEvent后,没有任何等待的线程,那么相应的事件对象仍
然保持为有信号。
c:SetEventData()
BOOL SetEventData(
HANDLE hEvent,
DWORD dwData
);
hEvent:由CreateEvent函数创建的事件的句柄。
dwData:与一个事件相关联的特定数据。
返回1表示成功,返回0表示失败。
d:GetEventData()
DWORD GetEventData(
HANDLE hEvent
);
hEvent:由CreateEvent函数创建的事件的句柄。
返回值:当SetEventData()设置的数据不是0时,如果GetEventData()返回0,则表示获取数据失
败。该函数返回由SetEventData函数发送的数据。
e:OpenEvent()
HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
该函数打开一个存在的指定名字的事件对象。
dwDesiredAccess:事件对象指定的需要的访问方式,必须设置成EVENT_ALL_ACCESS。
bInheritHandle:必须设置为FALSE.
lpName:指向要打开的事件对象的字符串名称。
返回成功,返回要打开的事件对象的句柄。
返回失败,返回值为NULL。
f:CreateThread()
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpsa,
DWORD cbStack,
LPTHREAD_START_ROUTINE lpStartAddr,
LPVOID lpvThreadParam,
DWORD fdwCreate,
LPDWORD lpIDThread
);
该函数创建一个线程,并在所在地址空间范围内执行调用的程序。
lpsa:必须为NULL。
cbStack:如果标志位STACK_SIZE_PARAM_IS_A_RESERVATION没有用到将被忽略。
lpStartAddr:指向线程的起始地址。
lpvThreadParam:指向一个单独的32位参数值,用于传给该线程。
fdwCreate:线程的指向标识位。当该值为CREATE_SUSPENDED时,线程创建了一个空闲状态,线
程并不运行,直到ResumeThread函数被调用。如果这个标识位没有指定,那么线程创建后会立即
运行。
lpIDThread:指向一个32位的接收线程标识符。
该函数创建成功时返回该线程的句柄,创建失败时返回0.
g:WaitForSingleObject()
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
当指定对象为有信号时,或者等待超时将会返回。
hHandle:指定对象的句柄。
dwMilliseconds:等待时间。当为0时,表示检测对象状态后马上返回;当为INFINITE时,表示
一直等待,走到等待对象为有信号。
返回值:返回WAIT_OBJECT_0时表示等待的对象有信号;
返回WAIT_TIMEOUT时表示超时返回。