|
|||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionAt the end of our 4th year in the HTL (technical college) we were looking for an interesting project. We decided to develop our own "Universal Remote Control for the PDA." With this program it is possible to control your TV. During our internet research, we didn't find much information about this topic, but we found some useful information on codeproject.com and therefore we decided to publish our own solution here. About the programGeneral:
Implementation requires:
TransmissionThe existing infrared port on the PDA uses the standard IrDA protocol. The problem is that although the IrDA class in C# is able to open the port, an endpoint is required for successful transmission. This means that the class needs a connection between two intelligent devices (PDA-PDA or PDA-Handy). Therefore it is not possible to use the IrDA for a connection-oriented transmission to a dumb device such as a TV. There are three types of infrared transmission:
CIR is the type you find in remote controls with a wide and long range. The wavelength of infrared light is between 940 and 950 nm. How the commands are coded depends on the manufacturer of the device.
We use the serial port and can generally create every code on the basis of the UART protocol. The first step is to find out which of the COM-ports on the PDA is responsible for the IR-port.
The COM2-port makes it possible to send serial data through the IR-port. The SerialPort-class in C# provides methods of accessing the serial driver properties. We wrote a program which could display all available COM-ports on the PDA. The initial problem was that only COM3-port was shown. The solution was to install an update for the SerialPort-class. The update is called Microsoft .NET Compact Framework 2.0 Service Pack 1 and repairs some of the faulty methods in the SerialPort-class. 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-codeFor further steps, basics of RC5-code are required. First we must initialize all parameters of the serial port: //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);
Manchester codeThe next step is to initialise the parameters to generate the Manchester code. In //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';
}
Generating burst and pauseTo guarantee a safe data transmission, a 38 kHz modulated signal is transferred. The signal is then demodulated by the IR-receiver. The signal looks like this:
In the following calculations, you have to consider that the IR-transmitter inverts the signal and the LSB will be transmitted first. The following steps will be explained from this point of view, which is also the view of the program. A low bit will be burst-modulated and a high bit will be a pause. So every 0 in the Manchester code is burst-modulated and every 1 stands for a pause of 889µs. What we know is that the baud rate must be 115 kBaud and the signal has to be modulated with 38 kHz. The period length of 26µs accords to the baud rate of 38.4kBaud. Because 38.4kBaud is not a standard baud rate, we have to bring it up to 115.2kBaud. One bit will take 8.7µs.
Where
Our desired burst should look like 100100100, including start- and stop-bit. This accords to the presentation bellow. From our starting base of 100100100, we calculate back and consider that the IR-transmitter will invert the signal and send the LSB first.
The word length is set to 1 start-bit, 7 data-bits and 1 stop-bit. The image on the top shows us that the start- and stop-bit is ideally integrated into the burst. Therefore one burst takes 79.7µs. Because the burst stands for the logical 0 in the data stream, we have to bring the burst up to the RC5 data length of 889µs. For one bit we have to send the burst 0x5B 11 times.
Where
A logical way to generate the pause would be to send 0x00 11 times, but this is not the solution because with each byte the stop-bit is part of the bit stream. So we had to find a timer that could create a pause of 889µs. There is a counter called QueryPerformanceCounter, which is a function in the coredll.dll on the PDA. Through the QueryPerformanceCounter, we could achieve an exact time measurement. This is because the QueryPerformanceCounter has direct access to the processor clock. In the following code, we import the coredll.dll and make the initialization for the QueryPerformanceCounter and the >//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;
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);
}
}
The modulation is done in the 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++;
}
}
History
|
||||||||||||||||||||||||||||||||||||||||||||||||||