wince下用c++实现掌上电脑遥控tv - 网易学院·教程.htm

来自「WinCE下串口编程应用实例」· HTM 代码 · 共 515 行 · 第 1/3 页

HTM
515
字号
  <LI><A href="http://tech.163.com/special/v/000915SN/videostu.html" 
  target=_self>视频中心</A> </LI>
  <LI><A href="http://tech.163.com/special/A/00091593/Arts1.html" 
  target=_self>艺术设计</A></LI>
  <LI><A href="http://tech.163.com/special/S/000915AH/S_InterView.html" 
  target=_self>教授视点</A> </LI>
  <LI><A href="http://tech.163.com/special/s/000915AS/softnews.html" 
  target=_self>软件新闻</A> </LI>
  <LI><A 
  href="http://tech.163.com/special/s/00091KUI/securitynews.html">病毒安全新闻</A> 
  </LI>
  <LI><A href="http://tech.163.com/special/s/000917D5/schoolbbs.html">论坛</A> 
  </LI>
  <LI><A href="http://tech.163.com/special/s/00091589/schoolroll.html">滚动更新</A> 
  </LI></UL></DIV>
<DIV id=MyPosition>你现在的位置是: <A href="http://www.163.com/">网易</A> &gt; <A 
href="http://tech.163.com/">网易科技</A> &gt; <A 
href="http://tech.163.com/school">网易学院·教程</A> </DIV>
<DIV id=TextTitle>
<H1>WinCE下用C++实现掌上电脑遥控TV</H1>http://tech.163.com/school · 2005-10-10 15:07:53 · 
来源: 天极网</DIV>
<DIV id=TextContent>
<P></P>&nbsp;&nbsp;<STRONG>&nbsp; 1. 
简介</STRONG><BR><BR>  你是否曾想过通过你的掌上电脑上的IR端口控制你的TV、Hi-Fi或者其它视频?本文将介绍怎样使用掌上电脑中的IR端口来编程控制一台TV。<BR><BR><STRONG>  2. 
背景</STRONG><BR><BR>  我近些日子丢失了我的老式索尼TV的遥控器。这本身没有什么问题,因为我买了个新的遥控器作为代替。然而,当电视失去了它的设定的颜色时,我遇到了问题,因为它只能显示黑白色了,而新的遥控器没有颜色调整按钮。我决定在我的老式的Jornada 
525掌上电脑上写一个程序使用IR端口把正确的代码发送给TV。<BR><BR>  共有三个主要协议可以用于发送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’协议的设备写出相似的类。<BR><BR>  我曾使用嵌入式C++写过一个类CirPulse,它封装了从一台运行Windows CE 
3.0的Jornada 525 
PC上控制索尼及其相匹配设备的功能。估计它能够与其它相匹配设备和操作系统一起工作,但是你需要试验才行!<BR><BR><STRONG>  3. 
实现过程分析</STRONG><BR><BR>  这个CIrPulse类暴露了几个函数,它们使得发送IR代码尽可能容易。在声明CIrPulse类时,你应该调用一次FindIrPort(),它返回一个描述IrDA端口的端口号的UINT,这通过搜索注册表得到。这个端口号用于后面的调用来打开IrDA端口进行串行通讯。<BR><BR><CODE>UINT 
CIrPulse::FindIrPort()<BR>{<BR>  // 查询注册表中的IR端口号<BR>  HKEY hKey = NULL;<BR>  
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("Comm\\IrDA"),0, 0, &amp;hKey) == 
ERROR_SUCCESS)<BR>  {<BR>   DWORD dwType = 0;<BR>   DWORD dwData = 0;<BR>   
DWORD dwSize = sizeof(dwData);<BR>   if (RegQueryValueEx(hKey, _T("Port"), NULL, 
&amp;dwType, (LPBYTE) &amp;dwData, &amp;dwSize) == ERROR_SUCCESS)<BR>   {<BR>    
if (dwType == REG_DWORD &amp;&amp; dwSize == sizeof(dwData))<BR>    {<BR>     
RegCloseKey(hKey);<BR>     return (UINT) dwData;<BR>    }<BR>   }<BR>   
RegCloseKey(hKey);<BR>  }<BR>  return 
0;<BR>}</CODE><BR>  得到端口号后,你可以调用Open(UINT)函数,把通过调用FindIrPort()得到的端口号传递过去。这打开该端口并设置串口参数,如果成功返回true。该端口被设置为115200波特,8个数据位,2个停止位和奇偶校验位。关于如何产生载波以及为什么我使用这些设置将在本文后面介绍。<BR><BR><CODE>BOOL 
CIrPulse::Open(UINT uiPort)<BR>{<BR>  ASSERT(uiPort &gt; 0 &amp;&amp; uiPort 
&lt;= 255);<BR>  Close(); <BR>  //打开IRDA端口<BR>  CString strPort;<BR>  
strPort.Format(_T("COM%d:"), uiPort);<BR>  m_irPort = CreateFile((LPCTSTR) 
strPort, GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING, 0, NULL);<BR>  if 
(m_irPort == INVALID_HANDLE_VALUE)<BR>  {<BR>   return FALSE;<BR>  }<BR>  
//设置输入和输出缓冲区的大小<BR>  VERIFY(SetupComm(m_irPort, 2048, 2048));<BR>  
//清除读和写缓冲区<BR>  VERIFY(PurgeComm(m_irPort,PURGE_TXABORT|PURGE_RXABORT|<BR>  
PURGE_TXCLEAR|PURGE_RXCLEAR));<BR>  //重新初始化所有的IRDA端口设置<BR>  DCB dcb;<BR>  
dcb.DCBlength = sizeof(DCB);<BR>  VERIFY(GetCommState(m_irPort, &amp;dcb));<BR>  
dcb.BaudRate = CBR_115200;<BR>  dcb.fBinary = TRUE;<BR>  dcb.fParity = 
TRUE;<BR>  dcb.fOutxCtsFlow = FALSE;<BR>  dcb.fOutxDsrFlow = FALSE;<BR>  
dcb.fDtrControl = DTR_CONTROL_DISABLE;<BR>  dcb.fDsrSensitivity = FALSE;<BR>  
dcb.fTXContinueOnXoff = FALSE;<BR>  dcb.fOutX = FALSE;<BR>  dcb.fInX = 
FALSE;<BR>  dcb.fErrorChar = FALSE; <BR>  dcb.fNull = FALSE;<BR>  
dcb.fRtsControl = RTS_CONTROL_DISABLE;<BR>  dcb.fAbortOnError = FALSE;<BR>  
dcb.ByteSize = 8; <BR>  dcb.Parity = EVENPARITY; <BR>  dcb.StopBits = 
TWOSTOPBITS;<BR>  VERIFY(SetCommState(m_irPort, &amp;dcb));<BR>  
//为所有的读和写操作设置超时值<BR>  COMMTIMEOUTS timeouts;<BR>  
VERIFY(GetCommTimeouts(m_irPort, &amp;timeouts));<BR>  
timeouts.ReadIntervalTimeout = MAXDWORD;<BR>  
timeouts.ReadTotalTimeoutMultiplier = 0;<BR>  timeouts.ReadTotalTimeoutConstant 
= 0;<BR>  timeouts.WriteTotalTimeoutMultiplier = 0;<BR>  
timeouts.WriteTotalTimeoutConstant = 0;<BR>  VERIFY(SetCommTimeouts(m_irPort, 
&amp;timeouts));<BR>  DWORD dwEvent=EV_TXEMPTY;<BR>  
SetCommMask(m_irPort,dwEvent);<BR>  return 
TRUE;<BR>}</CODE><BR>  调用函数SetCodeSize(DWORD)来设置要传送的位数(如12位)。这可以在任何时候完成且只需要做一次。它一直保持有效,直到后面的调用改变它为止。<BR><BR>  最后调用SendCode(long),传递实际要发送的代码。<BR><BR><CODE>BOOL 
CIrPulse::SendCode(DWORD lValue)<BR>{<BR>  DWORD dwCount;<BR>  int i=0;<BR>  
ASSERT(iDataLength&gt;0);<BR>  //清除传送缓冲区<BR>  
VERIFY(PurgeComm(m_irPort,PURGE_TXABORT| PURGE_RXABORT |PURGE_TXCLEAR | 
PURGE_RXCLEAR));<BR>  //每次按键设置代码6次<BR>  for(int x=0;x&lt;6;x++) {<BR>   
MakeStream(lValue); //发送代码<BR>   dwCount=GetTickCount();<BR>   
while(GetTickCount()&lt;dwCount+26) //延迟26ms<BR>    i++;<BR>  }<BR>  return 
true;<BR>}</CODE><BR>  注意这个函数调用另外一个函数MakeStream(long)6次,每两次调用之间停顿26毫秒。我发现该代码必须发送好几次才能使接收设备响应,大概是为防止假行为的缘故吧。26毫秒对于接收设备登记该代码是必需的,在下一个代码出现之前。<BR><BR>  这个函数MakeStream(long)把字节流写入IrPort,并根据是否有起始位(1或者0)来确保发送正确的数据包长度。包含数据字节(0xdb)的缓冲区是以一个ByteArray形式存在的。<BR><BR>  函数Close()用于在端口使用后,自然地关闭IrPort。<BR><BR>  这个函数在我的ornada上运行良好。请看下面的讨论以进一步确定你要做的可能性改变。<BR><BR><CODE>BOOL 
CIrPulse::MakeStream(DWORD lValue) {<BR>  DWORD dwStreamLength;<BR>  
//创建开始脉冲<BR>  dwStreamLength=iHPulse/charWidth;<BR>  ASSERT(Write((const char 
*)bPulseStream.GetData(),<BR>  dwStreamLength)==dwStreamLength);<BR>  // 
************************************<BR>  // ***** 在下一个脉冲到来前延迟一段时间<BR>  // 
************************************<BR>  //循环操作代码中的位来发送脉冲<BR>  for(int 
i=0;i&lt;iDataLength;i++) {<BR>   if(lValue &amp; 1) {<BR>    //创建一个脉冲1<BR>    
dwStreamLength=i1Pulse/charWidth;<BR>    ASSERT(Write((const char 
*)bPulseStream.GetData(),<BR>    dwStreamLength)==dwStreamLength);<BR>    // 
*********************************<BR>    // ***在下一个脉冲到来前延迟一段时间<BR>    // 
*********************************<BR>   }<BR>   else {<BR>    //创建一个脉冲 0 <BR>    
dwStreamLength=i0Pulse/charWidth;<BR>    ASSERT(Write((const char 
*)bPulseStream.GetData(),<BR>    dwStreamLength)==dwStreamLength);<BR>    // 
********************************<BR>    // **在下一个脉冲到来前延迟一段时间<BR>    // 
********************************<BR>   }<BR>   lValue &gt;&gt;= 1;<BR>  }<BR>  
return 
TRUE;<BR>}</CODE><BR>  我在所附源代码中包含了一个简单的应用程序,它使用CIrPulse来创建一台索尼TV的远距离遥控。它具有基本的频道选择、音量调整和开/关机的功能。<BR><BR> <STRONG> 4. 
特别注意</STRONG><BR><BR>  因为该CIrPort类使用一个串行端口连接到该IR端口,所以必须生成一个40KHz的载波信号,这通过从该串行端口发送恰当的字符来实现。幸好,如果我们发送字符0xdb,以115200波特,用8个数据位,2个停止位和奇偶校验,这样就能产生一种极接近38.4KHz的载波信号。我们所有的索尼设备接收这种数据是没有问题的。<BR><BR>  最大的问题是,如何实现间隔每次脉冲的沉默周期。不可能由串行端口来产生该沉默周期,因为就算你发送一个0x0字符,由于存在起始和停止位,你仍然在该IR上得到脉冲。我通过发送不同的字符进行试验,依据的前提是如果你不以40KHz的频率发送一个载波信号,这有可能使设备误把这个当作一个沉默。这样做的优点是你可以产生一个包含完整的代码的byteArray,以确保准确计时。但是结果并不一致,所以我拒绝使用这个方法,为的是实现在两次从串行端口发出成组的0xdb字符之间支持暂停。因为需要的延迟是以550μs的顺序;到目前为止,我还没有找到取得独立于处理器速度的暂停的方法。在我的Jornada上,是完全不必产生一个延迟的,因为每次调用Write函数看上去都使用了合适的时限。不管怎样,我担心的是,你可能胡乱产生一个可以使你的掌上电脑能工作的一个延迟。 

