RFID Reader 线路图收集
This 125 kHz RFID reader
http://www.serasidis.gr/circuits/RFID_reader/125kHz_RFID_reader.htm
http://www.serasidis.gr/circuits/RFID_reader/images/125kHz_RFID_reader_schem.GIF
I will try to explain with simple words how the RFID works. The ATtiny13 uses the PWM function to produce an 125 kHzsquare wave signal.
This signal comes out from PB0 pin. On the falling edge of the PB0 (Logic '0'), the T1 does not conduct.
So the L1 is energized from R1 (100 ohm) with +5V.
When PB0 pin rises (Logic '1') the T1 conducts and one side of L1 goes to GND.
The L1 goes in parallel with C2 creating an LC oscillator.
These transitions of L1 to logic '1' and logic '0' are made 125000 times in one second (125 kHz).
Data communication between Tag and reader.
How an RFID Tag communicates with the reader?
The idea is simple, but very clever!
When a Tag wants to send a logic '0' to the reader it puts a "load" to its power supply line to request more power from the reader.
That will make a small voltage drop on the RFID reader side.
That voltage level is logic '0' (picture 4).
Simultaneously, as long as the reader transmits the 125 kHz signal it reads the voltage of the transmitted signal trough the filters D1, C3 and R5, C1.
When the Tag drops the voltage as we said before, the reader reads this voltage drop as logic '0'.
When the Tag doesn't require any additional power, it doesn't make a voltage drop.
That is logic '1' (picture 3).
The 'Ones' or 'Zeros' length depends on the serial transmission data rate.
For example, for 125 kHz carrier frequency we don't have 125000 bits per second data transmission!
The data transmission from Tag to the reader varies from 500 bits per second up to 8000 bits per second.
The RFID tag content EM4100
The 125kHz RFID tag transmits 64 bits.
- The first 9 bits are the start communication bits ( always '1' ).
- The next 4 bits are the Low Significant Bits of the customer ID(D00,...,D03).
- The next 1 bit (P0) is the Even parity bit of the previous 4 bits .
- The next 4 bits are the High Significant Bits of the customer ID (D04,...,D07).
- The next 1 bit (P1) is the Even parity bit of the previous 4 bits.
- The next 4 bits are first part of the 32-bit Tag's serial number (D08,...,D11).
- ...
- The PC0 bit is the Even parity bit of bits D00, D04, D08, D12, D16, D20, D24, D28, D32 and D36 (the bits on the same column).
- The PC1, PC2, PC3 bits represent the parity bits of the next 3 columns.
The data verification is been done from ATtiny13 by calculating the Even parity bit of each line and each column with the parity bits that had been received form the RFID Tag transmitted data.
Simple RFID Reader Module Design
http://freshengineer.com/blog/simple-rfid-reader-module-design/
I first started by creating a simple, non-filtered, non-processed reader.
I’ve used a coil of about 1mH for both sides.
Since my chosen frequency was 125 KHz, my capacitor should be 1.62nF according to the following equation; I picked 1n5 standard value.
So this configuration is probably one of the simplest forms of an RFID reader-tag pair:
L1 is driven via a low-impedance 125 KHz oscillator, can be a sine or a square wave
since the LC circuit will filter out the unwanted harmonics that are presented in a square wave.
If the Q of the inductor is high, then a voltage that is greater than the oscillator’s output is going to be present in the “Out”.
I’ve seen 100 Vpp when I fed the LC circuit with 5Vpp!
So, the “Out” waveform at the top of the C1 is a sine wave of a 125 KHz frequency.
Now, the fun thing begins when we put the tag near the reader.
L2, C2 pair picks up the 125KHz waveform via L2. So, if you scope C2, you will see 125 KHz sine wave.
Now, if you scope “Out”, you will see that Vpp at C1 will drop when we close the switch SW1.
That is because we load L1′s magnetic field via L2.
Now, push the button like you are sending a Morse code and watch the “Output” waveform on the scope. Aha, modulation!
Simple! That is how real RFID passive tags work.
However, instead of sending Morse code, they modulate the signal with their specific modulation scheme.
I am going to work with EM4100 protocol since it is widely used.
Okay, let’s bring some real circuitry here.
http://freshengineer.com/Documents/RFID_Reader/KiCad/Outputs/Schematic.pdf
OK, L1 and C6 are our main guys.
They are the components that are mentioned before as “L1″ and “C1″ in Figure 1.
The circuitry on the left side of L1 is used to drive this LC circuit, and right side of C6 is used to read the changes in the signal.
C1 AC couples the clock signal of 125KHz to the circuit.
R1 and R2 biases the transistor Q1.
R4 limits its base current.
Q1 drives the input of push-pull follower formed by Q2 and Q3.
A push-pull follower will drive the signal at low output impedance.
D2 and D3 prevents distortions at the cross-overs from zero level.
Now, our signal at “TP1″ is something like this, with no processing and modulation:
We are going to use an “envelope detector” formed by D4, C8 and R13.
After the recovery, this is how our “modulated” signal looks like:
Of course, these measurements are made with the tag almost touching the reader.
If we move the tag away about 5 cm from the reader, we may not be able to see the signal even with the oscilloscope.
So, we have to filter and amplify this signal and make it ready to be processed by a microcontroller later on.
As you can see above, the signal we are dealing with is an AC signal.
To deal with AC signals with the OP-AMPs, you need either a dual supply which goes to negative (for example -12V, +12V),
or you need a virtual ground.
We are going to assume that half point of our supply voltage is ground.
So, if we are using a 5V single supply, our half point is +2.5V. If +2.5V is ground, then +5V is our new +2.5V and 0V is our new -2.5V.
There you have it, a dual supply.
We need the output impedance of this supply low, so we use an OP-AMP to buffer the +2.5V point
which is high impedance due to R15 and R16, and we get a low impedance output as shown:
OK, now that we have solved that problem, let’s go back to our filter design.
We have a square wave at certain frequency that we want to boost.
While boosting the desired frequency we want to kill the other frequencies.
But we see a bump there; square wave.
A square wave is a signal that includes lots of harmonics (theoretically; infinity) of its actual frequency.
These harmonics are hidden in the rise and fall waves, sharper the rise and fall, more the harmonics count.
So, that means, if you low pass filter a square wave -that is not letting higher frequencies to pass a filter,
you delete those harmonics and remember, those harmonics are in rise and fall times.
Thus, you end up with a sine wave.
We do not want that, that’s why we are going to let these frequencies pass as the way they are, however we are going to boost the original frequency.
To do this, we have a filter design like follows:
“SignalOut” is our input coming from the envelope detector.
C2 and R3 form a high pass filter to AC couple the input, and D1 protects the non-inverting input of the U1:A from over-voltage.
You may say that it is not needed as the capacitor C2 will not allow any DC voltage through, you are correct.
But only in steady state, if the capacitor is discharged, then it will let DC until it is charged.
By the way, think +2.5V point as a “ground” point, since it is a virtual ground.
C5 and R10 AC couples the output from U1:A in case of any DC offset.
Then, this signal is filtered again, resulting in more amplification.
Here is a graph showing the transfer characteristics of these filters:
Here is the waveform at the output node, pin 7 of U1:
Yay! We have a filtered, clean output!
But not so fast, because we need logic output.
This is done easily by a comparator.
Normally, OP-AMP comparators compare the input with a reference voltage, generally half the supply voltage.
However, this may not work well if the rise and fall times of the input waveform is not in symmetry or close enough.
Let’s demonstrate that with a reference voltage of half the supply:
The input signal has a loooong fall time.
It should fall down at 3ms point ideally, since this is a recovered, however badly distorted ~43% duty cycle square wave - well at least let’s assume.
See how the output waveform is a ~56% duty cycle square wave. We do not want that.
What you have to do is simple, compare the input signal with its average.
How do you find a signals average? That is simple too - put it into a low pass filter, and here is the output:
Now let’s look at our case and apply:
Let’s look at R14 and C10, we have selected them so that we have a good averaging (should I say weighted?) level for both 1KHz and 2KHz outputs we will have.
This is the final output, isn’t it great:
Finished PCB:
I am going to cover the digital section, that is the decoding part of this signal, in an another post.
One little hint; it is Manchester coding!
Until then, feel free to comment and share.
DIY FSK RFID Reader
http://playground.arduino.cc/Main/DIYRFIDReader
http://playground.arduino.cc/uploads/Main/FSK-RFID-reader-v2.png
This page describes the construction of an RFID reader using only an Arduino (Nano 3.0 was tested, but others may work),
a hand-wound wire coil, and some assorted low cost common components.
Credits
The hardware and software designs for this project are based in part on the ideas, code and schematics
posted by Micah Dowty here : SIMPLEST RFID READER
and Asher Glick here : AsherGlick AVRFID
Background
RFID readers are devices sold by companies such as Parallax to read RFID tags with embedded identification circuits
(we focus here on passive tags, activated by the reader's transmitted RF energy).
The design presented here shows how to wind a simple wire loop by hand
(or create an equivalent printed circuit spiral version), connect it to an Arduino (or its chip),
add a few low cost common components and create your own RFID reader.
To make it more interesting (i.e. challenging), we will focus on the FSK class of RFID tags,
which are fairly common among the 125kHz devices, but for some reason are not supported by the Parallax kits.
Micah Dowty has shown [ World's simplest RFID reader ] a design for an FSK/ASK RFID reader built around a Parallax Propeller device.
His code, which is in assembly language, implements an ingenious (but complex) algorithm to create a dynamically variable analog bias voltage,
which is used to pull the weak RFID signal into range, so it can be discriminated into binary signals by the Propeller's digital input circuitry.
He also dynamically tweaks the transmit/receive RF frequency to keep the antenna's tank circuit in peak resonance for optimal signal to noise.
All capacitances are in picofarads. C1 and C3 should be 1000 pF, not 1000 nF. Likewise, C2 is 2200 pF.
There are three problems with his approach:
first, the passive detection circuit lacks amplification, which makes it very sensitive to noise and therefore raises reliability issues.
Second, the design is based on the Propeller chip, and if you are a fan of the Arduino and/or associated Atmel AVR chips, it leaves you out.
And third, the dynamic slewing of frequencies and bias voltage is overly complicated, making it hard to debug.
His general concept is attractive, however: use a microcontroller chip and wind your own wire loop to create,
with some simple components and appropriate code, a complete DIY RFID reader.
Asher Glick has presented a solution [ AsherGlick AVRFID ] for reading and decoding FSK RFID tags
using the Arduino/AVR family (which he calls AVRFID),
which is good except it apparently requires obtaining and modifying an existing Parallax RFID reader device
(which natively only supports ASK).
Our goal here is to present a simple solution for reading FSK tags which addresses the above shortcomings:
make it robust and reliable for real-world noise environments, base it on the Arduino,
and build the RFID reader ourselves using a few simple low-cost parts, rather than buying and/or modifying one.
The circuit diagram above was derived from the "World's Simplest RFID Reader" design posted by Micah Dowty.
Based on the Parallax Propeller, Micah's approach was to use passive components only, without amplification,
in order to achieve the ultimate in simplicity.
The lack of amplification, however, results in a weak signal, potentially less than 2V PTP.
This signal is then biased by an analog level produced by the Propeller,
to try to maintain the signal's DC level near the discrimination point of the Propeller's binary-digital input circuitry.
His code attempts to dynamically calculate that optimal midpoint level, and feed it into the circuit using a filtered PWM DAC output.
Since the signal is weak, it can be distorted by interference and noise, which results in reduced reliability.
The circuit presented here includes (as Micah suggests in his documentation) one active component:
a common low-cost LM234quad-opamp IC (or equivalent).
This addition provides several significant advantages, at a negligible cost.
First, the signal is amplified (using one of the four opamps on the IC package) to a more noise-immune level (of 2-3 volts PTP).
Second, the DC level of the signal is maintained at exactly Vcc/2 using another opamp on the IC,
which eliminates the need for the DC propping code in the Arduino.
Third, having the signal amplifier in place allows another low-pass RC filter stage (another capacitor and resistor),
which makes the final discriminated digital signal cleaner and more reliable.
The end result is a more robust detected signal with improved noise immunity.
As a quick review of the circuit, the loop is made of a toroidally-wound #22-30 magnet wire
(we used an empty roll of Scotch 3.25" I.D. packing tape as former),
and can be remoted from the circuit if needed, via coaxial cable.
The inductor L1 and capacitance C1 should be matched to resonate at around 125 kHz.
When driven at its resonant frequency by the Arduino's 0-5 volt square wave signal,
the center point of the resonator (which connects to D1's cathode) will have a fairly pure voltage sine wave, of about 30V PTP.
When coupled to an RFID tag, the pure sine wave RF will fluctuate visibly as the tag opens and closes its own loop antenna to repeatedly transmit its code.
This modulation is then detected from the RF envelope by D1, C2 and R1,
which produce a negative bias voltage with the small detected coded signal, e.g. about 11 RF cycles per coded cycle.
The coded cycles are of two different wave lengths (or frequencies), which represent streams of logic ones and zeros,
and they need to arrive at the Arduino chip as binary levels which can be timed reasonably accurately
so as to reliably tell the difference between the two distinct frequencies.
The relatively large capacitor C3 decouples the negative bias voltage from the signal,
and is followed by a low-pass RC filter stage (R2 and C4) which attenuates some of the residual RF spikes
from the lower frequency coded RFID signal.
Capacitor C5 decouples the resulting signal and presents it to the amplification stage, implemented by the LM324opamp, IC1.
The latter amplifies the weak signal from about .15V to about 3V PTP (depending of the ratio of R4 to R3),
and places it on top of a Vcc/2 bias voltage, about 2.5V in the arduino's case.
This signal is then fed into one of the digital input ports on the Arduino (which also includes some helpful hysteresis),
and is discriminated by the internal comparator into a square wave of ones and zeroes.
Software
The Arduino sketch, derived from the code posted by Asher Glick, [ https://github.com/AsherGlick/AVRFID ]
uses a single timer channel in the Arduino (using the Timer1 library) for both RF signal generation
as well as timing clock to count the width of each input signal wave.
/*****************************************************************************\
| This program was written by Asher Glick aglick@tetrakai.com |
| This program is currently under the GNU GPL licence |
\*****************************************************************************/ /****************** CHIP SETTINGS ******************\
| This program was designed to run on an ATMEGA328 |
| chip running with an external clock at 8MHz |
\***************************************************/ /********** FUSE SETTINGS **********\
| Low Fuse 0xE2 |
| High Fuse 0xD9 | +- AVRDUDE COMMANDS -+
| Extra Fuse 0x07 | | -U lfuse:w:0xe0:m |
| | | -U hfuse:w:0xd9:m |
| These fuse calculations are | | -U efuse:w:0xff:m |
| based off of the usbtiny AVR | +--------------------+
| programmer. Other programmers |
| may have a different fuse number |
\***********************************/ /************************** AVRDUDE command for 8MHz **************************\
| sudo avrdude -p m328p -c usbtiny -U flash:w:myproject.hex |
| -U lfuse:w:0xE2:m -U hfuse:w:0xD9:m -U efuse:w:0x07:m |
| |
| NOTE: when messing with fuses, do this at your own risk. These fuses for the |
| ATMEGA328P (ATMEGA328) worked for me, however if they do not work for |
| you, it is not my fault |
| NOTE: '-c usbtiny' is incorrect if you are using a different programmer |
\******************************************************************************/ /******************************* CUSTOM SETTINGS ******************************\
| Settings that can be changed, comment or uncomment these #define settings to |
| make the AVRFID code do different things
\******************************************************************************/ //#define Binary_Tag_Output // Outputs the Read tag in binary over serial
#define Hexadecimal_Tag_Output // Outputs the read tag in Hexadecimal over serial
//#define Decimal_Tag_Output // Outputs the read tag in decimal #define Manufacturer_ID_Output // The output will contain the Manufacturer ID (NOT IMPLEMENTED)
#define Site_Code_Output // The output will contain the Site Code (NOT IMPLEMENTED)
#define Unique_Id_Output // The output will contain the Unique ID //#define Split_Tags_With '-' // The character to split tags pieces with //#define Whitelist_Enabled // When a tag is read it will be compaired
// against a whitelist and one of two functions
// will be run depending on if the id matches // some conststents // These values may need to be changed depending on the servo that you are using
#define SERVO_OPEN 575 // open signal value for the servo
#define SERVO_CLOSE 1000 // close signal value for the servo //20-bit manufacturer code,
//8-bit site code
//16-bit unique id #define MANUFACTURER_ID_OFFSET 0
#define MANUFACTURER_ID_LENGTH 20 #define SITE_CODE_OFFSET 20
#define SITE_CODE_LENGTH 8 #define UNIQUE_ID_OFFSET 28
#define UNIQUE_ID_LENGTH 16 // these settings are used internally by the program to optimize the settings above
#ifndef serialOut
#define serialOut
#endif /// Begin the includes #include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h> #define ARRAYSIZE 900 // Number of RF points to collect each time char * begin; // points to the bigining of the array
int * names; // array of valid ID numbers
int namesize; // size of array of valid ID numbers
volatile int iter; // the iterator for the placement of count in the array
volatile int count; // counts 125kHz pulses
volatile int lastpulse; // last value of DEMOD_OUT
volatile int on; // stores the value of DEMOD_OUT in the interrupt /********************************* ADD NAMES *********************************\
| This function add allocates the ammount of memory that will be needed to |
| store the list of names, and adds all the saved names to the allocated |
| memory for use later in the program |
\*****************************************************************************/
void addNames(void) {
namesize = ; // number of IDs in the access list
names = malloc (sizeof(int) * namesize);
// change or add more IDs after this point
names [] = ;
names [] = ;
} /******************************* INT0 INTERRUPT *******************************\
| This ISR(INT0_vect) is the interrupt function for INT0. This function is the |
| function that is run each time the 125kHz pulse goes HIGH. |
| 1) If this pulse is in a new wave then put the count of the last wave into |
| the array |
| 2) Add one to the count (count stores the number of 125kHz pulses in each |
| wave |
\******************************************************************************/
ISR(INT0_vect) {
//Save the value of DEMOD_OUT to prevent re-reading on the same group
on =(PINB & 0x01);
// if wave is rising (end of the last wave)
if (on == && lastpulse == ) {
// write the data to the array and reset the cound
begin[iter] = count;
count = ;
iter = iter + ;
}
count = count + ;
lastpulse = on;
} /************************************ WAIT ************************************\
| A generic wait function |
\******************************************************************************/
void wait (unsigned long time) {
long i;
for (i = ; i < time; i++) {
asm volatile ("nop");
}
} //////////////////////////////////////////////////////////////////////////////
//////////////////////////// SERIAL COMMUNICATION ////////////////////////////
////////////////////////////////////////////////////////////////////////////// /******************************** USART CONFIG ********************************\
| USART_Init(void) initilizes the USART feature, this function needs to be run |
| before any USART functions are used, this function configures the BAUD rate |
| for the USART and enables the format for transmission |
\******************************************************************************/
#define FOSC 8000000 // Clock Speed of the procesor
#define BAUD 19200 // Baud rate (to change the BAUD rate change this variable
#define MYUBRR FOSC/16/BAUD-1 // calculate the number the processor needs
void USART_Init(void) {
unsigned int ubrr = MYUBRR;
/*Set baud rate */
UBRR0H = (unsigned char)(ubrr>>);
UBRR0L = (unsigned char)ubrr;
/*Enable receiver and transmitter */
UCSR0B = (<<RXEN0)|(<<TXEN0);
/* Set frame format: 8data, 2stop bit */
UCSR0C = (<<USBS0)|(<<UCSZ00);
} /******************************* USART_Transmit *******************************\
| The USART_Transmit(int) function allows you to send numbers to USART serial |
| This function only handles numbers up to two digits. If there is one digit |
| the message contains a space, then the digit converted to ascii. If there |
| are two digits then the message is the first digit followed by the seccond |
| If the input is negative then the function will output a newline character |
\******************************************************************************/
void USART_Transmit(char input )
{
while ( !( UCSR0A & (<<UDRE0)) );
// Put the value into the regester to send
UDR0 = input;
}
//////////////////////////////////////////////////////////////////////////////
////////////////////////// BASE CONVERSION FUNCTIONS /////////////////////////
//////////////////////////////////////////////////////////////////////////////
char binaryTohex (int four, int three, int two, int one) {
int value = (one << ) + (two << ) + (three << ) + (four << );
if (value > ) return 'A' + value - ;
return '' + value;
} /*********************** GET HEX ARRAY FROM BINARY ARRAY **********************\
|
\******************************************************************************/
int * getHexFromBinary (int * array, int length, int * result) {
int i;
int resultLength = (length+)/; // +3 so that the resulting number gets rounded up
// 4 / 4 = 1 [correct]
// 7 / 4 = 1 (4+3) [still correct]
// 5 / 4 = 1 [not rounded up]
// 8 / 4 = 2 (5+3) [correct] for (i = ; i < resultLength; i++) {
result[i*] = (array[i+] << )
+ (array[i+] << )
+ (array[i+] << )
+ (array[i+] << );
}
return result;
}
/*************************** GET DECIMAL FROM BINARY **************************\
| This function will take in a binary input and return an intiger with the |
| corrisponding value, assumed as decimal |
\******************************************************************************/
int getDecimalFromBinary (int * array, int length) {
int result = ;
int i;
for (i = ; i < length; i++) {
result = result<<;
result += array[i]&0x01;
}
return result;
} void recurseDecimal (unsigned int val) {
if (val > ) {
recurseDecimal(val/);
USART_Transmit(''+val%);
}
return;
} void printDecimal (int array[]) {
#ifdef Manufacturer_ID_Output
int manufacturerId = getDecimalFromBinary( array + MANUFACTURER_ID_OFFSET,MANUFACTURER_ID_LENGTH);
manufacturerId = getDecimalFromBinary( array + MANUFACTURER_ID_OFFSET,MANUFACTURER_ID_LENGTH);
recurseDecimal(manufacturerId);
#endif #ifdef Split_Tags_With
USART_Transmit(Split_Tags_With);
#endif #ifdef Site_Code_Output int siteCode = getDecimalFromBinary( array + SITE_CODE_OFFSET,SITE_CODE_LENGTH);
recurseDecimal(siteCode);
#endif #ifdef Split_Tags_With
USART_Transmit(Split_Tags_With);
#endif #ifdef Unique_Id_Output
int lastId = getDecimalFromBinary( array + UNIQUE_ID_OFFSET,UNIQUE_ID_LENGTH);
recurseDecimal(lastId);
#endif USART_Transmit('\r');
USART_Transmit('\n');
}
void printHexadecimal (int array[]) {
int i;
#ifdef Manufacturer_ID_Output
for (i = MANUFACTURER_ID_OFFSET; i < MANUFACTURER_ID_OFFSET+MANUFACTURER_ID_LENGTH; i+=) {
USART_Transmit(binaryTohex(array[i],array[i+],array[i+],array[i+]));
}
#endif #ifdef Split_Tags_With
USART_Transmit(Split_Tags_With);
#endif #ifdef Site_Code_Output
for (i = SITE_CODE_OFFSET; i < SITE_CODE_OFFSET+SITE_CODE_LENGTH; i+=) {
USART_Transmit(binaryTohex(array[i],array[i+],array[i+],array[i+]));
}
#endif #ifdef Split_Tags_With
USART_Transmit(Split_Tags_With);
#endif #ifdef Unique_Id_Output
for (i = UNIQUE_ID_OFFSET; i < UNIQUE_ID_OFFSET+UNIQUE_ID_LENGTH; i+=) {
USART_Transmit(binaryTohex(array[i],array[i+],array[i+],array[i+]));
}
#endif
USART_Transmit('\r');
USART_Transmit('\n');
} void printBinary (int array[]) {
int i;
#ifdef Manufacturer_ID_Output
for (i = MANUFACTURER_ID_OFFSET; i < MANUFACTURER_ID_OFFSET+MANUFACTURER_ID_LENGTH; i++) {
USART_Transmit(''+array[i]);
}
#endif #ifdef Split_Tags_With
USART_Transmit(Split_Tags_With);
#endif #ifdef Site_Code_Output
for (i = SITE_CODE_OFFSET; i < SITE_CODE_OFFSET+SITE_CODE_LENGTH; i++) {
USART_Transmit(''+array[i]);
}
#endif #ifdef Split_Tags_With
USART_Transmit(Split_Tags_With);
#endif #ifdef Unique_Id_Output
for (i = UNIQUE_ID_OFFSET; i < UNIQUE_ID_OFFSET+UNIQUE_ID_LENGTH; i++) {
USART_Transmit(''+array[i]);
}
#endif
USART_Transmit('\r');
USART_Transmit('\n');
} /********************************* Search Tag *********************************\
| This function searches for a tag in the list of tags stored in the flash |
| memory, if the tag is found then the function returns 1 (true) if the tag |
| is not found then the function returns 0 (false) |
\******************************************************************************/
int searchTag (int tag) {
int i;
for (i = ; i < namesize; i++) {
if (tag == names[i]) {
return ;
}
}
return ;
} void whiteListSuccess () {
PORTB |= 0x04;
// open the door
OCR1A = - SERVO_OPEN;
{
unsigned long i;
for (i = ; i < ; i++) {
if (!((PINB & (<<))>>)) {
break;
}
}
}
//close the door
OCR1A = - SERVO_CLOSE;
{
unsigned long i;
for (i = ; i < ; i++) {
asm volatile ("nop");
}
}
OCR1A = ;
wait ();
}
void whiteListFailure () {
PORTB |= 0x08;
wait ();
} //////////////////////////////////////////////////////////////////////////////
///////////////////////////// ANALYSIS FUNCTIONS /////////////////////////////
////////////////////////////////////////////////////////////////////////////// /************************* CONVERT RAW DATA TO BINARY *************************\
| Converts the raw 'pulse per wave' count (5,6,or 7) to binary data (0, or 1) |
\******************************************************************************/
void convertRawDataToBinary (char * buffer) {
int i;
for (i = ; i < ARRAYSIZE; i++) {
if (buffer[i] == ) {
buffer[i] = ;
}
else if (buffer[i] == ) {
buffer[i] = ;
}
else if (buffer[i] == ) {
buffer[i] = buffer[i-];
}
else {
buffer[i] = -;
}
}
} /******************************* FIND START TAG *******************************\
| This function goes through the buffer and tries to find a group of fifteen |
| or more 1's in a row. This sigifies the start tag. If you took the fifteen |
| ones in multibit they would come out to be '111' in single-bit |
\******************************************************************************/
int findStartTag (char * buffer) {
int i;
int inARow = ;
int lastVal = ;
for (i = ; i < ARRAYSIZE; i++) {
if (buffer [i] == lastVal) {
inARow++;
}
else {
// End of the group of bits with the same value
if (inARow >= && lastVal == ) {
// Start tag found
break;
}
// group of bits was not a start tag, search next tag
inARow = ;
lastVal = buffer[i];
}
}
return i;
} /************************ PARSE MULTIBIT TO SINGLE BIT ************************\
| This function takes in the start tag and starts parsing the multi-bit code |
| to produce the single bit result in the outputBuffer array the resulting |
| code is single bit manchester code |
\******************************************************************************/
void parseMultiBitToSingleBit (char * buffer, int startOffset, int outputBuffer[]) {
int i = startOffset; // the offset value of the start tag
int lastVal = ; // what was the value of the last bit
int inARow = ; // how many identical bits are in a row// this may need to be 1 but seems to work fine
int resultArray_index = ;
for (;i < ARRAYSIZE; i++) {
if (buffer [i] == lastVal) {
inARow++;
}
else {
// End of the group of bits with the same value
if (inARow >= && inARow <= ) {
// there are between 4 and 8 bits of the same value in a row
// Add one bit to the resulting array
outputBuffer[resultArray_index] = lastVal;
resultArray_index += ;
}
else if (inARow >= && inARow <= ) {
// there are between 9 and 14 bits of the same value in a row
// Add two bits to the resulting array
outputBuffer[resultArray_index] = lastVal;
outputBuffer[resultArray_index+] = lastVal;
resultArray_index += ;
}
else if (inARow >= && lastVal == ) {
// there are more then 15 identical bits in a row, and they are 0s
// this is an end tag
break;
}
// group of bits was not the end tag, continue parsing data
inARow = ;
lastVal = buffer[i];
if (resultArray_index >= ) {
//return;
}
}
}
} /******************************* Analize Input *******************************\
| analizeInput(void) parses through the global variable and gets the 45 bit |
| id tag. |
| 1) Converts raw pulse per wave count (5,6,7) to binary data (0,1) |
| 2) Finds a start tag in the code |
| 3) Parses the data from multibit code (11111000000000000111111111100000) to |
| singlebit manchester code (100110) untill it finds an end tag |
| 4) Converts manchester code (100110) to binary code (010) |
\*****************************************************************************/
void analizeInput (void) {
int i; // Generic for loop 'i' counter
int resultArray[]; // Parsed Bit code in manchester
int finalArray[]; //Parsed Bit Code out of manchester
int finalArray_index = ; // Initilize the arrays so that any errors or unchanged values show up as 2s
for (i = ; i < ; i ++) { resultArray[i] = ; }
for (i = ; i < ; i++) { finalArray[i] = ; } // Convert raw data to binary
convertRawDataToBinary (begin); // Find Start Tag
int startOffset = findStartTag(begin);
PORTB |= 0x10; // turn an led on on pin B5) // Parse multibit data to single bit data
parseMultiBitToSingleBit(begin, startOffset, resultArray); // Error checking, see if there are any unset elements of the array
for (i = ; i < ; i++) { // ignore the parody bit ([88] and [89])
if (resultArray[i] == ) {
return;
}
}
//------------------------------------------
// MANCHESTER DECODING
//------------------------------------------
for (i = ; i < ; i+=) { // ignore the parody bit ([88][89])
if (resultArray[i] == && resultArray[i+] == ) {
finalArray[finalArray_index] = ;
}
else if (resultArray[i] == && resultArray[i+] == ) {
finalArray[finalArray_index] = ;
}
else {
// The read code is not in manchester, ignore this read tag and try again
// free the allocated memory and end the function
return;
}
finalArray_index++;
} #ifdef Binary_Tag_Output // Outputs the Read tag in binary over serial
printBinary (finalArray);
#endif #ifdef Hexadecimal_Tag_Output // Outputs the read tag in Hexadecimal over serial
printHexadecimal (finalArray);
#endif #ifdef Decimal_Tag_Output
printDecimal (finalArray);
#endif #ifdef Whitelist_Enabled
if (searchTag(getDecimalFromBinary(finalArray+UNIQUE_ID_OFFSET,UNIQUE_ID_LENGTH))){
whiteListSuccess ();
}
else {
whiteListFailure();
}
#endif
} /******************************* MAIN FUNCTION *******************************\
| This is the main function, it initilized the variabls and then waits for |
| interrupt to fill the buffer before analizing the gathered data |
\*****************************************************************************/
int main (void) {
int i = ; //------------------------------------------
// VARIABLE INITLILIZATION
//------------------------------------------ // Load the list of valid ID tags
addNames(); //==========> PIN INITILIZATION <==========//
DDRD = 0x00; // 00000000 configure output on port D
DDRB = 0x1E; // 00011100 configure output on port B //=========> SERVO INITILIZATION <=========//
ICR1 = ;// TOP count for the PWM TIMER // Set on match, clear on TOP
TCCR1A = (( << COM1A1) | ( << COM1A0));
TCCR1B = (( << CS11) | ( << WGM13)); // Move the servo to close Position
OCR1A = - SERVO_CLOSE;
{
unsigned long j;
for (j = ; j < ; j++) {
asm volatile ("nop");
}
}
// Set servo to idle
OCR1A = ; // USART INITILIZATION
USART_Init(); //========> VARIABLE INITILIZATION <=======//
count = ;
begin = malloc (sizeof(char)*ARRAYSIZE);
iter = ;
for (i = ; i < ARRAYSIZE; i ++) {
begin[i] = ;
} //=======> INTERRUPT INITILAIZATION <======//
sei (); // enable global interrupts
EICRA = 0x03; // configure interupt INT0
EIMSK = 0x01; // enabe interrupt INT0 //------------------------------------------
// MAIN LOOP
//------------------------------------------
while () {
sei(); //enable interrupts while () { // while the card is being read
if (iter >= ARRAYSIZE) { // if the buffer is full
cli(); // disable interrupts
break; // continue to analize the buffer
}
} PORTB &= ~0x1C; //analize the array of input
analizeInput (); //reset the saved values to prevent errors when reading another card
count = ;
iter = ;
for (i = ; i < ARRAYSIZE; i ++) {
begin[i] = ;
}
}
}
There are two distinct cycle lengths in the detected input signal, "long" and "short",
corresponding to logical ones and zeroes, respectively.
A binary stream of stretches of repeated ones and zeroes is assembled,
and then decimated into the original coded bits on the RFID tag, after decoding the Manchester encoding.
Here is the actual code:
/* Arduino program for DIY FSK RFID Reader
2 * See description and circuit diagram at http://playground.arduino.cc/Main/DIYRFIDReader
3 * Tested on Arduino Nano and several FSK RFID tags
4 * Hardware/Software design is based on and derived from:
5 * Arduino/Timer1 library example
6 * June 2008 | jesse dot tane at gmail dot com
7 * AsherGlick: / AVRFID https://github.com/AsherGlick/AVRFID
8 * Micah Dowty:
9 * http://forums.parallax.com/showthread.php?105889-World-s-simplest-RFID-reader
10 *
11 * Copyright (C) 2011 by edude/Arduino Forum
12
13 This program is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26 */ #include "TimerOne.h" int ledPin = ; // LED connected to digital pin 13
int inPin = ; // sensing digital pin 7
int val;
int bitlenctr = ;
int curState = ; #define maxBuf 1000 //reduce to 100 or so for debugging
#define debug 0 char raw[maxBuf]; int index = ;
int bufnum = ;
#define redLED 12
#define grnLED 11 void setup()
{
Serial.begin();
Timer1.initialize(); // initialize timer1, and set the frequency; this drives both the LC tank as well as the pulse timing clock
// note: modify this as needed to achieve resonance and good match with the desired tags
// the argument value is in microseconds per RF cycle, so 8us will yield RF of 125kHz, 7us --> 143kHz, etc. Timer1.pwm(, ); // setup pwm on pin 9, 50% duty cycle
Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt, once per RF cycle pinMode(ledPin, OUTPUT); // sets the digital pin 13 as output for scope monitoring
pinMode(inPin, INPUT); // sets the digital pin 7 as input to sense receiver input signal
pinMode(grnLED, OUTPUT);
pinMode(redLED, OUTPUT);
digitalWrite(grnLED, );
digitalWrite(redLED, );
} void callback()
{
val = digitalRead(inPin);
digitalWrite(ledPin, val); // for monitoring
bitlenctr++;
if(val != curState) {
// got a transition
curState = val;
if(val == ) {
// got a start of cycle (low to high transition)
if(index < maxBuf) {
raw[index++] = bitlenctr;
}
bitlenctr = ;
}
}
} void loop()
{
if(index >= maxBuf) { Serial.print("got buf num: ");
Serial.println(bufnum); if(debug) {
for(int i = ; i < maxBuf;
i++) {
Serial.print((int)raw[i]);
Serial.print("/");
}
Serial.println("///raw data");
delay();
} // analyze this buffer
// first convert pulse durations into raw bits
int tot1 = ;
int tot0 = ;
int tote = ;
int totp = ;
raw[] = ;
for(int i = ; i < maxBuf; i++) {
int v = raw[i];
if(v == ) {
raw[i] = ;
tot0++;
}
else if(v == ) {
raw[i] = raw[i - ];
totp++;
}
else if(v == || v == ) {
raw[i] = ;
tot1++;
}
else {
raw[i] = ; // error code
tote++;
}
} // next, search for a "start tag" of 15 high bits in a row
int samecnt = ;
int start = -;
int lastv = ;
for(int i = ; i < maxBuf; i++) {
if(raw[i] == lastv) {
// inside one same bit pattern, keep scanning
samecnt++;
}
else {
// got new bit pattern
if(samecnt >= && lastv == ) {
// got a start tag prefix, record index and exit
start = i;
break;
}
// either group of 0s, or fewer than 15 1s, so not a valid tag, keep scanning
samecnt = ;
lastv = raw[i];
}
} // if a valid prefix tag was found, process the buffer
if(start > && start < (maxBuf - *)) { //adjust to allow room for full dataset past start point
process_buf(start);
}
else {
Serial.println("no valid data found in buffer");
}
if(debug) {
for(int i = ; i < maxBuf;
i++) {
Serial.print((int)raw[i]);
Serial.print("/");
}
Serial.print("///\nbuffer stats: zeroes:");
Serial.print(tot0);
Serial.print("/ones:");
Serial.print(tot1);
Serial.print("/prevs:");
Serial.print(totp);
Serial.print("/errs:");
Serial.println(tote);
delay();
} // start new buffer, reset all parameters
bufnum++;
curState = ;
index = ;
}
else {
delay();
}
} // process an input buffer with a valid start tag
// start argument is index to first 0 bit past prefix tag of 15+ ones
void process_buf(int start) {
// first convert multi bit codes (11111100000...) into manchester bit codes
int lastv = ;
int samecnt = ;
char manch[];
char final[];
int manchindex = ; Serial.println("got a valid prefix, processing data buffer...");
for(int i = start + ; i < maxBuf && manchindex < ; i++) {
if(raw[i] == lastv) {
samecnt++;
}
else {
// got a new bit value, process the last group
if(samecnt >= && samecnt <= ) {
manch[manchindex++] = lastv;
}
else if(samecnt >= && samecnt <= ) {
// assume a double bit, so record as two separate bits
manch[manchindex++] = lastv;
manch[manchindex++] = lastv;
}
else if(samecnt >= && lastv == ) {
Serial.println("got end tag");
// got an end tag, exit
break;
}
else {
// last bit group was either too long or too short
Serial.print("****got bad bit pattern in buffer, count: ");
Serial.print(samecnt);
Serial.print(", value: ");
Serial.println(lastv);
err_flash();
return;
}
samecnt = ;
lastv = raw[i];
} //new bit pattern
} Serial.println("converting manchester code to binary...");
// got manchester version, convert to final bits
for(int i = , findex = ; i < ; i += , findex++) {
if(manch[i] == && manch[i+] == ) {
final[findex] = ;
}
else if(manch[i] == && manch[i+] == ) {
final[findex] = ;
}
else {
// invalid manchester code, exit
Serial.println("****got invalid manchester code");
err_flash();
return;
}
} // convert bits 28 thru 28+16 into a 16 bit integer
int code = ;
int par = ;
for(int i = , k = ; i < +; i++, k--) {
code |= (int)final[i] << k;
}
int paritybit = final[+];
for(int i = ; i < ; i++) {
par ^= final[i];
} if(par) {
Serial.print("got valid code: ");
Serial.println((unsigned int)code);
// do something here with the detected code...
//
//
digitalWrite(redLED, );
digitalWrite(grnLED, );
delay();
digitalWrite(grnLED, );
digitalWrite(redLED, );
}
else {
Serial.println("****parity error for retrieved code");
err_flash();
}
} // flash red for duration seconds
void err_flash(int duration) {
return;
for(int i = ; i < duration*; i++) {
digitalWrite(redLED, );
delay();
digitalWrite(redLED, );
delay();
}
}
Status
The device and transceiver antenna have been built and tested on multiple FSK RFID tags of various kinds,
in breadboard and soldered perfboard versions, connected to remote and local probes.
When the probe is properly tuned, the device can reliably detect FSK RFID tags within a range of 0 to at least 2 inches from the coil,
although it may be possible that this can be extended with larger coil sizes and/or other optimizations.
The circuit has also been simulated on Spice, as described below.
As seen in the LTspiceIV screenshot above, the circuit (with a passive virtual ground reference - see note below)
was simulated on a computer, and the results confirmed the essential design,
closely replicating the waveforms actually seen on the oscilloscope.
The RFID transponder tag was simulated as a coupled transformer winding with a resonantly tuned capacitor,
shunted to ground by a square-wave signal.
The RFID tag's ground is connected to the main circuit's ground for simulation purposes.
The inductive coupling between the two "transformer windings" is a variable which can be changed in LTspice,
and was varied for testing between 1 and 0.01 (0.015 is shown in the waveforms above),
equivalent to having the RFID tag positioned at different distances from the reader coil.
Notes
The Vcc/2 virtual ground voltage for IC1's non-inverting input
can also be taken directly from the midpoint of the 100K voltage divider resistors,
bypassing the second opamp.
In such a case, the divider's midpoint should be connected to pin3 of IC1 via a 1M resistor.
Arduino RFID reader and pin current sourcing
http://electronics.stackexchange.com/questions/82374/arduino-rfid-reader-and-pin-current-sourcing
I'm thinking about building the DIY RFID reader described here
http://playground.arduino.cc/Main/DIYRFIDReader, with an arduino uno.
For the LC filter, I have a 330uH inductor at home
(and I'm using a 4.7nF capacitor for C1 instead of the 7nF shown on the schematic, to get at 125khz resonant frequency)
and also I'm using 1N4148 diodes instead of 1N914 (I couldn't find them on stock at the local electronics shop).
I did the simulation of the circuit in LT Spice (schematic is quite similar to what the original author has)
and it shows we that the current draw from the arduino pin D9 goes up to 400 mA,
from what I know the AVR can't source more than 50 mA per pin.
Here's the LTSpice circuit:
And the simulation result:
My question is: what will happen in reality when building the circuit,
will the pin source less current than the simulation and it will simply work
or I run the risk of damaging the pin by attempting to source that much current
(perhaps not instant damage, but long term) ?
If there is risk for damaging the pin, what solutions do I have to prevent it?
Would a simple current limiting resistor do?
Would it be better to use a push-pull configuration like the one below ?
Firstly, the 330uH inductor you already have is almost certainly not suitable
unless it is an air cored inductor wound on some kind of inert magnetic material
or an open ended ferrite rod of reasonably low permeability.
Check what it says on the circuit you linked to.
This coil has to transmit and receive so it needs to be likely a non-bought-in (and likely hand-wound) part.
D9 excessive current in simulation - you need to current limit the top circuit because it's driving a series resonant coil
and capacitor and this will act like a short circuit at resonance. Maybe try 100 ohms or a bit less.
In your alternative oscillator circuit you've got Q3 upside down in your simulation of the RFID transmitter -
that will cause excessive base currents through Q3 and may easily account for the excessive current from IO D9.
Your tag circuit might also benefit from a 100 ohm in series with the collector of Q1 too.
What does the waveform look like when the tag isn't responding - does it still show amplitude modulation issues?
You are right, the original circuit suggests a hand-wound part, I thought I could use the 330uH filter inductor ...
It's not clear to me exactly what the difference would be between the inductor I have and a hand-wound inductor.
Would a 220 ohm resistor in series with D9 (not shown in the original circuit) +
the hand-wound inductor (as described in the original circuit) work?
As for the alternative circuit, you are right about Q3 (I did the picture in a hurry, next time I'll pay more attention).
SIMPLEST RFID READER?
http://scanlime.org/2008/08/simplest-rfid-reader/
That’s a Propeller microcontroller board with a few resistors and capacitors on it.
Just add a coil of wire, and you have an RFID reader.
Here’s a picture of it scanning my corporate ID badge,
and displaying the badge’s 512 bits of content on a portable TV screen:
I’ve been interested in building an RFID reader for a crazy project a friend of mine is working on
(automated beer dispensing system and it was an excuse to do some analog tinkering,
so I had a go at building an RFID reader with the Propeller.
Why not use Parallax’s fine RFID reader accessory?
I wanted to be able to read any off-the-shelf card, including common proximity ID badges.
I just got it working. I’d love to be proved wrong,
but as far as I know this is the world’s simplest RFID reader design
All capacitances are in picofarads. C1 and C3 should be 1000 pF, not 1000 nF. Likewise, C2 is 2200 pF.
Yep.. just a propeller and a few passive components.
To understand this circuit, it’s helpful to know how RFID works first.
Here’s the 10-second RFID primer for those who aren’t already familiar with it:
RFID tags work via magnetic fields, like a transformer.
A coil in the reader generates a magnetic carrier wave, which is picked up by a coil in the tag or card.
This carrier wave powers the card, and provides it with a clock reference.
To send data, the card varies the amount of current it draws from the reader’s field,
attenuating the carrier wave slightly.
This attenuation is usually used to send some kind of modulated signal.
The card I’ve been testing with uses a 125 kHz carrier wave,
and FSK (Frequency Shift Keyed) modulation.
Zeroes and ones are attenuation waveforms with different frequencies.
So, step one: generating a carrier wave with the propeller.
I use a counter, naturally, and I generate a differential-drive signal on two pins.
This gives me 125 kHz at about 6.6V peak-to-peak.
I use an LC tank tuned near 125 kHz to amplify this and shape it into a sine wave.
Here’s my carrier wave, measured at the junction between L1 and C1.
Note the scale- it’s about 20 volts peak-to-peak! (Also notice that my tuning isn’t quite perfect. Sadness and despair!)
When you bring a proximity card near the reader coil, you can see the attenuation waveform overlaying the carrier wave.
This is at the same voltage scale, measured at the same point, but I zoomed out on the X axis so you can see the pattern clearly:
To detect this signal, I use D1, C2, and R1 as a peak detector and low-pass filter,
to remove the majority of the high-voltage carrier wave.
This is then AC coupled via C3.
At this point, the left side of C3 has a “floating” low-voltage waveform
that I can position anywhere I want, by changing the bias on C3.
To generate this bias voltage, I use CTRB in DUTY mode.
I’ll explain why I call this pin “threshold” later.
R4 and C4 are a low-pass filter give me an analog output voltage from CTRB.
This signal is usually DC- it’s used for calibration.
R3 “pulls” the floating waveform toward the analog value output by CTRB.
Now I take advantage of the Propeller’s Vdd/2 input threshold, and use the “input” pin as a comparator.
(R2 is just to limit high-frequency noise.)
Now I can use the CTRB voltage to adjust the detection threshold- how much attenuation causes “input” to read 0 vs 1.
Now I have a digital signal, but it’s still really noisy.
I use an assembly-language cog to reject as much noise as possible,
and time the resulting FSK pulses.
This image shows the analog signal at the junction between R2 and C3, along with the digital pulse detector’s output.
The amount of time between pulses signifies a “1″ or “0″ bit.
In this case, the detection threshold is at 9 carrier wave cycles.
Less than 9, it’s a 0. More than 9, it’s a 1 bit.
End result: I can display the 512 bits of data from my corporate proximity badge. Neat.
I’d include more details on the actual data I’ve captured and the protocol,
but so far I’ve only been able to test this with a couple ID badges from the office and I’d rather not share those bits
I have a Parallax RFID starter kit on order, so I’ll let you know if I can read any of those tags successfully.
Source code is attached, but be warned it’s hugely messy.
Update: The latest code and schematics are now have a home in the Object Exchange:
http://obex.parallax.com/object/549.
{ rfid-lf - Minimalist software-only reader for low-frequency RFID tags
───────────────────────────────────────────────────────────────────── I. Supported Tags Tested with the EM4102 compatible RFID tags sold by Parallax, and with
HID proximity cards typically issued for door security. These are two fairly typical ASK and FSK tags, respectively. To support
other RFID protocols, it should be possible to use one of these two
decoders as a starting point. I. Theory of operation The Propeller itself generates the kHz carrier wave, which excites
an LC tank. The inductor in this tank is an antenna coil, which becomes
electromagnetically coupled to the RFID tag when it comes into range.
This carrier powers the RFID tag. The RFID sends back data by modulating
the amplitude of this carrier. We detect this modulation via a peak
detector and low-pass filter (D1, C2, R1). The detected signal is AC
coupled via C3, and an adjustable bias is applied via R3 from a PWM signal
which is filtered by R4 and C4. This signal is fed directly to an input
pin via R2 (which limits high-frequency noise) where the pin itself acts
as an analog comparator. The waveform which exits the peak detector is a sequence of narrow spikes
at each oscillation of the carrier wave. Each of these spikes occurs when
C2 charges quickly via D1, then discharges slowly via R1. We can use this
to fashion a simple A/D converter by having the Propeller use a counter to
measure the duty cycle of this waveform. The higher the carrier amplitude,
the more charge was stored in C2, and the longer the pulse will be. At this point, we have a digital representation of the baseband RF signal
from the RFID tags. This signal is then put through a bank of demodulators,
filters, and protocol decoders. We include multiple closed-loop control systems in order to keep the reader
calibrated. We adjust the Threshold (THR) voltage dynamically in order to
keep the duty cycle at a reasonable level so we don't over-saturate our
42 A/D converter. We also automatically tweak the carrier frequency in order
43 to keep the LC tank operating at its resonant peak.
44
45 This object requires two cogs, mostly just because it needs three counter
46 units: One to generate the carrier, one to measure incoming pulses, and one
47 to generate the threshold voltage.
48
49 II. Schematic
50
51 (P5) IN º─┐ (P1) C+ ©──┐
52 │ │
53 R2 ¼ ¶ L1
54 R4 │ C3 D1 │
55 (P3) THR ©──½¾──┳──½¾──┻──«──┳──┳──¦§──┫
56 │ R3 │ │ │
57 C4¬® R1 ¼ ¬®C2 ¬®C1
58 │
59 │
60 (P0) C- ©──┘
61
62 C1 1.5 nF
63 C2 2.2 nF
64 C3 1 nF
65 C4 2.2 nF
66 R1 1 MΩ
67 R2 270 Ω
68 R4 100 kΩ
69 R3 220 kΩ
70 D1 Some garden variety sigal diode from my junk drawer.
71 L1 About 1 mH. Tune for 125 kHz resonance for C1.
72
73 Optional parts:
74
75 - An amplifier for the carrier wave. Ideally it would be a very high
76 slew rate op-amp or buffer which could convert the 3.3v signal from
77 the Prop into a higher-voltage square wave for exciting the LC tank.
78
79 I've tried a MAX233A, but it was unsuccessful due to the low slew rate
which caused excessive harmonic distortion. The best option is probably
a high voltage H-bridge, but I haven't tested this yet.
82
83 - An external comparator for the input value. This makes the circuit
84 easier to debug, and it helps if you're in a noisy electrical environment. Hook the inverting input up to a voltage divider that generates a Vdd/
reference, and the non-inverting input up to the junction between C3 and R3.
I've tested this with a MAX473 op-amp.
89
90 III. License
91
92 ┌───────────────────────────────────┐
93 │ Copyright (c) 2008 Micah Dowty │
94 │ See end of file for terms of use. │
95 └───────────────────────────────────┘
96
97 }
98
99 CON
100 CARRIER_HZ = 125_000 ' Initial carrier frequency
DEFAULT_THRESHOLD = $ ' Initial comparator frequency
102 THRESHOLD_GAIN = 7 ' Log2 gain for threshold control loop
CARRIER_RATE = ' Inverse Log2 rate for carrier frequency control loop
104 CARRIER_GAIN = 300 ' Gain for carrier frequency control loop SHIELD2_PIN = ' Optional, driven to ground to shield INPUT.
107 INPUT_PIN = 5 ' Input signal
SHIELD1_PIN = ' Optional, driven to ground to shield INPUT.
109 THRESHOLD_PIN = 3 ' PWM output for detection threshold
DEBUG_PIN = ' Optional, for oscilloscope debugging
111 CARRIER_POS_PIN = 1 ' Carrier wave pin
CARRIER_NEG_PIN = ' Carrier wave pin
113
114 ' Code formats.
' The low 16 bits indicate length, in longs.
116 ' Other bits are used to make each format unique. FORMAT_EM4102 = $0001_0002 ' Two longs: 8-bit manufacturer code, 32-bit unique ID.
119 FORMAT_HID = $0002_0002 ' HID KHz prox cards. -bit code. VAR
byte cog1, cog2
long format_buf
long shared_pulse_len
long em_buffer[]
long hid_buffer[] PUB start | period, okay ' Fundamental timing parameters: Default carrier wave
131 ' drive frequency, and the period of the carrier in clock
' cycles.
133
134 init_frqa := fraction(CARRIER_HZ, clkfreq)
135 period := clkfreq / CARRIER_HZ
136
137 ' Derived timing parameters pulse_target := period / ' What is our 'center' pulse width?
140 next_hyst := pulse_target
141 hyst_constant := period / 100 ' Amount of pulse hysteresis ' Output buffers
144 em_buf_ptr := @em_buffer
145 hid_buf_ptr := @hid_buffer
146 format_ptr := @format_buf
147 pulse_ptr1 := @shared_pulse_len
148 pulse_ptr2 := @shared_pulse_len
149 format_buf~
150
151 cog1 := cognew(@cog1_entry, 0) + 1
152 okay := cog2 := cognew(@cog2_entry, 0) + 1
153
154
155 PUB stop
156 if cog2
157 cogstop(cog1~ - 1)
158 cogstop(cog2~ - 1)
159
160 PUB read(buffer) : format
161 '' Read an RFID code, if one is available.
162 ''
163 '' If a code is available, it is copied into 'buffer', and we return
164 '' a FORMAT_* constant that identifies the code's format. If no code
'' is available, returns zero.
''
'' The format code's low 16 bits indicate the length of the received
168 '' RFID code, in longs. The other format bits are used to uniquely
169 '' identify each format.
170 ''
171 '' The buffer must be at least 16 longs, to hold the largest code format.
172
173 format := format_buf
174
175 if format == FORMAT_EM4102
176 longmove(buffer, @em_buffer, constant(FORMAT_EM4102 & $FFFF))
177
178 if format == FORMAT_HID
179 longmove(buffer, @hid_buffer, constant(FORMAT_HID & $FFFF))
180
181 format_buf~
182
183 PRI fraction(a, b) : f
184 a <<= 1
185 repeat 32
186 f <<= 1
187 if a => b
188 a -= b
189 f++
190 a <<= 1
191
192 DAT
193
194 '==============================================================================
' Cog 1: Pulse timing
196 '============================================================================== org cog1_entry '
203 ' Measure a low pulse.
'
205 ' The length of this pulse is proportional to the amplitude of the carrier.
' To measure it robustly, we'll measure the average duty cycle rather than
' looking at actual rising or falling edges.
208 '
' We could easily do this in cog2, but we're out of counters there. Measure
' the pulse length using this cog's CTRA, and send it back to cog2 synchronously.
'
212
213 mov ctra, :ctra_value
214 mov frqa, #1
215
216 :loop mov t0, phsa
217 wrlong t0, pulse_ptr1
218 :wait rdlong t0, pulse_ptr1 wz
219 if_z jmp #:loop
220 jmp #:wait
221
222 :ctra_value long (%01100 << 26) | INPUT_PIN
223 pulse_ptr1 long 0
224 t0 res 1
225
226 fit
227
228
229 DAT
230
231 '==============================================================================
' Cog 2: Control loops and protocol decoding
233 '============================================================================== '======================================================
236 ' Initialization
'======================================================
238
239 org
240
241 cog2_entry mov dira, init_dira
242 mov ctra, init_ctra ' CTRA generates the carrier wave
mov frqa, init_frqa
mov ctrb, init_ctrb ' CTRB generates a pulse threshold bias
245 mov frqb, init_frqb
246
247
248 '======================================================
' Main A/D loop
250 '====================================================== mainLoop
'
254 ' Synchronize each loop iteration with a rising edge on
' the carrier wave. To avoid races when reading pulse_ptr,
256 ' we should ideally synchronize at a point degrees
' out of phase with the negative pulse from our analog peak
258 ' detector.
'
260
261 mov prev_pulse, cur_pulse ' Remember previous pulse info :high test h80000000, phsa wz
if_z jmp #:high
:low test h80000000, phsa wz
if_nz jmp #:low rdlong cur_pulse, pulse_ptr2 ' Fetch pulse count from cog1
269 wrlong zero, pulse_ptr2
270
271 mov pulse_len, cur_pulse ' Measure length of pulse
sub pulse_len, prev_pulse add pulse_count, # ' Global pulse counter, used below
275
276 '
' Adjust the comparator threshold in order to achieve our pulse_target,
278 ' using a linear proportional control loop.
'
280
281 mov r0, pulse_target
282 sub r0, pulse_len
283 shl r0, #THRESHOLD_GAIN
284 sub frqb, r0
285
286 '
' We also want to dynamically tweak the carrier frequency, in order
288 ' to hit the resonance of our LC tank as closely as possible. The
' value of frqb is actually a filtered representation of our overall
290 ' inverse carrier amplitude, so we want to adjust frqa in order to
' minimize frqb.
292 '
' Since we can't adjust frqa drastically while the RFID reader is
' operating, we'll make one small adjustment at a time, and decide
' whether or not it was an improvement. This process eventually converges
296 ' on the correct resonant frequency, so it should be enough to keep our
' circuit tuned as the analog components fluctuate slightly in value
298 ' due to temperature variations.
'
300 ' This algorithm is divided into four phases, sequenced using two
' bits from the pulse_count counter:
302 '
' 0. Store a reference frqb value, and increase frqa
304 ' . Test the frqb value. If it wasn't an improvement, decrease frqa
305 ' . Store a reference frqb value, and decrease frqa
' 3. Test the frqb value. If it wasn't an improvement, increase frqa
'
308
309 test pulse_count, carrier_mask wz
310 if_nz jmp #:skip_frqa
311 test pulse_count, carrier_bit0 wz
312 test pulse_count, carrier_bit1 wc
313 negc r0, #CARRIER_GAIN
314 if_nz mov prev_frqb, frqb
315 if_nz add frqa, r0
316 if_z cmp prev_frqb, frqb wc
317 if_z_and_c sub frqa, r0
318 :skip_frqa
319
320 '
' That takes care of all our automatic calibration tasks.. now to
322 ' receive some actual bits. Since our pulse length is proportional
' to the amount of carrier attenuation, our demodulated bits (or
324 ' baseband FSK signal) are determined by the amount of pulse width
' excursion from our center position.
326 '
' We don't need to measure the center, since we're actively balancing
328 ' our pulses around pulse_target. A simple bit detector would just
' compare pulse_len to pulse_target. We go one step further, and
330 ' include a little hysteresis.
'
332
333 cmp next_hyst, pulse_len wc
334 muxc outa, #|<DEBUG_PIN ' Output demodulated bit to debug pin mov next_hyst, pulse_target ' Update hysteresis for the next bit
337 sumc next_hyst, hyst_constant
338
339 if_nc add baseband_s32, #1
340 if_nc add baseband_s256, #1
341 rcl baseband_reg+0, #1 wc ' Store in our baseband shift register
if_nc sub baseband_s32, # ' ... and keep a running total of the bits.
343 rcl baseband_reg+1, #1 wc
344 rcl baseband_reg+2, #1 wc
345 rcl baseband_reg+3, #1 wc
346 rcl baseband_reg+4, #1 wc
347 rcl baseband_reg+5, #1 wc
348 rcl baseband_reg+6, #1 wc
349 rcl baseband_reg+7, #1 wc
350 if_nc sub baseband_s256, #1
351
352 '
' Our work here is done. Give each card-specific protocol decoder
354 ' a chance to find actual ones and zeroes.
'
356
357 call #rx_hid
358 call #rx_em4102
359
360 jmp #mainLoop
361
362
363 '======================================================
' EM4102 Decoder
365 '====================================================== ' The EM4102 chip actually supports multiple clock rates
368 ' and multiple encoding schemes: Manchester, Biphase, and PSK.
' Their "standard" scheme, and the one Parallax uses, is
370 ' ASK with Manchester encoding at clocks per code (
' clocks per bit). Our support for this format is hardcoded.
372 '
' The EM4102's data packet consists of payload bits (an -bit
' manufacturer ID and 32-bit serial number), 9 header bits, 1 stop
375 ' bit, and parity bits. This is a total of bits. These bits
' are manchester encoded into 128 baseband bits.
377 '
' We could decode this the traditional way- do clock/data recovery
379 ' on the Manchester signal using a DPLL, look for the header, do
' manchester decoding on the rest of the packet, etc. But this is
381 ' software, and we can throw memory and CPU at the problem in order
' to get a more noise-resistant decoding.
383 '
' A packet in its entirety is 4096 clocks. This is 128 manchester
385 ' codes by clocks per code. We can treat this as possible phases
' and 128 possible code alignments. In fact, it's a more convenient
' to treat it as 64 possible phases and 64 possible bits. We get the
388 ' same result, and it's less data to move around.
389 '
' To save memory, we'll decimate the signal and examine it only every
' other carrier cycle. This gives us only 32 possible phases.
392 '
' Every time a code arrives, we shift it into a 64-bit shift register.
394 ' We have total shift registers, which we cycle through. Every time
' we shift in a new bit, we examine the entire shift register and test
396 ' whether it's a valid packet.
397
398 rx_em4102
399 test pulse_count, #1 wz ' Decimate x2 (Opposite phase from the HID decoder)
if_nz jmp #rx_em4102_ret ' Low pass filter with automatic gain control. Look at an average of the last
403 ' bits, and correct our duty cycle to % by picking a threshold based on
' the average of the last 256 bits.
405
406 mov r0, baseband_s256
407 shr r0, #3
408 cmp baseband_s32, r0 wc
409
410 :shift1 rcl em_bits+1, #1 wc ' Shift in the new filtered bit
:shift2 rcl em_bits+, #
:load1 mov em_shift+, em_bits+ ' And save a copy in a static location
413 :load2 mov em_shift+1, em_bits+1
414
415 add :shift1, dest_2 ' Increment em_bits pointers
add :shift2, dest_2
add :load1, #
add :load2, #
cmp :shift1, em_shift1_end wz
if_z sub :shift1, dest_64 ' Wrap around
421 if_z sub :shift2, dest_64
422 if_z sub :load1, #64
423 if_z sub :load2, #64
424
425 rdlong r0, format_ptr wz ' Make sure the output buffer is available
if_nz jmp #rx_em4102_ret '
429 ' At this point, the encoded packet should have the following format:
' (Even bits in the manchester code are not shown.)
431 '
' bits+0: 11111111_1ddddPdd_ddPddddP_ddddPddd
433 ' bits+: dPddddPd_dddPdddd_PddddPdd_ddPPPPP0
'
435 ' Where 'd' is a data bit and 'P' is a parity bit.
'
437
438 mov r0, em_shift+0 ' Look for the header of nine "" bits
shr r0, #-
cmp r0, #$1FF wz
if_nz jmp #rx_em4102_ret rcr em_shift+, # nr,wc ' Look for a footer of one "0"
444 if_c jmp #rx_em4102_ret
445
446 ' Looking good so far. Now loop over the data rows... mov em_decoded, #
mov em_decoded+, #
mov em_parity, #
mov r0, #
:row
mov r2, em_shift+ ' Extract the next row's bits
shr r2, #
and r2, #% wc ' Check row parity
456 if_c jmp #rx_em4102_ret
457
458 mov r1, em_decoded+1 ' -bit left shift by
shl em_decoded+, #
shl em_decoded+, #
shr r1, #-
or em_decoded+, r1 shr r2, # ' Drop row parity bit
465 xor em_parity, r2 ' Update column parity
or em_decoded+, r2 ' Store 4 decoded bits
467
468 mov r1, em_shift+1 ' -bit left shift by
shl em_shift+, #
shl em_shift+, #
shr r1, #-
or em_shift+, r1 djnz r0, #:row mov r2, em_shift+ ' Extract the next 4 bits
477 shr r2, #19
478 and r2, #%1111
479 xor em_parity, r2 wc ' Test column parity
if_c jmp #rx_em4102_ret mov r0, em_buf_ptr ' Write the result
483 wrlong em_decoded+0, em_buf_ptr
484 add r0, #4
485 wrlong em_decoded+1, r0
486 wrlong c_format_em, format_ptr
487
488 rx_em4102_ret ret
489
490 em_shift1_end rcl em_bits+1+64, #1 wc ' This is ":shift1" above, after we've
491 ' passed the end of the em_bits array. '======================================================
495 ' HID Decoder
'======================================================
497
498 ' This is a data decoder for HID's 125 kHz access control cards.
499 ' I don't have any actual documentation from HID- this is all
500 ' gleaned from public documentation of other RFID systems, and
' from my own reverse engineering.
502 '
' These cards use a FSK scheme, which appears to be identical
504 ' to the one used by the Microchip MCRF200. Zeroes and ones are
' nominally encoded by attenuation pulses of 8 and 10 cycles,
506 ' respectively. Bits appear to last RF cycles, which is one
' of the configurable bit lengths for the MCRF200.
508 '
' See Figure 2-2 of the Microchip microID 125 kHz RFID System Design Guide:
510 ' http://ww1.microchip.com/downloads/en/devicedoc/51115F.pdf
'
512 ' So, to decode this signal we use a similar low-pass filter and shift
' register technique as the one we used above in the EM4102 decoder,
514 ' but only after doing an FSK detection stage.
'
516 ' After FSK detection and low-pass filtering, the signal appears to
' be a repeating pattern of 96 of these 50-cycle bits. Each repetition
518 ' begins with the special sequence ''. The packet data is manchester
' encoded, so runs of three zeroes or ones cannot occur during the packet body.
520 ' This means there should be bits of actual packet data after manchester
' decoding.
522 '
' XXX: There is almost certainly another level of encoding present,
524 ' since HID claims that these cards have either a -bit or -bit
' code. It's likely that there is an error correcting/detecting code
' embedded within the 45-bit signal we're receiving here. rx_hid
' This is the FSK decoder. We shift zeroes into hid_fsk_reg on every
530 ' carrier wave cycle. When we detect a stable rising edge on the baseband,
' the whole hid_fsk_reg is inverted. At this point, the most recent FSK
532 ' cycle will be all ones, and the previous cycle will be all zeroes.
'
534 ' On each stable rising edge, we can detect a or a by tapping the
' hid_fsk_reg. The location of the tap determines our threshold frequency.
536 ' This detected bit is latched until the next rising edge. shl hid_fsk_reg, # ' Advance the FSK detection shifter
539
540 mov r0, baseband_reg ' Detect rising edges
and r0, #%
cmp r0, #% wz ' If Z=1, it's an edge. if_z xor hid_fsk_reg, hFFFFFFFF ' Invert on every edge
545
546 if_nz rcr hid_lp_reg+0, #1 nr,wc ' Repeat the last bit, or tap a new bit.
if_z test hid_fsk_reg, hid_fsk_mask wc ' Now the detected FSK bit is in the carry flag. Feed it into
550 ' a -bit-wide low pass filter, which is close enough to our
' bit length of 50 cycles. We correct the 32-bit filter's DC bias
' using a 256-bit filter. This helps us maintain an even duty cycle
553 ' in the received manchester bits. if_nc add hid_lp_s32, #
if_nc add hid_lp_s256, #
rcl hid_lp_reg+, # wc
if_nc sub hid_lp_s32, #
rcl hid_lp_reg+, # wc
rcl hid_lp_reg+, # wc
rcl hid_lp_reg+, # wc
rcl hid_lp_reg+, # wc
rcl hid_lp_reg+, # wc
rcl hid_lp_reg+, # wc
rcl hid_lp_reg+, # wc
if_nc sub hid_lp_s256, # mov r0, hid_lp_s256
shr r0, #
cmp hid_lp_s32, r0 wc ' Now the carry flag holds a filtered version of the post-FSK-detection
573 ' signal. These are manchester-encoded bits. We need to detect the header,
' validate the manchester encoding, and extract the actual data bits
575 ' from the received signal. This is a typical data/clock recovery problem.
' We could use a PLL, but when doing this in software it's easier and less
' error-prone to just brute-force it, and try to detect a signal at all
578 ' possible phases.
'
580 ' At this point there are possible phases, and we need enough room to
' store 51 bits. (45 data bits + 3 bits each from 2 headers) We'll use a
' 64-bit shift register for each phase.
583 '
' To cut down on the amount of memory required, we'll decimate the signal
' by processing on only every other carrier cycle. This means we only have
586 ' possible phases. test pulse_count, # wz ' Decimate x2 (Opposite phase from the EM4102 decoder)
589 if_z jmp #rx_hid_ret
590
591 :shift1 rcl hid_bits+1, #1 wc ' Shift in the new filtered bit
:shift2 rcl hid_bits+, #
:load1 mov hid_shift+, hid_bits+ ' And save a copy in a static location
594 :load2 mov hid_shift+1, hid_bits+1
595
596 add :shift1, dest_2 ' Increment hid_bits pointers
add :shift2, dest_2
add :load1, #
add :load2, #
cmp :shift1, hid_shift1_end wz
if_z sub :shift1, dest_100 ' Wrap around
602 if_z sub :shift2, dest_100
603 if_z sub :load1, #100
604 if_z sub :load2, #100
605
606 ' Also save a -degree phase shifted version of hid_shift,
' for doing manchester validation and header detection.
608 ' The bits in hid_shiftp are delayed by / bit relative to
' hid_shift.
610
611 :load3 mov hid_shiftp+0, hid_bits+0+50
612 :load4 mov hid_shiftp+1, hid_bits+1+50
613 add :load3, #2
614 add :load4, #2
615 cmp :load3, hid_load3_end wz
616 if_z sub :load3, #100
617 if_z sub :load4, #100
618
619 or dira, #|<7
620 test hid_shift+1, #1 wz
621 muxz outa, #|<7
622
623 rdlong r0, format_ptr wz ' Make sure the output buffer is available
if_nz jmp #rx_hid_ret '
627 ' Ok. At this point, we can validate the contents of hid_shift and
' hid_shiftp, and extract the packet data if possible. hid_shiftp
629 ' has our actual packet data, and em_shift has the complement of that
' data. The header sequence of "000111" turns into "001" in hid_shift
631 ' and "" in hid_shiftp.
'
633 ' To provide further validation, we look for both the current packet's
634 ' header and the next packet's header. So, the data bits are actually
635 ' in bits through .
'
637
638 mov r0, hid_shiftp+0 ' Check 1st header at -°
and r0, hid_s_mask
cmp r0, hid_sp_check wz
if_z mov r0, hid_shift+ ' Check 1st header at 0°
642 if_z and r0, hid_s_mask
643 if_z cmp r0, hid_s_check wz
644
645 if_z mov r0, hid_shiftp+1 ' Check 2nd header at -°
if_z and r0, #%
if_z cmp r0, #% wz
if_z mov r0, hid_shift+ ' Check 2nd header at 0°
649 if_z and r0, #%111
650 if_z cmp r0, #%011 wz
651
652 if_z mov r0, hid_shiftp+0 ' Check Manchester encoding
if_z xor r0, hid_shift+ ' (high long)
654 if_z xor r0, hFFFFFFFF
655 if_z and r0, hid_data_mask wz
656 if_z mov r0, hid_shiftp+1 ' (low long)
if_z xor r0, hid_shift+
if_z xor r0, hFFFFFFFF
if_z andn r0, #% wz if_nz jmp #rx_hid_ret ' Exit on any decoding error
662
663 ' Hooray, we have a correct-looking packet. Justify and
' trim the 45 bits of data, and store it.
665
666
667 mov r0, hid_shiftp+0
668 mov r1, hid_shiftp+1
669
670 and r0, hid_data_mask ' Cut off 1st header and unused bits shr r0, # wc ' Cut off 2nd header
673 rcr r1, #1 wc
674 shr r0, #1 wc
675 rcr r1, #1 wc
676 shr r0, #1 wc
677 rcr r1, #1 wc
678
679 mov r2, hid_buf_ptr ' Write the result
wrlong r0, r2
add r2, #
wrlong r1, r2
wrlong c_format_hid, format_ptr rx_hid_ret ret hid_shift1_end rcl hid_bits++, # wc ' This is ":shift1" above, after we've
' passed the end of the hid_bits array.
689
690 hid_load3_end mov hid_shiftP+0, hid_bits+0+100
691
692
693 '------------------------------------------------------------------------------
' Initialized Data
695 '------------------------------------------------------------------------------ hFFFFFFFF long $FFFFFFFF
h80000000 long $
h7FFF0000 long $7FFF0000
hFFFF long $FFFF
zero long
c_format_em long FORMAT_EM4102
c_format_hid long FORMAT_HID dest_2 long <<
dest_100 long <<
dest_64 long <<
input_mask2 long |<INPUT_PIN init_dira long |<CARRIER_POS_PIN | |<CARRIER_NEG_PIN | |<THRESHOLD_PIN | |<DEBUG_PIN | |<SHIELD1_PIN | |<SHIELD2_PIN
init_frqa long
init_frqb long DEFAULT_THRESHOLD
init_ctra long (% << ) | (CARRIER_POS_PIN << ) | CARRIER_NEG_PIN
init_ctrb long (% << ) | THRESHOLD_PIN carrier_mask long |<CARRIER_RATE -
carrier_bit0 long |<(CARRIER_RATE + )
carrier_bit1 long |<(CARRIER_RATE + ) pulse_target long
next_hyst long
hyst_constant long baseband_reg long ,,,,,,,
baseband_s32 long ' Number of zeroes in last 32 baseband bits
726 baseband_s256 long 256 ' Number of zeroes in last baseband bits em_buf_ptr long
hid_buf_ptr long
format_ptr long
pulse_ptr2 long hid_header long |< -
hid_max_bits long
hid_fsk_mask long |< hid_lp_reg long ,,,,,,,
hid_lp_s32 long ' Number of zeroes in last 32 FSK bits
739 hid_lp_s256 long 256 ' Number of zeroes in last FSK bits hid_s_mask long % << ( + - )
hid_s_check long % << ( + - )
hid_sp_check long % << ( + - )
hid_data_mask long ( << ( + - )) - '------------------------------------------------------------------------------
748 ' Uninitialized Data
'------------------------------------------------------------------------------
750
751 r0 res 1
752 r1 res 1
753 r2 res 1
754
755 cur_pulse res 1
756 prev_pulse res 1
757 pulse_len res 1
758
759 pulse_count res 1
760 prev_frqb res 1
761
762 hid_bit_len res 1
763 hid_bit_count res 1
764 hid_fsk_reg res 1
765 hid_reg res 1
766
767 em_bits res 64 ' -bit shift register for each of the phases
em_shift res ' Just the current shift register
769 em_decoded res 2 ' Decoded -bit EM4102 packet
em_parity res ' Column parity accumulator
771
772 hid_bits res 100 ' -bit shift register for each of the phases
hid_shift res ' Just the current shift register
774 hid_shiftp res 2 ' degrees out of phase with hid_shift
hid_decoded res ' Decoded 45-bit HID packet
776
777 fit
125 kHz RFID Reader
http://www.changpuak.ch/electronics/RFID-READER-125kHz.php
A small circuit to read RFID badges operating at 125 kHz (EM4102)
This circuit is used here : Coffee Tally Sheet • Kaffee-Strichliste
Reading RFID-badges operating at 125 kHz is very easy. You just have to put a carrier (125 kHz) into the air and have the badge do the work.
The badge will do the ASK (Amplitude Shift Keying) and you only have to monitor the envelope of the carrier.
This can be done by a simple diode rectifier.
The carrier is generated by a 74HC4060 and a 4 MHz crystal.
Using "Q4" delivers the desired 125 kHz signal.
The AM Demodulator is build of a AA113 (for historic reasons) and R5, C5.
The demodulator is followed by a limiting amplifier.
The voltage across the coil is about 14 Vpp.
The envelope of the carrier, when a tag / badge is brought close to the coil ...
ProxClone - Proximity Card Reader / Cloner
http://proxclone.com/reader_cloner.html
Reader / Cloner Overview
The picture below is of my prototype combination card reader and cloner.
The unit is self contained and does not require the use of a PC or other external equipment to operate.
Operation is simple and straightforward.
Simply hold a card near the antenna and the unit reads and decodes the information from the card.
The information is then formatted and displayed on a 4x20 character LCD.
If the operator wishes to make a copy of the card he simply brings a T5557/5567 Read/Write card near the antenna
and presses the "write" button.
The LED flashes and in less than a second the R/W card has been programmed with the information that was read from the original.
Voila !! - A clone card.
The cost to build the device was minimal (approx. $30) including the LCD display and circuit board.
The design fit on a single sided circuit board that I etched myself.
The PWB was made to be the same size as the LCD so that they could be plugged together as a single assembly.
A detailed description of my design concept is included below.
Background
I initially began this activity by trying to build a simple card reader that could be used
to obtain all of the information that was transmitted by the card during a simple read operation.
Most commercial card readers do not output all of the data that is read.
Information such as the header and card format are never transmitted as part of the readers normal output stream.
Knowing this information is critical for being able to replicate a cards operation.
As a result, I set out to build my own custom reader.
The Design
A document that I found to be invaluable during my learning process was
Microchip's 125 Khz RFID Sysem Design Guide which can be found on their website.
Their FSK reference design circuit was basically what I used for my design.
I made a couple of small modifications to simplify the design and to allow the use of a Parallax SX28 microcontroller instead of the PIC.
A photo of my initial "reader-only" design (without write capability) is shown below:
After studying the datasheet for the T5557/5567 IC (used in many vendors access cards),
I soon realized that the reader circuit would only have to be modified slightly in order to also be able to function as an RFID writer.
To function as a writer the design simply needed to be able to modulate the 125Khz RF carrier using On/Off Keying (OOK) modulation
since this is how the T55x7 chips are programmed.
I modified the design to accomplish this by basically giving the microcontroller the ability to control the 125 Khz divide counter reset signal.
An extra push button was also added to an unused GPIO input on the microcontroller.
A schematic of my reader circuit (modified to become a writer) is shown below:
The reader/writer circuit design can be broken down into four main components.
1) The clocking circuit, which generates a 4 Mhz clock for the microcontroller and a 125Khz carrier signal for the RFID interface.
2) The RF front end consisting of a tuned LC resonator and an AM peak detector
3) A series of low pass and band pass filters to extract the 12.5Khz and 15.6Khz FSK signals.
4) The SX28 microcontroller which performs the following functions:
- LCD initialization
- Decoding and storage of the FSK data from the op amp filter output.
- Parsing and formatting of the card data.
- Driving the LCD display.
- Programming the clone card by modulating the 125 Khz carrier (per the T55x7 datasheet).
My updated "write-capable" version of the Reader/Writer assembly along with a commercial 44780 4x20 LCD
and 125Khz loop antenna is shown in the photo below.
The completed unit was installed onto a piece of acryllic plastic in lieu of trying to find an off-the-shelf plastic box
that everything would fit in and still look halfway decent.
I have tested the unit with numerous card types including 26-bit, 34-bit, 36-bit, 37-bit and the 35-bit Corporate 1000 formats.
The cloner was able to duplicate all of them without any difficulty.
In all cases, the vendors own commercially available readers were unable to distinguish between the original and the clone cards.
Final Design Project -
RFID Proximity Security System
https://instruct1.cit.cornell.edu/Courses/ee476/FinalProjects/s2006/cjr37/Website/index.htm
Introduction and Motivations:
For our final project, we designed and built (and exhaustively tested) an RFID-based proximity security system
for use with Cornell Identification cards, which have been RFID-embedded since fall of 2003.
The idea for this project was sort of spawned from our general interest in RFID technologies
and the near-simultaneous occurance of Lab 2 (Keypad Security System)
and the antiquated lock system at our fraternity house breaking.
At the highest level, our device uses an antenna coil to power the RFID tag embedded in our Cornell ID's
and read the induced response from the card.
This response is then filtered and manipulated into useful data and interpreted by the Atmel Mega32 microcontroller
which runs the actual security program. In addition to interactions with the ID cards, the system is in contact
with an administrator computer via a serial communications link and hyperterm.
The security system can store up to 20 45-bit codes which are derived from communications with each unique RFID tag.
If a card is read and it is not in the code database, a red LED flashes for 3 seconds.
Likewise, if the code can be found in the database, a green LED lights for 3 seconds.
From hyperterm, the administrator has the power to add codes, delete codes, list all codes,
"unlock" the door (the equivalent of the green LED flashing),
and initialize routines which allow codes to be added to the database by gathering data from the reader itself.
Educational topics explored in this lab include (but are not limited to)
passive filter design, active filter design, amplification circuits, RF antenna design, digital logic, serial communications, RFID theory,
pin interrupts, timer interrupts, and soldering.
In short, for this project we used elements of basically every introductory level ECE course we have taken.
Since we are dealing with such a complicated topic, on the hardware side of things we tried to rely as much as we could on proven circuit designs.
This would enable us to focus more on getting our system working well as a whole rather than spending countless hours debugging small parts of our project.
For this, the Microchip® microID 125 KHz Reference Guide (see citations section) proved to be an invaluable resource for both theory and results.
High Level Hardware Design:
Before we start with actual circuit design, it is neccessary to understand the principals behind the technology
that this project has set out to harness; passive RFID communications.
Passive RFID tags work in such a way that they are actually powered by an external signal,
which, in most cases is the carrier signal from the tag reader circuit.
These tags are fairly simple and are comprised of merely an L-C antenna (similar to the one shown in the block diagram below)
and the circuitry neccessary to modulate this carrier signal once powered on.
The reader and tag communicate using magnetic coupling since their respective antennas can sense changes in magnetic field,
which is observed as a change in voltage in the reader circuit.
The Cornell ID cards we use in this project were developed by HID®; specifically the HID DuoProx II cards.
These are useful because they have both embedded RFID as will as a magnetic strip, while much of campus is starting
to switch over to proximity entry systems, many current systems (including the dining halls) are still swipe-operated.
From looking at their website, it was difficult to determine much information about the card's operation,
asside from the fact that it operates at a 125 KHz carrier frequency and it could have a tag size anywhere between 26 and 128 bits long.
After many hours of research we discovered that the modulation type used in the cards is Frequency Shift Keying (FSK),
one of the more common ways used in RFID.
FSK modulates the signal by essencially multiplying a lower amplitude, lower frequency signal with the carrier signal, creating an AM-like effect;
the lower frequency enveloping the carrier frequency.
To switch between a "1" and a "0", the tag switches the modulating frequency.
The two frequencies used by our cards were 12.5 KHz (125 KHz/10) and 15.625 KHz (125 KHz/8), which correspond to 1 and 0 respectively.
The modulation produces an effect that looks similar to the figure below:
Figure 1: Simulation of FSK Modulation With Modulation Frequencies of 12.5 and 15.625 KHz and Carrier Frequency of 125 KHz
The job of the reader circuit is to provide the 125 KHz carrier frequency, transmit that to the tag, and detect the magnetic coupling from the tag,
which should look like the figure above.
In order to interpret this data, the carrier frequency must be removed, and the enveloping frequencies must be magnified into something measureable.
The block diagram/flow chart for our reader circuit can be found in the figure below:
Although each individual part of the circuit and program will be described in detail later,
the general idea for circuit operation is as such:
The Mega32 provides a timer-driven 125 KHz square wave for our carrier frequency.
This is then sent through an RF choke, which is essentially a passive low-pass filter with steep drop-off
to knock out the upper harmonics and leave us with only a sine wave.
The sine wave is then amplified using an emmitter follower PNP transistor and a half bridge to maximize current.
Since our resonant circuit is a series L-C circuit, maximum resonance is achieved at minimum impedance,
so it is very important that we provide adequate current amplification as to not overdrive our microcontroller.
To help reduce the strain (and ramp up the current more) further,
the square wave output from the MCU is put through parallel inverters.
On the recieving end, the signal is first half-wave rectified, since the negative part of the signal doesn't really make a difference,
and is then fed through a half-wave R-C filter to help knock out most of the 125 KHz carrier and detect the envelope signal.
This signal is then bandpass filtered using a series a Twin-T active bandpass filters,
and lowpass filtered with an active Butterworth filter to further decrease gain in frequencies outside of the 10-20 KHz area
and increase gain of the envelope signals such that it saturates the op-amps of the filters.
As a final stage the signal is put through a comparator and resistive divider to produce a nice square wave at logic levels.
Some D-flip flops and a decade counter are used to extract data from the modulating square waves.
Which are fed into the MCU and processed.
Hardware and Software Tradeoffs:
There are many ways to design a proximity card reader in terms of tradeoffs between hardware and software.
In most cases, software is cheaper because you don't need to purchase any parts but at the same time you are costing the MCU processing time.
Using more hardware will obviously increase the cost of the design but ultimately may alleviate painfully tedious optimizations
that would have been necessary had you used code to replace a component or device.
One of the first tradeoffs we encountered was whether to use the Mega32 or a separate counter to generate the 125 kHz carrier frequency.
The microID 125 kHz RFID System Design Guide suggested using a 14-stage binary counter to divide the clock from the crystal to 125 kHz.
However, since the Mega32 has built-in hardware timers that can output to one of the pins, there was no need to use a counter.
Another tradeoff we encountered was whether to use DSP or hardware to analyze the signal on the antenna.
Recall that this signal is the carrier signal and the magnetically coupled response from the card superimposed onto each other.
Using DSP, we could sample at the Nyquist frequency and compute the FFT of the signal
to find what frequencies are present in the response and from there decode the response.
If we were to use DSP, we would have to sample at greater than 250 kHz
meaning there would only be 64 cycles between samples to compute the FFT.
This imposed a huge constraint on the rest of our security system so we decided to implement the most of the decoding in hardware.
Specific Circuit Elements:
Transmit Stage: RF Choke and Power Amplifier:
The circuit of Figure 3 below is an RF choke followed by a current buffer and half-bridge amplifier.
The RF choke is used to filter out most, if not all of the upper harmonic frequencies found in the square wave output from the MCU,
leaving the fundamental frequency, 125 KHz, as a sine wave to be amplified.
The square wave generator seen in the figure below is, in actuality, the output from the MCU and a set of inverters to ramp up the current.
Diodes are used in the half bridge to help reduce crossover distortion caused from differing points of either transistor in the half bridge turning on and off.
In our design we used the 2N3904 and 2N3906 NPN and PNP BJT transistors from the lab since they were cheap and convenient.
In order to get better amplifier gain, and thus increase read range of our circuit, we could have used power MOSFETS instead for the half-bridge,
but we found the BJT's gave us a mostly acceptable level of gain, especially once the circuit was tuned.
Resonant Antenna Circuit:
While this portion of our circuit is only comprised of two components, it is also arguably the most important hardware element;
if it performs poorly then our security system performs poorly.
Because this design was recommended for proximity solutions from the Microchip® guide,
we decided to go with a series L-C resonant curcuit as opposed to one where the resistor and inductive antenna were in parallel.
Because of this, at maximum resonance we also observe maximum current.
In order to determine values for the inductance and capacitance needed, we used the equation:
where f0 is the resonant frequency (in Hertz), L is inductance (in Henries) and C is capacitance (in Farads).
Since f = 125 KHz and we had plenty of 1 nF ceramic capacitors in the lab, we settled on an inductance of 1.62 mH
To construct an antenna with the neccessary inductance we used coils of laquered copper wire, since it works well and is fairly compact.
In our final construction revision, we used a rectangular-shaped antenna coil since it fit well with the design.
The figure above shows the coil as circular, which is what we used for most of our preliminary testing
before we actually put the unit together.
Both antennas operated roughly the same as each other, although the rectangular coiled one resonates more.
Inductance for the rectangular coil is determined by the following equation:
where L is in microHenries, x and y are the width/length of the coil (in cm), h is the height of the coil (in cm),
b is the width across the conducting part of the coil (in cm) and N is the number of turns.
In our case, x=3.6cm, y=13.8 cm, h=1 cm, and we estimated b=.3 cm.
Using the equation, we calculated the coil to need approximately 90 turns.
It turned out this was a pretty good estimate.
After constructing the coil, we proceeded to tune it by removing coils until we saw the highest resonant voltage from our carrier frequency,
which was at roughly 88 turns.
Oscilloscope results from this circuit, with both just the carrier frequency, and with the modulated signal from the RFID tag can be seen below.
Half-Wave Rectify and Filtering:
This portion of the circuit is devoted to separating out the carrier frequency from the modulating envelope,
since its really only the envelope that has the data we care about.
The first stage is half-wave rectifying the signal to make things simpler and then filtering it slightly with an R-C filter.
As is the norm for filtering AC signals in this manner there is some 125 KHz ripple,
but choosing good values we could make the enveloping frequencies stand out from the ripple.
For this we chose R=390 KOhms and C= 2.2 nF.
Scope readings are shown in the figure below.
Note that the peaks are the ripple, and the whole signal seems to oscillate at 15.625 KHz.
You can tell this because there are 8 125 KHz ripple peaks per oscillation of the envelope.
At a 12.5 KHz envelope, there would be 10 ripples per oscillation.
Once signal leaves this stage, it passes through a capacitor to knock out the DC offset and into the next set of filters;
a pair of active Twin-T filters and an active Butterworth filter with the TL084 OpAmp as the gain element.
The circuit diagram for this is in the figure below:
Figure 8: Circuit Diagram for the Filter Stage. Signal Comes in From the Right and Exits out the Butterworth on the Left
As can be seen from the Bode Plot in the figure below, the first filter mostly isolates the pass band (10-20 KHz),
with roughly unity gain for all frequencies outside the pass band.
The second filter further accentuates gain in the pass-band while slightly reducing the magnitude of frequencies outside the pass band.
After this, the signal goes through a massive Butterworth Low-Pass filter to drastically increase gain of lower frequencies already in the pass band
and virtually eliminate the higher frequencies, including the 125 KHz carrier signal.
Once out of the filters, the signal is then put through a TL084-based comparator and a resistive divider to generate a nice square wave at logic levels.
The 12.5 KHz and 15.625 KHz frequencies come out of the filters beautifully.
When no card is present, the system reports a 28.75 KHz wave
which represents the highest frequency to come out of the filters with enough gain to saturate the opamps.
Figure 10: Protoboard With Our Filter Circuit (left) and 28.75 KHz idle Frequency From the Filter Output
Figure 11: Envelope Frequencies After Filtering and Reduction to Logic Levels. 12.5 KHz is on Left While 15.625 KHz is on Right
Data Creation Stage:
Technically, from the output of the comparator we should have been able to read and interpret data from the card using a timer interrupt.
We quickly realized, however, that by doing this we would cripple the functionality of our system.
In order to accurately measure the frequency of the incoming data stream we would realistically need to sample at 125 KHz,
which means that, with a clock rate of 16 MHz we would have 128 clock cycles to compute everything before the next sampling interrupt fired.
This would have been extremely difficult to implement.
Looking for an alternate method to obtain data, we found a brilliant design in the Microchip reference guide,
which makes use of flip-flops and a decade (Johnson) counter. This circuit can be seen in the figure below:
The way this works is as follows:
The comparator output serves as the clock for the first D flip-flop, which also takes logic 1 as its D value.
On the rising edge of the comparator clock, Q is immediately set to 1.
However, simulataneously ~Q goes low and clears the flip-flop.
This creates an extremely short pulse which serves as a reset for the decade counter and clock for the second flip-flop.
The decade counter is just a 1-hot counter which takes a 125 KHz clock.
With every rising edge of this clock, the counter outputs the next pin to logic 1;
so typical output would look like (if one were looking at output pins 0-9 of the counter) 1000000000 0100000000 00100000000 etc.
However, this counter is being reset with every rising edge of the comparator output.
Thus, since we've already determined that 125 KHz/10 = 12.5 KHz is to be our frequency that represents logical 1,
all we have to do is check for the output on pin9 to know whether or not we see that frequency.
If the system is operating at either one of the other possible frequencies, the counter will be reset before pin9 can go active.
The pin9 output serves as input to the second flip-flop and also to the clock inhibitor, which keeps the 9th pin high until the counter is reset.
Because of this set-up, the Q output of the second flip-flop will remain logical 1 so long as modulating frequency is 12.5 KHz
and will drop down to 0 if its anything else.
Theoretically, this circuit should work perfectly.
However, experimentally it did not, and thus required a small modification.
The 100 KOhm resistor on the first flip-flop serves to lengthen the time it takes for the ~Q signal to get to CLEAR.
Since all transistors have some amount of natural capacitance, this forms an RC circuit of sorts with a set RC time constant for the signal to rise or fall.
As it turns out, this time was too short for the decade counter.
The original design from the reference guide specified only a 10KOhm resistor between ~Q and CLEAR.
With the 10 KOhm resitor, pulse widths for the reset pulse were a mere 50 ns long, while the counter required at least 250 ns.
This caused some very eratic behaviour.
After many hours of debugging, we finally pinpointed the problem and replaced the resistor with the 100 KOhm resistor
which increased the pulse width long enough for the counter to correctly operate.
The figures below show the behaviour of the circuit when operating correctly:
Figure 13: Comparator Output with Reset Pulse (left). Comparator Output with Data Output (right)
Figure 14: A Close-Up of our Reset Pulse Reveals That it is now 350 ns; 100 ns Over the Minimum for the Decade Counter
Software Design and Program Details:
Initialize:
This function initializes the various interrupts, timers, input-output,
and global variables utilized in the program. The following are all initialized or setup here:
- Timer2 is used to toggle OC2 to generate a 125 kHz square wave.
- Port A is the output of the digital counter/flip-flop circuit.
- Port C is the LED output.
- Timer0 is used to count milliseconds for keeping track of time
and controlling the timing on scheduled tasks and timer0 interrupt is enabled.- Enable the transmitter and receiver in the USART and set the baud rate to 9600.
- Initialize flags, counts, buffers, and other state variables.
- External interrupt 2 is enabled and set to trigger on the rising edge.
- Set interrupt bit.
- Hold and prompt for the date and time before entering the while(1) loop.
USART Receive Interrupt
This interrupt handles any typed characters received from the terminal through the RS232 connection
and stores it into a buffer.It also echoes the character to the terminal. Once a carriage return is detected, the receive-ready flag is set indicating
that a line command has been entered by the user via the terminal.
USART Transmit Interrupt
This interrupt handles transmitting characters to the terminal. It simply loops through the transmit buffer until the last character is sent and then is ready for another transmission.
Timer0 Compare Match Interrupt
This interrupt serves as a timer to control the scheduling of different timers.
It decrements the timers for timing how long the door is unlocked, timing how long a second lasts, and for checking the receive-ready flag.
External Interrupt2
This interrupt samples the data (output of the counter/flip-flop circuit). It is triggered by the rising edge of the output of the comparator. The output of the comparator is approximately a 29 kHz square wave if there is no modulation, and a 15.625 kHz or 12.5 kHz square wave if there is modulation. When the output of the comparator is 15.625 kHz the data is 0 and when the output is 12.5 kHz the data is 1. The point of this interrupt is to detect how many bits are in a 0 or 1 pulse of the data. Thus every time this interrupt fires, we increment a counter and add the sample to an array.
Main Method
In main, there are 3 major modes of operation.
The mode of operation at startup is normal;
however the modes can be changed through the terminal.
In normal mode, the reader waits for External Interrupt2 to finish reading a response from the card.
It does this in about a thousand executions of the interrupt.
When enough bits have been sampled, we turn off the External Interrupt2.
When first analyzing the response from the card, we noticed periodicity every 540 bits.
Thus, to guarantee that our sample window captures a full continuous 540 bit cycle,
we sampled 1080 bits before turning off the interrupt.
An example of a 1080 bit response looked something like this:
001111100000011111000000111111000000000000111111111100000011111000000000000111111000000111110000001111100000011111100000 11111100000011111111110000000000001111110000011111100000011111111110000000000001111110000011111111111000000111110000001111
00000000000011111100000011111111110000000000001111110000011111100000000000000000011111111111111110000011111100000011111000000 1111100000011111100000111111000000111110000001111111111000000000000111111111110000001111100000000000011111100000111111000000
111110000001111111111000000111111000001111110000001111100000011111000000111111000000000000111111111100000011111000000000000
1111110000001111100000011111000000111111000001111110000001111111111000000000000111111000001111110000001111111111000000000000
11111100000111111111110000001111100000011111000000000000111111000000111111111100000000000011111100000111111000000000000000000 11111111111111110000011111100000011111000000111110000001111110000011111100000011111000000111111111100000000000011111111111000000
1111100000000000011111100000111111000000111110000001111111111000000111111000001111110000There is a long sequence of 1's and 0's that stand out;
we used these as references to identify the start and end of the 540 bit response.
To extract the 540 bit response we wrote a function to detect a sequence of 15 to 18 1's.
This function loops until it finds the start sequence and stores it in a global variable and calculates the end sequence.
There is also something noticeable about the 540 bit sequence.
There are 1's and 0's in groups of 5, 6, 10, 11, or 12 excluding the start and stop sequence.
Since 10, 11, and 12 can be made from combinations of 5 and 6,
we hypothesized that maybe these longer groups are combinations of two groups of 5 or 6.
With this in mind, we wondered whether a group of 5 or 6 bits possibly represents a single bit.
This would make sense because the card cannot perfectly transition from one modulated frequency to another without some transition.
Thus a group of 10, 11, or 12 represents two bits.
Thus the reduced (90 bit) version of the 540 bit response excluding the start and stop sequence is extracted
by detecting sequences of bits and replacing them with a single or a double bit:
010101010101011001101001010101101010101010011010010101010101100101011001011010100101100101
From this code, it is fairly obvious that the reduced sequence is encoded in Manchester code.
If you split up the 90 bit response into pairs of bits, there are transitions within each pair:
01 01 01 01 01 01 01 10 01 10 10 01 01 01 01 10 10 10 10 10 10 01 10 10 01 01 01 01 01 01 10 01 01 01 10 01 01 10 10 10 01 01 10 01 01
A transition from low to high corresponds to a 1 and a transition from high to low corresponds to a 0.
Since two bits correspond to a single bit, the Manchester decoded response has half the bandwidth and is thus only 45 bits long:
111111101001111000000100111111011101100011011
We believe this code is the raw data stored on the card.
We have not been able to decode this further to find it's relation to Cornell student ID number
but it is not necessary since this number is unique to each card.
After decoding the initial 540 bit response to this 45 bit code, we store this data and sampled again.
To prevent false reads, we keep sampling until we successfully read 3 consecutive identical codes.
If we get 3 consecutive identical codes, we compare this code to the code bank (where all the authorized codes are stored).
The code bank is stored in EEPROM due to limited space in SRAM.
If the 3 consecutive identical codes match a code in the code bank, a green LED is lit for 3 seconds to signify that the door is unlocked.
If the code does not match the code bank, a red LED is lit to signify that the door is not opened and that the code is not authorized.
A statement is also printed to the administrative terminal providing the card code,
whether the card was accepted, and the time at which the event occurred.
The other mode is called remote operation.
In this mode, the admin has a choice of remotely adding a code to a specific code bank position
or remotely adding any number of codes (bound between 1 and 20 inclusive).
When adding a code to a specific code bank position, we turn on External Interrupt2 and read the card response.
Just like in normal mode, we find the start code, reduce the sequence, and Manchester decode the sequence.
We do this until we read 5 consecutive identical codes and then store it into the specified position in the code bank.
When adding a specific quantity of codes, we first search through the status of the code bank and find the first unused position.
Then we go into remote add by position mode and we add the code at the first unused position.
We do this until either the specified quantity of codes are stored or until the code bank is full.
After either of these modes finishes executing, the reader goes back to its normal mode,
but now with the new stored codes in the code bank.
The end of the main loop serves as a scheduler that checks the timers for certain tasks and executes the task.
It executes the function to check the receive-ready flag, turns off the LED's after 3 seconds,
and executes the counter that keeps track of time and date.
Check Receive Ready
The last major part of the security system is the administrative interface through the terminal. This function checks to se if a command has been received from the admin. It checks the command and executes the corresponding actions. The commands that are implemented are:
- a <pos> <code> – add a code
- l – list all codes
- d <pos> – delete a code
- u – unlock door
- rp <pos> – remote add (positional)
- ra <amt> – remote add (amount)
Helper Functions
The rest of the functions are simple, self-explanatory, helper functions that accomplish simple tasks such as keeping track of the time and date, storing codes into the code bank, verifying that a code matches a code in the code bank, comparing two codes, copying a code, and completing or starting a receive or transmit using the USART.
Results of Design:
Certain aspects of the proximity security system performed equal to what we initially expected at the start of the project.
The maximum range of the proximity card reader is about 1.5 to 2.0 inches from the antenna coil.
According to the microID 125 kHz RFID System Design Guide, different dimensions of reader and tag coils will affect the maximum read distance.
For example, a 3 x 6 inch reader antenna and a 0.5 inch diameter card antenna will have a maximum read distance of about 1.5 inches.
For a 1.0 inch diameter card antenna the read distance increases to 4 inches.
Since we do not know the exact dimensions of the antenna inside of our Cornell University identification cards,
we do not know whether we are performing below or above ideal expectations,
however, the current read range is sufficient for our purposes.
The approximate read time for most cards is about 1 to 3 seconds once the card is within the maximum read distance.
The latency is due to the redundant code check.
Once the card is in range and we start receiving data, we read the data 3 consecutive times.
If the code ever differs, then we try to read another 3 consecutive times.
Thus, if the card is near the maximum range or has poor modulation consistency then it will take longer to read.
The proximity security system is fairly accurate as we expected.
There are little to no false positives, although sometimes there are false negatives.
If the card is held around the maximum range, we occasionally receive incorrect data.
However, if the card is within an inch of the coil, the data can be read with virtually no errors.
Although important, safety was a minor concern when designing a proximity security system.
Since there is no contact between the user and the reader, there is no danger of harming the individual through direct contact.
The 125 kHz signal transmitted from the reader is also harmless but may cause interference with other devices operating at the same frequency.
From our time in lab however, we noticed almost no interference from other people's designs.
Our goal for this project was to build a security system that used our current Cornell University identification cards.
Thus by completing that goal, we believe we have built a fully operational concept/prototype of an ideal proximity security system for our fraternity house.
This project is very usable by anyone wanting a hands-free front door security system while utilizing a card that is necessary to have with you anyway.
/* Authors: Ricardo Goto and Craig Ross
2 Project: RFID Security system
3 */ #include <Mega32.h>
#include <stdio.h>
#include <delay.h> /* Define Constants */
#define NUM_BITS 1080
#define NUM_CODES 20
#define RED 0b11111110
#define GREEN 0b11111101
#define OFF 0b11111111
#define CHECK_RECEIVE_TIME 50
#define NORMAL 0
#define REMOTEPOS 1
#define REMOTEAMT 2 /* Define input pin from RFID circuit */
#define RFIDIN PINA.0
#define LED PORTC /* Prototypes */
void initialize(void);
void gets_str(void);
void puts_str(void);
void find_start_code(void);
void reduce_sequence(void);
void manchester_decode(void);
unsigned char match_code(unsigned char *char1,unsigned char *char2);
void copy_code(unsigned char *char1,unsigned char *char2);
unsigned char verify_code(unsigned char char1[]);
void check_receive(void);
void time_counter(void);
void store_into_bank(char position, char code[], char offset); /* Global Variables */
char curr_sample;
char code_count;
char start_flag;
int totalBits;
char bit_array[NUM_BITS+];
int sample_buffer;
int start_sequence;
int end_sequence;
char reduced_array[];
char final_code[];
char code_check[];
char check_receive_timer;
int door_timer;
char mode;
signed char add_pos;
char del_pos;
char add_amt;
unsigned int seconds;
unsigned int minutes;
unsigned int hours;
unsigned int days;
unsigned char r_days[];
unsigned char r_hours[];
unsigned char r_minutes[];
char no_code[];
eeprom char code_bank[NUM_CODES][];
char temp[];
eeprom char bank_status[NUM_CODES];
char r_index, r_buffer[], r_ready, r_char;
char t_index, t_buffer[], t_ready, t_char; /* USART recieve interrupt */
interrupt[USART_RXC] void uartr(void)
{
r_char = UDR;
UDR = r_char;
if (r_char == )
{
if(r_index != ) r_index--;
}
else if (r_char != '\r') r_buffer[r_index++] = r_char;
else
{
r_buffer[r_index] = 0x00;
r_ready = ;
UCSRB. = ;
putchar('\n');
}
} /* USART transmit interrupt */
interrupt[USART_DRE] void uartt(void)
{
t_char = t_buffer[++t_index];
if (t_char == )
{
UCSRB. = ;
t_ready = ;
}
else UDR = t_char;
} /* Timer0 compare match ISR */
interrupt [TIM0_COMP] void timer0_compare(void)
{
/* Decrement timers every millisecond */
if(check_receive_timer > ) --check_receive_timer;
if(door_timer > ) door_timer--;
if (seconds > ) --seconds;
} /* External pin interrupt */
interrupt[EXT_INT2] void int2(void)
{
/* In this interrupt, we want to sample the output of the
115 second DFF just after the rising edge of the output of
116 the comparator stage. This will tell us how the length
117 of the pulse corresponds to the number of bits. We only
118 start caring about the data when it first goes high,
119 however, the card will probably be close to the
120 maximum reading distance. Therefore, we wait until the
121 card gets closer (we assume the person is bringing the
122 card to about 1-2 inches from the reader coil) and then
123 we start to sample. Once we have enough samples to
124 capture a full period of the looping response, we stop
125 sampling.
126 */
delay_us();
curr_sample= RFIDIN;
if((start_flag == ) && (curr_sample == ))
{
start_flag= ;
sample_buffer++;
}
else if((start_flag == ) && (sample_buffer <= )) sample_buffer++;
else if((start_flag == ) && (totalBits < NUM_BITS))
{
if(curr_sample == ) bit_array[totalBits++]= ;
else bit_array[totalBits++]= ;
}
} void main(void)
{
char i;
initialize();
while()
{
if((totalBits == NUM_BITS) && (door_timer == ) && (mode == NORMAL))
{
/* Normal Operation:
152 When a full period of the looping card response is
153 captured, we want to decode the response. We execute
154 the following steps until we get 3 identical codes
155 in a row.
156 - turn off the external pin interrupt since we are
157 not going to be reading anything in this window of
158 time
159 - look for a start code and take only data
160 - reduce the bit sequence to 90 bits
161 - manchester decode to 45 bits
162 If we get 3 identical codes, then we compare that code
163 with the code bank to see if the code is currently
164 allowed access to the facility. If so, we blink a green
165 LED (open the door) for 3 seconds. If not, then we
166 blink a red LED (do not open the door) which also lasts
167 for 3 seconds.
168 */
GICR= 0b00000000;
find_start_code();
reduce_sequence();
manchester_decode();
if(code_count == )
{
copy_code(code_check,final_code);
code_count= ;
}
else if(code_count == )
{
if(match_code(code_check,final_code)) code_count= ;
else code_count= ;
}
else if(code_count == )
{
if(match_code(code_check,final_code))
{
if(verify_code(final_code))
{
sprintf(t_buffer,"\r\nCard ID: %s\r\nAccess Granted at %03d:%02d:%02d\r\n\r\n",final_code,days,hours,minutes);
puts_str();
while(t_ready == ){};
LED= GREEN;
door_timer= ;
}
else
{
sprintf(t_buffer,"\r\nCard ID: %s\r\nAccess Denied at %03d:%02d:%02d\r\n\r\n",final_code,days,hours,minutes);
puts_str();
while(t_ready == ){};
LED= RED;
door_timer= ;
}
}
code_count= ;
}
/* Make sure to reset the interrupt variables
207 and turn on the external interrupt.
208 */
totalBits= ;
start_flag= ;
sample_buffer= ;
GICR= 0b00100000;
}
else if(mode == REMOTEAMT)
{
/* Remote Quantity Add Operation:
217 If we are in this mode of operation, this segment of code
218 makes it so that we loop in Remote Positional Add until
219 the amount of codes have been added. Our algorithm searches
220 the code bank status to find open positions and uses Remote
221 Positional Add to add the card ID.
222 */
add_pos= -;
for(i= ; i<NUM_CODES; i++)
{
if(bank_status[i]== )
{
add_pos= i;
bank_status[i]= ;
break;
}
}
if(add_pos != -)
{
mode= REMOTEPOS;
printf("Please place card in front of reader\r\n");
delay_ms();
LED= GREEN & RED;
delay_ms();
LED= OFF;
delay_ms();
LED= GREEN & RED;
delay_ms();
LED= OFF;
add_amt--;
}
else
{
printf("Code bank is full, please delete unwanted codes and re-run\r\n");
add_amt= ;
mode= NORMAL;
}
}
else if((totalBits == NUM_BITS) && (mode == REMOTEPOS))
{
/* Remote Positional Add Operation:
257 Similar to normal mode except we are storing the
258 next card into the code bank. To prevent a bad code
259 from getting into the code bank, we add even more
260 error protection by having to read the same code 5
261 times in a row before it is accepted into the code
262 bank.
263 */
GICR= 0b00000000;
find_start_code();
reduce_sequence();
manchester_decode();
if(code_count < )
{
if(code_count == )
{
copy_code(code_check,final_code);
code_count++;
}
else
{
if(match_code(code_check,final_code)) code_count++;
else code_count= ;
}
}
if(code_count == )
{
store_into_bank(add_pos,final_code,);
sprintf(t_buffer,"Code added to position %d\r\n",add_pos);
puts_str();
LED= GREEN;
delay_ms();
LED= RED;
delay_ms();
LED= GREEN & RED;
delay_ms();
LED= OFF;
code_count= ;
if(add_amt > )mode= REMOTEAMT;
else mode= NORMAL;
}
totalBits= ;
start_flag= ;
sample_buffer= ;
GICR= 0b00100000;
} /* Check timers for timed tasks */
if(check_receive_timer == ) check_receive();
if((door_timer == ) && ((LED == GREEN) || (LED == RED))) LED= OFF;
if(seconds == ) time_counter();
}
} /* Function: Check Receive
311 This function checks the r_ready flag every 50 milliseconds. If
312 there is a requested command in the r_buffer then we process the
313 command and execute the corresponding code.
314 */
void check_receive(void)
{
char i,j;
check_receive_timer= CHECK_RECEIVE_TIME;
if(r_ready == )
{
/* Add input code at specific position */
if(r_buffer[] == 'a')
{
if(r_buffer[] == ' ')
{
add_pos= r_buffer[]-;
if((add_pos >= ) && (add_pos <= ))
{
store_into_bank(add_pos,r_buffer,);
bank_status[add_pos]= ;
printf("Code added at position %d\r\n",add_pos);
}
else printf("Invalid code position\r\n");
}
else if(r_buffer[] == ' ')
{
add_pos= (r_buffer[]-)*+(r_buffer[]-);
if((add_pos >=) && (add_pos < NUM_CODES))
{
store_into_bank(add_pos,r_buffer,);
bank_status[add_pos]= ;
printf("Code added at position %d\r\n",add_pos);
}
else printf("Invalid code position\r\n");
}
else printf("Incorrect syntax\r\n");
}
/* List all codes and corresponding status */
else if(r_buffer[] == 'l')
{
putsf("Code Bank:\r\n");
for(i= ; i < NUM_CODES; i++)
{
for(j= ; j < ; j++)
{
temp[j]= code_bank[i][j];
}
printf("%02d: %d - %s\r\n",i,bank_status[i],temp);
}
}
/* Delete code at specific position */
else if(r_buffer[] == 'd')
{
if(r_buffer[] == '\0')
{
del_pos= r_buffer[]-;
if((del_pos >= ) && (del_pos <= ))
{
store_into_bank(del_pos,no_code,);
bank_status[del_pos]= ;
printf("Code deleted at position %d\r\n",del_pos);
}
else printf("Invalid code position\r\n");
}
else if(r_buffer[] == '\0')
{
del_pos= (r_buffer[]-)*+(r_buffer[]-);
if((del_pos >=) && (del_pos < NUM_CODES))
{
store_into_bank(del_pos,no_code,);
bank_status[del_pos]= ;
printf("Code deleted at position %d\r\n",del_pos);
}
else printf("Invalid code position\r\n");
}
else printf("Incorrect syntax\r\n");
}
/* Unlock door for 3 seconds */
else if(r_buffer[] == 'u')
{
LED= GREEN;
door_timer= ;
}
/* Enable remote positional add mode */
else if((r_buffer[] == 'r') && (r_buffer[] == 'p'))
{ if(r_buffer[] == '\0')
{
add_pos= r_buffer[]-;
if((add_pos >= ) && (add_pos <= ))
{
bank_status[add_pos]= ;
printf("Please place card in front of reader\r\n");
LED= GREEN & RED;
delay_ms();
LED= OFF;
delay_ms();
LED= GREEN & RED;
delay_ms();
LED= OFF;
mode= REMOTEPOS;
}
else printf("Invalid code position\r\n");
}
else if(r_buffer[] == '\0')
{
add_pos= (r_buffer[]-)*+(r_buffer[]-);
if((add_pos >=) && (add_pos < NUM_CODES))
{
bank_status[add_pos]= ;
printf("Please place card in front of reader\r\n");
LED= GREEN & RED;
delay_ms();
LED= OFF;
delay_ms();
LED= GREEN & RED;
delay_ms();
LED= OFF;
mode= REMOTEPOS;
}
else printf("Invalid code position\r\n");
}
else printf("Incorrect syntax\r\n");
}
/* Enable remote quantity add mode */
else if((r_buffer[] == 'r') && (r_buffer[] == 'a'))
{
if(r_buffer[] == '\0')
{
add_amt= r_buffer[]-;
if((add_amt >= ) && (add_amt <= )) mode= REMOTEAMT;
else printf("Invalid add quantity\r\n");
}
else if(r_buffer[] == '\0')
{
add_amt= (r_buffer[]-)*+(r_buffer[]-);
if((add_amt >=) && (add_amt <= NUM_CODES)) mode= REMOTEAMT;
else printf("Invalid add quantity\r\n");
}
else printf("Incorrect syntax\r\n");
}
/* Help */
else if(r_buffer[] == 'h')
{
putsf("Commands:\r");
putsf("a - add a code - syntax: a <pos> <code>\r");
putsf("l - list all codes - syntax: l\r");
putsf("d - delete a code - syntax: d <pos>\r");
putsf("u - unlock door - syntax: u\r");
putsf("rp - remote add (positional) - syntax: rp <pos>\r");
putsf("ra - remote add (amount) - syntax: ra <amt>\r");
}
/* Unrecognized commands */
else printf("Unrecognized Command\r\n");
gets_str();
}
} /* Function: Time Counter
471 This function keeps track of time for reporting to
472 the hyperterm.
473 */
void time_counter(void)
{
seconds= ;
if(minutes < ) minutes++;
else
{
minutes= ;
if(hours < ) hours++;
else
{
hours= ;
if(days < ) days++;
else days= ;
}
}
} /* Function: Store Into Bank
492 Stores a code into code bank at a specific position starting with
493 a specific offset of code.
494 */
void store_into_bank(char position, char code[], char offset)
{
unsigned char i;
for(i= ; i < ; i++) code_bank[position][i]= code[i+offset];
} /* Function: Verify Code
502 This function takes a code for an argument and checks
503 to see if the code matches with any of the codes in
504 the code bank. Returns a 1 if there is a match and a 0
505 otherwise.
506 */
unsigned char verify_code(unsigned char char1[])
{
unsigned char i= ;
unsigned char j= ;
unsigned char code_match= ;
unsigned char element_match;
while((code_match == ) && (i < NUM_CODES))
{
if(bank_status[i]==)
{
element_match= ;
while((element_match == ) && (j <))
{
if(code_bank[i][j] != char1[j]) element_match= ;
j++;
}
code_match= element_match;
}
i++;
}
return code_match;
} /* Function: Match Code
531 This function compares two codes by comparing the
532 elements of each array. Returns a 1 if there if
533 there is a match and a 0 otherwise.
534 */
unsigned char match_code(unsigned char char1[], unsigned char char2[])
{
char i= ;
char correct= ;
while((correct == ) && (i < ))
{
if(char1[i] != char2[i]) correct= ;
i++;
}
return correct;
} /* Function: Copy Code
548 Copies a code from source char2 to destination char1.
549 */
void copy_code(unsigned char char1[], unsigned char char2[])
{
unsigned char i;
for(i= ; i < ; i++) char1[i]= char2[i];
} /* Function: Find Start Code
557 To find the start code, we must look for a sequence of 15 to 18 1's.
558 We run through the bit stream received from the card and look for 1's.
559 Anytime there is a 0, we reset the counter. Eventually the counter
560 will get to 15 or more and then we have detected the start sequence.
561 Since we know there are 540 bits in a period of the card response, we
562 know the end of the sequence is 539 bits later.
563 */
void find_start_code(void)
{
int i= ;
char sequence= ;
char count= ;
start_sequence= ;
end_sequence= ;
while((i < totalBits))
{
if(bit_array[i] == )
{
sequence= ;
count++;
}
else
{
if((sequence == ) && (count >= ))
{
start_sequence= i-count;
end_sequence= start_sequence+;
i= totalBits+;
}
sequence= ;
count= ;
}
i++;
}
if (i == totalBits) start_sequence= -;
} /* Function: Reduce Sequence
595 Once we have the 540 bit card response, we need to remove
596 the redundancy. The code looks something similar to:
597 00000111111000000111110000000000001111110000011111...
598 We recognize 5 to 6 bits as a single bit and a stream of
599 10, 11, or 12 bits is two bits. Thus the code above would
600 decode to 010100101. There are 90 bits in the reduced form.
601 This function transforms the redundant bit stream to the
602 reduced form by detecting sequences of 5/6 and 10/11/12
603 and creating a new array.
604 */
void reduce_sequence(void)
{
int i;
signed char j= -;
unsigned char count;
unsigned char value;
i= start_sequence;
while(i <= end_sequence)
{
count= ;
value= bit_array[i];
if(j == )
{
reduced_array[j]= value;
i= end_sequence+;
}
else
{
while(value == bit_array[i])
{
count++;
i++;
}
if(j == -) j++;
else if((count == ) || (count ==)) reduced_array[j++]= value;
else if((count == ) || (count == ) || (count == ))
{
reduced_array[j++]= value;
reduced_array[j++]= value;
}
}
}
}
/* Function: Manchester Decoder
639 This function translates the reduced bit stream that
640 is Manchester coded to raw data. This final bit stream
641 is the code we used to identify different cards.
642 */
void manchester_decode(void)
{
unsigned char i;
unsigned char j= ;
for(i= ; i< ; i+=)
{
if((reduced_array[i] == ) && (reduced_array[i+] == )) final_code[j++]= ;
else final_code[j++]= ;
}
} void initialize(void)
{
char i;
/* PORTD.7 generates the 125kHz square wave using timer2
658 output compare.
659 - PORTD is set for output
660 - Waveform Generation Mode is set to "CTC"
661 - Compare Output Mode is set to "Toggle 0C2 on compare match"
662 - Output Compare Register is set to 63
663 - Clock Select is set to "No prescaling"
664 Timer2 will increment at clock speed (16 MHz) to 63 and then reset.
665 Once it reaches this value, it will toggle OC2 to generate the 0V-5V
666 125kHz square wave.
667 */
DDRD= 0xff;
TCCR2= 0b00011001;
OCR2= ; /* RFIDIN is PORTA.0 */
DDRA= 0x00; /* Enable LED port as output */
DDRC= 0xff;
LED= OFF; /* Setup Timer0
680 - Turn on timer0 ISR
681 - Waveform Generation Mode is set to "CTC"
682 - Output Compare Register is set to 249
683 - Clock Select is set to "Clock/64"
684 - Initialize the counter to 0
685 Timer0 interrupt occurs every millisecond.
686 */
TIMSK= 0b00000010;
TCCR0= 0b00001011;
OCR0= ;
TCNT0= ; /* Setup the USART
694 - Enable the Transmitter and Receiver
695 - Set the Baud Rate to 9600Hz
696 */
UCSRB = 0x18;
UBRRL = ; /* Initialize variables */
start_flag= ;
curr_sample= ;
totalBits= ;
sample_buffer= ;
code_count= ;
check_receive_timer= CHECK_RECEIVE_TIME;
mode= NORMAL; /* Initialize Code Bank Status */ for(i= ; i < NUM_CODES; i++)
{
if (bank_status[i] == "") bank_status[i]= ;
} /* Enable Interrupts */
#asm
sei
#endasm /* External pin interrupt
722 - Enable in GICR
723 - Set for rising edge
724 - Clear interrupt flag
725 - PORTB as input
726 */
GICR= 0b00100000;
MCUCSR= 0b01000000;
DDRB.= ; /* Send a starting message to hypertrm */
putsf("\r\nInitializing the Reader...\r\n");
r_ready = ;
t_ready = ;
gets_str(); /* Initialize the data and time by prompting admin */
putsf("Please enter time in the format: DDD:HH:MM\r\n"); while(r_ready == ){};
r_days[] = r_buffer[];
r_days[] = r_buffer[];
r_days[] = r_buffer[];
r_days[] = '\0';
r_hours[] = r_buffer[];
r_hours[] = r_buffer[];
r_hours[] = '\0';
r_minutes[] = r_buffer[];
r_minutes[] = r_buffer[];
r_minutes[] = '\0';
sscanf(r_hours,"%d",&hours);
sscanf(r_minutes,"%d",&minutes);
sscanf(r_days,"%d",&days);
gets_str();
seconds = ;
sprintf(t_buffer,"Current Date and Time: %03d:%02d:%02d\r\n",days,hours,minutes);
puts_str();
while(t_ready == ); putsf("Please type h for help\r");
} /* Complete receiving from hypertrm */
void gets_str(void)
{
r_ready = ;
r_index = ;
UCSRB. = ;
} /* Begin transmitting to hypertrm */
void puts_str(void)
{
t_ready = ;
t_index = ;
putchar(t_buffer[]);
UCSRB. = ;
}
RFIDler - An open source Software Defined RFID Reader/Writer/Emulator
http://adamsblog.aperturelabs.com/2013/08/rfidler-open-source-software-defined.html
I've said it before and I'll say it again:
I don't understand how it works.
Not only that, but I don't want to understand, and I don't need to understand!
Well, that's not quite true - I need to understand enough to know
which bits I don't need to understand, but then that's it! Stop! Enough already!!!
RFID is, as with a lot of these technologies, mysterious by nature.
It relies on strange physical phenomena like "induction" and "electro-magnetism" and "near-fields", etc.
Yes, what we Code Monkeys like to call "Magic Moonbeams".
It's all very nasty and analoguey. I don't like it. Give me the nice binary digital, please!
So in my never ending quest to find tools that convert the scary analogue world into a nice friendly digital one,
RFID is clearly a prime candidate.
There are lots of RFID/NFC devices out there these days, and you've probably got one or two in your pocket right now -
whether it's your car keys, alarm fob, door entry card, credit card, etc.
Of course, there are endless varieties of RFID readers to access them with,
but what I'd like is something that reads them all, and meets my standard criteria: small and cheap.
To be fair, there are plenty of readers out there that seem to meet this criteria.
You can buy a simple RFID USB reader for as little as 10-15 quid,
but you'll find that it's of limited use as it will almost certainly be dedicated to one 'standard',
and you'd therefore need dozens of them to be able to read 'everything'.
There are also tools like the Proxmark3 that are truly universal and can read pretty much anything,
but, unfortunately, these are not cheap.
However, it is certainly worth looking at the PM3 as it really is quite an amazing bit of kit -
often described as the 'Swiss Army Knife of RFID',
it is versatile enough to read pretty much any tag in the standard LF/HF frequency ranges,
so will at least be useful in giving us an idea as to what we're up against...
We'll be using it later to look at some specific examples.
So, going right to the beginning, what does an RFID tag actually do?
Well, it depends. There are basically two functions, and the rest is 'details':
Firstly, pretty much every RFID tag will IDENTIFY itself. That is function one.
Secondly, some tags will store DATA. That is function two.
The 'details' revolve around how it does those two things - is it blindly spitting out an ID and/or DATA,
or is there some security or other command structure built around it?
That's the simple view, and if you want the longer, more detailed explanation,
there are entire volumes written about it. The 'details' can run into hundreds of pages, so I'm not going to even start.
My goal, here, is to talk about the low level fundamental communications
that seperate the evil analogue underworld from the lovely, friendly digital fairy garden, where we all like to play.
And it all begins with our friend "induction".
At the very low level, RFID/NFC relies on the fact that if you energise a coil
and place another coil near it, the second coil will pick up some of that energy through induction.
Moreover, the two coils become magically (or magnetically, depending which world you come from) 'coupled',
so it's possible for the second coil to effect the voltage on the first, and it does this by shorting itself out.
If it does so, there will be a drop in the voltage on the first coil, and this is called "DAMPING".
That's it. In a nutshell, that's how RFID works:
the coils 'talk' to each-other by either sending energy (from the READER), or causing an energy drop (from the TAG).
In a little more detail, what happens is this (and for the purpose of this section, we'll assume the TAG is the dumbest type that just spits out an ID):
the READER energises its coil by powering it on and off repeatedly.
For a standard LF system, this will be 125,000 times per second, or 125KHz.
This is known as the 'CARRIER'.
The TAG, when placed in this field, will scavenge some power from its now inductively-coupled coil and come to life.
If the reader needs to send an extra 'wake up' (or any other) command,
it can do so by simply switching it's CARRIER off altogether for short periods.
The TAG stores enough energy that it can keep running for long enough to interpret these gap periods,
even though it's temporarily lost its power source.
The length of the CARRIER signal between the gaps will usually signify a 0 or a 1,
so like this the READER can send binary messages.
In other words, they're talking 'ASK':
Amplitude Shift Keying.
Data is sent by shifting the amplitude of the signal.
More accurately, they're talking 'OOK':
On Off Keying.
A message going from the READER to the TAG is signalled by the CARRIER being ON or OFF
and the message coming back is either DAMPED or UN-DAMPED.
Things get a lot easier to understand if we visualise them, so here is a plain 125KHz CARRIER viewed from an oscilloscope:
And here is the READER sending a message to the TAG:
In this case it's using a long pulse to represent a '1' and a short a '0', so the message here is '11000', or 'START_AUTH' if you're a HITAG2.
As I mentioned, the TAG can also send messages back to the READER by shorting it's own coil and DAMPING the READER's coil.
The result looks something like this:
It looks quite similar to when the READER sends a message,
but you'll note that it can't reduce the CARRIER all the way down to nothing -
instead it's either a 'DAMPED' or 'UN-DAMPED' wave.
This is because it's not directly controlling the voltage on the READER's coil,
only affecting it through induction.
However, it is clearly still perfectly readable.
In this case, if we take the damping action as a '1' and non-damped as a '0', we get '1010101010010110011010',
which is, in fact, a MANCHESTER encoded bitstream.
So what has MANCHESTER got to do with anything?
Well, this is where it starts to get interesting - if you look in details at the specs for these kind of tags,
you'll find that they mention modulation schemes such as 'Manchester', 'Bi-Phase', 'FSK', 'PSK', 'NRZI', 'ASK'...
WTF? We've already established that the two devices can only do ASK,
so where does all this FSK/PSK/Manchester malarky come from???
This is where I think a lot of the confusion lies.
Once you start trying to do something other than what a particular manufacturer intended with an RFID tag,
you quickly get lost in a mire of conflicting modulation schemes and other irrelevancies.
This particularly applies to the readers as well - if I want to find a reader that gives me access to the raw data, just forget it.
Most readers want to go that little bit further and fully de-modulate the signal for me.
And to do that, they need to know exactly how the signal was modulated in the first place...
And to know that, they need to know which standard tag type they're going to read, and so on, and so on...
It's easy to see how we've ended up with this situation.
A lot of these devices were built back in the days when computing power didn't come cheap,
so the designers tried to do as much as possible in the analogue world before handing over to the digital at the last moment.
This meant building circuitry that not only handled the low-level ASK communication,
but also the secondary modulation schemes that were layered on top.
The RF world have been going through a revolution, moving to SDR, in which they're doing the same thing -
handling only the very low level analogue stuff in circuitry and
doing the rest in software on small powerful microcomputers, and it's time we did the same for RFID!
So when I asked our tame (well, nearly... he's mostly house-trained and has at least stopped biting the postman) Chip Monkey
to build me an RFID reader that only gave me the lowest level data,
he was somewhat surprised to be unable to find an existing reference circuit that exactly fit the bill.
We both thought it would be simple, but no - every circuit was tied to a further demodulation scheme - FSK, PSK, Bi-Phase, etc.,
and some of them were horrendous!
They look more like they're designed to take you to the moon, rather than read a few bits from a wibbly coil!
For example, here is Microchip's 200 page document with separate example circuits for ASK, PSK and FSK.
However, after a bit more searching, we found a 'simple' design: 'The World's Simplest RFID Reader'.
This led to an 'improved' version, although it's still described as a 'DIY FSK RFID Reader', so possibly still not quite what we're looking for.
But hang on a minute, surely it doesn't matter what type of tag they were using it to read.
We've established that the low level communication is ALWAYS using ASK,
so the final demodulation of FSK/PSK/Manchester/Whatever is going to be handled by the microprocessor,
so we should be able to use this circuit to do any type of tag, not just FSK.
So this brings us back to the question of these 'extra' modulation schemes.
Where do they come from, and how do we deal with them?
Let's use the PM3 to take a look at the raw data we get from each type of tag...
The PM3 will act as a basic reader circuit and filter out the CARRIER, leaving only the effect of the DAMPING.
This is an ASK modulated tag:
Exactly what we would expect - a straightforward square wave created by the CARRIER either being DAMPED or UN-DAMPED.
Here is an FSK tag:
Note the two different pulse types - thin ones and fat ones, so we really are seeing different frequency pulses. Weird!
And the strangest yet, PSK:
Now that's just crazy. What the hell is that all about?
First, it helps to understand exactly what we're looking at.
The green line is showing us the voltage on the READER coil, after the tag has done it's DAMPING (or not).
We don't really care what the scale is, just that the bottom of the screen is 0 volts or fully DAMPED
and the top of the screen is *some* volts or UN-DAMPED.
The circuit that produces this is effectively looking for the presence of a 125KHz CARRIER and raising or lowering the output line accordingly.
How it works is irrelevant. I just don't care. That is Chip Monkey's problem! :)
So now I know what the lines mean, the question is how they get to look like they do.
The first one is simple:
ASK/OOK modulation is either ON or OFF,
so we get a line at the top of the screen when we're UN-DAMPED and a line at the bottom when we're DAMPED.
If we think of what the tag's doing as creating a mask which either lets the CARRIER through or not,
this makes perfect sense. Here I've marked the image with a red blob whenever the tag is DAMPING its coil:
So far, so obvious. Let's look at the FSK signal in the same way:
So now, instead of signalling data directly by DAMPING for a 0 or a 1,
we are creating a whole new CARRIER by DAMPING for different periods and allowing short or long pulses of the original CARRIER through.
The fully DAMPED signal doesn't mean anything, it's the width and number of pulses of the UN-DAMPED signal that carries the information.
In this case, 5 fat spikes means '0' and 7 thin spikes means '1' (or 12 thin spikes means '11'), so we've got '0-1101-0'. Neat!
OK, so what about the crazy PSK guy?
Well, this is interesting because what's actually happening here is that the coil is being DAMPED relative to the frequency of the original CARRIER itself.
In this case, it's going at exactly half the speed, so it's effectively blocking half the CARRIER pulses,
and most of the time producing a signal that isn't quite strong enough to reach the top of the screen,
and doesn't have time to reach the bottom either, which gives us the chunky bits in the middle.
However, whenever there is a phase change, the resulting half-bit repetition means that the DAMPING or UN-DAMPING now lasts for a full clock cycle of the original CARRIER,
so we get a little burst of HIGH or LOW popping through, hence the spikes.
In this images I've marked the mask in pink for where we're DAMPING 50% of the time, and the phase shifts are red or black as normal.
We can see that the fully DAMPED sections line up with the LOW spikes and the UN-DAMPED with the HIGH.
It's harder to read these by eye, but basically whenever there is a phase change (i.e. a spike in either direction), the bit value changes.
If there is no phase change, the bit value stays the same.
The number of bits depends on how long the gap is between the spikes,
so if one was to overlay a grid and you knew how long a bit period was,
you could simply count off the periods between bit changes and you've got your bitstream.
If we assume we're starting with a 0, this decodes as: '01101010001111100111000100010110000111010011100101101100001'. Easy-peasy! :)
Great, so now we understand what's going on, let's see if the circuit we've found will do the job...
As it turned out, the answer was 'not quite' for two reasons:
1. It couldn't handle the 'down' spikes of PSK.
2. As soon as we started playing with it, we were hit with a flurry of feature-creep! Yes, we wanted it to do more...
For a start, why have only a reader when you could also have a writer?
Unlike some technologies, there is really no difference in the RFID world between a reader and a writer.
As long as the reader can send signals to the tag, it can send 'write' and 'data' commands,
just as easily as reading the tag's emissions.
Well, almost as easily - it just needs to be able to switch off it's coil as well as modulating it.
Clearly that's not an issue.
And why have only a writer when you could have an emulator?
Again, the only fundamental difference between a TAG and a READER/WRITER is that one energises it's coil and the other grounds it.
Everything else works pretty much the same, so given the right hardware, we have the perfect platform for a Software Defined device...
So Chip Monkey built us one. And it was good.
In fact, it was so good that we decided that instead of just publishing the schematics and blogging about it,
we would try and give it life and free it into the world...
Proximity Cards
[There is a better device, although the writeup for this one is longer. It took me a month of evenings to clone my first Flexpass, with basically no equipment. Using my latest hardware, I was able to clone a Verichip—which, like the Flexpass, is an ID-only tag with no security—with only a few hours' work.]
* * *
Lots of companies use proximity cards to control physical access. An employee holds their card within a few inches of the reader; the reader receives a unique id from the card and transmits it to some central computer that tells it whether or not to open the door.
This is rather magical, considering that the tag is credit card-thin and contains no battery. The trick is the same as for RFID tags. The reader constantly transmits a rather strong carrier; the tag derives its power and clock from this carrier, kind of like a crystal radio. The tag changes how much carrier it reflects back at the reader—loosely, it makes the circuit across its antenna more like a short or more like an open—to transmit its code. The reader and the tag both have antenna coils tuned to the carrier frequency; they work like a loosely-coupled resonant transformer.
Or at least that's the theory. I couldn't find any credible documentation on the protocol used by the particular proximity cards that were available to me (Motorola's Flexpass). I did find a datasheet that claimed that they worked with a 125 kHz carrier. I wound a couple dozen turns of magnet wire on a 4" form, taped it to a reader, and 'scoped the coil. There was indeed a 125 kHz sine wave, large, a few volts peak to peak. The cards did, at least, work at 125 kHz.
A Card Reader
The next step was to build a reader. The reader must transmit a fairly powerful carrier; figure at least tens of milliamps through a coil of about a millihenry. This is easy: just blast a 125 kHz square wave into the reader coil. It's a tuned circuit so it will pick out the 125 kHz component very nicely. Then, we want to look for slight (at least 40 dB down) changes in the coil current. We do this by sampling the voltage across the tuning capacitor and feeding it into a detector circuit.
Tags can use lots of different modulation schemes—PSK, FSK, plain AM. I decided to build an AM reader, assuming that that was the most likely modulation scheme, and that if the cards used something else then I could probably do the detection in software.
B1 models the voltage on the reader cap when the tag is backscattering a 1/π kHz square wave. D1 and C1 form a leaky peak detector, producing a 125 kHz sawtooth whose peak is determined by the peak value of B1 during that cycle. R2 and C2 are a low pass filter, to even out the sawtooth quality of our measured amplitude. C3 and R8 AC-couple the signal down to ground; otherwise it would be hard to work with because its average value is around the amplitude of the sine wave, which will be around a hundred volts (V = IZ = IjωL = (50 mA)×j2π×(125kHz)×(1.6 mH)).
U4 buffers the signal and amplifies it a bit. U3 is a Sallen-Key low-pass stage. U1 is a comparator, to turn the measured amplitude into something that can drive a digital input. R10 and R11 add hysteresis, which later turned out to be very important.
I don't think there's anything very novel about my circuit. I realized from a Microchip app note that the key is to start with a peak detector. I later found Microchip's reference design for an ASK reader. It's fairly close to mine: peak detector, passive low-pass, AC couple it down, active low-pass, comparator.
I built the reader on two printed circuit boards. The largest one contains a PIC16F877, used to generate the 125 kHz square wave, and the circuit to drive and tune the coil. The reader coil is a ~1.6 mH inductor wound from about 140 turns of 30 gauge magnet wire on a 2.5" square form.
The smaller board implements the detector circuit shown above. Both are single-sided boards made on my milling machine.
So I placed a Flexpass card on top of my reader coil, and I watched the output. Sure enough, I got modulation; alternating positive- and negative-going pulses, unevenly spaced, like what you'd get if you took a serial bitstream and high-pass filtered it. This was very encouraging.
A Card Simulator
I designed a tag that could produce identical AM when I tested it against my reader. This was pretty boring. I used a micro and alternately tri-stated or asserted (one low, one high) two GPIOs connected to one terminal of the tuned coil, with the other antenna terminal grounded. My tag produced perfect waveforms on my reader. It did not, however, work with the Motorola readers—the door did not open.
So then I tried a lot of things. Eventually I took a closer look at the output of the peak detector; it was a 62.5 kHz sawtooth, not 125 kHz. The card was transmitting PSK, attenuating every other peak, but my peak detector didn't drop fast enough to follow it. The AM that I saw was a modulation artifact; I expect (and it's intuitive) that it can be shown that those sorts of amplitude variations can be obtained by bandpass-filtering a PSK signal.
A bit time is 256 μs. The tag's id is periodic over 64 bit times, 16384 μs. When there is no phase shift, the signal is a 125 kHz sinusoid slightly modulated by a 62.5 kHz sinusoid. The modulation is greatly exaggerated in the figure.
To indicate a bit transition, the tag inserts a phase shift of π. There is always an even number of carrier cycles between phase shifts, so that if the most recent phase shift was achieved by skipping a small-amplitude cycle then the next phase shift will be achieved by skipping a large-amplitude cycle.
Since a bit time is 256 μs, phase shifts are an integer multiple of 256 μs apart. Thus, in the above picture, we could have t = 256, 512, 768, ... μs.
A code consists of 64 bit times. There are no transitions for the last 29 bits for all but one of the cards that I have tested. Possibly the one card is in a newer format, or possibly it's just weird. There appears to be some structure within the bits—if I get some of the bits wrong then the reader doesn't even beep, but if I get others wrong then the reader still beeps but the door doesn't open. I haven't had any need to figure out which are which though.
My first tag sort of disintegrated (too many flywires) so I built a new combination reader/tag. In tag mode I chose to ignore the reader's carrier: I just blast my own modulated carrier at the reader. This works perfectly well, and you'd expect it to; if we're not in phase with the reader's oscillator then we will be in a few hundred milliseconds. (The beat frequency is ~1 Hz, for a variation of a few ppm, typical for a crystal).
A Toy With Blinking Lights
The hardware to pretend to be a tag is very simple. The hardware to read a tag is not much more complicated; I could do it with a micro, a quad opamp, and a dozen passives. This meant that I could build a combination card reader/simulator in a few square inches of board space. Add a couple of lithium batteries and some nice plastics and I would have a clever and mostly useless pocket-sized toy. Of course I couldn't resist building it.
For a sense of scale, the PIC16F628 (largest IC) and the opamp are both SOICs. There aren't any unreasonably fine-pitch components on this board; the tightest are the SuperSOT FETs (bottom left), with 0.95 mm lead spacing.
The hardware is very similar to the larger reader/simulator described above. I decided not to attempt PSK demodulation; I just detect the modulation artifacts and use the hysteresis, so that I know that if the comparator output has changed state since I looked at it last then there has been a phase shift since then. This sacrifices a bit of sensitivity but my read range is small enough (by design, for reasonable battery life) that this doesn't bother me. This hardware could also be used for AM cards, if I ever came across one.
The PIC can kill power to the detector section with a couple of FET switches. This plus the PIC's sleep mode means that I can do on/off in software without ruining my battery life.
The circuit is again built on a milled PCB, with one signal layer and a ground plane. The ground plane is split into analog (GND = 0V) and digital (V- = -3V) sections. It is powered by a pair of CR2032 lithium coin cells. They determine the thickness of the device; the batteries, in a holder, are 0.217" thick.
There are no connectors on the board because I couldn't find any low-profile surface mount connectors that I liked. Instead there are test points to program the PIC and tune the coil; I actually built a test/programming fixture (with pogo pins). This is pretty easy with a CNC machine. Power and coil leads are soldered directly to the board. The blue wires are mostly test points, for debugging only. The 8 pin header connects to my PIC programmer.
All the plastics were routed from sheet on my milling machine, using an 1/8" carbide straight bit intended for use in wood. Yes, the workpiece is held to the table by carpet tape; this is much cheaper than a vacuum chuck.
Both the top and bottom screw into the core with #2 tapping screws. I put a lot of effort into finding a local source for a methylene chloride solvent cement (so that I could weld the bottom on instead of screwing it) but I gave up after many failures. I first tried machine screws but small-diameter machine screws tend to have too many threads per inch to work in plastics. Next time I'd probably look for threaded inserts.
I left the wires long so that I can remove the the board from the plastics without desoldering anything. This is necessary to put it on the programming jig, and it helps when I'm trying to figure out whether I have a circuit-does-the-wrong-thing problem or a 125-kHz-pickup-on-everything problem.
The user interface comprises four LEDs and the two tact switches. The software can currently read a card, store a single id, transmit that id over the air to a reader, blink out that id on the LEDs, and accept a new id on the tact switches. There is also a “sniff” mode, in which the detector is active but the coil is not powered; this allows me to read a card while a legitimate reader is powering it. (The read range of the cards is limited by the tag power requirements, not by reader sensitivity; it goes up substantially when another reader is powering the card.)
Most of the software is pretty straightforward. To transmit the id, I must apply a square wave with, on average, every other cycle missing. I do this entirely in software, flipping a GPIO every 4 μs. I managed to make this work on a 4 MHz device (4 cycles between edges); it's much easier on a 20 MHz device, though. The antenna is resonant around 125 kHz, so that it effectively bandpass filters the pulse train that I generate. In the figure below, the blue trace is obtained by passing the red trace through a bandpass filter centred at twice the frequency of the pulse train:
For the reader functionality I configure the PWM module to generate the unmodulated square wave so that I can be a bit sloppier with my timing.
I sync on the word by waiting for an edge after a long idle period (though this is of course unnecessary; I only need bit sync). Then I read the word into memory. Then, I read the word ten more times, comparing it against the recorded copy each time; if they all match then I decide that it's right, else I lose sync and try again. This works reasonably well, but if the card is held just barely outside the read range then I will eventually false-sync. This is bad, because there is no other verification. Legitimate readers can false-sync with no major repercussions, since they would just fail to open the door; the user would stand there waiting until the card was read correctly.
Security Implications
I can copy a proximity card at least as easily as I can take an impression of a key. This means that it's not a very good idea to reuse visitor cards without changing the id (and that it doesn't really matter whether you get the physical card back from the guy you just fired).
More insidiously, it's quite practical to read someone's card without removing it from their wallet. A bit of deliberate clumsiness, a reader up my sleeve, and I would have little trouble cloning anyone's card. I could also exploit the fact the distance at which the cards will be powered is less than the distance at which they can be read; if another reader is exciting the card then my reader can read that card from the other side of a wall!
This means that a sniffer concealed somewhere near a legitimate reader could intercept real transactions at a significant distance. This sort of attack is particularly good because the card repeats its id over and over as long as it is in the field, so that I could use signal processing techniques to combine multiple copies of the pattern to further improve my read range. This is easy—if I sample all 64 bits of the id then I don't have to get word-sync, and if I oversample then I don't even have to get bit-sync. Even if I capture the id with a few bit errors it is still useful; I could try the captured id, then every id with a Hamming distance of 1 from the captured id (one bit flipped), then 2, and so on. One or two bit errors would take seconds; three would take minutes.
If I were willing to spend money on a four (or even two) layer board then I could build a sniffer/reader much smaller than anything shown above. If I used black Lexan (or even acrylic) for the case then the device would look less like something that an image-conscious terrorist might carry. This would make it much easier to carry out the attacks outlined above.
All of these attacks can be stopped with a challenge/response scheme. I've seen brochures for cards and readers that do this; I guess it's not just a marketing gimmick.
Hardware Notes
The coil driver consists of an N/P FET pair, with the FETs working as switches. For my initial reader I connected the drains together, like in a CMOS inverter. This drove the RLC circuit that was the coil, the tuning cap, and a current-limiting resistor. This has two flaws. First, it has a software self-destruct mode; if the input floats between the positive and negative rails then both FETs will conduct, shorting V+ to V-, and the FETs will get very, very hot. This caught me because the PIC tri-states all its GPIOs when it's being programmed in system.
There was a bit of a shoot-through problem too; the switching transients got in to everything. Smaller FETs would actually have been better, but I just put some resistance between the drains, because I needed some resistance to limit the current anyways.
My layout seems to be reasonably good. There's hundreds of millivolts of noise on most of the signals around the PIC, but noise on the detector signals is in the tens of millivolts. More bypassing might have cleaned things up further. Not placing the circuit inside the antenna might also have helped....
The detector is rather poor. When reading (or sniffing) from a large distance, it would be nice to be able to turn down the hysteresis to get some chance at a read. This is not currently possible. It's probably not worth messing with the envelope detector though; it would be better to build a proper PSK detector (by correlating and then integrating the peak detector output in hardware, or with a sharp notch filter to reject the 125 kHz component, allowing me to work only with the sidebands.).
The coin cells probably aren't a very good choice for this application considering the high peak currents—Panasonic's datasheet doesn't even mention what happens if you try to draw more than a milliamp. I don't know what would be better, though; thin batteries are hard to find.
The opamps that I use (TLC2274s) are not low-current. I knew this when I chose them but I assumed that they would only need to be powered when the coil was energized. In sniff mode this is not true; the coil is not driven, and I could even put the PIC to sleep and wake on an edge from the detector. Next time, I guess.
I was careless when I designed the power switching stuff; there were a couple of leakage paths that added almost 60 microamps of off current. I was able to fix this by depopulating a couple of components and faking out the functionality that they used to provide in software. Off current is about 4 μA. The CR2032s can deliver about 200 mAh, for a standby life comparable to the shelf life of the cells.
The presence of metallic objects inside the antenna probably does weird things. Certainly it detunes the coil a bit, and even after I tweaked it back to resonance the voltage on the coil was smaller (indicating that some of my battery life is going to eddy currents in the mounting screws).
I had a few interesting problems relating to 125 kHz pickup from the read coil. The wires from the board to the coil are quite vicious; seriously bad things happen if they rest on the analog traces.
I had a lot of trouble machining the polycarbonate until I got my feeds and speeds right. Too slow of a feed for your speed is very bad; the plastic melts, the cutter loads, friction increases and chip ejection goes to nothing, and you get thermal runaway. Once I figured that out everything went quite easily. Surface finish with an 0.015" finish pass was acceptable as machined almost everywhere. Where it wasn't I used the non-serrated edge of a hacksaw blade to clean it up.
Acrylic makes a nicer case than polycarbonate—it's more rigid and less prone to scratching. An acrylic case probably wouldn't survive a two foot drop onto concrete, though.
Future Plans
The toy described above is nice, but it could be better. I believe that I could sample the peak detector output directly (after AC-coupling it down) and do the demodulation in software. I have a very clever idea that uses the PIC's comparators and voltage reference module to do PSK detection, possibly one so clever that it works only in simulation. This would allow me to lose the opamp entirely. I could drive the coil from a few GPIOs tied together, at the cost of read range. This would get the design down to the micro plus some passives.
I'd also like to get rid of the split supply and run from a single 3 V rail. I think I'd have to do some sort of trick with multiple coils (like a transformer) to get a reasonable input impedance without an unreasonably high Q.
Alternatively, I could build a long range sniffer (better detector, one foot diameter read coil, enough current that it's just on the edge of melting, a motorcycle battery to power the thing...). This wouldn't be nearly as cool as a smaller version of my toy but it would be better for convincing people that the cards are insecure.
October 2003, Waterloo
Mivo- RFID based mobile payment system
Port/Pin Configuration on MCU:
Pin D.7 - 125[kHz] square wave to generate the RFID carrier frequency.
Pin D.3 - External Interrupt 1 to accept filtered data from the RFID reception circuit.
Transmission- RF Choke and Power Amplifier: The circuit represents a 125 [kHz] square-wave signal fed into an RF choke followed by a current buffer and half-bridge amplifier.
The RF choke is used to filter majority of the high frequency signals in the square-wave, passing through only the fundamental harmonic (sine-wave) of 125 [kHz]. We could have generated a sine-wave in the MCU and passed that directly to the power amplifier however this would have been much more taxing on the MCU (as we would have had to implement a sine-wave table, etc). Generating a square-wave on the other hand is extremely simple and uses the MCU resources to a bare minimum (please see the RFID “Software” section for details). Diodes are used here, in order to remove crossover distortion (transients due to the half-bridge transistors switching between on and off states).
All of the components in this circuit were found in the ECE 4760 lab apart from the 1 [mH] inductor, which we ordered through DigiKey (part numbers can be found in the “Parts List” section).
Reception Antenna: This is definitely one of the most crucial elements of the circuit and we made sure to follow the recommendation (by the microID guide) of using a series L-C resonant circuit, as opposed to a parallel R-C circuit. This topology ensures that maximum current flow occurs at maximum resonance and also provides for a simpler design.
The resonance frequency is given by the equation:
As the ECE 4760 lab had 1nF capacitors in abundance, we decided to use C = 1 [nF]. This, along with (resonant frequency) f0 = 125 [kHz] gave us a value for L ≈ 1.62 [mH].
Now, for the construction of the antenna we decided to go for a rectangular shaped antenna as opposed to a circular one because from our preliminary end-product designs (i.e. aesthetic box) we knew that we would need a rectangular antenna. As far as the dimensions of this antenna are concerned, we decided that we would go with an antenna about the size of our whiteboard (as our box would have to be at least as large as the whiteboard (or solder board).
The [microID guide] came in handy here as well because we could simply use the equation for calculating the inductance of a coil. The equation for the inductance of an N-turn rectangular coil, with multilayer is:
Here: L- Inductance in [µH], {x,y}- {Length, Breadth} of coil in [cms], h- Height of coil in [cms], b- diameter of wire in [cms].
The values we used: L = 1620[µH], {x, y, h} = {4.2, 15.2, 1} [cms], b = 0.32[cms] (AWG28 lacquered copper wire from the ECE 4760 lab). Solving this equation for N gave us N ≈ 83 turns. Oscilloscope results below show the inductance coil output, both with and without RFID tags being present.
Reception Circuit: Since the capacitor (in series with the coil) is grounded, the carrier signal (125 kHz) is filtered out to ground after passing the antenna coil. The circuit provides minimum impedance at the resonance frequency. This results in maximizing the antenna current, and therefore, the magnetic field strength is maximized.
We now need to half-wave rectify and R-C filter the received signal. The inductor, capacitor and diode (and the other bottom parts in the circuit) form a signal receiving section. The voltage drop in the antenna coil is a summation (superposition) of the transmitting signal and backscattering signal. The diode is a demodulator which detects the envelope of the backscattering signal. The diode and capacitor (2.2nF) form a half-wave capacitor-filtered rectifier circuit. The detected envelope signal is charged into the 2.2nF capacitor. The resistor provides a discharge path for the voltage charged in the capacitor. This voltage passes active filters and the pulse shaping circuitry. Finally this signal is passed through a capacitor (1nF) which knocks off the DC offset.
Filtering Circuit: Next we use active Twin-T and Butterworth filters using the TL084CN (high-speed) Op Amp. The first filter acts as an isolator with bandwidth being 10[Hz] – 20[Hz], and unity-gain for all frequencies outside the band. The second filter introduces some more gain into the pass-band. Next, the signal goes into a Butterworth filter with sharp roll-off. This filter gets rid of any high-frequency components in the signal.
Finally, the filtered signal is passed through a comparator to generate a square-wave type signal. The final result is that we get very clean 12.5[kHz] and 15.625[kHz] frequencies out of the system.
One of the major differences in our design from the previous year’s design was that we got no signal when a tag was not present near the coil. This is great because we didn’t need any hack to ignore this signal.
Oscilloscope outputs from comparator (12.5[kHz])
Oscilloscope outputs from comparator (15.625[kHz])
Note: We did not simulate any of these circuits simply because we were using a commercial guide (presumably thoroughly tested)
and we were lucky to have the Sp 2006 group’s simulation results.
In fact, we devoted our time to trying out different values of components in order to achieve the best performance.
For further details (such as how to increase the range, etc, please refer tothe microID 125kHz RFID System Guide).
Hardware/Software Tradeoff & Data Creation:
We debated whether we could take the filtered signals and feed them directly to the MCU or if we would need some more hardware decoding. However, in order to accurately measure the frequency of the incoming data we would have to sample at a rate of about 125[kHz] and this would take up a lot of the resources of the MCU (at a clock rate of 16[MHz] this would give us 128 cycles to do everything else). We realized this because Craig and Ricardo had explained this pretty well in their report and also because we knew that it would be much faster to do this in hardware. Now, the microID RFID guide provided a really smart way that made use of D flip flops and a decade counter. The circuit is designed as follows:
Digital Component of RFID Circuit
The explanation for this circuit is taken from Craig and Ricardo’s webpage as they did a great job of documenting and explaining the logic:
The comparator output serves as the clock for the first D flip-flop, which also takes logic 1 as its D value.
On the rising edge of the comparator clock, Q is immediately set to 1.
However, simultaneously ~Q goes low and clears the flip-flop.
This creates an extremely short pulse which serves as a reset for the decade counter and clock for the second flip-flop.
The decade counter is just a 1-hot counter which takes a 125 KHz clock.
With every rising edge of this clock, the counter outputs the next pin to logic 1;
so typical output would look like (if one were looking at output pins 0-9 of the counter)
1000000000 0100000000 00100000000 etc.
However, this counter is being reset with every rising edge of the comparator output.
Thus, since we've already determined that 125 KHz/10 = 12.5 KHz is to be our frequency that represents logical 1,
all we have to do is check for the output on pin9 to know whether or not we see that frequency.
If the system is operating at either one of the other possible frequencies, the counter will be reset before pin9 can go active.
The pin9 output serves as input to the second flip-flop and also to the clock inhibitor, which keeps the 9th pin high until the counter is reset.
Because of this set-up, the Q output of the second flip-flop will remain logical 1 so long as modulating frequency is 12.5 KHz
and will drop down to 0 if its anything else.
Theoretically, this circuit should work perfectly.
However, experimentally it did not, and thus required a small modification.
The 100 KOhm resistor on the first flip-flop serves to lengthen the time it takes for the ~Q signal to get to CLEAR.
Since all transistors have some amount of natural capacitance, this forms an RC circuit of sorts with a set RC time constant for the signal to rise or fall.
As it turns out, this time was too short for the decade counter.
The original design from the reference guide specified only a 10KOhm resistor between ~Q and CLEAR.
With the 10[KOhm] resistor, pulse widths for the reset pulse were a mere 50 [ns] long, while the counter required at least 250 [ns].
This caused some very erratic behavior.
After many hours of debugging, we finally pinpointed the problem and replaced the resistor with the 100 [KOhm] resistor
which increased the pulse width long enough for the counter to correctly operate.
RFID Software: Once the data was converted into a digital Manchester signal by the RFID circuit, we then had to process it and convert it into a useful format to the microcontroller. For this, we decided to write a library for the RFID, called ‘rfid.c’, which dealt specifically with the task of receiving and interpreting an RFID signal. Unlike Ricardo and Craig’s design for RFID, we did not use a sampling method for Manchester decoding. Instead, we opted for a more robust method with less interrupts – a timing-based method. This method only interrupts when it has to – i.e. on a change of logic value, and then the microcontroller interprets the amount of time between interrupts in order to determine the value of the Manchester bit. To do this, we go through a sequence of events after turning on the external interrupt for any logic change:
- Wait until we receive a start sequence (extra long logic high)
- On next interrupt, decode the first Manchester bit: delay by a very short amount to ensure steady state, read the logic level.
- If time difference is short, save the read logic level into a data buffer only if it wasn’t recorded last time. If time difference is long, save the logic level.
- If anything was recorded in the previous step, add to the checksum such that it is sample number dependent (we used sample * i % 9, where i is the sample number)
- Repeat steps 2 and 3 until we read an end sequence (extra long logic low)
- Insert a ‘2’ at the end of the buffer, so we know where we stopped recording
The sample number dependent checksum was added as a feature since it is more likely to be wrong in the case of multiple bit errors. If we only added the binary values, we would have created a parity check, which may not have been sufficient in cases of a lot of noise. This allowed us to cut down on the number of redundancy checks by a factor of three, and still get the same reliable results, leading to a faster system. To better explain what we mean by ‘short’, ‘long’, and ‘extra long’: since Manchester encoding has a logic change at every data bit, the longest delay we could have would be 0110. That means that during the pair of 1s, it stays logic high for two half-cycles. The shortest delay we could have would be 0101, where the 1 stays high for only one half-cycle. For the RFID system, if we clock it at the same scale as the previous two, there is also another state, which is 111110. This is a very long time, easily detectable, and denotes the start of a data stream. Once we successfully fill the buffer, we ensure the reliability by checking it multiple times. We checked 20 times, which was way overkill, but we really wanted to minimize the chance of a wrong ID. In reality, two checks actually is sufficient for most of the time, with three checks delivering near perfect results. Since we are only reading a 44-bit value from the RFID tag, we can do the 20 checks very quickly (approximately one second in most cases). We saved each data bit as a char in a string, so then we just convert it into a long using bin2dec so it can be returned using less space than a string. One cool final note was that by doing Manchester decoding with the timing-based method, we were able to get the data by only connecting one wire for ground and one wire for data to the microcontroller!
Manchester Encoding [from Wikipedia]:
In telecommunications, Manchester code is a line code in which the encoding of each data bit has at least one transition and occupies the same time. It therefore has no DC component, and is self-clocking, which means that it may be inductively or capacitively coupled, and that a clock signal can be recovered from the encoded data. Essentially Manchester encoding works in the following way: A transition from 0 to 1 corresponds to a single “1” value and a transition from 1 to 0 corresponds to a single “0” value. Thus 01 -> 1, 10 -> 0, and the reduced bitstream is half as long as the original bitstream.
RFID Reader 线路图收集的更多相关文章
- DIY FSK RFID Reader
This page describes the construction of an RFID reader using only an Arduino (Nano 3.0 was tested, b ...
- RFIDler - An open source Software Defined RFID Reader/Writer/Emulator
https://www.kickstarter.com/projects/1708444109/rfidler-a-software-defined-rfid-reader-writer-emul h ...
- RFID Reader ICs
http://www.advanide.com/readeric.htm Low Frequency Reader ICs Manufacturer Product Frequency ISO Com ...
- RFID 读写器 Reader Writer Cloner
RFID读写器的工作原理 RFID的数据采集以读写器为主导,RFID读写器是一种通过无线通信,实现对标签识别和内存数据的读出和写入操作的装置. 读写器又称为阅读器或读头(Reader).查询器(Int ...
- RFID Exploration and Spoofer a bipolar transistor, a pair of FETs, and a rectifying full-bridge followed by a loading FET
RFID Exploration Louis Yi, Mary Ruthven, Kevin O'Toole, & Jay Patterson What did you do? We made ...
- Radio Basics for RFID
Radio Basics for RFID The following is excerpted from Chapter 3: Radio Basics for UHF RFID from the ...
- RFID 仿真/模拟/监控/拦截/检测/嗅探器
Sound card based RFID sniffer/emulator (Too tired after recon.cx to do draw the schematics better th ...
- RFID 基础/分类/编码/调制/传输
不同频段的RFID产品会有不同的特性,本文详细介绍了无源的感应器在不同工作频率产品的特性以及主要的应用. 目前定义RFID产品的工作频率有低频.高频和甚高频的频率范围内的符合不同标准的不同的产品,而且 ...
- 我的一个关于RFID的项目总结
去年做的一个项目,今天在这里想总结一下,这是主要流程: [0]RFID(Reader)---->[1]网络---->[2]接收处理程序---->[3]队列---->[4]读/存 ...
随机推荐
- XX.frame.origin.x 赋值问题
can't use that myView.frame.origin.x=25.0; 因为.操作 和 = 一起用的话会调用 set方法.所以上式是行不通的. 可以用下面的方式来实现. that I h ...
- PHP中设置、使用、删除Cookie方法
1.设置Cookie PHP用SetCookie函数来设置Cookie.必须注意的一点是:Cookie是HTTP协议头的一部分,用于浏览器和服务器之间传递信息,所以必须在任何属于HTML文件本身的内容 ...
- 【进阶——最小费用最大流】hdu 1533 Going Home (费用流)Pacific Northwest 2004
题意: 给一个n*m的矩阵,其中由k个人和k个房子,给每个人匹配一个不同的房子,要求所有人走过的曼哈顿距离之和最短. 输入: 多组输入数据. 每组输入数据第一行是两个整型n, m,表示矩阵的长和宽. ...
- hdu 2177(威佐夫博奕)
题意:容易理解,在威佐夫博奕的基础上新增加了一条要求:就是如果在赢得条件下,输出第一步怎么走. 分析:使用暴力判断,详细见代码. 代码: #include<stdio.h> #includ ...
- Codeforces Round #217 (Div. 2) c题
C. Mittens time limit per test 1 second memory limit per test 256 megabytes input standard input out ...
- YII Framework学习教程-YII的安全
web应用的安全问题是很重要的,在“黑客”盛行的年代,你的网站可能明天都遭受着攻击,为了从某种程度上防止被攻击,YII提供了防止攻击的几种解决方案.当然这里讲的安全是片面的,但是值得一看. 官方提供的 ...
- eclipse 文本编辑器
Eclipse文本编辑器拥有编辑器的标准功能,包括数目不限的Undo(Ctrl+Z)和Redo(Ctrl+Y)操作.使用快捷键Ctrl+F后,会出现Find/Replace对话框,快捷键Ctrl+K或 ...
- DOM笔记(十):JavaScript正则表达式
一.RegExp ECMAScript通过RegExp类型类支持正则表达式,语法和Perl类似: var exp = /pattern/flags; patternb部分是任何简单的或复杂的正则表达式 ...
- javaScript document对象详解
Document对象内容集合 document 文挡对象 - JavaScript脚本语言描述———————————————————————注:页面上元素name属性和JavaScript引用的名称必 ...
- asp.net mvc下ckeditor使用
资源下载:ckeditor 第一步,引入必须文件“~/ckeditor/ckeditor.js” 第二步,替换文本域 <%: Html.TextArea("Content", ...