ftCommunity | Wiki | IR on RI (or IR Control Set and the Robo Interface)


Thema: IR on RI (or IR Control Set and the Robo Interface)

Aktuelle Version

von: Ad

am: 11.07.2009, 13:15:32 Uhr

New IR Control Set and the Robo Interface

The Robo Interface (RI) has support for the old IR remote control (RC5 code). There has never been a firmware

update for use with the new Control Set. The TX controller has no IR support at all. Someone (I forgot his name)

has analysed the new IR protocol and published it on the FT forum. Unfortunately FT deemed it necessary to

remove this post. Luckily some people saved the text and I used it to implement receiver software for the RI.

The Protocol
The protocol uses Pulse Distance Modulation (PDM) with two bits (dibit) per pulse, hence four different

distances. A transmission consists of 16 pulses (15 distances) for a total of 30 bits. The structure of the

message is disclosed in the header file.

//header file

typedef union
{ long asLong;
struct//lowest bit first
{ unsigned stat:2; //low order byte, low word
unsigned tail:1;
unsigned par:1;
unsigned mot2d:1;
unsigned mot1d:1;
unsigned mot4d:1;
unsigned mot3d:1;
unsigned mot2s:4; //high byte, low word
unsigned mot1s:4;
unsigned mot4s:4; //low byte high word
unsigned mot3s:4;
unsigned on:1; //high byte high word
unsigned off:1;
unsigned freq:1;
unsigned rec:1;
unsigned head:4;
} asStruct;
} IR_type;

typedef void(* irqentry)(void);

void copy_irqtable();
irqentry setIRQhandler(char n, irqentry h);
void initIRhandler();

The Concept
The RC5 code was copied to a space in the TransferArea and available as an input to a RoboPro Program (RPP). We

probably cannot simply replace this with the new IR code because of the totally different format. But I haven't

tried it so RP may actually return something useful. Instead I used my old approach (see the Wiki on Combining

RoboPro with C) and used messages instead. There are four different SubIds for each of the four possible

