这几天要完成个任务,就是利用一个红外线适配器接受红外线遥控器的信号,在PC上读取信号,并判断按的是哪个键,因为整个项目的需要,要求用C#实现。
首先,需要一个红外线适配器(废话~),因为我是用本本开发的,而我的本本没有串口,所以就用了一个USB的红外线适配器,这里需要一个USB转串口的驱动,网上搜一下就有了,CSDN的下载里也有朋友上传。
驱动装上之后,就可以进行开发了。.net 为我们提供了一个串口类(有且仅有一个~~),就是SerialPort类,在名称空间System.IO.Ports里。要实例化一个SerialPort类,我们必须知道串口的名字,使用方法System.IO.Ports.SerialPort.GetPortNames()可以获得当前电脑可用的串口,如果计算机上有多个串口,计算机很难判断哪个串口是用来接受红外线数据的,因此只能由用户决定,这里我们可以用一个comboBox将可用串口列出来,供用户选择
view plaincopy to clipboardPRint?
string[] ports = System.IO.Ports.SerialPort.GetPortNames();   
           foreach (string port in ports)   
           {   
               combComName.Items.Add(port);   
           }  
 string[] ports = System.IO.Ports.SerialPort.GetPortNames();
            foreach (string port in ports)
            {
                combComName.Items.Add(port);
            } 
用户选择完之后就可以通过构造方法实例化一个SerialPort对象了。对于一般的红外线遥控器,使用以下的参数就可以了。
view plaincopy to clipboardprint?
port = new SerialPort( combComName.SelectedItem.ToString() , 9600, Parity.None, 8, StopBits.One);   
            port.Open();  
port = new SerialPort( combComName.SelectedItem.ToString() , 9600, Parity.None, 8, StopBits.One);
            port.Open(); 
打开串口之后,我们就可以读取串口信号了。这里我们使用read方法。由于一条按键信号一般为32位,这里我们就只接收32位数据了。
view plaincopy to clipboardprint?
byte[] buffer = new byte[36];   
System.Threading.Thread.Sleep(100);   
port.Read(buffer, 0, 36);  
byte[] buffer = new byte[36];
System.Threading.Thread.Sleep(100);
port.Read(buffer, 0, 36); 
接下来是要把byte转化成为16进制代码。可以写一个函数
view plaincopy to clipboardprint?
private string BytesToHexString(byte[] buffer, int offset, int length)   
       {   
           string info = "";   
           for (int i = offset; i < offset + length; i++)   
           {   
               info += string.Format("{0:X} ", buffer[i]).Trim();   
           }   
           return info;   
       }  
 private string BytesToHexString(byte[] buffer, int offset, int length)
        {
            string info = "";
            for (int i = offset; i < offset + length; i++)
            {
                info += string.Format("{0:X} ", buffer[i]).Trim();
            }
            return info;
        } 
