這幾天要完成個任務,就是利用一個紅外線適配器接受紅外線遙控器的信號,在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);
}
}
}
}
新聞熱點
疑難解答