<CENTER><A href="http://tech.163.com/school" target=_blank><IMG 
src="WinCE下用C++实现掌上电脑遥控TV - 网易学院·教程.files/20051009160245d41d8.gif" 
border=0></A></CENTER>
<P style="TEXT-ALIGN: left"><IMG alt=gigi_miao 
src="WinCE下用C++实现掌上电脑遥控TV - 网易学院·教程.files/Editor.gif" 
align=absMiddle>[编辑:gigi_miao] <A class=goback 
href="http://tech.163.com/">[返回首页]</A></P></DIV>
<P class=prenext></P></DIV>
<DIV id=OtherNews><STRONG>相关文章:</STRONG> 
<UL></UL></DIV>
<DIV class=Othercomment>
<P style="BORDER-BOTTOM: #ccc 1px solid"><STRONG>推荐文章:</STRONG>·<A 
href="http://tech.163.com/05/0916/09/1TOSFO2M000915AS.html">IE 7 最新清晰截图</A> ·<A 
href="http://tech.163.com/05/0824/12/1RTV7736000915BF.html">GoogleTalk学院独家试用</A> 
·<A href="http://tech.163.com/05/0824/10/1RTQLTLP00091589.html">抽出滤镜抠发法</A> ·<A 
href="http://tech.163.com/05/0824/11/1RTSK6MA00091589.html">高级:抠取颜色相近的头发</A> 
</P>
<P>
<MARQUEE id=idMarquee onmouseover="document.all('idMarquee').stop();" 
onmouseout="document.all('idMarquee').start();" scrollAmount=2>·<A 
href="http://tech.163.com/05/1012/16/1VSKS5KH00091589.html">Photoshop 
CS镜头模糊滤镜:轻松实现背景虚化</A> ·<A 
href="http://tech.163.com/05/1012/16/1VSKI06H00091589.html">图解Photoshop绘制金属风格XP主题桌面壁纸</A> 
·<A 
href="http://tech.163.com/05/1012/16/1VSK9GAE00091589.html">ASP程序中提供在线文章翻译功能</A> 
·<A href="http://tech.163.com/05/1012/15/1VSHN9P700091589.html">一些奇特的 JavaScript 
功能</A> ·<A 
href="http://tech.163.com/05/1012/15/1VSH86H400091589.html">PS初级技巧:Photoshop抠图的技巧</A> 
·<A 
href="http://tech.163.com/05/1012/15/1VSH3MSD00091589.html">Photoshop抠图之毛茸茸的牧羊犬</A> 
·<A href="http://tech.163.com/05/1012/15/1VSGKOM3000917IO.html">学院艺术:Medusateam 
平面设计</A> ·<A 
href="http://tech.163.com/05/1012/15/1VSG630V00091589.html">代号Kahuna 
MSN邮件服务beta版预览</A> ·<A 
href="http://tech.163.com/05/1012/15/1VSG4DF300091589.html">Flash图文教程:flash文字跳动(教程)</A> 
·<A 
href="http://tech.163.com/05/1012/15/1VSFNA4I00091589.html">Photoshop打造火焰字+光照效果</A> 
·<A 
href="http://tech.163.com/05/1012/15/1VSEOKMK00091589.html">Photoshop图文教程:一种简单的数码照片后期润饰</A> 
·<A 
href="http://tech.163.com/05/1012/15/1VSEHFLL00091589.html">[激辩]哪个是适用的桌面操作系统?</A> 
·<A 
href="http://tech.163.com/05/1012/14/1VSDKBOF00091589.html">Foxmail自动分拣并删除垃圾邮件</A> 
·<A 
href="http://tech.163.com/05/1012/14/1VSDGFAU00091589.html">巧用Photoshop滤镜绘制仿真水纹效果</A> 
·<A href="http://tech.163.com/05/1012/14/1VSCOT9T00091589.html">巧妙施招 
手机瞬间变无线猫</A> ·<A 
href="http://tech.163.com/05/1012/14/1VSCHJOO00091589.html">RealOne在线播放不畅解决办法</A> 
·<A href="http://tech.163.com/05/1012/14/1VSB5SJV00091589.html">PS CS2视频教程:138 
图层的编辑操作6</A> ·<A 
href="http://tech.163.com/05/1012/14/1VSB5ECN00091589.html">PS CS2视频教程:137 
图层的编辑操作5</A> ·<A 
href="http://tech.163.com/05/1012/14/1VSB4OO100091589.html">PS CS2视频教程:136 
图层的编辑操作4</A> ·<A 
href="http://tech.163.com/05/1012/14/1VSB4DDK00091589.html">PS CS2视频教程:135 
图层的编辑操作3</A></MARQUEE></P></DIV>
<DIV style="MARGIN: 0px auto; WIDTH: 750px"><A name=0></A>
<TABLE cellSpacing=0 cellPadding=0 width="100%">
  <TBODY>
  <TR>
    <TD align=middle bgColor=#15477c height=23><FONT style="FONT-SIZE: 14px" 
      color=#ffffff>最新评论</FONT></TD></TR></TBODY></TABLE><SPAN 
id=firstcomment></SPAN><A name=rightnow></A>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
  <TBODY>
  <TR>
    <TD width=173><A 
      href="http://comment.news.163.com/reply/post.jsp?board=tech_bbs&amp;threadid=1VNA328M00091589"><IMG 
      height=63 src="WinCE下用C++实现掌上电脑遥控TV - 网易学院·教程.files/arc_b1.gif" 
      border=0></A></TD>
    <TD background="WinCE下用C++实现掌上电脑遥控TV - 网易学院·教程.files/arc_b5.gif">&nbsp;</TD>
    <TD width=180 
      background="WinCE下用C++实现掌上电脑遥控TV - 网易学院·教程.files/arc_b6.gif"><IMG 
      height=13 src="WinCE下用C++实现掌上电脑遥控TV - 网易学院·教程.files/arc_i3.gif" width=16> 
      <STRONG><A 
      href="http://comment.news.163.com/reply/post.jsp?type=re&amp;board=tech_bbs&amp;threadid=1VNA328M00091589">更多评论</A></STRONG> 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?