unit Rc6_Decoder;
// D. Rosseel
// Original: 26-03-2009
// Latest update: 21-04-2009
// For 36Khz IR receivers with inverse output (= output active low), like the TSOP1736.
// The output of the Rc6 receiver is to be connected to the PIC's external interrupt pin (PortB.0).
{.$DEFINE DEBUG}
// --------------- interface ------------------
procedure Rc6_Interrupt;
// always to be called (unconditionally) in the program's "interrupt" procedure
procedure Rc6_Init;
// to be called before the Rc6 decoder can be used
function GetRc6(var Toggle, System, Command: byte): boolean;
// Returns true if an Rc6 code is received and decoded.
// If true, then "Toggle", "System" and "Command" contain the last decoded Rc6 code.
// Restarts Rc6 decoding (was stopped until this function gets the Rc6 data already received).
{$IFNDEF DEBUG}
implementation
{$ENDIF}
const Rc6_IDLE = 0; // Rc6 decoder state constants
Rc6_DECODING_HEADER = 1; // leader, startbit, mode bits and Toggle bit decoding
Rc6_DECODING = 2; // System and Command decoding
Rc6_COMPLETED = 3; // correct Rc5 code received and decoded
var Rc6_State: byte; // State of the Rc6 decoder
{ ------ !!!!! -------
The timer0 requires a clock (after prescaler) of 62.5 KHz. This means, when the
CPU clock is 4 MHz a prescaler value of 16 is required: 4 MHz / 4 / 16 = 62.5 KHz.
The time/timerstep is then 16 microsecs. (1/62500)
For other CPU clock speeds the prescaler has to be adjusted accordingly, or
if not possible both prescaler and "Rc6_TIME_..." constants have to be adapted.
}
const
Rc6_NR_Rc6_Bits = 16;
// Leader pulses
Rc6_LEADER_TIME_BOUNDARY = 125;
{ the leader Time "On" = 2666 uS = timer value 167
the leader Time "Off" = 889 uS = timer value 83 }
// "normal pulses"
Rc6_TIME_BOUNDARY = 42; // below 42 = short, above = long
{ 1 bit time = 888 uS
"short" time = 1/2 bittime = 444 uS, = timer value 28
"long" time = 1 bittime = 888 uS, = timer value 55 }
// First bit after the Toggle (trailer) bit
Rc6_TRAILER_TIME_BOUNDARY = 69;
{ this is 889 + (444 / 2) millisecs = timer value 69 }
{
Rc6 Bit coding:
<--1 bit Time -->
| |
--------
| "0" bit, no IR signal to IR signal transition
--------
--------
| "1" bit, IR signal to no IR signal transition
--------
RC6 coding/timing
Legend: (1) = leader
(2) = startbit (always "1")
(3) = 3 mode bits (here always "000" is expected)
(4) = trailer bit (= Toggle bit). Timing is different from regular bits
(5) = 8 system bits
(6) = 8 Command bits
|<--- (1) ---->|<-(2)->|<-------- (3) -------->|<-- (4)--->|<--- (5) --->...<--- (6) --->|
| | | | | | | | ... | |
Edge# -> 0 1 2 3 4 5 6 7 8 9 10 11 12
--------- ---- ---- ---- ---- ------ ---- Case A:
| | | | | | | | | | | | | Toggle = 0 and
--- ------ -------- ---- ---- ------ ---- first sys bit = 0
Edge# -> 0 1 2 3 4 5 6 7 8 9 10 11
--------- ---- ---- ---- ---------- ---- Case B:
| | | | | | | | | | | | Toggle = 1 and
--- ------ -------- ---- ---- ------ ---- first sys bit = 1
Edge# -> 0 1 2 3 4 5 6 7 8 9 10 11
--------- ---- ---- ---- ---- ---------- Case C:
| | | | | | | | | | | | Toggle = 0 and
--- ------ -------- ---- ---- ------ ---- first sys bit = 1
Edge# -> 0 1 2 3 4 5 6 7 8 9 10
--------- ---- ---- ---- ---------- ---- Case D:
| | | | | | | | | | | Toggle = 1 and
--- ------ -------- ---- ---- ---------- first sys bit = 0
}
var
{$IFDEF DEBUG}
Rc6_Times: array[50] of byte; // array for the Rc6_Times measured
Rc6_TimesIndex: byte; // index/count for above
{$ENDIF}
Rc6_Bits: word; // will hold the received bits
Rc6_BitCount: byte; // count for above
Rc6_PrevBit: byte; // the value of the previous bit
Rc6_ShortPulses: byte; // nr of subsequent short pulses measured
Rc6_EdgeCount: byte;
Rc6_Toggle : byte;
Rc6_Time : byte;
Rc6_Error : boolean;
{$IFDEF DEBUG}
implementation
{$ENDIF}
procedure Rc6_Interrupt;
begin
if (INTCON.INTE > 0) and (INTCON.INTF > 0) then
begin // Total execution time is about 55 uS at 4 Mhz CPU clock
OPTION_REG := OPTION_REG xor %01000000; // toggle the INTEDG (interrupt edge)
INTCON.INTF := 0;
Rc6_Time := TMR0; // get time
TMR0 := 0; // restart timer
Inc(Rc6_EdgeCount); // a new edge is there
{$IFDEF DEBUG}
if Rc6_State = Rc6_IDLE then
begin
Rc6_TimesIndex := 0;
end
else
if (Rc6_State = Rc6_DECODING_HEADER) or
(rc6_State = Rc6_DECODING) then
begin
Rc6_Times[Rc6_TimesIndex] := Rc6_Time;
Inc(Rc6_TimesIndex);
end;
{$ENDIF}
case Rc6_State of
Rc6_IDLE: //
begin
Rc6_State := Rc6_DECODING_HEADER;
RC6_EdgeCount := 0;
INTCON.T0IF := 0; // reset Timer0 overflow flag
INTCON.T0IE := 1; // enable timer0 interrupt
end;
Rc6_DECODING_HEADER: // the "on" part of the leader has gone
// Execution time is about 20 uS at 4 Mhz CPU clock
begin
Rc6_Error := true;
case Rc6_EdgeCount of
1: if Rc6_Time > Rc6_LEADER_TIME_BOUNDARY then Rc6_Error := false;
2: if Rc6_Time < Rc6_LEADER_TIME_BOUNDARY then Rc6_Error := false;
3,5,6,7,8: if Rc6_Time < Rc6_TIME_BOUNDARY then Rc6_Error := false;
4: if Rc6_Time > Rc6_TIME_BOUNDARY then Rc6_Error := false;
9: begin
if Rc6_Time > Rc6_TIME_BOUNDARY
then Rc6_Toggle := 1 // Cases B and D
else Rc6_Toggle := 0; // Cases A and C
Rc6_Error := false;
end;
10: begin
if Rc6_Toggle = 1 then
begin
if Rc6_Time > Rc6_TRAILER_TIME_BOUNDARY then
begin // Case D ends here
Rc6_State := Rc6_DECODING; // actual data reached
Rc6_Bits := 0;
Rc6_PrevBit := 0; // the first System bit is a "0"
Rc6_BitCount := 1;
Rc6_ShortPulses := 0;
end;
end
else
begin
// Case B (no action here)
end; // wait for the next edge
Rc6_Error := false;
end;
11: begin
if Rc6_Toggle = 1 then
begin // Case B ends here
Rc6_State := Rc6_DECODING; // actual data reached
Rc6_Bits := 1;
Rc6_PrevBit := 1; // the first System bit is a "1"
Rc6_BitCount := 1;
Rc6_ShortPulses := 0;
end
else
begin // Rc6_Toggle = 0
if Rc6_Time > Rc6_TRAILER_TIME_BOUNDARY then
begin // Case C ends here
Rc6_State := Rc6_DECODING; // actual data reached
Rc6_Bits := 1;
Rc6_PrevBit := 1; // the first bit is a "1"
Rc6_BitCount := 1;
Rc6_ShortPulses := 0;
end;
end;
Rc6_Error := false;
end;
12: begin // Case A ends here
Rc6_State := Rc6_DECODING; // actual data reached
Rc6_Bits := 0;
Rc6_PrevBit := 0;
Rc6_BitCount := 1;
Rc6_ShortPulses := 0;
Rc6_Error := false;
end;
end; // case
if Rc6_Error then
begin
Rc6_State := Rc6_IDLE; // restart
OPTION_REG.INTEDG := 0; // next Rc6 interrupt is negative going
INTCON.T0IE := 0; // disable timer0 interrupt
end;
end;
Rc6_DECODING: // Execution time is about 35 uS at 4 Mhz CPU clock
begin
if (Rc6_Time < Rc6_TIME_BOUNDARY) then // short pulse detected
begin
inc(Rc6_ShortPulses);
if (Rc6_ShortPulses = 2) then
begin
Rc6_Bits := Rc6_Bits shl 1; // store the bit received
Rc6_Bits.0 := Rc6_PrevBit;
inc(Rc6_BitCount);
Rc6_ShortPulses := 0;
end;
end else // long pulse detected
begin
Rc6_PrevBit := Rc6_PrevBit xor 1; // toggle the bit
Rc6_Bits := Rc6_Bits shl 1; // store the bit received
Rc6_Bits.0 := Rc6_PrevBit;
inc(Rc6_BitCount);
Rc6_ShortPulses := 0;
end;
if Rc6_BitCount = Rc6_NR_Rc6_Bits then // all bits are received
begin
Rc6_State := Rc6_COMPLETED;
OPTION_REG.INTEDG := 0; // next Rc6 interrupt is negative going
INTCON.T0IE := 0; // disable timer0 interrupt
end;
end;
Rc6_COMPLETED: //
begin
OPTION_REG.INTEDG := 0; // next Rc6 interrupt is negative going
end;
end;
end;
if (INTCON.T0IF = 1) and (INTCON.T0IF > 0) then // timer 0 overflow, timeout
begin
if (Rc6_State = Rc6_DECODING_HEADER) or
(Rc6_State = Rc6_DECODING) then // something is wrong
begin
Rc6_State := Rc6_IDLE; // start all over
OPTION_REG.INTEDG := 0; // next Rc6 interrupt is negative going
INTCON.T0IE := 0; // disable timer0 interrupt
end;
end;
INTCON.T0IF := 0; // always reset timer0 overflow flag
end;
procedure Rc6_Init;
{$IFDEF DEBUG}
var I: byte;
{$ENDIF}
begin
Rc6_State := Rc6_IDLE;
// Timer 0
OPTION_REG.T0CS := 0; // bit 5 TMR0 Clock Source Select bit:0=Internal Clock (CLKO) / 1=Transition on T0CKI pin
OPTION_REG.T0SE := 0; // bit 4 TMR0 Source Edge Select bit: 0=low/high / 1=high/low
OPTION_REG.PSA := 0; // bit 3 Prescaler Assignment bit: 0=Prescaler is assigned to the Timer0
OPTION_REG.PS2 := 0; // Rc6_Bits 2-0 PS2:PS0: Prescaler Rate Select Rc6_Bits
OPTION_REG.PS1 := 1;
OPTION_REG.PS0 := 1;
TMR0 := 0; // preset for timer register
INTCON.PEIE := 1;
INTCON.T0IF := 0;
INTCON.T0IE := 0; // disable timer 0 interrupt for now
// external interrupt
TRISB.0 := 1; // "Int" input
OPTION_REG.INTEDG := 0; // start with downgoing edge
INTCON.INTF := 0;
INTCON.INTE := 1;
{$IFDEF DEBUG}
for I := 0 to SizeOf(Rc6_Times) - 1 do Rc6_Times[I] := 0;
{$ENDIF}
end;
function GetRc6(var Toggle, System, Command: byte): boolean;
begin
Result := false;
if Rc6_State = Rc6_COMPLETED then
begin
Result := true;
Toggle := Rc6_Toggle;
System := Hi(Rc6_Bits);
Command := Lo(Rc6_Bits);
Rc6_State := Rc6_IDLE;
end;
end;
end.