上一篇:Oracle数据库密码文件的使用与维护 >>
WinCE下用C++实现掌上电脑遥控TV
| 天极软件专题专区精选 到天极软件“读编交流区”畅所欲言 | |||
| Google专区 | POPO专区 | QQ专区 QQ挂机 | 了解Web2.0 |
| Flash MX 视频教程 | Photoshop视频教程 | 网页设计视频教程 | 照片处理数字暗房 |
| PPT动画演示教程 | Excel动画教程集 | Word动画演示教程 | Windows Vista专区 |
| 特洛伊木马专区 | 黑客知识教程专区 | 防火墙应用专区 | 注册表应用专区 |
| Windows API开发专区 | 网络编程专区 | VB数据库编程专区 | 图像处理与多媒体编程 |
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上控制索尼及其相匹配设备的功能。估计它能够与其它相匹配设备和操作系统一起工作,但是你需要试验才行!
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_TXABORT|PURGE_RXABORT| PURGE_TXCLEAR|PURGE_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; } |
调用函数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。
这个函数在我的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函数看上去都使用了合适的时限。不管怎样,我担心的是,你可能胡乱产生一个可以使你的掌上电脑能工作的一个延迟。
下一篇:利用Visual C#编程模拟鼠标操作 >>
相关文章:
- · C#+Direct3D9.0开发实例之月亮绕着地球转
- · Visual C# 3.0 新特性概览
- · 利用Visual C#实现Reversi游戏开发
- · 对C# 2.0中匿名方法的怀疑分析
- · 在C#中利用DirectX实现声音播放
- · 对C#开发的两个基本原则的深入讨论
- · 开源的真正影响:扩大软件资产的生命力
- · 10岁学编程 拥有19项专利的女程序员
- · SOA:编织未来IT架构
- · 项目管理之我见-程序员程序开发步骤
- · 技术与人生 谈程序员的成长
- · 分析:Web2.0的喧哗与阵痛
- · Gartner称企业将在Windows上运行开源软件
- · 开放源代码正被恶意软件作者所利用
- · 开源JBoss牵手Sun成NetBeans合作伙伴
- · ASP.NET2.0数据库入门之SQL Server
- · 基于Socket的Java网络编程集粹
- · Vista可在发布后立即升级 无需等待补丁
- · OEM被指是鸦片 技术黑洞吞噬高新技术产业
- · 软件产业政策将逐步法制化 国家新政将出台
- · 鲍尔默称四年前预言失败 Linux并未击倒视窗
- · Vista再次大缩水 PC2PC同步功能被取消
- · 中国软件遭遇逼迫式打法 本土化趋势加速
- · 公测版Vista发布 服务器不堪重负暂停下载
- · 中国软件业“芯”太软
- · 微软杀毒欲玩捆绑之计图谋通吃
- · 微软不再提供Win98补丁 将停止支持XP SP1
- · Google CEO:未来50年仍以搜索为核心业务
- · IBM成为ISV和开发者合作方面领导商
- · 倪光南:中国软件人对开源社区贡献太少
- · 开源世界遭遇尴尬 商业味日趋浓厚
- · 微软攻击开源 称Office领先十年
- · 微软的存在成为开源软件最大的动力
- · AVS对外开放解码器源代码 可免费下载
- · 从AOL的开放看即时通讯竞合
- · 华盛顿现美国版木子美 写情爱日记遭开除
- · 首个博客公约4月出台 效果业内并不看好
- · 中国博客门耍了西方媒体 开超级大玩笑
