Modular Mid-Range Picmicro K L Decoder in C: Ee Oq
Modular Mid-Range Picmicro K L Decoder in C: Ee Oq
NU 11 18 NU
The set of modules presented in this application note
implement the following features: NU 12 17 NU
NU 13 16 NU
• Source compatible with HITECH and CCS C
compilers NU 14 15 NU
• Pin out compatible with PICDEM-2 board
• Normal Learn mode
TABLE 1: FUNCTIONAL INPUTS AND
• Learn up to 8 transmitters, using the internal OUTPUTS
EEPROM memory of PIC16F872
• Interrupt driven Radio Receive (PWM) routine Pin Pin Input/
Function
• Compatible with all existing KEELOQ hopping code Name Number Output
encoders with PWM transmission format RFIN 3 I Demodulated
selected, operating in "slow mode" (TE = 400 µs) PWM signal from
• Automatic synchronization during receive, using a RF receiver
4 MHz RC oscillator
LEARN 6 I Input to enter learn
mode
LEARN- 25 O Output to show the
OUT status of the learn
process
OUT0..3 21,22,2 O Function outputs,
3, 24 correspond to
encoder input pin
VLOW 26 O Low Battery indica-
tor, as transmitted
by the encoder
VDD 20 PWR 5V power supply
VSS 19, 8 GND Common ground
Note: All NU pins are tristate
Timer0
RXI.C Interrupt
Radio Receiver
1st Buffer X
RF_FULL
Flag
MEM-87X.C
Learn Receive Buffer
TABLE.C
Out S0
MAIN.C - Insert
Out S3 - Search
Main Loop
Learnout - IDwrite EEPROM
- HopUpdate
VLOW - ClearMem
- RDword
- WRWord
KEYGEN.C
- Normal KEYGEN
- Load Manufacturer Code
- DecCHK
- HopCHK
FASTDEC.C
- Decrypt
KEELOQ®
PIC16F872
LOGIC ‘0’
LOGIC ‘1’
Bit
Period
Encrypted using
BLOCK CIPHER Algorithm
Transmission Direction
Other implementations of the same receiver module A detailed description of the Normal Learn key gener-
can be obtained using other peripherals and detection ation scheme can be found in Technical Brief TB003
techniques. These include: "An Introduction To KEELOQ Code Hopping"
[DS91002].
• Using the INT pin and selectable edge interrupt
source More advanced Key Generation Schemes can be
implemented replacing this module with the techniques
• Using the Timer1 and CCP module in capture
described in Technical Brief TB001 "Secure Learning
mode
RKE Systems Using KEELOQ Encoders" [DS91000].
• Using comparator inputs interrupt
All of these techniques pose different constraints on the TABLE MODULE
pin out, or the PICmicro MCU, that can be used. This
One of the major tasks of a decoder is to properly main-
would lead to different performances in terms of
tain a database that contains all the unique ID’s (serial
achievable immunity from noise and or CPU overhead,
numbers) of the learned transmitters. In most cases,
etc.
the database can be as simple as a single table, which
associates those serial numbers to the synchronization
FAST DECRYPTION MODULE
counters (that are at the heart of the hopping code
This module contains an implementation of the KEELOQ technology).
decryption algorithm that has been optimized for speed This module implements the easiest of all methods, a
on a mid-range PICmicro microcontroller. It allows fast simple "linear list" of records.
decryption times for maximum responsiveness of the
system even at 4 MHz clock. Each transmitter learned is assigned a record of 8
bytes (shown in Table 2), where all the relevant infor-
The decryption function is also used in all learning mation is stored and regularly updated.
schemes and represents the fundamental building
block of all KEELOQ decoders.
TABLE 2: TRANSMITTER RECORD
KEY GENERATION MODULE Offset Data Description
This module shows a simple and linear implementation +0 FCODE Function code (4 bits) and
of the Normal Learn Key Generation . upper 4 Serial Number bits
This module uses the KEELOQ Decrypt routine from the [24..28]
Fast Decryption module to generate the key at every +1 IDLo Serial Number bits [0..7]
received code word instead of generating it during the
+2 IDHi Serial Number bits [8..15]
learn phase and storing it in memory. The advantage is
a smaller Transmitter Record of 8 bytes instead of 16 +3 IDMi Serial Number bits [16..23]
bytes (see Table 2). This translates in a double number +4 SYNCH Sync Counter 8 MSB
of transmitters that can be learned using the 64 byte
internal EEPROM available inside the PIC16F872. +5 SYNCL Sync Counter 8 LSB
This space reduction comes at the expense of more +6 SYNCH2 Second copy of SYNCH
computational power required to process every code +7 Second copy of SYNCL
SYNCL2
word. When a new code word is received, the key gen-
eration algorithm is applied (Normal Learn) and the
resulting Description key is placed in the array
DKEY[0..7]. During a continous transmission (the
user is holding the button on the transmitter), the key
generation is not repeated, to save time, the last com-
puted Decryption Key value is used safely instead (the
serial number being the same).
#include <pic.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0
#DEVICE PIC16C63
// un-supported directives
#define static
#define volatile
#define interrupt
#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0
//
// F872 special function registers
//
#byte TMR0 = 0x01 // Timer 0
#bit T0IF = 0x0B.2 // Timer 0 interrupt flag
#bit T0IE = 0x0B.5 // Timer 0 interrupt enable
#bit GIE = 0x0B.7 // Global Interrupt Enable
// *********************************************************************
// Filename: MAIN.c
// *********************************************************************
// Author: Lucio Di Jasio
// Company: Microchip Technology
// Revision: Rev 1.00
// Date: 08/07/00
//
// Keeloq Normal Learn Decoder on a mid range PIC
// full source in C
//
// Compiled using HITECH PIC C compiler v.7.93
// Compiled using CCS PIC C compiler v. 2.535
// ********************************************************************
//---------------------------------------------------------------
//
//--------------------------------------------------------------------
// timings
//
#define TOUT 5 // 5 * 71ms = 350ms output delay
#define TFLASH 2 // 4 * 71ms = 280ms half period
#define TLEARN 255 // 255 * 71ms = 18s learn timeout
//
// interrupt receiver
//
#include "rxim.c"
//
// external modules
//
#include "mem-87x.c" // EEPROM I2C routines
//
// prototypes
//
void Remote( void);
//
// MAIN
//
// Main program loop, I/O polling and timing
//
void main ()
{
// init
ADCON1 = 0x7; // disable analog inputs
TRISA = MASKPA; // set i/o config.
TRISB = MASKPB;
TRISC = MASKPC;
PORTA = 0; // init all outputs
PORTB = 0;
PORTC = 0;
OPTION = 0x8f; // prescaler assigned to WDT,
// TMR0 clock/4, no pull ups
// main loop
while ( TRUE)
{
if ( RFFull) // buffer contains a message
Remote();
// poll learn
if ( !Learn) // low -> button pressed
{
CLearn++;
// outputs timing
if ( COut > 0) // if timer running
{
COut--;
if ( COut == 0) // when it reach 0
{
Led = OFF; // all outputs off
Out0 = OFF;
Out1 = OFF;
Out2 = OFF;
Out3 = OFF;
Vlow = OFF;
}
}
// Led Flashing
if ( CFlash > 0)
{
CTFlash--; // count down
if ( CTFlash == 0) // if timed out
{
CTFlash = TFLASH; // reload timer
CFlash--; // count one flash
Led = OFF; // toggle Led
if ( CFlash & 1)
Led = ON;
}
}
} // main loop
} // main
//
// Remote Routine
//
// Decrypts and interprets receive codes
// Does Normal Operation and Learn Mode
//
// INPUT: Buffer contains the received code word
//
// OUTPUT: S0..S3 and LearnOut
//
void Remote()
{
// a frame was received and is stored in the receive buffer
// move it to decryption Buffer, and restart receiving
memcpy( Buffer, B, 9);
RFFull = FALSE; // ready to receive a new frame
// decoding
NormalKeyGen(); // compute the decryption key
Decrypt(); // decrypt the hopping code portion
if ( FLearn)
{
// Learn Mode
if ( Find()== FALSE)
// could not find the Serial Number in memory
{
if ( !Insert()) // look for new space
return; // fail if no memory available
}
} // remote
#define TRFreset 0
#define TRFSYNC 1
#define TRFUNO 2
#define TRFZERO 3
case TRFUNO:
if ( RFBit == 0)
{ // falling edge detected ----+
// |
case TRFZERO:
if ( RFBit)
{ // rising edge detected +----
// |
// ----+
RFstate= TRFUNO;
B[Bptr] >>= 1; // rotate
if ( RFcount >= 0)
{
B[Bptr]+=0x80; // shift in bit
}
RFcount = 0; // reset length counter
if ( ( ++BitCount & 7) == 0)
Bptr++; // advance one byte
if (BitCount == NBIT)
{
RFstate = TRFreset; // finished receiving
RFFull = TRUE;
}
}
else
{ // still low
RFcount++;
if ( RFcount >= LOW_TO) // too long low
{
RFstate = TRFSYNC; // fall back into RFSYNC state
Bptr = 0; // reset pointers, while keep counting on
BitCount = 0;
}
}
break;
case TRFSYNC:
if ( RFBit)
{ // rising edge detected +---+ +---..
// | | <-Theader-> |
// +----------------+
if ( ( RFcount < SHORT_HEAD) || ( RFcount >= LONG_HEAD))
{
RFstate = TRFreset;
break; // too short/long, no header
}
else
{
RFcount =0; // restart counter
RFstate= TRFUNO;
}
}
else
{ // still low
RFcount++;
}
break;
case TRFreset:
default:
RFstate = TRFSYNC; // reset state machine in all other cases
RFcount = 0;
Bptr = 0;
BitCount = 0;
break;
} // switch
} // rxi
void InitReceiver()
{
T0IF = 0;
T0IE = 1; // TMR0 overflow interrupt
GIE = 1; // enable interrupts
RFstate = TRFreset; // reset state machine in all other cases
RFFull = 0; // start with buffer empty
XTMR = 0; // start extended timer
}
// ------------------------------------------------------------
//Table structure definition:
//
// the EEPROM is filled with an array of MAX_USER user records
// starting at address 0000
// each record is EL_SIZE byte large and contains the following fields:
// EEPROM access is in 16 bit words for efficiency
//
// DatoHi DatoLo offset
// +-------+-------+
// | FCode | IDLo | 0 XF contains the function codes (buttons) used during learning
// +-------+-------+ and the top 4 bit of Serial Number
// | IDHi | IDMi | +2 IDHi IDMi IDLo contain the 24 lsb of the Serial Number
// +-------+-------+
// | HopHi | HopLo | +4 sync counter
// +-------+-------+
// | HopHi2| HopLo2| +6 second copy of sync counter for integrity checking
// +-------+-------+
//
// NOTE a function code of 0f0 (seed transmission) is considered
// invalid during learning and is used here to a mark location free
//
// -----------------------------------------------------------
// FIND Routine
//
// search through the whole table the given a record whose ID match
//
// INPUT:
// IDHi, IDMi, IDLo, serial number to search
//
// OUTPUT:
// Ind address of record (if found)
// EHop sync counter value
// ETemp second copy of sync counter
// RETURN: TRUE if matching record found
//
byte Find()
{
byte Found;
Found = FALSE; // init to not found
if (Found == TRUE)
{
RDnext(); // read HopHi/Lo
EHop = Dato;
RDnext(); // read HopHi2/Lo2
ETemp= Dato;
}
return Found;
}
// -----------------------------------------------------------
//INSERT Routine
//
//search through the whole table for an empty space
//
//INPUT:
// IDHi, IDMi, IDLo, serial number to insert
//
//OUTPUT:
// Ind address of empty record
//
//RETURN: FALSE if no empty space found
//
byte Insert()
{
for (Ind=0; Ind < (EL_SIZE * MAX_USER); Ind+=EL_SIZE)
{
RDword(Ind); // read first Word
FCode = (Dato>>8);
// check if 1111xxxx
if ( (FCode & 0xf0) == 0xf0)
return TRUE; // insert point found
} // for
//-----------------------------------------------------------
//Function IDWrite
// store IDHi,Mi,Lo + XF at current address Ind
//INPUT:
// Ind point to record + offset 0
// IDHi, IDMi, IDLo Serial Number
// XF function code
//OUTPUT:
//
byte IDWrite()
{
if (!FLearn)
Dato = Buffer[7];
Dato = (Dato<<8) + IDLo;
WRword(Ind); // write first word
Dato = IDHi;
Dato = (Dato<<8) + IDMi;
WRword(Ind+2); // write second word
return TRUE;
} // IDWrite
//------------------------------------------------------------
//Function HopUpdate
// update sync counter of user record at current location
//INPUT:
// Ind record + offset 0
// Hop current sync counter
//OUTPUT:
// none
//
byte HopUpdate()
{
if (!FHopOK)
return FALSE; // Guard statement: check if Hop update
return TRUE;
} // HopUpdate
//-----------------------------------------------------------
//Function ClearMem
// mark all records free
//INPUT:
//OUTPUT:
//USES:
//
byte ClearMem()
{
for (Ind=0; Ind < (EL_SIZE * MAX_USER); Ind+=EL_SIZE)
{
Dato = 0xffff;
WRword( Ind);
}
return TRUE;
} // ClearMem
void RDnext()
{
// continue reading
EEADR++; // NOTE generate no carry
Dato = ((RD=1), EEDATA);
EEADR++;
Dato += ((RD=1), EEDATA)<<8;
}
#ifdef HITECH
#include "fastdech.c" // for HITECH optimized version
#else
#include "fastdecc.c" // for CCS optimized version
#endif
// ----------------------------------------------------------------------
void LoadManufCode()
{
DKEY[0]=0xef; // DKEY=0123456789ABCDEF
DKEY[1]=0xcd;
DKEY[2]=0xAB;
DKEY[3]=0x89;
DKEY[4]=0x67;
DKEY[5]=0x45;
DKEY[6]=0x23;
DKEY[7]=0x01;
}
//----------------------------------------------------------------------
//
// Key Generation routine
//
// Normal Learn algorithm
//
// INPUT: Serial Number (Buffer[4..7])
// Manufacturer code
// OUTPUT: DKEY[0..7] computed decryption key
//
void NormalKeyGen()
{
byte HOPtemp[4]; // HOP temp buffer
byte SKEYtemp[4]; // temp decryption key
} // Normal KeyGen
//----------------------------------------------------------------------
//
// Valid Decryption Check
//
// INPUT: Serial Number (Buffer[4..7])
// Hopping Code (Buffer[0..3])
// OUTPUT: TRUE if discrimination bits == lsb Serial Number
// and decrypted function code == plain text function code
byte DecCHK()
{
// verify discrimination bits
if ( DisLo != IDLo) // compare low 8bit of Serial Number
return FALSE;
return TRUE;
} // DecCHK
//----------------------------------------------------------------------
//
// Hopping Code Verification
//
// INPUT: Hopping Code (Buffer[0..3])
// and previous value stored in EEPROM EHop
// OUTPUT: TRUE if hopping code is incrementing and inside a safe window (16)
//
byte HopCHK()
{
FHopOK = FALSE; // Hopping Code is not verified yet
FSame = FALSE; // Hopping Code is not the same as previous
if ( F2Chance)
if ( NextHop == Hop)
{
F2Chance = FALSE; // resync success
FHopOK = TRUE;
return TRUE;
}
// main comparison
ETemp = Hop - EHop; // subtract last value from new one
FHopOK = TRUE;
return TRUE;
}
} // HopCHK
byte aux;
void Decrypt()
{
byte i, j, key;
sbyte p;
p = 1;
// shift in buffer
// shift_left( Buffer, 4, BIT_TEST( aux,7));
#asm
// rotate Dkey
// rotate_left( DKEY, 8);
key<<=1;
} // for i
} // for j
} // decrypt
void Decrypt()
{
byte i, j, key;
sbyte p;
p = 1;
// shift in buffer
key<<=1;
} // for i
} // for j
} // decrypt
01/30/01
All rights reserved. © 2001 Microchip Technology Incorporated. Printed in the USA. 3/01 Printed on recycled paper.
Information contained in this publication regarding device applications and the like is intended through suggestion only and may be superseded by
updates. It is your responsibility to ensure that your application meets with your specifications. No representation or warranty is given and no liability is
assumed by Microchip Technology Incorporated with respect to the accuracy or use of such information, or infringement of patents or other intellectual
property rights arising from such use or otherwise. Use of Microchip’s products as critical components in life support systems is not authorized except with
express written approval by Microchip. No licenses are conveyed, implicitly or otherwise, except as maybe explicitly expressed herein, under any intellec-
tual property rights. The Microchip logo and name are registered trademarks of Microchip Technology Inc. in the U.S.A. and other countries. All rights
reserved. All other trademarks mentioned herein are the property of their respective companies.