王朝网络
分享
 
 
 

C++实现用掌上电脑遥控电视

王朝c/c++·作者佚名  2008-06-01
宽屏版  字体: |||超大  

1. 简介

你是否曾想过通过你的掌上电脑上的IR端口控制你的TV、Hi-Fi或者其它视频?本文将介绍怎样使用掌上电脑中的IR端口来编程控制一台TV。

2. 背景

我近些日子丢失了我的老式索尼TV的遥控器。这本身没有什么问题,因为我买了个新的遥控器作为代替。然而,当电视失去了它的设定的颜色时,我碰到了问题,因为它只能显示黑白色了,而新的遥控器没有颜色调整按钮。我决定在我的老式的Jornada 525掌上电脑上写一个程序使用IR端口把正确的代码发送给TV。

共有三个主要协议可以用于发送IR代码到设备上。索尼TV使用 ’Pulse Coded’ 方法,它需要发送一个包含头(header)位的以空格隔开的’1’位和’0’位的数据流。这些位被调制成一种40KHz的载波信号。其中,头长度为2200 μs,’1’位为110 μs,’0’位为550 μs,而空格是550μs的沉默(silence)。大多数索尼设备使用12位数据,它被分离成6位的地址(设备类型)和6位命令。因此数据看起来象这个样子:hxxxxxxyyyyyy,其中h是头位,xxxxxx是6位的命令(msb first),yyyyyy是6位的地址。对此我不再细述,因为网上有很多资源描述这种协议,并列举了针对不同设备的代码。一些新的索尼设备使用19位代码,我相信另外的制造商也使用和我描述的相同的格式。还有可能为使用’Space Coded’或’Shift Coded’协议的设备写出相似的类。

我曾使用嵌入式C++写过一个类CirPulse,它封装了从一台运行Windows CE 3.0的Jornada 525 PC上控制索尼及其相匹配设备的功能。估计它能够与其它相匹配设备和操作系统一起工作,但是你需要试验才行!

更多内容请看C/C++技术专题 电脑配置手册 电脑维修专题专题,或

3. 实现过程分析

这个CIrPulse类暴露了几个函数,它们使得发送IR代码尽可能轻易。在声明CIrPulse类时,你应该调用一次FindIrPort(),它返回一个描述IrDA端口的端口号的UINT,这通过搜索注册表得到。

这个端口号用于后面的调用来打开IrDA端口进行串行通讯。

UINT CIrPulse::FindIrPort()

{

// 查询注册表中的IR端口号

HKEY hKey = NULL;

if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("Comm\IrDA"),0, 0, &hKey) == ERROR_SUCCESS)

{

DWord dwType = 0;

DWORD dwData = 0;

DWORD dwSize = sizeof(dwData);

if (RegQueryValueEx(hKey, _T("Port"), NULL, &dwType, (LPBYTE) &dwData, &dwSize) == ERROR_SUCCESS)

{

if (dwType == REG_DWORD && dwSize == sizeof(dwData))

{

RegCloseKey(hKey);

return (UINT) dwData;

}

}

RegCloseKey(hKey);

}

return 0;

}

得到端口号后,你可以调用Open(UINT)函数,把通过调用FindIrPort()得到的端口号传递过去。这打开该端口并设置串口参数,假如成功返回true。该端口被设置为115200波特,8个数据位,2个停止位和奇偶校验位。关于如何产生载波以及为什么我使用这些设置将在本文后面介绍。

BOOL CIrPulse::Open(UINT uiport)

{

ASSERT(uiPort > 0 && uiPort <= 255);

Close();

//打开IRDA端口

CString strPort;

strPort.Format(_T("COM%d:"), uiPort);

m_irPort = CreateFile((LPCTSTR) strPort, GENERIC_READ GENERIC_WRITE,0, NULL, OPEN_EXISTING, 0, NULL);

if (m_irPort == INVALID_HANDLE_VALUE)

{

return FALSE;

}

//设置输入和输出缓冲区的大小

VERIFY(SetupComm(m_irPort, 2048, 2048));

//清除读和写缓冲区

VERIFY(PurgeComm(m_irPort,PURGE_TXABORTPURGE_RXABORT

PURGE_TXCLEARPURGE_RXCLEAR));

//重新初始化所有的IRDA端口设置

DCB dcb;

dcb.DCBlength = sizeof(DCB);

VERIFY(GetCommState(m_irPort, &dcb));

dcb.BaudRate = CBR_115200;

dcb.fBinary = TRUE;

dcb.fParity = TRUE;

dcb.fOutxCtsFlow = FALSE;

dcb.fOutxDsrFlow = FALSE;

dcb.fDtrControl = DTR_CONTROL_DISABLE;

dcb.fDsrSensitivity = FALSE;

dcb.fTXContinueOnXoff = FALSE;

dcb.fOutX = FALSE;

dcb.fInX = FALSE;

dcb.fErrorChar = FALSE;

dcb.fNull = FALSE;

dcb.fRtsControl = RTS_CONTROL_DISABLE;

dcb.fAbortOnError = FALSE;

dcb.ByteSize = 8;

dcb.Parity = EVENPARITY;

dcb.StopBits = TWOSTOPBITS;

VERIFY(SetCommState(m_irPort, &dcb));

//为所有的读和写操作设置超时值

COMMTIMEOUTS timeouts;

VERIFY(GetCommTimeouts(m_irPort, &timeouts));

timeouts.ReadIntervalTimeout = MAXDWORD;

timeouts.ReadTotalTimeoutMultiplier = 0;

timeouts.ReadTotalTimeoutConstant = 0;

timeouts.WriteTotalTimeoutMultiplier = 0;

timeouts.WriteTotalTimeoutConstant = 0;

VERIFY(SetCommTimeouts(m_irPort, &timeouts));

DWORD dwEvent=EV_TXEMPTY;

SetCommMask(m_irPort,dwEvent);

return TRUE;

}