好的,现在如果我们用上述方法把接收到的信号显示出来,应该是类似于这样的:
00FEFCFEFEFCFEFEFC0000000000000000FCFCFCFC00F8F8F000000000F00000
而且即使是同一个按键,有时候编码还会有不一样的。
对于一般的红外遥控器来说,不可能会是这么长的编码的,例如NEC的编码规则就是:用户识别码(8位)+ 用户识别码反码(8位)+ 数据码(8位)+ 数据码反码(8位)。经过一番研究,将上述编码按字节分开:
00 FE FC FE FE FC FE FE FC 00 00 00 00 00 00 00 00 FC FC FC FC 00 F8 F8 F0 00 00 00 00 F0 00 00 00 00 00 00
如果把00当做0,把FE、FC这些当做1,改写一下:
0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 1 0 0 0 0 1 0 0
再把这些0、1代码8位分成一组,末尾多余的0去掉,转化为16进制:
7F807B84
哈哈,看到这里应该清楚了吧,经过这样一番转换的信号完全符合NEC的编码规则,而我使用的遥控器确实就是采用了NEC的芯片的。
回头分析一下,我猜想产生这样情况的原因可能是和串口设置的波特率有关,因为我们我们不知道红外遥控器的频率,不知道一个脉冲式多少时间,因此在较高波特率的情况下,0信号变成了多个0,1信号变成了多个1,至于为什么会有FC、FE这样不一样的编码,我猜想应该是因为一个脉冲的末尾电压不稳定造成的。
知道了这些,接下来的事情就简单了,写个函数转化一下就可以了,下面我附上源码。具体实现的时候我用到了DataReceived事件对串口数据进行监听,这样只要遥控器有信号过来就可以显示了。
view plaincopy to clipboardprint?
using System;   
using System.Collections.Generic;   
using System.ComponentModel;   
using System.Data;   
using System.Drawing;   
using System.Linq;   
using System.Text;   
using System.Windows.Forms;   
using System.IO.Ports;   
namespace testseral   
{   
    public partial class Form1 : Form   
    {   
        private SerialPort port;   
        private const int CodeLength = 32;   
        delegate void SetInfo(string info);   
        public Form1()   
        {   
            InitializeComponent();   
        }   
        private void button1_Click(object sender, EventArgs e)   
        {   
            //初始化并打开串口   
            port = new SerialPort( combComName.SelectedItem.ToString() , 9600, Parity.None, 8, StopBits.One);   
            port.Open();   
            //监听串口数据   
            port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);   
            btOpen.Enabled = false;   
            btClose.Enabled = true;   
        }   
        private void button2_Click(object sender, EventArgs e)   
        {   
            port.Close();   
            btOpen.Enabled = true;   
            btClose.Enabled = false;   
        }   
        void port_DataReceived(object sender, SerialDataReceivedEventArgs e)   
        {   
               
                byte[] buffer = new byte[CodeLength];   
                System.Threading.Thread.Sleep(100);   
                int length = port.Read(buffer, 0, CodeLength);   
                if (length < CodeLength)   
                    return;   
                this.Invoke(new SetInfo(DataReceived), BytesToHexString(buffer, 0, length));   
        }   
        private string BytesToHexString(byte[] buffer, int offset, int length)   
        {   
            string info = "";   
            for (int i = offset; i < offset + length; i++)   
            {   
                info += string.Format("{0:X2} ", buffer[i]).Trim();   
            }   
            return info;   
        }   
        protected void DataReceived(string info)   
        {   
            rtbSerialInfo.Text += SignalToHexCode( info );   
        }   
        //将原始的二进制信号转化为二进制编码   
        private string SignalToBinaryCode(string Signal)   
        {   
            if (string.IsNullOrEmpty(Signal))   
            {   
                return null;   
            }   
            else  
            {   
                string Code = "";   
                for (int i = 0; i < CodeLength*2; i = i + 2)   
                {   
                    if (Signal.Substring(i,1).Equals( "0") )   
                    {   
                        Code = Code + "0";   
                    }   
                    else  
                    {   
                        Code = Code + "1";   
                    }   
                }   
                return Code;   
            }   
        }   
        //将二进制编码转化为16进制编码   
        private string SignalToHexCode(string Signal)   
        {   
            Signal = SignalToBinaryCode(Signal);   
            if (!string.IsNullOrEmpty(Signal))   
            {   
                string HexCode = "";   
                string HexCodePiece = "";   
                for (int i = 0; i < CodeLength; i = i + 4)   
                {   
                    HexCodePiece = Signal.Substring(i, 4);   
                    HexCode = HexCode + Convert.ToString(Convert.ToInt32(HexCodePiece, 2), 16);   
                }   
                return HexCode;   
            }   
            else  
                return null;   
        }   
        private void comboBox1_DropDown(object sender, EventArgs e)   
        {   
            //获得所有串口   
            combComName.Items.Clear();   
            string[] ports = System.IO.Ports.SerialPort.GetPortNames();   
            foreach (string port in ports)   
            {   
                combComName.Items.Add(port);   
            }   
        }   
    }   
} 
新闻热点
疑难解答