65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 PDA 的通用遥控器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (21投票s)

2007年5月10日

6分钟阅读

viewsIcon

135135

downloadIcon

1522

开发“PDA 的通用遥控器”

引言

在结束了技术学院(HTL)第四年的学习后,我们正在寻找一个有趣的项目。我们决定开发自己的“PDA 通用遥控器”。有了这个程序,就可以控制你的电视。我们在互联网研究中,没有找到太多关于这个主题的信息,但我们在 codeproject.com 上找到了一些有用的信息,因此我们决定在这里发布我们自己的解决方案。

关于程序

左键单击 gadget 并拖动以移动它。左键单击 gadget 的右下角并拖动以调整其大小。右键单击 gadget 以访问其属性。

  • 适用于 PocketPC(Windows Mobile 3.0 及更高版本)的应用程序
  • Windows CE 操作系统
  • C# 代码(Visual Studio 2005)

实现需要

  • 打开 PDA 上的 COM 端口(COM1、COM2、…)
  • 创建要发送到 PDA 的代码(RC5-Code)
  • 创建暂停(单位:µs)

传输

PDA 上现有的红外端口使用标准的 IrDA 协议。问题是,虽然 C# 中的 IrDA 类能够打开端口,但需要一个终端才能成功传输。这意味着该类需要连接两个智能设备(PDA-PDA 或 PDA-Handy)。因此,不能使用 IrDA 进行面向连接到电视等哑设备的传输。

红外传输有三种类型

  • 标准红外(SIR 115Kbps)
  • 快速红外(FIR 4Mbps)
  • 消费级红外(CIR 115Kbps)

CIR 是你在遥控器中发现的那种,具有宽广的射程。红外光的波长在 940 到 950 纳米之间。命令如何编码取决于设备的制造商。

Screenshot - one.jpg

我们使用串行端口,并且通常可以基于 UART 协议创建每个代码。第一步是找出 PDA 上的哪个 COM 端口负责 IR 端口。

COM1 DS 上的 RS232 连接器,如果 DS 处于正常(非透明)模式
COM2 原始红外,由 IrDA 协议内部使用(如果你真的需要原始 IrDA,请以与 RS232 通信相同的方式使用此端口。)
COM3 由 IrDA 协议内部使用(使用 IrDA 套接字与 IrDA 通信)
COM4 设备中的 GPS 模块
COM5 GSM 模块
COM6 DS 中的 GPS 模块,如果 DS 处于正常状态(只读访问)
COM7 设备上的 RS232 连接器(H41RS 设备上的 Lemo 连接器)
COM8 DS 上的 RS232 连接器,如果 DS 处于透明模式
COM9 免费

COM2 端口使得可以通过 IR 端口发送串行数据。C# 中的 SerialPort 类提供了访问串行驱动程序属性的方法。我们编写了一个程序,可以显示 PDA 上所有可用的 COM 端口。最初的问题是只显示了 COM3 端口。解决方案是为 SerialPort 类安装一个更新。该更新称为 Microsoft .NET Compact Framework 2.0 Service Pack 1,它修复了 SerialPort 类中一些有缺陷的方法。

public void GetName()
        {
            StringBuilder s = new StringBuilder();
            String s1;

            //get a list of port names
            string[] ports = SerialPort.GetPortNames();

            //display port names
            foreach (string port in ports)
            {
                s.Append(port+" ");
            }

            s1 = s.ToString();
            label1.Text = "Port Names: "+s1;
        }

RC5 代码

接下来的步骤需要 RC5 代码的基础知识。首先,我们必须初始化串行端口的所有参数

//initialize the parameters for the SerialPort object
        public static String portName = "COM2";
        static int baudRate = 115200;
        public static Parity parity = Parity.None;
        static int dataBits = 7;
        public static StopBits stopBits = StopBits.One;
        SerialPort IR_COM = new SerialPort(portName, baudRate, parity, 
            dataBits, stopBits);

曼彻斯特编码

下一步是初始化参数以生成曼彻斯特编码。在 `RohCodeToManchester()` 中,原始数据数组将被传递给 `ManchesterCode()`。`ManchesterCode()` 将 1 生成为从 1 到 0 的变化,将 0 生成为从 0 到 1 的变化。

//initialize the parameters for the function ManchesterCode and 
//RohCodeToManchester
        char[] rohData = new char[14] {
             '1','1','0','0','0','0','0','0','0','0','0','1','0','0' };  
        public char[] manchesterData = new char[29];
        int index = 0, md = 0;

public void ManchesterCode(char c)
        {   
             if (c == '1')
             {  // 1 -> 10
                 manchesterData[index] = '1';
                 manchesterData[++index] = '0';
             }
             if (c == '0')
             {  // 0 -> 01
                 manchesterData[index] = '0';
                 manchesterData[++index] = '1';
             }
             index++;    
        }

        public void RohCodeToManchester()
        {   
            for (int i=0; i < rohData.Length; i++)
            {   //the array rohData is passed to the function ManchesterCode
                ManchesterCode(rohData[i]);
            }

            //mark the end
            manchesterData[28] = 'e';
        }

生成突发和暂停

为了确保数据传输安全,会传输 38 kHz 调制的信号。然后信号会被 IR 接收器解调。信号看起来像这样