receivers (dip switch settings) and five different MsgIds for the four channels and the buttons (Tempomat

The Tempomat function (if needed) is not implemented in this software and should be implemented in the RPP.

The IR interface uses an interrupt input (INT0) for the demodulated IR signal and a timer A2 with associated

interrupt. As we have to replace at least the INT0 handler, the vector table has to be changed. At present the

vector table resides in firmware flash but can be moved to RAM where it can be changed. This can be done as


irqentry irqtable[64];

void copy_irqtable() //move the firmware irq table E0020 to near RAM
{ char j;
for (j=0;j<64;j++)
irqtable[j] = ((irqentry far*)0xE0020)[j];
asm("FCLR I");
asm("ldc #_irqtable,INTBL");
asm("ldc #0,INTBH");
asm("FSET I");

irqentry setIRQhandler(char n, irqentry h)
{ irqentry old = irqtable[n];
asm("FCLR I");
irqtable[n] = h;
asm("FSET I");
return old;

Timer A2
The timer is a free running down counter with a 0.5us clock. The width of a pulse is measured by reading the

counter value twice and calculating the difference. It is inevitable that the counter will underflow every now

then. This is not a problem at all as long as it happens only once in a measurement. If it happens more often

the pulse is clearly too long. The firmware interrupt routine just counts the number of underflows, we could use

this handler (the underflow counter is the byte at 0x14A8) but for clarity I decided to implement my own


The Interrupt Handlers

#include "sfr24.h"

#define PULSEW0L 1400
#define PULSEW0U 1700
#define PULSEW1L 1700
#define PULSEW1U 1900
#define PULSEW2L 1900
#define PULSEW2U 2100
#define PULSEW3L 2100
#define PULSEW3U 2400

IR_type IR_code;
int underflow = 0;

#pragma INTERRUPT timerA2
void timerA2(void) //timer (downcounter) to measure length of IR pulses
{ if (underflow < 5)
else if (underflow==5)//pause > intermessage pause (max 120ms), timer takes max 33ms to overflow and

more than 165ms to overflow 5 times
{ IR_code.asLong = 0L;//set all values to neutral
IR_code.asStruct.stat = 2;

#pragma INTERRUPT int0
void int0(void) //interrupt on any edge of IR signal but use only falling edges
{ static unsigned old;
static unsigned long code = 0L;
static char ones = 0;
static int state = 0;

if (!p8_2) //diff represents the pulse + pause length, ! because IR sensor inverts
{ unsigned val = ta2;
int diff = old - val; // diff should be positive and multiples of 500ns because ta2 is a down counter
old = val;

if (diff<PULSEW0L || UNDERFLOW>1) // too short or underflow
{ state = 0;
code = 0L;
ones = 0;
else if (diff>=PULSEW0L && diff<PULSEW0U)
{ //code 00 received
code <<=2;
} else if (diff>=PULSEW1L && diff<PULSEW1U)
{ //code 01 received
code <<=2;
code +=1;
ones ++;
} else if (diff>=PULSEW2L && diff<PULSEW2U)
{ //code 10 received
code <<=2;
code +=2;
} else if (diff>=PULSEW3L && diff<PULSEW3U)
{ //code 11 received
code <<=2;
code +=3;
ones +=2;
} else //error, too long, go back to idle state
{ state = 0;
code = 0L;
ones = 0;
if (state == 15)//15 pulses and 15 pauses, all bits received
{//check frame and parity
code <<=2;
if ((code>>28) == 8 && !(ones&1) && !(code&4))
{ IR_code.asLong = code;
IR_code.asStruct.stat = 2;
{ IR_code.asStruct.stat = ones&1 ? 1 : 3; //1=PE,3=FE
code = 0L;
state = 0;
ones = 0;
underflow = 0; //reset timeout, short timeout is 33-65ms, long timeout is 128-160ms

Although the pulse width should be constant and only the pulse distance should be variable, it turned out to be

much more accurate to measure the time of the pulse plus the distance, hence between two leading edges of the

pulses (which are in fact falling edges because the IR receiver inverts). After all bits have been received, the

header and the parity are checked and if everything is OK the send_IR_msg() is called to send the code to the


#include "TA_FirmwareTAF_00D.h"
#include "TA_FirmwareTAF_00P.h"
#include "TA_FirmwareMsg_00D.h"

#define IRBASEPORT 210

void send_IR_msg()
{ char subid = 0;
static long old[4] = ;

//for test only
sTrans.MPWM_Update = 0x01; // Update PWM values every 10ms

if (IR_code.asStruct.rec) subid++;
if (IR_code.asStruct.freq) subid+=2;
if ((IR_code.asLong^old[subid]) & 0x00f00080L)
{ int s = IR_code.asStruct.mot3d ? IR_code.asStruct.mot3s : -IR_code.asStruct.mot3s;
SendFtMessage(HWID_IRQ, IRBASEPORT+subid, make_message("IR3",s), 0 /*ms*/, MSG_SEND_NORMAL);
if ((IR_code.asLong^old[subid]) & 0x000f0040L)
{ int s = IR_code.asStruct.mot4d ? IR_code.asStruct.mot4s : -IR_code.asStruct.mot4s;
SendFtMessage(HWID_IRQ, IRBASEPORT+subid, make_message("IR4",s), 0 /*ms*/, MSG_SEND_NORMAL);
if ((IR_code.asLong^old[subid]) & 0x0000f020L)
{ int s = IR_code.asStruct.mot1d ? IR_code.asStruct.mot1s : -IR_code.asStruct.mot1s;
SendFtMessage(HWID_IRQ, IRBASEPORT+subid, make_message("IR1",s), 0 /*ms*/, MSG_SEND_NORMAL);

//for test only
sTrans.MPWM_Main[0] = (IR_code.asStruct.mot1s+1) >> 1; // PWM for Output O1
sTrans.MPWM_Main[1] = (IR_code.asStruct.mot1s+1) >> 1; // PWM for Output O2
if (IR_code.asStruct.mot1s != 0)
sTrans.M_Main = IR_code.asStruct.mot1d ? 0x01 : 0x02; // switch Output O1/O2 ON
sTrans.M_Main = 0; // switch Output O1/O2 OFF

if ((IR_code.asLong^old[subid]) & 0x00000f10L)
{ int s = IR_code.asStruct.mot2d ? IR_code.asStruct.mot2s : -IR_code.asStruct.mot2s;
SendFtMessage(HWID_IRQ, IRBASEPORT+subid, make_message("IR2",s), 0 /*ms*/, MSG_SEND_NORMAL);
if ((IR_code.asLong^old[subid]) & 0x03000000L)
{ int s = 2 * IR_code.asStruct.on + IR_code.asStruct.off;
SendFtMessage(HWID_IRQ, IRBASEPORT+subid, make_message("IRT",s), 0 /*ms*/, MSG_SEND_NORMAL);
old[subid] = IR_code.asLong;

The function make_message scrambles the argument string into a 16 bit integer and combines it with a 16 bit

value to the 32 bit FT (msgid, msg) pair. The msgids could advantageously be precomputed because they are

The interrupt handlers must be installed before RoboPro is started, this is achieved in a small C main() program

that first installs the handlers and then calls RoboPro().

void initIRhandler()
{ setIRQhandler(31,int0);
//precompute message names IR1, IR2 etc.
ir1 = make_message("IR1",0);
ir2 = make_message("IR2",0);
ir3 = make_message("IR3",0);
ir4 = make_message("IR4",0);
irt = make_message("IRT",0);

void main()
{ copy_irqtable();
RoboPro(); //see other wiki on how this works

I hope that eventually something similar to this can be done with the TX controller, either with one of the fast

inputs or via an extension. Let's just hope FT lays the documentation open this time.


Version 4 von Ad am 10.07.2009, 14:15:18 Uhr
Version 3 von Ad am 10.07.2009, 14:06:18 Uhr
Version 2 von Ad am 10.07.2009, 14:03:51 Uhr
Version 1 von Ad am 10.07.2009, 14:02:45 Uhr


Wenn sie sich einloggen, können Sie dieses Thema bearbeiten.


Zurück zur Übersicht