这几天要完成个任务,就是利用一个红外线适配器接受红外线遥控器的信号,在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);
}
}
}
}
新闻热点
疑难解答