• 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.

    1. The first 9 bits are the start communication bits ( always '1' ).  
    2. The next 4 bits are the Low Significant Bits of the customer ID(D00,...,D03).
    3. The next 1 bit (P0) is the Even parity bit of the previous 4 bits .
    4. The next 4 bits are the High Significant Bits of the customer ID (D04,...,D07).
    5. The next 1 bit (P1) is the Even parity bit of the previous 4 bits.
    6. The next 4 bits are first part of the 32-bit Tag's serial number (D08,...,D11).
    7. ...
    8. 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).
    9. 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.

      1  /***************************************************************************** 
      2  |         This program was written by Asher Glick aglick@tetrakai.com         | 
      3  |             This program is currently under the GNU GPL licence             |
      4  *****************************************************************************/
      5 
      6 /****************** CHIP SETTINGS ******************
      7 | This program was designed to run on an ATMEGA328  |
      8 | chip running with an external clock at 8MHz       |
      9 ***************************************************/
     10 
     11 /********** FUSE SETTINGS **********
     12 |   Low Fuse 0xE2                   |
     13 |  High Fuse 0xD9                   |       +- AVRDUDE COMMANDS -+
     14 | Extra Fuse 0x07                   |       | -U lfuse:w:0xe0:m  |
     15 |                                   |       | -U hfuse:w:0xd9:m  |
     16 | These fuse calculations are       |       | -U efuse:w:0xff:m  |
     17 | based off of the usbtiny AVR      |       +--------------------+
     18 | programmer. Other programmers     |
     19 | may have a different fuse number  |
     20 ***********************************/
     21 
     22 /************************** AVRDUDE command for 8MHz ************************** 
     23 | sudo avrdude -p m328p -c usbtiny -U flash:w:myproject.hex                    |
     24 |                       -U lfuse:w:0xE2:m -U hfuse:w:0xD9:m -U efuse:w:0x07:m  |
     25 |                                                                              |
     26 | NOTE: when messing with fuses, do this at your own risk. These fuses for the |
     27 |        ATMEGA328P (ATMEGA328) worked for me, however if they do not work for |
     28 |        you, it is not my fault                                               |
     29 | NOTE: '-c usbtiny' is incorrect if you are using a different programmer      |
     30 ******************************************************************************/
     31 
     32 /******************************* CUSTOM SETTINGS ******************************
     33 | Settings that can be changed, comment or uncomment these #define settings to |
     34 | make the AVRFID code do different things
     35 ******************************************************************************/
     36 
     37 //#define Binary_Tag_Output         // Outputs the Read tag in binary over serial
     38 #define Hexadecimal_Tag_Output    // Outputs the read tag in Hexadecimal over serial
     39 //#define Decimal_Tag_Output        // Outputs the read tag in decimal
     40 
     41 #define Manufacturer_ID_Output    // The output will contain the Manufacturer ID (NOT IMPLEMENTED)
     42 #define Site_Code_Output          // The output will contain the Site Code       (NOT IMPLEMENTED)
     43 #define Unique_Id_Output          // The output will contain the Unique ID
     44 
     45 //#define Split_Tags_With '-'       // The character to split tags pieces with
     46 
     47 //#define Whitelist_Enabled         // When a tag is read it will be compaired 
     48                                   // against a whitelist and one of two functions
     49                                   // will be run depending on if the id matches
     50                                  
     51                                  
     52                                  // some conststents
     53                                   
     54 // These values may need to be changed depending on the servo that you are using
     55 #define SERVO_OPEN 575    // open signal value for the servo
     56 #define SERVO_CLOSE 1000  // close signal value for the servo
     57 
     58 
     59 //20-bit manufacturer code,
     60 //8-bit site code
     61 //16-bit unique id
     62 
     63 #define MANUFACTURER_ID_OFFSET 0
     64 #define MANUFACTURER_ID_LENGTH 20
     65 
     66 #define SITE_CODE_OFFSET 20
     67 #define SITE_CODE_LENGTH 8
     68      
     69 #define UNIQUE_ID_OFFSET 28
     70 #define UNIQUE_ID_LENGTH 16
     71 
     72 
     73 
     74 // these settings are used internally by the program to optimize the settings above
     75 #ifndef serialOut
     76   #define serialOut
     77 #endif
     78 
     79 
     80 /// Begin the includes
     81 
     82 #include <avr/io.h>
     83 #include <avr/interrupt.h>
     84 #include <stdlib.h>
     85 
     86 #define ARRAYSIZE 900   // Number of RF points to collect each time
     87 
     88 char * begin;           // points to the bigining of the array
     89 int * names;            // array of valid ID numbers
     90 int namesize;           // size of array of valid ID numbers
     91 volatile int iter;      // the iterator for the placement of count in the array
     92 volatile int count;     // counts 125kHz pulses
     93 volatile int lastpulse; // last value of DEMOD_OUT
     94 volatile int on;        // stores the value of DEMOD_OUT in the interrupt
     95 
     96 /********************************* ADD NAMES *********************************
     97 | This function add allocates the ammount of memory that will be needed to    |
     98 | store the list of names, and adds all the saved names to the allocated      |
     99 | memory for use later in the program                                         |
    100 *****************************************************************************/
    101 void addNames(void) {
    102   namesize = 2; // number of IDs in the access list
    103   names = malloc (sizeof(int) * namesize);
    104   // change or add more IDs after this point
    105   names [0] = 12345;
    106   names [1] = 56101;
    107 }
    108 
    109 /******************************* INT0 INTERRUPT *******************************
    110 | This ISR(INT0_vect) is the interrupt function for INT0. This function is the |
    111 | function that is run each time the 125kHz pulse goes HIGH.                   |
    112 | 1) If this pulse is in a new wave then put the count of the last wave into   |
    113 |     the array                                                                |
    114 | 2) Add one to the count (count stores the number of 125kHz pulses in each    |
    115 |     wave                                                                     |
    116 ******************************************************************************/
    117 ISR(INT0_vect) {
    118   //Save the value of DEMOD_OUT to prevent re-reading on the same group
    119   on =(PINB & 0x01);
    120   // if wave is rising (end of the last wave)
    121   if (on == 1 && lastpulse == 0 ) {
    122     // write the data to the array and reset the cound
    123     begin[iter] = count; 
    124     count = 0;
    125     iter = iter + 1;
    126   }
    127   count = count + 1;
    128   lastpulse = on;
    129 }
    130 
    131 /************************************ WAIT ************************************
    132 | A generic wait function                                                      |
    133 ******************************************************************************/
    134 void wait (unsigned long time) {
    135   long i;
    136   for (i = 0; i < time; i++) {
    137     asm volatile ("nop");
    138   }
    139 }
    140 
    141   //////////////////////////////////////////////////////////////////////////////
    142  //////////////////////////// SERIAL COMMUNICATION ////////////////////////////
    143 //////////////////////////////////////////////////////////////////////////////
    144 
    145 /******************************** USART CONFIG ********************************
    146 | USART_Init(void) initilizes the USART feature, this function needs to be run |
    147 | before any USART functions are used, this function configures the BAUD rate  |
    148 | for the USART and enables the format for transmission                        |
    149 ******************************************************************************/
    150 #define FOSC 8000000 // Clock Speed of the procesor
    151 #define BAUD 19200    // Baud rate (to change the BAUD rate change this variable
    152 #define MYUBRR FOSC/16/BAUD-1 // calculate the number the processor needs
    153 void USART_Init(void) {
    154   unsigned int ubrr = MYUBRR;
    155   /*Set baud rate */
    156   UBRR0H = (unsigned char)(ubrr>>8);
    157   UBRR0L = (unsigned char)ubrr;
    158   /*Enable receiver and transmitter */
    159   UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    160   /* Set frame format: 8data, 2stop bit */
    161   UCSR0C = (1<<USBS0)|(3<<UCSZ00);
    162 }
    163 
    164 /******************************* USART_Transmit *******************************
    165 | The USART_Transmit(int) function allows you to send numbers to USART serial  |
    166 | This function only handles numbers up to two digits. If there is one digit   |
    167 | the message contains a space, then the digit converted to ascii. If there    |
    168 | are two digits then the message is the first digit followed by the seccond   |
    169 | If the input is negative then the function will output a newline character   |
    170 ******************************************************************************/
    171 void USART_Transmit(char input )
    172 {
    173   while ( !( UCSR0A & (1<<UDRE0)) );
    174   // Put the value into the regester to send
    175   UDR0 = input;
    176 }
    177   //////////////////////////////////////////////////////////////////////////////
    178  ////////////////////////// BASE CONVERSION FUNCTIONS /////////////////////////
    179 //////////////////////////////////////////////////////////////////////////////
    180 char binaryTohex (int four, int three, int two, int one) {
    181   int value = (one << 0) + (two << 1) + (three << 2) + (four << 3);
    182   if (value > 9) return 'A' + value - 10;
    183   return '0' + value;
    184 }
    185 
    186 /*********************** GET HEX ARRAY FROM BINARY ARRAY **********************
    187 |
    188 ******************************************************************************/
    189 int * getHexFromBinary (int * array, int length, int * result) {
    190   int i;
    191   int resultLength = (length+3)/4; // +3 so that the resulting number gets rounded up
    192   // 4 / 4 = 1       [correct]
    193   // 7 / 4 = 1 (4+3) [still correct]
    194   // 5 / 4 = 1       [not rounded up]
    195   // 8 / 4 = 2 (5+3) [correct]
    196   
    197   for (i = 0; i < resultLength; i++) {
    198     result[i*4] = (array[i+0] << 0)
    199                 + (array[i+1] << 1)
    200                 + (array[i+2] << 2)
    201                 + (array[i+3] << 3);
    202   }
    203   return result;
    204 }
    205 /*************************** GET DECIMAL FROM BINARY **************************
    206 | This function will take in a binary input and return an intiger with the     |
    207 | corrisponding value, assumed as decimal                                      |
    208 ******************************************************************************/
    209 int getDecimalFromBinary (int * array, int length) {  
    210   int result = 0;
    211   int i;
    212   for (i = 0; i < length; i++) {
    213     result = result<<1;
    214     result += array[i]&0x01;
    215   }
    216   return result;
    217 }
    218 
    219 
    220 
    221 void recurseDecimal (unsigned int val) {
    222   if (val > 0 ) {
    223     recurseDecimal(val/10);
    224     USART_Transmit('0'+val%10);
    225   }
    226   return;
    227 }
    228 
    229 void printDecimal (int array[45]) {
    230   #ifdef Manufacturer_ID_Output
    231   int manufacturerId = getDecimalFromBinary( array + MANUFACTURER_ID_OFFSET,MANUFACTURER_ID_LENGTH);
    232   manufacturerId = getDecimalFromBinary( array + MANUFACTURER_ID_OFFSET,MANUFACTURER_ID_LENGTH);
    233   recurseDecimal(manufacturerId);
    234   #endif
    235   
    236   #ifdef Split_Tags_With
    237     USART_Transmit(Split_Tags_With);
    238   #endif
    239   
    240   #ifdef Site_Code_Output
    241   
    242   int siteCode = getDecimalFromBinary( array + SITE_CODE_OFFSET,SITE_CODE_LENGTH);
    243   recurseDecimal(siteCode);
    244   #endif
    245 
    246   #ifdef Split_Tags_With
    247     USART_Transmit(Split_Tags_With);
    248   #endif
    249 
    250   #ifdef Unique_Id_Output
    251   int lastId = getDecimalFromBinary( array + UNIQUE_ID_OFFSET,UNIQUE_ID_LENGTH);
    252   recurseDecimal(lastId);
    253   #endif
    254   
    255   USART_Transmit('
    ');
    256   USART_Transmit('
    ');
    257 }
    258 void printHexadecimal (int array[45]) {
    259   int i;
    260   #ifdef Manufacturer_ID_Output
    261   for (i = MANUFACTURER_ID_OFFSET; i < MANUFACTURER_ID_OFFSET+MANUFACTURER_ID_LENGTH; i+=4) {
    262     USART_Transmit(binaryTohex(array[i],array[i+1],array[i+2],array[i+3]));
    263   }
    264   #endif
    265   
    266   #ifdef Split_Tags_With
    267     USART_Transmit(Split_Tags_With);
    268   #endif
    269   
    270   #ifdef Site_Code_Output
    271   for (i = SITE_CODE_OFFSET; i < SITE_CODE_OFFSET+SITE_CODE_LENGTH; i+=4) {
    272     USART_Transmit(binaryTohex(array[i],array[i+1],array[i+2],array[i+3]));
    273   }
    274   #endif
    275 
    276   #ifdef Split_Tags_With
    277     USART_Transmit(Split_Tags_With);
    278   #endif
    279 
    280   #ifdef Unique_Id_Output
    281   for (i = UNIQUE_ID_OFFSET; i < UNIQUE_ID_OFFSET+UNIQUE_ID_LENGTH; i+=4) {
    282     USART_Transmit(binaryTohex(array[i],array[i+1],array[i+2],array[i+3]));
    283   }
    284   #endif
    285   USART_Transmit('
    ');
    286   USART_Transmit('
    ');
    287 }
    288 
    289 
    290 
    291 void printBinary (int array[45]) {
    292   int i;
    293   #ifdef Manufacturer_ID_Output
    294   for (i = MANUFACTURER_ID_OFFSET; i < MANUFACTURER_ID_OFFSET+MANUFACTURER_ID_LENGTH; i++) {
    295     USART_Transmit('0'+array[i]);
    296   }
    297   #endif
    298   
    299   #ifdef Split_Tags_With
    300     USART_Transmit(Split_Tags_With);
    301   #endif
    302   
    303   #ifdef Site_Code_Output
    304   for (i = SITE_CODE_OFFSET; i < SITE_CODE_OFFSET+SITE_CODE_LENGTH; i++) {
    305     USART_Transmit('0'+array[i]);
    306   }
    307   #endif
    308 
    309   #ifdef Split_Tags_With
    310     USART_Transmit(Split_Tags_With);
    311   #endif
    312 
    313   #ifdef Unique_Id_Output
    314   for (i = UNIQUE_ID_OFFSET; i < UNIQUE_ID_OFFSET+UNIQUE_ID_LENGTH; i++) {
    315     USART_Transmit('0'+array[i]);
    316   }
    317   #endif
    318   USART_Transmit('
    ');
    319   USART_Transmit('
    ');
    320 }
    321 
    322 
    323 
    324 
    325 /********************************* Search Tag *********************************
    326 | This function searches for a tag in the list of tags stored in the flash     |
    327 | memory, if the tag is found then the function returns 1 (true) if the tag    |
    328 | is not found then the function returns 0 (false)                             |
    329 ******************************************************************************/
    330 int searchTag (int tag) {
    331   int i;
    332   for (i = 0; i < namesize; i++) {
    333     if (tag == names[i]) {
    334       return 1;
    335     }
    336   }
    337   return 0;
    338 }
    339 
    340 
    341 
    342 
    343 void whiteListSuccess () {
    344   PORTB |= 0x04;
    345   // open the door
    346   OCR1A = 10000 - SERVO_OPEN;
    347   {
    348     unsigned long i;
    349     for (i = 0; i < 2500000; i++) {
    350       if (!((PINB & (1<<7))>>7)) {
    351         break;
    352       }
    353     }
    354   }
    355   //close the door
    356   OCR1A = 10000 - SERVO_CLOSE;
    357   {
    358     unsigned long i;
    359     for (i = 0; i < 500000; i++) {
    360       asm volatile ("nop");
    361     }
    362   }
    363   OCR1A = 0;
    364   wait (5000);
    365 }
    366 void whiteListFailure () {
    367   PORTB |= 0x08;
    368   wait (5000);
    369 }
    370 
    371 
    372 
    373   //////////////////////////////////////////////////////////////////////////////
    374  ///////////////////////////// ANALYSIS FUNCTIONS /////////////////////////////
    375 //////////////////////////////////////////////////////////////////////////////
    376 
    377 /************************* CONVERT RAW DATA TO BINARY *************************
    378 | Converts the raw 'pulse per wave' count (5,6,or 7) to binary data (0, or 1)  |
    379 ******************************************************************************/
    380 void convertRawDataToBinary (char * buffer) {
    381   int i;
    382   for (i = 1; i < ARRAYSIZE; i++) {
    383     if (buffer[i] == 5) {
    384       buffer[i] = 0;
    385     }
    386     else if (buffer[i] == 7) {
    387       buffer[i] = 1;
    388     }
    389     else if (buffer[i] == 6) {
    390        buffer[i] = buffer[i-1];
    391     }
    392     else {
    393       buffer[i] = -2;
    394     }
    395   }
    396 }
    397 
    398 /******************************* FIND START TAG *******************************
    399 | This function goes through the buffer and tries to find a group of fifteen   |
    400 | or more 1's in a row. This sigifies the start tag. If you took the fifteen   |
    401 | ones in multibit they would come out to be '111' in single-bit               |
    402 ******************************************************************************/
    403 int findStartTag (char * buffer) {
    404   int i;
    405   int inARow = 0;
    406   int lastVal = 0;
    407   for (i = 0; i < ARRAYSIZE; i++) {
    408     if (buffer [i] == lastVal) {
    409       inARow++;
    410     }
    411     else {
    412       // End of the group of bits with the same value
    413       if (inARow >= 15 && lastVal == 1) {
    414         // Start tag found
    415         break;
    416       }
    417       // group of bits was not a start tag, search next tag
    418       inARow = 1;
    419       lastVal = buffer[i];
    420     }
    421   }
    422   return i;
    423 }
    424 
    425 /************************ PARSE MULTIBIT TO SINGLE BIT ************************
    426 | This function takes in the start tag and starts parsing the multi-bit code   |
    427 | to produce the single bit result in the outputBuffer array the resulting     |
    428 | code is single bit manchester code                                           |
    429 ******************************************************************************/
    430 void parseMultiBitToSingleBit (char * buffer, int startOffset, int outputBuffer[]) {
    431   int i = startOffset; // the offset value of the start tag
    432   int lastVal = 0; // what was the value of the last bit
    433   int inARow = 0; // how many identical bits are in a row// this may need to be 1 but seems to work fine
    434   int resultArray_index = 0;
    435   for (;i < ARRAYSIZE; i++) {
    436     if (buffer [i] == lastVal) {
    437       inARow++;
    438     }
    439     else {
    440       // End of the group of bits with the same value
    441       if (inARow >= 4 && inARow <= 8) {
    442         // there are between 4 and 8 bits of the same value in a row
    443         // Add one bit to the resulting array
    444         outputBuffer[resultArray_index] = lastVal;
    445         resultArray_index += 1;
    446       }
    447       else if (inARow >= 9 && inARow <= 14) {
    448         // there are between 9 and 14 bits of the same value in a row
    449         // Add two bits to the resulting array
    450         outputBuffer[resultArray_index] = lastVal;
    451         outputBuffer[resultArray_index+1] = lastVal;
    452         resultArray_index += 2;
    453       }
    454       else if (inARow >= 15 && lastVal == 0) {
    455         // there are more then 15 identical bits in a row, and they are 0s
    456         // this is an end tag
    457         break;
    458       }
    459       // group of bits was not the end tag, continue parsing data
    460       inARow = 1;
    461       lastVal = buffer[i];
    462       if (resultArray_index >= 90) {
    463         //return;
    464       }
    465     }
    466   }
    467 }
    468 
    469 /******************************* Analize Input *******************************
    470 | analizeInput(void) parses through the global variable and gets the 45 bit   |
    471 | id tag.                                                                     |
    472 | 1) Converts raw pulse per wave count (5,6,7) to binary data (0,1)           |
    473 | 2) Finds a start tag in the code                                            |
    474 | 3) Parses the data from multibit code (11111000000000000111111111100000) to |
    475 |     singlebit manchester code (100110) untill it finds an end tag           |
    476 | 4) Converts manchester code (100110) to binary code (010)                   |
    477 *****************************************************************************/
    478 void analizeInput (void) {
    479   int i;                // Generic for loop 'i' counter
    480   int resultArray[90];  // Parsed Bit code in manchester
    481   int finalArray[45];   //Parsed Bit Code out of manchester
    482   int finalArray_index = 0;
    483   
    484   // Initilize the arrays so that any errors or unchanged values show up as 2s
    485   for (i = 0; i < 90; i ++) { resultArray[i] = 2; }
    486   for (i = 0; i < 45; i++)  { finalArray[i] = 2;  }
    487   
    488   // Convert raw data to binary
    489   convertRawDataToBinary (begin);
    490     
    491   // Find Start Tag
    492   int startOffset = findStartTag(begin);
    493   PORTB |= 0x10; // turn an led on on pin B5)
    494   
    495   // Parse multibit data to single bit data
    496   parseMultiBitToSingleBit(begin, startOffset, resultArray);
    497   
    498   // Error checking, see if there are any unset elements of the array
    499   for (i = 0; i < 88; i++) { // ignore the parody bit ([88] and [89])
    500     if (resultArray[i] == 2) {
    501       return;
    502     }
    503   }
    504   //------------------------------------------
    505   // MANCHESTER DECODING
    506   //------------------------------------------
    507   for (i = 0; i < 88; i+=2) { // ignore the parody bit ([88][89])
    508     if (resultArray[i] == 1 && resultArray[i+1] == 0) {
    509       finalArray[finalArray_index] = 1;
    510     }
    511     else if (resultArray[i] == 0 && resultArray[i+1] == 1) {
    512       finalArray[finalArray_index] = 0;
    513     }
    514     else {
    515       // The read code is not in manchester, ignore this read tag and try again
    516       // free the allocated memory and end the function
    517       return;
    518     }
    519     finalArray_index++;
    520   }
    521   
    522   #ifdef Binary_Tag_Output         // Outputs the Read tag in binary over serial
    523     printBinary (finalArray);
    524   #endif
    525     
    526   #ifdef Hexadecimal_Tag_Output    // Outputs the read tag in Hexadecimal over serial
    527     printHexadecimal (finalArray);
    528   #endif
    529     
    530   #ifdef Decimal_Tag_Output
    531     printDecimal (finalArray);
    532   #endif
    533   
    534   
    535   #ifdef Whitelist_Enabled
    536   if (searchTag(getDecimalFromBinary(finalArray+UNIQUE_ID_OFFSET,UNIQUE_ID_LENGTH))){
    537     whiteListSuccess ();
    538   }
    539   else {
    540     whiteListFailure();
    541   }
    542   #endif
    543 }
    544 
    545 /******************************* MAIN FUNCTION *******************************
    546 | This is the main function, it initilized the variabls and then waits for    |
    547 | interrupt to fill the buffer before analizing the gathered data             |
    548 *****************************************************************************/
    549 int main (void) {
    550   int i = 0;
    551 
    552   //------------------------------------------
    553   // VARIABLE INITLILIZATION
    554   //------------------------------------------
    555 
    556   // Load the list of valid ID tags
    557   addNames(); 
    558   
    559   //==========> PIN INITILIZATION <==========//
    560   DDRD = 0x00; // 00000000 configure output on port D
    561   DDRB = 0x1E; // 00011100 configure output on port B
    562   
    563   //=========> SERVO INITILIZATION <=========//
    564   ICR1 = 10000;// TOP count for the PWM TIMER
    565   
    566   // Set on match, clear on TOP
    567   TCCR1A  = ((1 << COM1A1) | (1 << COM1A0));
    568   TCCR1B  = ((1 << CS11) | (1 << WGM13));
    569   
    570   // Move the servo to close Position
    571   OCR1A = 10000 - SERVO_CLOSE;
    572   {
    573     unsigned long j;
    574     for (j = 0; j < 500000; j++) {
    575       asm volatile ("nop");
    576     }
    577   }
    578   // Set servo to idle
    579   OCR1A = 0;
    580   
    581   // USART INITILIZATION
    582   USART_Init();
    583   
    584   //========> VARIABLE INITILIZATION <=======//
    585   count = 0;
    586   begin = malloc (sizeof(char)*ARRAYSIZE);
    587   iter = 0;
    588   for (i = 0; i < ARRAYSIZE; i ++) {
    589     begin[i] = 0;
    590   }
    591   
    592   //=======> INTERRUPT INITILAIZATION <======//
    593   sei ();       // enable global interrupts
    594   EICRA = 0x03; // configure interupt INT0
    595   EIMSK = 0x01; // enabe interrupt INT0
    596   
    597   //------------------------------------------
    598   // MAIN LOOP
    599   //------------------------------------------
    600   while (1) {
    601     sei(); //enable interrupts
    602     
    603     while (1) { // while the card is being read
    604       if (iter >= ARRAYSIZE) { // if the buffer is full
    605         cli(); // disable interrupts
    606         break; // continue to analize the buffer
    607       }
    608     }  
    609     
    610     PORTB &= ~0x1C;
    611     
    612     //analize the array of input
    613     analizeInput ();
    614     
    615     //reset the saved values to prevent errors when reading another card
    616     count = 0;
    617     iter = 0;
    618     for (i = 0; i < ARRAYSIZE; i ++) {
    619       begin[i] = 0;
    620     }
    621   }
    622 }
    View Code

     

    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:

     1 /*  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  */
     27 
     28 #include "TimerOne.h"
     29 
     30 int ledPin = 13; // LED connected to digital pin 13
     31 int inPin = 7;   // sensing digital pin 7
     32 int val;
     33 int bitlenctr = 0;
     34 int curState = 0;
     35 
     36 #define maxBuf 1000 //reduce to 100 or so for debugging
     37 #define debug  0
     38 
     39 char raw[maxBuf];
     40 
     41 int index = 0;
     42 int bufnum = 0;
     43 #define   redLED 12
     44 #define   grnLED 11 
     45 
     46 void setup()
     47 {
     48   Serial.begin(9600);
     49   Timer1.initialize(7);  // initialize timer1, and set the frequency; this drives both the LC tank as well as the pulse timing clock
     50   // note: modify this as needed to achieve resonance and good match with the desired tags
     51   // the argument value is in microseconds per RF cycle, so 8us will yield RF of 125kHz, 7us --> 143kHz, etc.
     52 
     53   Timer1.pwm(9, 512);           // setup pwm on pin 9, 50% duty cycle
     54   Timer1.attachInterrupt(callback);  // attaches callback() as a timer overflow interrupt, once per RF cycle
     55 
     56   pinMode(ledPin, OUTPUT);      // sets the digital pin 13 as output for scope monitoring
     57   pinMode(inPin, INPUT);      // sets the digital pin 7 as input to sense receiver input signal
     58   pinMode(grnLED, OUTPUT);
     59   pinMode(redLED, OUTPUT);
     60   digitalWrite(grnLED, 0);
     61   digitalWrite(redLED, 1);
     62 }
     63 
     64 void callback()
     65 {
     66   val = digitalRead(inPin);
     67   digitalWrite(ledPin, val); // for monitoring
     68   bitlenctr++;
     69   if(val != curState) {
     70     // got a transition
     71     curState = val;
     72     if(val == 1) {
     73       // got a start of cycle (low to high transition)
     74       if(index < maxBuf) {
     75         raw[index++] = bitlenctr;
     76       }
     77       bitlenctr = 1;
     78     }
     79   }  
     80 }
     81 
     82 void loop()
     83 {  
     84   if(index >= maxBuf) {
     85 
     86     Serial.print("got buf num: ");
     87     Serial.println(bufnum);
     88 
     89     if(debug) {
     90       for(int i = 0; i < maxBuf;
     91       i++) {
     92           Serial.print((int)raw[i]);
     93         Serial.print("/");
     94       }
     95       Serial.println("///raw data");
     96       delay(2000);
     97     }
     98 
     99     // analyze this buffer
    100     // first convert pulse durations into raw bits
    101     int tot1 = 0;
    102     int tot0 = 0;
    103     int tote = 0;
    104     int totp = 0;
    105     raw[0] = 0;
    106     for(int i = 1; i < maxBuf; i++) {
    107       int v = raw[i];
    108       if(v == 4) {
    109         raw[i] = 0;
    110         tot0++;
    111       } 
    112       else if(v == 5) {
    113         raw[i] = raw[i - 1];
    114         totp++;
    115       } 
    116       else if(v == 6 || v == 7) {
    117         raw[i] = 1;
    118         tot1++;
    119       } 
    120       else {
    121         raw[i] = 101; // error code
    122         tote++;
    123       }  
    124     }   
    125 
    126     // next, search for a "start tag" of 15 high bits in a row
    127     int samecnt = 0;
    128     int start = -1;
    129     int lastv = 0;
    130     for(int i = 0; i < maxBuf; i++) {
    131       if(raw[i] == lastv) {
    132         // inside one same bit pattern, keep scanning
    133         samecnt++;
    134       } 
    135       else {
    136         // got new bit pattern
    137         if(samecnt >= 15 && lastv == 1) {
    138           // got a start tag prefix, record index and exit
    139           start = i;
    140           break;
    141         }
    142         // either group of 0s, or fewer than 15 1s, so not a valid tag, keep scanning
    143         samecnt = 1;
    144         lastv = raw[i];
    145       }  
    146     }
    147 
    148     // if a valid prefix tag was found, process the buffer
    149     if(start > 0 && start < (maxBuf - 5*90)) { //adjust to allow room for full dataset past start point
    150       process_buf(start);
    151     } 
    152     else {
    153       Serial.println("no valid data found in buffer");
    154     }
    155     if(debug) {
    156       for(int i = 0; i < maxBuf;
    157         i++) {
    158           Serial.print((int)raw[i]);
    159         Serial.print("/");
    160       }
    161       Serial.print("///
    buffer stats: zeroes:");
    162       Serial.print(tot0); 
    163       Serial.print("/ones:"); 
    164       Serial.print(tot1);
    165       Serial.print("/prevs:"); 
    166       Serial.print(totp);
    167       Serial.print("/errs:");
    168       Serial.println(tote);  
    169       delay(1000);
    170     }
    171 
    172     // start new buffer, reset all parameters
    173     bufnum++;
    174     curState = 0;
    175     index = 0;
    176   } 
    177   else {
    178     delay(5);
    179   }  
    180 }
    181 
    182 // process an input buffer with a valid start tag
    183 // start argument is index to first 0 bit past prefix tag of 15+ ones
    184 void process_buf(int start) {
    185   // first convert multi bit codes (11111100000...) into manchester bit codes
    186   int lastv = 0;
    187   int samecnt = 0;
    188   char manch[91];
    189   char final[45];
    190   int manchindex = 0;
    191 
    192   Serial.println("got a valid prefix, processing data buffer...");
    193   for(int i = start + 1; i < maxBuf && manchindex < 90; i++) {
    194     if(raw[i] == lastv) {
    195       samecnt++;
    196     } 
    197     else {
    198       // got a new bit value, process the last group
    199       if(samecnt >= 3 && samecnt <= 8) {
    200         manch[manchindex++] = lastv;
    201       } 
    202       else if(samecnt >= 9 && samecnt <= 14) {
    203         // assume a double bit, so record as two separate bits
    204         manch[manchindex++] = lastv;
    205         manch[manchindex++] = lastv;
    206       } 
    207       else if(samecnt >= 15 && lastv == 0) {
    208         Serial.println("got end tag");
    209         // got an end tag, exit
    210         break;
    211       } 
    212       else {
    213         // last bit group was either too long or too short
    214         Serial.print("****got bad bit pattern in buffer, count: ");
    215         Serial.print(samecnt);
    216         Serial.print(", value: ");
    217         Serial.println(lastv);
    218         err_flash(3);
    219         return;
    220       }  
    221       samecnt = 1;
    222       lastv = raw[i];
    223     } //new bit pattern
    224   }
    225 
    226   Serial.println("converting manchester code to binary...");
    227   // got manchester version, convert to final bits
    228   for(int i = 0, findex = 0; i < 90; i += 2, findex++) {
    229     if(manch[i] == 1 && manch[i+1] == 0) {
    230       final[findex] = 1;
    231     } 
    232     else if(manch[i] == 0 && manch[i+1] == 1) {
    233       final[findex] = 0;
    234     } 
    235     else {
    236       // invalid manchester code, exit
    237       Serial.println("****got invalid manchester code");
    238       err_flash(3);
    239       return;
    240     }
    241   }
    242 
    243   // convert bits 28 thru 28+16 into a 16 bit integer
    244   int code = 0;
    245   int par = 0;
    246   for(int i = 28, k = 15; i < 28+16; i++, k--) {
    247     code |= (int)final[i] << k;
    248   }
    249   int paritybit = final[28+16];
    250   for(int i = 0; i < 45; i++) {
    251     par ^= final[i];
    252   }
    253 
    254   if(par) {  
    255     Serial.print("got valid code: ");
    256     Serial.println((unsigned int)code);
    257     // do something here with the detected code...
    258     // 
    259     //
    260     digitalWrite(redLED, 0);
    261     digitalWrite(grnLED, 1);
    262     delay(2000);
    263     digitalWrite(grnLED, 0);
    264     digitalWrite(redLED, 1);
    265   } 
    266   else {
    267     Serial.println("****parity error for retrieved code");
    268     err_flash(3);
    269   }  
    270 }
    271 
    272 // flash red for duration seconds
    273 void err_flash(int duration) {
    274   return;
    275   for(int i = 0; i < duration*10; i++) {
    276     digitalWrite(redLED, 0);
    277     delay(50);
    278     digitalWrite(redLED, 1);
    279     delay(50);
    280   } 
    281 }
    View Code

    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.

    1 {                                   
      2 
      3 rfid-lf - Minimalist software-only reader for low-frequency RFID tags
      4 ─────────────────────────────────────────────────────────────────────
      5 
      6 I. Supported Tags
      7 
      8   Tested with the EM4102 compatible RFID tags sold by Parallax, and with
      9   HID proximity cards typically issued for door security.
     10 
     11   These are two fairly typical ASK and FSK tags, respectively. To support
     12   other RFID protocols, it should be possible to use one of these two
     13   decoders as a starting point.
     14 
     15 I. Theory of operation
     16 
     17   The Propeller itself generates the 125 kHz carrier wave, which excites
     18   an LC tank. The inductor in this tank is an antenna coil, which becomes
     19   electromagnetically coupled to the RFID tag when it comes into range.
     20   This carrier powers the RFID tag. The RFID sends back data by modulating
     21   the amplitude of this carrier. We detect this modulation via a peak
     22   detector and low-pass filter (D1, C2, R1). The detected signal is AC
     23   coupled via C3, and an adjustable bias is applied via R3 from a PWM signal
     24   which is filtered by R4 and C4. This signal is fed directly to an input
     25   pin via R2 (which limits high-frequency noise) where the pin itself acts
     26   as an analog comparator.
     27 
     28   The waveform which exits the peak detector is a sequence of narrow spikes
     29   at each oscillation of the carrier wave. Each of these spikes occurs when
     30   C2 charges quickly via D1, then discharges slowly via R1. We can use this
     31   to fashion a simple A/D converter by having the Propeller use a counter to
     32   measure the duty cycle of this waveform. The higher the carrier amplitude,
     33   the more charge was stored in C2, and the longer the pulse will be.
     34 
     35   At this point, we have a digital representation of the baseband RF signal
     36   from the RFID tags. This signal is then put through a bank of demodulators,
     37   filters, and protocol decoders.
     38   
     39   We include multiple closed-loop control systems in order to keep the reader
     40   calibrated. We adjust the Threshold (THR) voltage dynamically in order to
     41   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
     80      which caused excessive harmonic distortion. The best option is probably
     81      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.
     85 
     86      Hook the inverting input up to a voltage divider that generates a Vdd/2
     87      reference, and the non-inverting input up to the junction between C3 and R3.
     88      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
    101   DEFAULT_THRESHOLD  = $80000000    ' Initial comparator frequency
    102   THRESHOLD_GAIN     = 7            ' Log2 gain for threshold control loop
    103   CARRIER_RATE       = 7            ' Inverse Log2 rate for carrier frequency control loop
    104   CARRIER_GAIN       = 300          ' Gain for carrier frequency control loop  
    105 
    106   SHIELD2_PIN        = 6            ' Optional, driven to ground to shield INPUT.
    107   INPUT_PIN          = 5            ' Input signal
    108   SHIELD1_PIN        = 4            ' Optional, driven to ground to shield INPUT.
    109   THRESHOLD_PIN      = 3            ' PWM output for detection threshold
    110   DEBUG_PIN          = 2            ' Optional, for oscilloscope debugging
    111   CARRIER_POS_PIN    = 1            ' Carrier wave pin
    112   CARRIER_NEG_PIN    = 0            ' Carrier wave pin
    113 
    114   ' Code formats.
    115   ' The low 16 bits indicate length, in longs.
    116   ' Other bits are used to make each format unique.
    117 
    118   FORMAT_EM4102      = $0001_0002   ' Two longs: 8-bit manufacturer code, 32-bit unique ID.
    119   FORMAT_HID         = $0002_0002   ' HID 128 KHz prox cards. 45-bit code.
    120   
    121 VAR
    122   byte cog1, cog2
    123   long format_buf
    124   long shared_pulse_len
    125   long em_buffer[2]
    126   long hid_buffer[2]
    127   
    128 PUB start | period, okay
    129 
    130   ' Fundamental timing parameters: Default carrier wave
    131   ' drive frequency, and the period of the carrier in clock
    132   ' cycles.
    133 
    134   init_frqa := fraction(CARRIER_HZ, clkfreq)
    135   period := clkfreq / CARRIER_HZ
    136 
    137   ' Derived timing parameters
    138   
    139   pulse_target := period / 3      ' What is our 'center' pulse width?
    140   next_hyst := pulse_target
    141   hyst_constant := period / 100   ' Amount of pulse hysteresis
    142 
    143   ' 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
    165   '' is available, returns zero.
    166   ''
    167   '' 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 '==============================================================================
    195 ' Cog 1: Pulse timing
    196 '==============================================================================
    197 
    198                         org
    199 
    200 cog1_entry
    201 
    202                         '
    203                         ' Measure a low pulse.
    204                         '
    205                         ' The length of this pulse is proportional to the amplitude of the carrier.
    206                         ' To measure it robustly, we'll measure the average duty cycle rather than
    207                         ' looking at actual rising or falling edges.
    208                         '
    209                         ' We could easily do this in cog2, but we're out of counters there. Measure
    210                         ' the pulse length using this cog's CTRA, and send it back to cog2 synchronously.
    211                         '                        
    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 '==============================================================================
    232 ' Cog 2: Control loops and protocol decoding
    233 '==============================================================================
    234 
    235                         '======================================================
    236                         ' Initialization
    237                         '======================================================
    238 
    239                         org
    240 
    241 cog2_entry              mov     dira, init_dira
    242                         mov     ctra, init_ctra         ' CTRA generates the carrier wave
    243                         mov     frqa, init_frqa
    244                         mov     ctrb, init_ctrb         ' CTRB generates a pulse threshold bias
    245                         mov     frqb, init_frqb
    246 
    247 
    248                         '======================================================
    249                         ' Main A/D loop
    250                         '======================================================
    251 
    252 mainLoop
    253                         '
    254                         ' Synchronize each loop iteration with a rising edge on
    255                         ' the carrier wave. To avoid races when reading pulse_ptr,
    256                         ' we should ideally synchronize at a point 180 degrees
    257                         ' out of phase with the negative pulse from our analog peak
    258                         ' detector.
    259                         '
    260 
    261                         mov     prev_pulse, cur_pulse   ' Remember previous pulse info 
    262 
    263 :high                   test    h80000000, phsa wz
    264               if_z      jmp     #:high
    265 :low                    test    h80000000, phsa wz      
    266               if_nz     jmp     #:low
    267 
    268                         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
    272                         sub     pulse_len, prev_pulse
    273                         
    274                         add     pulse_count, #1         ' Global pulse counter, used below
    275 
    276                         '
    277                         ' Adjust the comparator threshold in order to achieve our pulse_target,
    278                         ' using a linear proportional control loop.
    279                         '
    280 
    281                         mov     r0, pulse_target
    282                         sub     r0, pulse_len
    283                         shl     r0, #THRESHOLD_GAIN
    284                         sub     frqb, r0
    285 
    286                         '
    287                         ' 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
    289                         ' value of frqb is actually a filtered representation of our overall
    290                         ' inverse carrier amplitude, so we want to adjust frqa in order to
    291                         ' minimize frqb.
    292                         '
    293                         ' Since we can't adjust frqa drastically while the RFID reader is
    294                         ' operating, we'll make one small adjustment at a time, and decide
    295                         ' 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
    297                         ' circuit tuned as the analog components fluctuate slightly in value
    298                         ' due to temperature variations.
    299                         '
    300                         ' This algorithm is divided into four phases, sequenced using two
    301                         ' bits from the pulse_count counter:
    302                         '
    303                         '   0. Store a reference frqb value, and increase frqa
    304                         '   1. Test the frqb value. If it wasn't an improvement, decrease frqa
    305                         '   2. Store a reference frqb value, and decrease frqa
    306                         '   3. Test the frqb value. If it wasn't an improvement, increase frqa
    307                         '
    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                         '
    321                         ' That takes care of all our automatic calibration tasks.. now to
    322                         ' receive some actual bits. Since our pulse length is proportional
    323                         ' to the amount of carrier attenuation, our demodulated bits (or
    324                         ' baseband FSK signal) are determined by the amount of pulse width
    325                         ' excursion from our center position.
    326                         '
    327                         ' 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
    329                         ' compare pulse_len to pulse_target. We go one step further, and
    330                         ' include a little hysteresis.
    331                         '
    332 
    333                         cmp     next_hyst, pulse_len wc    
    334                         muxc    outa, #|<DEBUG_PIN         ' Output demodulated bit to debug pin
    335 
    336                         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
    342               if_nc     sub     baseband_s32, #1           '   ... 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                         '
    353                         ' Our work here is done. Give each card-specific protocol decoder
    354                         ' a chance to find actual ones and zeroes.
    355                         '
    356                         
    357                         call    #rx_hid
    358                         call    #rx_em4102
    359 
    360                         jmp     #mainLoop
    361 
    362 
    363                         '======================================================
    364                         ' EM4102 Decoder 
    365                         '======================================================
    366 
    367                         ' The EM4102 chip actually supports multiple clock rates
    368                         ' and multiple encoding schemes: Manchester, Biphase, and PSK.
    369                         ' Their "standard" scheme, and the one Parallax uses, is
    370                         ' ASK with Manchester encoding at 32 clocks per code (64
    371                         ' clocks per bit). Our support for this format is hardcoded.
    372                         '
    373                         ' The EM4102's data packet consists of 40 payload bits (an 8-bit
    374                         ' manufacturer ID and 32-bit serial number), 9 header bits, 1 stop
    375                         ' bit, and 14 parity bits. This is a total of 64 bits. These bits
    376                         ' are manchester encoded into 128 baseband bits. 
    377                         '
    378                         ' We could decode this the traditional way- do clock/data recovery
    379                         ' on the Manchester signal using a DPLL, look for the header, do
    380                         ' 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
    382                         ' to get a more noise-resistant decoding.
    383                         '
    384                         ' A packet in its entirety is 4096 clocks. This is 128 manchester
    385                         ' codes by 32 clocks per code. We can treat this as 32 possible phases
    386                         ' and 128 possible code alignments. In fact, it's a more convenient
    387                         ' 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                         '
    390                         ' To save memory, we'll decimate the signal and examine it only every
    391                         ' other carrier cycle. This gives us only 32 possible phases.
    392                         '
    393                         ' Every time a code arrives, we shift it into a 64-bit shift register.
    394                         ' We have 32 total shift registers, which we cycle through. Every time
    395                         ' 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)
    400               if_nz     jmp     #rx_em4102_ret
    401 
    402                         ' Low pass filter with automatic gain control. Look at an average of the last
    403                         ' 32 bits, and correct our duty cycle to 50% by picking a threshold based on
    404                         ' 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
    411 :shift2                 rcl     em_bits+0, #1
    412 :load1                  mov     em_shift+0, em_bits+0   ' 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
    416                         add     :shift2, dest_2
    417                         add     :load1, #2
    418                         add     :load2, #2
    419                         cmp     :shift1, em_shift1_end wz
    420               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
    426               if_nz     jmp     #rx_em4102_ret
    427               
    428                         '
    429                         ' At this point, the encoded packet should have the following format:
    430                         ' (Even bits in the manchester code are not shown.)
    431                         '
    432                         '   bits+0:  11111111_1ddddPdd_ddPddddP_ddddPddd                        
    433                         '   bits+1:  dPddddPd_dddPdddd_PddddPdd_ddPPPPP0
    434                         '
    435                         ' Where 'd' is a data bit and 'P' is a parity bit.
    436                         '
    437 
    438                         mov     r0, em_shift+0          ' Look for the header of nine "1" bits
    439                         shr     r0, #32-9
    440                         cmp     r0, #$1FF wz
    441               if_nz     jmp     #rx_em4102_ret
    442 
    443                         rcr     em_shift+1, #1 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 10 data rows...
    447 
    448                         mov     em_decoded, #0
    449                         mov     em_decoded+1, #0
    450                         mov     em_parity, #0
    451                         mov     r0, #10
    452 :row
    453                         mov     r2, em_shift+0          ' Extract the next row's 5 bits
    454                         shr     r2, #18
    455                         and     r2, #%11111 wc          ' Check row parity
    456               if_c      jmp     #rx_em4102_ret
    457 
    458                         mov     r1, em_decoded+1        ' 64-bit left shift by 4
    459                         shl     em_decoded+1, #4
    460                         shl     em_decoded+0, #4
    461                         shr     r1, #32-4
    462                         or      em_decoded+0, r1 
    463                         
    464                         shr     r2, #1                  ' Drop row parity bit
    465                         xor     em_parity, r2           ' Update column parity
    466                         or      em_decoded+1, r2        ' Store 4 decoded bits
    467 
    468                         mov     r1, em_shift+1          ' 64-bit left shift by 5
    469                         shl     em_shift+1, #5
    470                         shl     em_shift+0, #5
    471                         shr     r1, #32-5
    472                         or      em_shift+0, r1 
    473 
    474                         djnz    r0, #:row
    475 
    476                         mov     r2, em_shift+0          ' Extract the next 4 bits
    477                         shr     r2, #19
    478                         and     r2, #%1111
    479                         xor     em_parity, r2 wc        ' Test column parity
    480               if_c      jmp     #rx_em4102_ret
    481 
    482                         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.
    492 
    493 
    494                         '======================================================
    495                         ' HID Decoder
    496                         '======================================================
    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
    501                         ' from my own reverse engineering.
    502                         '
    503                         ' These cards use a FSK scheme, which appears to be identical
    504                         ' to the one used by the Microchip MCRF200. Zeroes and ones are
    505                         ' nominally encoded by attenuation pulses of 8 and 10 cycles,
    506                         ' respectively. Bits appear to last 50 RF cycles, which is one
    507                         ' of the configurable bit lengths for the MCRF200.
    508                         '
    509                         ' See Figure 2-2 of the Microchip microID 125 kHz RFID System Design Guide:
    510                         '   http://ww1.microchip.com/downloads/en/devicedoc/51115F.pdf
    511                         '
    512                         ' So, to decode this signal we use a similar low-pass filter and shift
    513                         ' register technique as the one we used above in the EM4102 decoder,
    514                         ' but only after doing an FSK detection stage.
    515                         '
    516                         ' After FSK detection and low-pass filtering, the signal appears to
    517                         ' be a repeating pattern of 96 of these 50-cycle bits. Each repetition
    518                         ' begins with the special sequence '000111'. The packet data is manchester
    519                         ' encoded, so runs of three zeroes or ones cannot occur during the packet body.
    520                         ' This means there should be 45 bits of actual packet data after manchester
    521                         ' decoding.
    522                         '
    523                         ' XXX: There is almost certainly another level of encoding present,
    524                         '      since HID claims that these cards have either a 26-bit or 34-bit
    525                         '      code. It's likely that there is an error correcting/detecting code
    526                         '      embedded within the 45-bit signal we're receiving here.
    527 
    528 rx_hid
    529                         ' 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,
    531                         ' 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.
    533                         '
    534                         ' On each stable rising edge, we can detect a 1 or a 0 by tapping the
    535                         ' hid_fsk_reg. The location of the tap determines our threshold frequency.
    536                         ' This detected bit is latched until the next rising edge.
    537 
    538                         shl     hid_fsk_reg, #1         ' Advance the FSK detection shifter
    539 
    540                         mov     r0, baseband_reg        ' Detect rising edges
    541                         and     r0, #%1111
    542                         cmp     r0, #%0001 wz           ' If Z=1, it's an edge.
    543 
    544               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.
    547               if_z      test    hid_fsk_reg, hid_fsk_mask wc
    548 
    549                         ' Now the detected FSK bit is in the carry flag. Feed it into
    550                         ' a 32-bit-wide low pass filter, which is close enough to our
    551                         ' bit length of 50 cycles. We correct the 32-bit filter's DC bias
    552                         ' using a 256-bit filter. This helps us maintain an even duty cycle
    553                         ' in the received manchester bits.
    554 
    555               if_nc     add     hid_lp_s32, #1
    556               if_nc     add     hid_lp_s256, #1
    557                         rcl     hid_lp_reg+0, #1 wc
    558               if_nc     sub     hid_lp_s32, #1              
    559                         rcl     hid_lp_reg+1, #1 wc
    560                         rcl     hid_lp_reg+2, #1 wc
    561                         rcl     hid_lp_reg+3, #1 wc
    562                         rcl     hid_lp_reg+4, #1 wc
    563                         rcl     hid_lp_reg+5, #1 wc
    564                         rcl     hid_lp_reg+6, #1 wc
    565                         rcl     hid_lp_reg+7, #1 wc
    566               if_nc     sub     hid_lp_s256, #1              
    567 
    568                         mov     r0, hid_lp_s256
    569                         shr     r0, #3
    570                         cmp     hid_lp_s32, r0 wc
    571                         
    572                         ' 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,
    574                         ' validate the manchester encoding, and extract the actual data bits
    575                         ' from the received signal. This is a typical data/clock recovery problem.
    576                         ' We could use a PLL, but when doing this in software it's easier and less
    577                         ' error-prone to just brute-force it, and try to detect a signal at all
    578                         ' possible phases.
    579                         '
    580                         ' At this point there are 100 possible phases, and we need enough room to
    581                         ' store 51 bits. (45 data bits + 3 bits each from 2 headers) We'll use a
    582                         ' 64-bit shift register for each phase.
    583                         '
    584                         ' To cut down on the amount of memory required, we'll decimate the signal
    585                         ' by processing on only every other carrier cycle. This means we only have
    586                         ' 50 possible phases.
    587 
    588                         test    pulse_count, #1 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
    592 :shift2                 rcl     hid_bits+0, #1
    593 :load1                  mov     hid_shift+0, hid_bits+0 ' 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
    597                         add     :shift2, dest_2
    598                         add     :load1, #2
    599                         add     :load2, #2
    600                         cmp     :shift1, hid_shift1_end wz
    601               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 180-degree phase shifted version of hid_shift,
    607                         ' for doing manchester validation and header detection.
    608                         ' The bits in hid_shiftp are delayed by 1/2 bit relative to
    609                         ' 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
    624               if_nz     jmp     #rx_hid_ret
    625 
    626                         '
    627                         ' Ok. At this point, we can validate the contents of hid_shift and
    628                         ' 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
    630                         ' data. The header sequence of "000111" turns into "001" in hid_shift
    631                         ' and "011" in hid_shiftp.
    632                         '
    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 3 through 47.
    636                         '
    637 
    638                         mov     r0, hid_shiftp+0        ' Check 1st header at -180°
    639                         and     r0, hid_s_mask
    640                         cmp     r0, hid_sp_check wz
    641               if_z      mov     r0, hid_shift+0         ' 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 -180°
    646               if_z      and     r0, #%111
    647               if_z      cmp     r0, #%001 wz
    648               if_z      mov     r0, hid_shift+1         ' 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
    653               if_z      xor     r0, hid_shift+0         '   (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)
    657               if_z      xor     r0, hid_shift+1
    658               if_z      xor     r0, hFFFFFFFF
    659               if_z      andn    r0, #%111 wz
    660 
    661               if_nz     jmp     #rx_hid_ret             ' Exit on any decoding error
    662 
    663                         ' Hooray, we have a correct-looking packet. Justify and
    664                         ' 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   
    671 
    672                         shr     r0, #1 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
    680                         wrlong  r0, r2
    681                         add     r2, #4
    682                         wrlong  r1, r2
    683                         wrlong  c_format_hid, format_ptr
    684                         
    685 rx_hid_ret              ret
    686 
    687 hid_shift1_end          rcl     hid_bits+1+100, #1 wc   ' This is ":shift1" above, after we've
    688                                                         ' passed the end of the hid_bits array.
    689                         
    690 hid_load3_end           mov     hid_shiftP+0, hid_bits+0+100
    691 
    692 
    693 '------------------------------------------------------------------------------
    694 ' Initialized Data
    695 '------------------------------------------------------------------------------
    696 
    697 hFFFFFFFF     long      $FFFFFFFF
    698 h80000000     long      $80000000
    699 h7FFF0000     long      $7FFF0000
    700 hFFFF         long      $FFFF
    701 zero          long      0
    702 c_format_em   long      FORMAT_EM4102
    703 c_format_hid  long      FORMAT_HID
    704 
    705 dest_2        long      2 << 9
    706 dest_100      long      100 << 9
    707 dest_64       long      64 << 9
    708 input_mask2   long      |<INPUT_PIN
    709 
    710 init_dira     long      |<CARRIER_POS_PIN | |<CARRIER_NEG_PIN | |<THRESHOLD_PIN | |<DEBUG_PIN | |<SHIELD1_PIN | |<SHIELD2_PIN
    711 init_frqa     long      0
    712 init_frqb     long      DEFAULT_THRESHOLD
    713 init_ctra     long      (%00101 << 26) | (CARRIER_POS_PIN << 9) | CARRIER_NEG_PIN 
    714 init_ctrb     long      (%00110 << 26) | THRESHOLD_PIN
    715 
    716 carrier_mask  long      |<CARRIER_RATE - 1
    717 carrier_bit0  long      |<(CARRIER_RATE + 0)
    718 carrier_bit1  long      |<(CARRIER_RATE + 1)
    719 
    720 pulse_target  long      0
    721 next_hyst     long      0
    722 hyst_constant long      0
    723 
    724 baseband_reg  long      0,0,0,0,0,0,0,0
    725 baseband_s32  long      32      ' Number of zeroes in last 32 baseband bits
    726 baseband_s256 long      256     ' Number of zeroes in last 256 baseband bits
    727 
    728 em_buf_ptr    long      0
    729 hid_buf_ptr   long      0
    730 format_ptr    long      0
    731 pulse_ptr2    long      0
    732 
    733 hid_header    long      |<16 - 1
    734 hid_max_bits  long      512
    735 hid_fsk_mask  long      |<9
    736 
    737 hid_lp_reg    long      0,0,0,0,0,0,0,0
    738 hid_lp_s32    long      32      ' Number of zeroes in last 32 FSK bits
    739 hid_lp_s256   long      256     ' Number of zeroes in last 256 FSK bits
    740 
    741 hid_s_mask    long      %111 << (3 + 45 - 32)
    742 hid_s_check   long      %011 << (3 + 45 - 32)
    743 hid_sp_check  long      %001 << (3 + 45 - 32)
    744 hid_data_mask long      (1 << (3 + 45 - 32)) - 1
    745 
    746 
    747 '------------------------------------------------------------------------------
    748 ' Uninitialized Data
    749 '------------------------------------------------------------------------------
    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      ' 64-bit shift register for each of the 32 phases
    768 em_shift      res       2       ' Just the current shift register
    769 em_decoded    res       2       ' Decoded 40-bit EM4102 packet       
    770 em_parity     res       1       ' Column parity accumulator
    771 
    772 hid_bits      res       100     ' 64-bit shift register for each of the 50 phases
    773 hid_shift     res       2       ' Just the current shift register
    774 hid_shiftp    res       2       ' 180 degrees out of phase with hid_shift
    775 hid_decoded   res       2       ' Decoded 45-bit HID packet
    776 
    777                         fit
    View Code

    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.

     RFID System Design Guide.

    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
    1111100000000000011111100000111111000000111110000001111111111000000111111000001111110000

    There 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.

     

    1 /*     Authors: Ricardo Goto and Craig Ross
      2     Project: RFID Security system
      3 */    
      4 
      5 #include <Mega32.h> 
      6 #include <stdio.h>
      7 #include <delay.h>
      8 
      9 /* Define Constants */
     10 #define NUM_BITS 1080
     11 #define NUM_CODES 20
     12 #define RED 0b11111110
     13 #define GREEN 0b11111101
     14 #define OFF 0b11111111
     15 #define CHECK_RECEIVE_TIME 50
     16 #define NORMAL 0
     17 #define REMOTEPOS 1
     18 #define REMOTEAMT 2
     19 
     20 /* Define input pin from RFID circuit */
     21 #define RFIDIN PINA.0
     22 #define LED PORTC
     23 
     24 
     25 /* Prototypes */
     26 void initialize(void);
     27 void gets_str(void);
     28 void puts_str(void);  
     29 void find_start_code(void);
     30 void reduce_sequence(void);
     31 void manchester_decode(void);
     32 unsigned char match_code(unsigned char *char1,unsigned char *char2);
     33 void copy_code(unsigned char *char1,unsigned char *char2);
     34 unsigned char verify_code(unsigned char char1[]);
     35 void check_receive(void);
     36 void time_counter(void);
     37 void store_into_bank(char position, char code[], char offset);
     38 
     39 /* Global Variables */                   
     40 char curr_sample;
     41 char code_count;
     42 char start_flag;
     43 int totalBits;
     44 char bit_array[NUM_BITS+1];
     45 int sample_buffer;
     46 int start_sequence;
     47 int end_sequence;
     48 char reduced_array[91];
     49 char final_code[46];
     50 char code_check[46];
     51 char check_receive_timer;
     52 int door_timer;
     53 char mode;
     54 signed char add_pos;
     55 char del_pos;
     56 char add_amt;
     57 unsigned int seconds;
     58 unsigned int minutes;
     59 unsigned int hours;
     60 unsigned int days;
     61 unsigned char r_days[4];
     62 unsigned char r_hours[3];
     63 unsigned char r_minutes[3];
     64 char no_code[46];
     65 eeprom char code_bank[NUM_CODES][46];
     66 char temp[46];
     67 eeprom char bank_status[NUM_CODES];                                
     68 char r_index, r_buffer[128], r_ready, r_char;
     69 char t_index, t_buffer[128], t_ready, t_char;
     70 
     71 /* USART recieve interrupt */
     72 interrupt[USART_RXC] void uartr(void)
     73 {
     74     r_char = UDR;
     75     UDR = r_char;
     76     if (r_char == 8) 
     77     {
     78         if(r_index != 0) r_index--;
     79     }
     80     else if (r_char != '
    ') r_buffer[r_index++] = r_char;
     81     else
     82     {
     83           r_buffer[r_index] = 0x00;
     84         r_ready = 1;
     85         UCSRB.7 = 0;
     86         putchar('
    ');
     87     }
     88 }
     89 
     90 /* USART transmit interrupt */
     91 interrupt[USART_DRE] void uartt(void)
     92 {
     93     t_char = t_buffer[++t_index];
     94     if (t_char == 0)
     95     {
     96         UCSRB.5 = 0;
     97         t_ready = 1;
     98     }
     99     else UDR = t_char;
    100 }
    101 
    102 /* Timer0 compare match ISR */
    103 interrupt [TIM0_COMP] void timer0_compare(void)
    104 {
    105     /* Decrement timers every millisecond */
    106     if(check_receive_timer > 0) --check_receive_timer;
    107     if(door_timer > 0) door_timer--;
    108      if (seconds > 0) --seconds;
    109 }
    110 
    111 /* External pin interrupt */
    112 interrupt[EXT_INT2] void int2(void)
    113 {
    114     /* 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     */
    127     delay_us(16);
    128     curr_sample= RFIDIN;
    129     if((start_flag == 0) && (curr_sample == 1)) 
    130     {
    131         start_flag= 1;
    132         sample_buffer++;
    133     }
    134     else if((start_flag == 1) && (sample_buffer <= 500)) sample_buffer++;
    135     else if((start_flag == 1) && (totalBits < NUM_BITS))
    136     {
    137         if(curr_sample == 1) bit_array[totalBits++]= 49;
    138         else bit_array[totalBits++]= 48;
    139     }
    140 }
    141 
    142 
    143 void main(void)
    144 {
    145     char i;
    146     initialize();
    147     while(1)
    148     {
    149         if((totalBits == NUM_BITS) && (door_timer == 0) && (mode == NORMAL))
    150         {
    151             /* 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             */
    169             GICR= 0b00000000;
    170             find_start_code();
    171             reduce_sequence();
    172             manchester_decode();
    173             if(code_count == 0)
    174             {   
    175                 copy_code(code_check,final_code);
    176                 code_count= 1;
    177             }
    178             else if(code_count == 1)
    179             {
    180                 if(match_code(code_check,final_code)) code_count= 2;
    181                 else code_count= 0;
    182             }
    183             else if(code_count == 2)
    184             {
    185                 if(match_code(code_check,final_code))
    186                 {
    187                     if(verify_code(final_code))
    188                     {
    189                         sprintf(t_buffer,"
    Card ID: %s
    Access Granted at %03d:%02d:%02d
    
    ",final_code,days,hours,minutes);
    190                         puts_str();
    191                         while(t_ready == 0){};
    192                         LED= GREEN;
    193                         door_timer= 3000;
    194                     }
    195                     else
    196                     {
    197                         sprintf(t_buffer,"
    Card ID: %s
    Access Denied at %03d:%02d:%02d
    
    ",final_code,days,hours,minutes);
    198                         puts_str();
    199                         while(t_ready == 0){};
    200                         LED= RED;
    201                         door_timer= 3000;
    202                     }
    203                 }
    204                 code_count= 0;
    205             }
    206             /* Make sure to reset the interrupt variables
    207                and turn on the external interrupt.
    208             */
    209             totalBits= 0;
    210             start_flag= 0;
    211             sample_buffer= 0;
    212             GICR= 0b00100000;
    213         }
    214         else if(mode == REMOTEAMT)
    215         {   
    216             /* 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             */
    223             add_pos= -1;
    224             for(i= 0; i<NUM_CODES; i++)
    225             {
    226                 if(bank_status[i]== 0)
    227                 {
    228                     add_pos= i;
    229                     bank_status[i]= 1;
    230                     break;
    231                 }
    232             }
    233             if(add_pos != -1)
    234             {
    235                 mode= REMOTEPOS;
    236                 printf("Please place card in front of reader
    ");
    237                 delay_ms(500);
    238                 LED= GREEN & RED;
    239                 delay_ms(200);
    240                 LED= OFF;
    241                 delay_ms(300);
    242                 LED= GREEN & RED;
    243                 delay_ms(200);
    244                 LED= OFF; 
    245                 add_amt--;
    246             }
    247             else
    248             {
    249                 printf("Code bank is full, please delete unwanted codes and re-run
    ");
    250                 add_amt= 0;
    251                 mode= NORMAL;
    252             }
    253         }    
    254         else if((totalBits == NUM_BITS) && (mode == REMOTEPOS))
    255         {
    256             /* 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             */
    264             GICR= 0b00000000;
    265             find_start_code();
    266             reduce_sequence();
    267             manchester_decode();
    268             if(code_count < 5)
    269             {
    270                 if(code_count == 0) 
    271                 {
    272                     copy_code(code_check,final_code);
    273                     code_count++;
    274                 }
    275                 else
    276                 {
    277                     if(match_code(code_check,final_code)) code_count++;
    278                     else code_count= 0;
    279                 }
    280             }
    281             if(code_count == 5)
    282             {
    283                 store_into_bank(add_pos,final_code,0);
    284                 sprintf(t_buffer,"Code added to position %d
    ",add_pos);
    285                 puts_str();
    286                 LED= GREEN;
    287                 delay_ms(200);
    288                 LED= RED;
    289                 delay_ms(200);
    290                 LED= GREEN & RED;
    291                 delay_ms(500);
    292                 LED= OFF; 
    293                 code_count= 0;
    294                 if(add_amt > 0)mode= REMOTEAMT;    
    295                 else mode= NORMAL;
    296             }
    297             totalBits= 0;
    298             start_flag= 0;
    299             sample_buffer= 0;
    300             GICR= 0b00100000;      
    301         }
    302         
    303         /* Check timers for timed tasks */
    304         if(check_receive_timer == 0) check_receive();
    305         if((door_timer == 0) && ((LED == GREEN) || (LED == RED))) LED= OFF;
    306          if(seconds == 0) time_counter();
    307     }
    308 }
    309 
    310 /* 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 */
    315 void check_receive(void)
    316 {
    317     char i,j;
    318     check_receive_timer= CHECK_RECEIVE_TIME;
    319     if(r_ready == 1)
    320     {
    321         /* Add input code at specific position */
    322         if(r_buffer[0] == 'a')
    323         {
    324             if(r_buffer[3] == ' ')
    325             {
    326                 add_pos= r_buffer[2]-48;
    327                 if((add_pos >= 0) && (add_pos <= 9))
    328                 {
    329                     store_into_bank(add_pos,r_buffer,4);
    330                     bank_status[add_pos]= 1;
    331                     printf("Code added at position %d
    ",add_pos);
    332                 }
    333                 else printf("Invalid code position
    ");
    334             }
    335             else if(r_buffer[4] == ' ')
    336             {  
    337                 add_pos= (r_buffer[2]-48)*10+(r_buffer[3]-48);
    338                 if((add_pos >=0) && (add_pos < NUM_CODES))
    339                 {
    340                     store_into_bank(add_pos,r_buffer,4);
    341                     bank_status[add_pos]= 1;
    342                     printf("Code added at position %d
    ",add_pos);
    343                 }
    344                 else printf("Invalid code position
    ");
    345             }
    346             else printf("Incorrect syntax
    ");
    347         }
    348         /* List all codes and corresponding status */
    349         else if(r_buffer[0] == 'l')
    350         {
    351             putsf("Code Bank:
    ");
    352             for(i= 0; i < NUM_CODES; i++)
    353             {
    354                 for(j= 0; j < 45; j++)
    355                 {
    356                     temp[j]= code_bank[i][j];
    357                 }
    358                 printf("%02d: %d - %s
    ",i,bank_status[i],temp);
    359             }
    360         }
    361         /* Delete code at specific position */
    362         else if(r_buffer[0] == 'd')
    363         {
    364             if(r_buffer[3] == '')
    365             {
    366                 del_pos= r_buffer[2]-48;
    367                 if((del_pos >= 0) && (del_pos <= 9))
    368                 {
    369                     store_into_bank(del_pos,no_code,0);
    370                     bank_status[del_pos]= 0;
    371                     printf("Code deleted at position %d
    ",del_pos);
    372                 }
    373                 else printf("Invalid code position
    ");
    374             }
    375             else if(r_buffer[4] == '')
    376             {  
    377                 del_pos= (r_buffer[2]-48)*10+(r_buffer[3]-48);
    378                 if((del_pos >=0) && (del_pos < NUM_CODES))
    379                 {
    380                     store_into_bank(del_pos,no_code,0);
    381                     bank_status[del_pos]= 0;
    382                     printf("Code deleted at position %d
    ",del_pos);
    383                 }
    384                 else printf("Invalid code position
    ");
    385             }
    386             else printf("Incorrect syntax
    ");
    387         }
    388         /* Unlock door for 3 seconds */
    389         else if(r_buffer[0] == 'u')
    390         {
    391             LED= GREEN;
    392             door_timer= 3000;      
    393         }
    394         /* Enable remote positional add mode */
    395         else if((r_buffer[0] == 'r') && (r_buffer[1] == 'p'))
    396         {
    397             
    398             if(r_buffer[4] == '') 
    399             {
    400                 add_pos= r_buffer[3]-48;
    401                 if((add_pos >= 0) && (add_pos <= 9))
    402                 {
    403                     bank_status[add_pos]= 1;
    404                     printf("Please place card in front of reader
    ");
    405                     LED= GREEN & RED;
    406                     delay_ms(400);
    407                     LED= OFF;
    408                     delay_ms(400);
    409                     LED= GREEN & RED;
    410                     delay_ms(400);
    411                     LED= OFF; 
    412                     mode= REMOTEPOS;
    413                 }
    414                 else printf("Invalid code position
    "); 
    415             }
    416             else if(r_buffer[5] == '')
    417             {
    418                 add_pos= (r_buffer[3]-48)*10+(r_buffer[4]-48);
    419                 if((add_pos >=0) && (add_pos < NUM_CODES))
    420                 {
    421                     bank_status[add_pos]= 1;
    422                     printf("Please place card in front of reader
    ");
    423                     LED= GREEN & RED;
    424                     delay_ms(400);
    425                     LED= OFF;
    426                     delay_ms(400);
    427                     LED= GREEN & RED;
    428                     delay_ms(400);
    429                     LED= OFF; 
    430                     mode= REMOTEPOS;
    431                 }
    432                 else printf("Invalid code position
    ");  
    433             }
    434             else printf("Incorrect syntax
    ");
    435         }
    436         /* Enable remote quantity add mode */
    437         else if((r_buffer[0] == 'r') && (r_buffer[1] == 'a'))
    438         {
    439             if(r_buffer[4] == '') 
    440             {   
    441                 add_amt= r_buffer[3]-48;
    442                 if((add_amt >= 1) && (add_amt <= 9)) mode= REMOTEAMT;
    443                 else printf("Invalid add quantity
    ");
    444             }
    445             else if(r_buffer[5] == '')
    446             {   
    447                 add_amt= (r_buffer[3]-48)*10+(r_buffer[4]-48);
    448                 if((add_amt >=1) && (add_amt <= NUM_CODES)) mode= REMOTEAMT;
    449                 else printf("Invalid add quantity
    ");
    450             }
    451             else printf("Incorrect syntax
    ");
    452         }
    453         /* Help */
    454         else if(r_buffer[0] == 'h')
    455         {
    456             putsf("Commands:
    ");
    457             putsf("a  - add a code -                 syntax: a <pos> <code>
    ");
    458             putsf("l  - list all codes -             syntax: l
    ");
    459             putsf("d  - delete a code -              syntax: d <pos>
    ");
    460             putsf("u  - unlock door -                syntax: u
    ");
    461             putsf("rp - remote add (positional) -    syntax: rp <pos>
    ");
    462             putsf("ra - remote add (amount) -        syntax: ra <amt>
    ");
    463         }
    464         /* Unrecognized commands */
    465         else printf("Unrecognized Command
    ");
    466         gets_str();
    467     }           
    468 }
    469 
    470 /* Function: Time Counter
    471    This function keeps track of time for reporting to
    472    the hyperterm.
    473 */
    474 void time_counter(void)
    475 {
    476     seconds= 59999;
    477     if(minutes < 59) minutes++;
    478     else
    479     {
    480         minutes= 0;
    481         if(hours < 23) hours++;
    482         else
    483         {
    484             hours= 0;
    485             if(days < 364) days++;
    486             else days= 0;
    487         }
    488     }
    489 }
    490 
    491 /* Function: Store Into Bank
    492    Stores a code into code bank at a specific position starting with
    493    a specific offset of code.
    494 */
    495 void store_into_bank(char position, char code[], char offset)
    496 {
    497     unsigned char i;
    498     for(i= 0; i < 45; i++) code_bank[position][i]= code[i+offset];
    499 }
    500 
    501 /* 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 */
    507 unsigned char verify_code(unsigned char char1[])
    508 {
    509     unsigned char i= 0;
    510     unsigned char j= 0;
    511     unsigned char code_match= 0;
    512     unsigned char element_match;
    513     while((code_match == 0) && (i < NUM_CODES))
    514     {
    515         if(bank_status[i]==1)
    516         {
    517             element_match= 1;
    518             while((element_match == 1) && (j <45))
    519             {
    520                 if(code_bank[i][j] != char1[j]) element_match= 0;
    521                 j++;
    522             }
    523             code_match= element_match;
    524         }
    525         i++;
    526     }
    527     return code_match;
    528 }
    529 
    530 /* 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 */
    535 unsigned char match_code(unsigned char char1[], unsigned char char2[])
    536 {
    537     char i= 0;
    538     char correct= 1;
    539     while((correct == 1) && (i < 45))
    540     {
    541         if(char1[i] != char2[i]) correct= 0;
    542         i++;
    543     }
    544     return correct;
    545 }
    546 
    547 /* Function: Copy Code
    548    Copies a code from source char2 to destination char1.
    549 */
    550 void copy_code(unsigned char char1[], unsigned char char2[])
    551 {
    552     unsigned char i;
    553     for(i= 0; i < 45; i++) char1[i]= char2[i];    
    554 }
    555 
    556 /* 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 */
    564 void find_start_code(void)
    565 {
    566     int i= 0;
    567     char sequence= 0;
    568     char count= 0;
    569     start_sequence= 0;
    570     end_sequence= 0;
    571     while((i < totalBits))
    572     {
    573         if(bit_array[i] == 49)
    574         {
    575             sequence= 1;
    576             count++;        
    577         }
    578         else
    579         {
    580             if((sequence == 1) && (count >= 15))
    581             {
    582                 start_sequence= i-count;
    583                 end_sequence= start_sequence+539;
    584                 i= totalBits+1;
    585             }
    586             sequence= 0;
    587             count= 0;
    588         }
    589         i++;
    590     }
    591     if (i == totalBits) start_sequence= -1;
    592 }
    593             
    594 /* 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 */
    605 void reduce_sequence(void)
    606 {
    607     int i;
    608     signed char j= -1;
    609     unsigned char count;
    610     unsigned char value;
    611     i= start_sequence;
    612     while(i <= end_sequence)
    613     {
    614         count= 0;
    615         value= bit_array[i];
    616         if(j == 89)
    617         {
    618             reduced_array[j]= value;
    619             i= end_sequence+1;
    620         }
    621         else
    622         {
    623             while(value == bit_array[i])
    624             {
    625                 count++;
    626                 i++;
    627             }
    628             if(j == -1) j++;
    629             else if((count == 5) || (count ==6)) reduced_array[j++]= value;
    630             else if((count == 10) || (count == 11) || (count == 12))
    631             {
    632                 reduced_array[j++]= value;
    633                 reduced_array[j++]= value;
    634             }
    635         }
    636     }   
    637 }
    638 /* 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 */
    643 void manchester_decode(void)
    644 {
    645     unsigned char i;
    646     unsigned char j= 0;
    647     for(i= 0; i< 90; i+=2)
    648     {
    649         if((reduced_array[i] == 49) && (reduced_array[i+1] == 48)) final_code[j++]= 48;
    650         else final_code[j++]= 49;
    651     }
    652 }
    653             
    654 void initialize(void)
    655 {    
    656     char i;
    657     /* 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     */
    668     DDRD= 0xff;
    669     TCCR2= 0b00011001;
    670     OCR2= 63;
    671     
    672     /* RFIDIN is PORTA.0 */
    673     DDRA= 0x00;
    674     
    675     /* Enable LED port as output */
    676     DDRC= 0xff;
    677     LED= OFF;
    678     
    679     /* 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     */
    687     TIMSK= 0b00000010;
    688     TCCR0= 0b00001011;
    689     OCR0= 249;
    690     TCNT0= 0;
    691     
    692     
    693     /* Setup the USART
    694        - Enable the Transmitter and Receiver
    695        - Set the Baud Rate to 9600Hz
    696     */
    697     UCSRB = 0x18;
    698     UBRRL = 103;        
    699     
    700     /* Initialize variables */
    701     start_flag= 0;
    702     curr_sample= 0;
    703     totalBits= 0;
    704     sample_buffer= 0;
    705     code_count= 0;
    706     check_receive_timer= CHECK_RECEIVE_TIME;
    707     mode= NORMAL;
    708     
    709     /* Initialize Code Bank Status */
    710     
    711     for(i= 0; i < NUM_CODES; i++)
    712     {
    713         if (bank_status[i] == "") bank_status[i]= 0;
    714     }
    715     
    716     /* Enable Interrupts */
    717     #asm
    718         sei
    719     #endasm        
    720     
    721     /* External pin interrupt
    722        - Enable in GICR
    723        - Set for rising edge
    724        - Clear interrupt flag
    725        - PORTB as input
    726     */
    727     GICR= 0b00100000;
    728     MCUCSR= 0b01000000;
    729     DDRB.2= 0; 
    730     
    731     /* Send a starting message to hypertrm */
    732     putsf("
    Initializing the Reader...
    ");
    733       r_ready = 0;
    734       t_ready = 1;
    735       gets_str();
    736       
    737     /* Initialize the data and time by prompting admin */
    738       putsf("Please enter time in the format: DDD:HH:MM
    ");
    739       
    740       while(r_ready == 0){};
    741     r_days[0] = r_buffer[0];
    742     r_days[1] = r_buffer[1];
    743     r_days[2] = r_buffer[2];
    744     r_days[3] = '';
    745     r_hours[0] = r_buffer[4];
    746     r_hours[1] = r_buffer[5];
    747     r_hours[2] = '';
    748     r_minutes[0] = r_buffer[7];
    749     r_minutes[1] = r_buffer[8];
    750     r_minutes[2] = '';
    751     sscanf(r_hours,"%d",&hours);
    752     sscanf(r_minutes,"%d",&minutes);
    753     sscanf(r_days,"%d",&days);
    754     gets_str();
    755     seconds = 59999;
    756     sprintf(t_buffer,"Current Date and Time: %03d:%02d:%02d
    ",days,hours,minutes);
    757     puts_str();
    758     while(t_ready == 0);
    759     
    760     putsf("Please type h for help
    ");
    761 }
    762 
    763 /* Complete receiving from hypertrm */
    764 void gets_str(void)
    765 { 
    766   r_ready = 0;
    767   r_index = 0;
    768   UCSRB.7 = 1;
    769 }
    770 
    771 /* Begin transmitting to hypertrm */
    772 void puts_str(void)
    773 {
    774   t_ready = 0;
    775   t_index = 0;
    776   putchar(t_buffer[0]);
    777   UCSRB.5 = 1;
    778 }
    View Code

     

    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

    http://cq.cx/prox.pl

    [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 to the 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:

    1. Wait until we receive a start sequence (extra long logic high)
    2. On next interrupt, decode the first Manchester bit: delay by a very short amount to ensure steady state, read the logic level.
    3. 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.
    4. 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)
    5. Repeat steps 2 and 3 until we read an end sequence (extra long logic low)
    6. 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.

     

     

     

  • 相关阅读:
    代理模式
    面向对象设计原则
    砝码破碎
    阿里EasyExcel使用
    IBM的OpenJ9
    java反射 (复习)
    DecimalFormat保留小数
    Object类
    SQLMAP用法
    SQL盲注之时间注入
  • 原文地址:https://www.cnblogs.com/shangdawei/p/3729375.html
Copyright © 2020-2023  润新知