更多内容请看C/C++技术专题 电脑配置手册 电脑维修专题专题,或

调用函数SetCodeSize(DWORD)来设置要传送的位数(如12位)。这可以在任何时候完成且只需要做一次。它一直保持有效,直到后面的调用改变它为止。

最后调用SendCode(long),传递实际要发送的代码。

BOOL CIrPulse::SendCode(DWORD lValue)

{

DWORD dwCount;

int i=0;

ASSERT(iDataLength>0);

//清除传送缓冲区

VERIFY(PurgeComm(m_irPort,PURGE_TXABORT PURGE_RXABORT PURGE_TXCLEAR PURGE_RXCLEAR));

//每次按键设置代码6次

for(int x=0;x<6;x++) {

MakeStream(lValue); //发送代码

dwCount=GetTickCount();

while(GetTickCount()<dwCount+26) //延迟26ms

i++;

}

return true;

}

注重这个函数调用另外一个函数MakeStream(long)6次,每两次调用之间停顿26毫秒。我发现该代码必须发送好几次才能使接收设备响应,大概是为防止假行为的缘故吧。26毫秒对于接收设备登记该代码是必需的,在下一个代码出现之前。

这个函数MakeStream(long)把字节流写入IrPort,并根据是否有起始位(1或者0)来确保发送正确的数据包长度。包含数据字节(0xdb)的缓冲区是以一个ByteArray形式存在的。

函数Close()用于在端口使用后,自然地关闭IrPort。

更多内容请看C/C++技术专题 电脑配置手册 电脑维修专题专题,或

这个函数在我的ornada上运行良好。请看下面的讨论以进一步确定你要做的可能性改变。

BOOL CIrPulse::MakeStream(DWORD lValue) {

DWORD dwStreamLength;

//创建开始脉冲

dwStreamLength=iHPulse/charWidth;

ASSERT(Write((const char *)bPulseStream.GetData(),

dwStreamLength)==dwStreamLength);

// ************************************

// ***** 在下一个脉冲到来前延迟一段时间

// ************************************

//循环操作代码中的位来发送脉冲

for(int i=0;i<iDataLength;i++) {

if(lValue & 1) {

//创建一个脉冲1

dwStreamLength=i1Pulse/charWidth;

ASSERT(Write((const char *)bPulseStream.GetData(),

dwStreamLength)==dwStreamLength);

// *********************************

// ***在下一个脉冲到来前延迟一段时间

// *********************************

}

else {

//创建一个脉冲 0

dwStreamLength=i0Pulse/charWidth;

ASSERT(Write((const char *)bPulseStream.GetData(),

dwStreamLength)==dwStreamLength);

// ********************************

// **在下一个脉冲到来前延迟一段时间

// ********************************

}

lValue >>= 1;

}

return TRUE;

}

我在所附源代码中包含了一个简单的应用程序,它使用CIrPulse来创建一台索尼TV的远距离遥控。它具有基本的频道选择、音量调整和开/关机的功能。

4. 非凡注重

因为该CIrPort类使用一个串行端口连接到该IR端口,所以必须生成一个40KHz的载波信号,这通过从该串行端口发送恰当的字符来实现。幸好,假如我们发送字符0xdb,以115200波特,用8个数据位,2个停止位和奇偶校验,这样就能产生一种极接近38.4KHz的载波信号。我们所有的索尼设备接收这种数据是没有问题的。

最大的问题是,如何实现间隔每次脉冲的沉默周期。不可能由串行端口来产生该沉默周期,因为就算你发送一个0x0字符,由于存在起始和停止位,你仍然在该IR上得到脉冲。我通过发送不同的字符进行试验,依据的前提是假如你不以40KHz的频率发送一个载波信号,这有可能使设备误把这个当作一个沉默。这样做的优点是你可以产生一个包含完整的代码的byteArray,以确保准确计时。但是结果并不一致,所以我拒绝使用这个方法,为的是实现在两次从串行端口发出成组的0xdb字符之间支持暂停。因为需要的延迟是以550μs的顺序;到目前为止,我还没有找到取得独立于处理器速度的暂停的方法。在我的Jornada上,是完全不必产生一个延迟的,因为每次调用Write函数看上去都使用了合适的时限。不管怎样,我担心的是,你可能胡乱产生一个可以使你的掌上电脑能工作的一个延迟。

更多内容请看C/C++技术专题 电脑配置手册 电脑维修专题专题,或

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有