//----------------------------------------------------------------------------- // Miscellaneous routines for low frequency tag operations. // Tags supported here so far are Texas Instruments (TI), HID // Also routines for raw mode reading/simulating of LF waveform // //----------------------------------------------------------------------------- #include #include "apps.h" #include "hitag2.h" #include "../common/crc16.c" void AcquireRawAdcSamples125k(BOOL at134khz) { if(at134khz) { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); } else { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); } // Connect the A/D to the peak-detected low-frequency path. SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Give it a bit of time for the resonant antenna to settle. SpinDelay(50); // Now set up the SSC to get the ADC samples that are now streaming at us. FpgaSetupSsc(); // Now call the acquisition routine DoAcquisition125k(at134khz); } // split into two routines so we can avoid timing issues after sending commands // void DoAcquisition125k(BOOL at134khz) { BYTE *dest = (BYTE *)BigBuf; int n = sizeof(BigBuf); int i; memset(dest,0,n); i = 0; for(;;) { if(SSC_STATUS & (SSC_STATUS_TX_READY)) { SSC_TRANSMIT_HOLDING = 0x43; LED_D_ON(); } if(SSC_STATUS & (SSC_STATUS_RX_READY)) { dest[i] = (BYTE)SSC_RECEIVE_HOLDING; i++; LED_D_OFF(); if(i >= n) { break; } } } DbpIntegers(dest[0], dest[1], at134khz); } void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,BYTE *command) { BOOL at134khz; /* Make sure the tag is reset */ FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(2500); // see if 'h' was specified if(command[strlen((char *) command) - 1] == 'h') at134khz= TRUE; else at134khz= FALSE; if(at134khz) { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); } else { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); } // Give it a bit of time for the resonant antenna to settle. SpinDelay(50); // And a little more time for the tag to fully power up SpinDelay(2000); // Now set up the SSC to get the ADC samples that are now streaming at us. FpgaSetupSsc(); // now modulate the reader field while(*command != '\0' && *command != ' ') { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelayUs(delay_off); if(at134khz) { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); } else { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); } LED_D_ON(); if(*(command++) == '0') { SpinDelayUs(period_0); } else { SpinDelayUs(period_1); } } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelayUs(delay_off); if(at134khz) { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); } else { FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); } // now do the read DoAcquisition125k(at134khz); } /* blank r/w tag data stream ...0000000000000000 01111111 1010101010101010101010101010101010101010101010101010101010101010 0011010010100001 01111111 101010101010101[0]000... [5555fe852c5555555555555555fe0000] */ void ReadTItag() { // some hardcoded initial params // when we read a TI tag we sample the zerocross line at 2Mhz // TI tags modulate a 1 as 16 cycles of 123.2Khz // TI tags modulate a 0 as 16 cycles of 134.2Khz #define FSAMPLE 2000000 #define FREQLO 123200 #define FREQHI 134200 signed char *dest = (signed char *)BigBuf; int n = sizeof(BigBuf); // int *dest = GraphBuffer; // int n = GraphTraceLen; // 128 bit shift register [shift3:shift2:shift1:shift0] DWORD shift3 = 0, shift2 = 0, shift1 = 0, shift0 = 0; int i, cycles=0, samples=0; // how many sample points fit in 16 cycles of each frequency DWORD sampleslo = (FSAMPLE<<4)/FREQLO, sampleshi = (FSAMPLE<<4)/FREQHI; // when to tell if we're close enough to one freq or another DWORD threshold = (sampleslo - sampleshi + 1)>>1; // TI tags charge at 134.2Khz FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz // Place FPGA in passthrough mode, in this mode the CROSS_LO line // connects to SSP_DIN and the SSP_DOUT logic level controls // whether we're modulating the antenna (high) // or listening to the antenna (low) FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU); // get TI tag data into the buffer AcquireTiType(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); for (i=0; i0) ) { cycles++; // after 16 cycles, measure the frequency if (cycles>15) { cycles=0; samples=i-samples; // number of samples in these 16 cycles // TI bits are coming to us lsb first so shift them // right through our 128 bit right shift register shift0 = (shift0>>1) | (shift1 << 31); shift1 = (shift1>>1) | (shift2 << 31); shift2 = (shift2>>1) | (shift3 << 31); shift3 >>= 1; // check if the cycles fall close to the number // expected for either the low or high frequency if ( (samples>(sampleslo-threshold)) && (samples<(sampleslo+threshold)) ) { // low frequency represents a 1 shift3 |= (1<<31); } else if ( (samples>(sampleshi-threshold)) && (samples<(sampleshi+threshold)) ) { // high frequency represents a 0 } else { // probably detected a gay waveform or noise // use this as gaydar or discard shift register and start again shift3 = shift2 = shift1 = shift0 = 0; } samples = i; // for each bit we receive, test if we've detected a valid tag // if we see 17 zeroes followed by 6 ones, we might have a tag // remember the bits are backwards if ( ((shift0 & 0x7fffff) == 0x7e0000) ) { // if start and end bytes match, we have a tag so break out of the loop if ( ((shift0>>16)&0xff) == ((shift3>>8)&0xff) ) { cycles = 0xF0B; //use this as a flag (ugly but whatever) break; } } } } } // if flag is set we have a tag if (cycles!=0xF0B) { DbpString("Info: No valid tag detected."); } else { // put 64 bit data into shift1 and shift0 shift0 = (shift0>>24) | (shift1 << 8); shift1 = (shift1>>24) | (shift2 << 8); // align 16 bit crc into lower half of shift2 shift2 = ((shift2>>24) | (shift3 << 8)) & 0x0ffff; // if r/w tag, check ident match if ( shift3&(1<<15) ) { DbpString("Info: TI tag is rewriteable"); // only 15 bits compare, last bit of ident is not valid if ( ((shift3>>16)^shift0)&0x7fff ) { DbpString("Error: Ident mismatch!"); } else { DbpString("Info: TI tag ident is valid"); } } else { DbpString("Info: TI tag is readonly"); } // WARNING the order of the bytes in which we calc crc below needs checking // i'm 99% sure the crc algorithm is correct, but it may need to eat the // bytes in reverse or something // calculate CRC DWORD crc=0; crc = update_crc16(crc, (shift0)&0xff); crc = update_crc16(crc, (shift0>>8)&0xff); crc = update_crc16(crc, (shift0>>16)&0xff); crc = update_crc16(crc, (shift0>>24)&0xff); crc = update_crc16(crc, (shift1)&0xff); crc = update_crc16(crc, (shift1>>8)&0xff); crc = update_crc16(crc, (shift1>>16)&0xff); crc = update_crc16(crc, (shift1>>24)&0xff); DbpString("Info: Tag data_hi, data_lo, crc = "); DbpIntegers(shift1, shift0, shift2&0xffff); if (crc != (shift2&0xffff)) { DbpString("Error: CRC mismatch, expected"); DbpIntegers(0, 0, crc); } else { DbpString("Info: CRC is good"); } } } void WriteTIbyte(BYTE b) { int i = 0; // modulate 8 bits out to the antenna for (i=0; i<8; i++) { if (b&(1<= TIBUFLEN) break; } WDT_HIT(); } // return stolen pin to SSP PIO_DISABLE = (1<=0; i--) { // DbpIntegers(0, 0, BigBuf[i]); for (j=0; j<32; j++) { if(BigBuf[i] & (1 << j)) { dest[--n] = 1; } else { dest[--n] = -1; } } } } // arguments: 64bit data split into 32bit idhi:idlo and optional 16bit crc // if crc provided, it will be written with the data verbatim (even if bogus) // if not provided a valid crc will be computed from the data and written. void WriteTItag(DWORD idhi, DWORD idlo, WORD crc) { // WARNING the order of the bytes in which we calc crc below needs checking // i'm 99% sure the crc algorithm is correct, but it may need to eat the // bytes in reverse or something if(crc == 0) { crc = update_crc16(crc, (idlo)&0xff); crc = update_crc16(crc, (idlo>>8)&0xff); crc = update_crc16(crc, (idlo>>16)&0xff); crc = update_crc16(crc, (idlo>>24)&0xff); crc = update_crc16(crc, (idhi)&0xff); crc = update_crc16(crc, (idhi>>8)&0xff); crc = update_crc16(crc, (idhi>>16)&0xff); crc = update_crc16(crc, (idhi>>24)&0xff); } DbpString("Writing the following data to tag:"); DbpIntegers(idhi, idlo, crc); // TI tags charge at 134.2Khz FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz // Place FPGA in passthrough mode, in this mode the CROSS_LO line // connects to SSP_DIN and the SSP_DOUT logic level controls // whether we're modulating the antenna (high) // or listening to the antenna (low) FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU); LED_A_ON(); // steal this pin from the SSP and use it to control the modulation PIO_ENABLE = (1<>8 )&0xff ); WriteTIbyte( (idlo>>16)&0xff ); WriteTIbyte( (idlo>>24)&0xff ); WriteTIbyte( (idhi )&0xff ); WriteTIbyte( (idhi>>8 )&0xff ); WriteTIbyte( (idhi>>16)&0xff ); WriteTIbyte( (idhi>>24)&0xff ); // data hi to lo WriteTIbyte( (crc )&0xff ); // crc lo WriteTIbyte( (crc>>8 )&0xff ); // crc hi WriteTIbyte(0x00); // write frame lo WriteTIbyte(0x03); // write frame hi PIO_OUTPUT_DATA_SET = (1< 36 */ #define HITAG_T_WRESP 208 /* T_wresp should be 204..212 */ static void hitag_handle_frame(int t0, int frame_len, char *frame); //#define DEBUG_RA_VALUES 1 #define DEBUG_FRAME_CONTENTS 1 void SimulateTagLowFrequencyBidir(int divisor, int t0) { #if DEBUG_RA_VALUES || DEBUG_FRAME_CONTENTS int i = 0; #endif char frame[10]; int frame_pos=0; DbpString("Starting Hitag2 emulator, press button to end"); hitag2_init(); /* Set up simulator mode, frequency divisor which will drive the FPGA * and analog mux selection. */ FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_SIMULATOR); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor); SetAdcMuxFor(GPIO_MUXSEL_LOPKD); RELAY_OFF(); /* Set up Timer 1: * Capture mode, timer source MCK/2 (TIMER_CLOCK1), TIOA is external trigger, * external trigger rising edge, load RA on rising edge of TIOA, load RB on rising * edge of TIOA. Assign PA15 to TIOA1 (peripheral B) */ PMC_PERIPHERAL_CLK_ENABLE = (1 << PERIPH_TC1); PIO_PERIPHERAL_B_SEL = (1 << GPIO_SSC_FRAME); TC1_CCR = TC_CCR_CLKDIS; TC1_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG | TC_CMR_LDRA_RISING | TC_CMR_LDRB_RISING; TC1_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; /* calculate the new value for the carrier period in terms of TC1 values */ t0 = t0/2; int overflow = 0; while(!BUTTON_PRESS()) { WDT_HIT(); if(TC1_SR & TC_SR_LDRAS) { int ra = TC1_RA; if((ra > t0*HITAG_T_EOF) | overflow) ra = t0*HITAG_T_EOF+1; #if DEBUG_RA_VALUES if(ra > 255 || overflow) ra = 255; ((char*)BigBuf)[i] = ra; i = (i+1) % 8000; #endif if(overflow || (ra > t0*HITAG_T_EOF) || (ra < t0*HITAG_T_0_MIN)) { /* Ignore */ } else if(ra >= t0*HITAG_T_1_MIN ) { /* '1' bit */ if(frame_pos < 8*sizeof(frame)) { frame[frame_pos / 8] |= 1<<( 7-(frame_pos%8) ); frame_pos++; } } else if(ra >= t0*HITAG_T_0_MIN) { /* '0' bit */ if(frame_pos < 8*sizeof(frame)) { frame[frame_pos / 8] |= 0<<( 7-(frame_pos%8) ); frame_pos++; } } overflow = 0; LED_D_ON(); } else { if(TC1_CV > t0*HITAG_T_EOF) { /* Minor nuisance: In Capture mode, the timer can not be * stopped by a Compare C. There's no way to stop the clock * in software, so we'll just have to note the fact that an * overflow happened and the next loaded timer value might * have wrapped. Also, this marks the end of frame, and the * still running counter can be used to determine the correct * time for the start of the reply. */ overflow = 1; if(frame_pos > 0) { /* Have a frame, do something with it */ #if DEBUG_FRAME_CONTENTS ((char*)BigBuf)[i++] = frame_pos; memcpy( ((char*)BigBuf)+i, frame, 7); i+=7; i = i % sizeof(BigBuf); #endif hitag_handle_frame(t0, frame_pos, frame); memset(frame, 0, sizeof(frame)); } frame_pos = 0; } LED_D_OFF(); } } DbpString("All done"); } static void hitag_send_bit(int t0, int bit) { if(bit == 1) { /* Manchester: Loaded, then unloaded */ LED_A_ON(); SHORT_COIL(); while(TC1_CV < t0*15); OPEN_COIL(); while(TC1_CV < t0*31); LED_A_OFF(); } else if(bit == 0) { /* Manchester: Unloaded, then loaded */ LED_B_ON(); OPEN_COIL(); while(TC1_CV < t0*15); SHORT_COIL(); while(TC1_CV < t0*31); LED_B_OFF(); } TC1_CCR = TC_CCR_SWTRG; /* Reset clock for the next bit */ } static void hitag_send_frame(int t0, int frame_len, const char const * frame, int fdt) { OPEN_COIL(); PIO_OUTPUT_ENABLE = (1 << GPIO_SSC_DOUT); /* Wait for HITAG_T_WRESP carrier periods after the last reader bit, * not that since the clock counts since the rising edge, but T_wresp is * with respect to the falling edge, we need to wait actually (T_wresp - T_g) * periods. The gap time T_g varies (4..10). */ while(TC1_CV < t0*(fdt-8)); int saved_cmr = TC1_CMR; TC1_CMR &= ~TC_CMR_ETRGEDG; /* Disable external trigger for the clock */ TC1_CCR = TC_CCR_SWTRG; /* Reset the clock and use it for response timing */ int i; for(i=0; i<5; i++) hitag_send_bit(t0, 1); /* Start of frame */ for(i=0; i0xFFF) { DbpString("Tags can only have 44 bits."); return; } fc(0,&n); // special start of frame marker containing invalid bit sequences fc(8, &n); fc(8, &n); // invalid fc(8, &n); fc(10, &n); // logical 0 fc(10, &n); fc(10, &n); // invalid fc(8, &n); fc(10, &n); // logical 0 WDT_HIT(); // manchester encode bits 43 to 32 for (i=11; i>=0; i--) { if ((i%4)==3) fc(0,&n); if ((hi>>i)&1) { fc(10, &n); fc(8, &n); // low-high transition } else { fc(8, &n); fc(10, &n); // high-low transition } } WDT_HIT(); // manchester encode bits 31 to 0 for (i=31; i>=0; i--) { if ((i%4)==3) fc(0,&n); if ((lo>>i)&1) { fc(10, &n); fc(8, &n); // low-high transition } else { fc(8, &n); fc(10, &n); // high-low transition } } if (ledcontrol) LED_A_ON(); SimulateTagLowFrequency(n, ledcontrol); if (ledcontrol) LED_A_OFF(); } // loop to capture raw HID waveform then FSK demodulate the TAG ID from it void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol) { BYTE *dest = (BYTE *)BigBuf; int m=0, n=0, i=0, idx=0, found=0, lastval=0; DWORD hi=0, lo=0; FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); // Connect the A/D to the peak-detected low-frequency path. SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Give it a bit of time for the resonant antenna to settle. SpinDelay(50); // Now set up the SSC to get the ADC samples that are now streaming at us. FpgaSetupSsc(); for(;;) { WDT_HIT(); if (ledcontrol) LED_A_ON(); if(BUTTON_PRESS()) { DbpString("Stopped"); if (ledcontrol) LED_A_OFF(); return; } i = 0; m = sizeof(BigBuf); memset(dest,128,m); for(;;) { if(SSC_STATUS & (SSC_STATUS_TX_READY)) { SSC_TRANSMIT_HOLDING = 0x43; if (ledcontrol) LED_D_ON(); } if(SSC_STATUS & (SSC_STATUS_RX_READY)) { dest[i] = (BYTE)SSC_RECEIVE_HOLDING; // we don't care about actual value, only if it's more or less than a // threshold essentially we capture zero crossings for later analysis if(dest[i] < 127) dest[i] = 0; else dest[i] = 1; i++; if (ledcontrol) LED_D_OFF(); if(i >= m) { break; } } } // FSK demodulator // sync to first lo-hi transition for( idx=1; idx>1)&0xffff); /* if we're only looking for one tag */ if (findone) { *high = hi; *low = lo; return; } hi=0; lo=0; found=0; } } if (found) { if (dest[idx] && (!dest[idx+1]) ) { hi=(hi<<1)|(lo>>31); lo=(lo<<1)|0; } else if ( (!dest[idx]) && dest[idx+1]) { hi=(hi<<1)|(lo>>31); lo=(lo<<1)|1; } else { found=0; hi=0; lo=0; } idx++; } if ( dest[idx] && dest[idx+1] && dest[idx+2] && (!dest[idx+3]) && (!dest[idx+4]) && (!dest[idx+5]) ) { found=1; idx+=6; if (found && (hi|lo)) { DbpString("TAG ID"); DbpIntegers(hi, lo, (lo>>1)&0xffff); /* if we're only looking for one tag */ if (findone) { *high = hi; *low = lo; return; } hi=0; lo=0; found=0; } } } WDT_HIT(); } }