注册通行证 用户名 密码
  • 文章投稿
  • 博客
  • 论坛
  • 设为首页
  • 加入收藏
jztop.com网络技术
  • 首页
  • | iT新闻
  • | 操作系统
  • | 组网建网
  • | 网络安全
  • | 程序开发
  • | 办公一族
  • | 工具软件
  • | 网页制作
  • | 多媒体制作
  • | 网吧技术
  • | 服务器
  • | 专题教程
Vista | 软件评测 | 系统备份 | 优化 | 进程 | 聊天 | 病毒 | Linux | 黑客 | 防火墙 | 数据库 | Web开发 | Java | Word | 游戏 | 32位开发 | 移动开发
当前位置:首页 > 程序开发 > 32位开发 > C/C++ 内容正文:WinCE下用C++实现掌上电脑遥控TV

WinCE下用C++实现掌上电脑遥控TV

发布时间:2006-05-05 19:03:24 来源:天极网 网友评论 0 条

    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函数看上去都使用了合适的时限。不管怎样,我担心的是,你可能胡乱产生一个可以使你的掌上电脑能工作的一个延迟。

相关文章
  • 伪随机数生成及在VC++中的实现
  • Visual C++常用数据类型转换详解
  • 为VC++应用程序对话框添加透明位图
  • 用Visual C++实现屏幕抓程序
  • 用VC++模拟实现QQ密码盗窃软件
【评论】【收藏本文】【打印】【关闭】
上一篇文章:利用VC实现图像的特殊显示效果
下一篇文章:VC++动态链接库编程之MFC扩展 DLL
讨论区
查看
已有 0 位对此新闻感兴趣的网友发表了看法
匿名发表
注册通行证 登陆
图文阅读推荐
推荐阅讯
  • C++ 通过WIN32 API 获取逻辑磁盘详细信息
  • 聆听混沌的声音
  • 明晰C++内存分配的五种方法的区别
  • VC编程轻松获取局域网连接通知
  • exit()子程序终止函数与return()函数的差别
  • 把其他C/C++编译器集成到VC2005中
  • Visual C++实现WinXP关机特效
  • C++启蒙之编写简单的C++程序
  • 在MFC下如何定义全局变量和全局函数
  • C++对象计数
阅读排行
  • 1.Borland 发布C++ Builder 2006 RAD 环境
  • 2.C/C++程序员应聘常见面试题深入剖析
  • 3.Visual C++常用数据类型转换详解
  • 4.C++中的 static 关键字
  • 5.利用VC++实现局域网实时视频传输
  • 6.浅谈C/C++内存泄漏及其检测工具
  • 7.英国投票否决C++/CLI 微软强攻ISO标准受挫
  • 8.VC++下用MSComm控件实现串口通讯
  • 9.伪随机数生成及在VC++中的实现
  • 10.VC++编程实现对波形数据的频谱分析
专题教程
  • 大话G游 专题:手机病毒揭密
  • ARP攻击防范与解决方案 路由故障处理手册
  • Picasa中文版_Picasa教程 专题:清除流氓软件
  • Firefox专题 seo搜索引擎优化专区
  • 重装Windows必知的事情 装机之必备软件大行动
病毒专杀栏
  • 杀毒软件反被病毒杀 连"救命"都不能喊
  • 金山ARP防火墙
  • 还原卡神话破灭“机器狗”病毒来势汹汹
  • cctv经济半小时:你的手机现在安全吗?
  • 新挂马方式开始流行 ARP挂马称雄局域网
  • 木马和病毒清除的通用解法
  • IP地址不再冲突 查找ARP攻击者元凶
  • 教你几招识别和防御Web网页木马
  • 分析:封杀BT只是暂时的止痛药
  • QQ爆危险漏洞,“QQ游戏邀请大盗”邀请你玩病
关于我们 | 诚聘英才 | 联系我们 | 版权声明 | 网站大事 | 网站地图 | 意见建议
CopyRight 2005-2007 Jztop.Com 版权所有 未经许可 请勿转载