Screenshot - two.jpg

在接下来的计算中,你必须考虑到 IR 发射器会反转信号,并且 LSB 会首先传输。接下来的步骤将从这个角度进行解释,这同时也是程序的视角。

低位信号会被突发调制,高位信号会是暂停。所以曼彻斯特编码中的每个 0 都会被突发调制,每个 1 代表 889µs 的暂停。我们知道波特率必须是 115 kBaud,并且信号必须以 38 kHz 调制。

26µs 的周期长度对应于 38.4kBaud 的波特率。由于 38.4kBaud 不是标准波特率,我们需要将其提高到 115.2kBaud。一个比特需要 8.7µs。

Screenshot - three.jpg

其中

  • Ts 是周期长度
  • ts 是一个比特的持续时间
  • Vs 是波特率
  • f 是频率

我们期望的突发信号应该像 100100100,包括起始位和停止位。这与下面的表示一致。从我们开始的基础 100100100,我们向后计算,并考虑到 IR 发射器会反转信号并首先发送 LSB。

100100100 带起始位和停止位
0010010 不带起始位和停止位
1101101 反转的
1011011 先发送 LSB
0x5B 十六进制

Screenshot - four.jpg

字长设置为 1 个起始位、7 个数据位和 1 个停止位。顶部的图像显示起始位和停止位理想地集成到了突发信号中。因此,一个突发信号需要 79.7µs。因为突发信号代表数据流中的逻辑 0,所以我们需要将突发信号扩展到 RC5 数据长度 889µs。对于一个比特,我们需要发送 11 次突发信号 0x5B。

Screenshot - five.jpg

其中

  • tb 是突发的持续时间
  • z 是突发的数量

生成暂停的一个合乎逻辑的方法是发送 11 次 0x00,但这并不是解决方案,因为每个字节的停止位都是比特流的一部分。所以我们必须找到一个计时器来创建 889µs 的暂停。有一个名为 QueryPerformanceCounter 的计数器,它是 PDA 上 `coredll.dll` 中的一个函数。通过 QueryPerformanceCounter,我们可以实现精确的时间测量。这是因为 QueryPerformanceCounter 可以直接访问处理器时钟。

在下面的代码中,我们导入 `coredll.dll` 并为 QueryPerformanceCounter 和 `BurstOut` 函数进行初始化。使用 DllImport,你可以导入 `.dll` 文件,之后你可以像在程序中使用普通函数一样使用 `QueryPerformanceCounter()`。名为 BufferBurst 的字节数组包含 22 个 0x5B。这是上面计算的结果,因为对于一个 RC5 位,我们需要发送 11 次 0x5B。由于两个逻辑 0 可能在数据流中连续出现,BufferBurst 的长度是两倍。

>//import the coredll.dll with the functions QueryPerformanceCounter
//and QueryPerformanceFrequency
        [DllImport("coredll.dll")]
        extern static int QueryPerformanceCounter(ref long perfCounter);
        [DllImport("coredll.dll")]
        extern static int QueryPerformanceFrequency(ref long frequency);

        //initialize the parameters for the QueryPerformanceCounter
        long ctrStart = 0, ctrAkt = 0, ctrEnd = 0;

`BurstOut()` 函数将一个 `long` 类型的变量传递给 `QPC()` 方法。这个变量设置了 QueryPerformanceCounter 的时间间隔。在 `QPC()` 中,我们停留在 while 循环中,直到达到指定的时间。

public void QPC(long data)
        {   //start the counter
            QueryPerformanceCounter(ref ctrStart);
            
            //specified the end time
            ctrEnd = ctrStart + data;

            while (ctrAkt < ctrEnd)
            {   //wait until the counter reaches the end time
                QueryPerformanceCounter(ref ctrAkt);
            }
        }

调制是在 `BurstOut()` 函数中完成的,其中曼彻斯特编码数组通过一个 while 循环传递。在那里,我们检查当前位和下一位。如果比特序列是 10,则会产生 889µs 的暂停;如果比特序列是 11,则等待两倍的时间。当比特序列是 01 时,我们发送 11 次突发信号;当比特序列是 00 时,发送 22 次。

public void BurstOut()
       {
           while (manchesterData[md] != 'e')
           {   
               //pause of 889µs
               if (manchesterData[md] == '1' && manchesterData[md + 1] == '0')
               {
                   QPC(319);
               }

               //pause of 1,778ms
               if (manchesterData[md] == '1' && manchesterData[md + 1] == '1')
               {
                   QPC(766);
                   md++;
               }

               //burst of 889µs
               if (manchesterData[md] == '0' && manchesterData[md + 1] == '1')
               {
                   IR_COM.Write(bufferBurst, 0, 10);
               }

               //pause of 1,778ms
               if (manchesterData[md] == '0' && manchesterData[md + 1] == '0')
               {
                   IR_COM.Write(bufferBurst, 0, 22);
                   md++;
               }

               //the end
               if (manchesterData[md] == '0' && manchesterData[md + 1] == 'e')
               {
                   IR_COM.Write(bufferBurst, 0, 10);
               }

               md++;
           }
       } 

历史

  • 2007 年 5 月 10 日 - 发布原始版本
© . All rights reserved.