www.pudn.com > rc500.rar > M500AuC.c


/////////////////////////////////////////////////////////////////////////////// 
//    Copyright (c), Philips Semiconductors Gratkorn 
// 
//                  (C)PHILIPS Electronics N.V.2000 
//       All rights are reserved. Reproduction in whole or in part is  
//      prohibited without the written consent of the copyright owner. 
//  Philips reserves the right to make changes without notice at any time. 
// Philips makes no warranty, expressed, implied or statutory, including but 
// not limited to any implied warranty of merchantibility or fitness for any 
//particular purpose, or that the use will not infringe any third party patent, 
// copyright or trademark. Philips must not be liable for any loss or damage 
//                          arising from its use. 
/////////////////////////////////////////////////////////////////////////////// 
#define DLL_EXPORT      // library source module definition 
#include  
#include  
#include  
#include  
 
#include  
#include  
#include  
 
//////////////////////////////////////////////////////////////////////////////// 
//                M O D U L E   D E F I N I T I O N 
//////////////////////////////////////////////////////////////////////////////// 
// COMMENT: This library module is modified from the original source code for a 
//	    microcontroller C164 CI, to suit the general purpose 8051 mcu. 
//          The source can be ported to other platforms very easily.  
//          The communication channel to the RC500 reader IC is assumed to be  
//          unknown. All data is written with the generic IO functions  
//          of the module ReaderIO.h. In our case the reader module is  
//          connected via memory mapped io at base address 0x7f00. 
//          The interrupt pin of the reader IC is assumed to be connected to  
//          the fast external interrupt pin INT0# (active low) and the reset 
//          pin of the reader IC should be connected to a dedicated port pin 
//          (Port3: Pin: 3). 
//          In this configuration, a reset of the reader module is independend 
//          from the reset of the microcontroller. 
//          In order to generate communication timeouts,  
//          general purpose timer 2 of the microcontroller is used. This  
//          timer need not to be initialised in advance. Before every usage  
//          the timer is completely initialised in each function.  
//          Non of the timers is essential for the functionality of the reader 
//          module, but are helpful furing software development. All protocoll  
//          relevant timing constraints are generated 
//          by the internal timer of the reader module. 
// 
//          Some explanations to the programming method of this library. 
//          There are three kind of functions coded in this module. 
//            a) internal functions, which have no prototypes in a header 
//               file and are not intended to be used outside of this file 
//            b) commands, which are intended for the reader module itself 
//            c) commands, which are intended for any tag in the rf field. 
//               These commands are send to the reader and the reader module 
//               transmitts the data to the rf interface. 
//          Commands for the reader and for the tag have the appropriate  
//          prefix (PCD for proximity coupled device or reader module 
//                  PICC for proximity integrated circuit card or tag) 
//          and their protypes are defined in the header file. 
//          Each command for a PICC consists of a PCD command. Therefore 
//          the function M500PcdCmd is very important for the understanding 
//          of the communication. 
// 
//          The basic functionality is provided by the interrupt service 
//          routine (ISR), which closely works together with the function 
//          M500PcdCmd. All kinds of interrupts are serviced by the  
//          same ISR.  
 
 
// inline structure in order to reset the communication channel between  
// function and ISR 
#define ResetInfo(info)    \ 
            info.cmd            = 0; \ 
            info.status         = MI_OK;\ 
            info.irqSource      = 0;   \ 
            info.nBytesSent     = 0;   \ 
            info.nBytesToSend   = 0;  \ 
            info.nBytesReceived = 0;  \ 
            info.nBitsReceived  = 0;   \ 
            info.collPos        = 0; 
 
// struct definition for a communication channel between function and ISR 
typedef struct  
         { 
            unsigned char  cmd;           //!< command code  
            char           status;        // communication status 
            unsigned char  nBytesSent;    // how many bytes already sent 
            unsigned char  nBytesToSend;  // how many bytes to send 
            unsigned char  nBytesReceived;// how many bytes received 
            unsigned short nBitsReceived; // how many bits received 
            unsigned char  irqSource;     // which interrupts have occured 
            unsigned char  collPos;       // at which position occured a 
                                          // collision 
         } MfCmdInfo; 
 
// modul variables  
extern	 unsigned char xdata *GpBase; 
 
static   unsigned char idata MFIFOLength = DEF_FIFO_LENGTH; // actual FIFO length 
 
static   unsigned char xdata MKeys[16][12]; // storage for authentication keys 
                                      // in order to provide a calling  
                                      // compatible interface to old libraries 
                             // Other reader modules keep several sets 
                             // of keys in an E2PROM. In this case, 
                             // these keys are stored in the uC and 
                             // transfered to the reader module  
                             // before authentication 
 
// Infomation concerning data processing 
         // send buffer for general use 
static   volatile unsigned char xdata MSndBuffer[SND_BUF_LEN]; 
         // receive buffer for general use 
static   volatile unsigned char xdata MRcvBuffer[RCV_BUF_LEN]; 
         // info struct for general use 
static   volatile MfCmdInfo     MInfo;                   
 
// Interrupt service routine 
// Variable in order to exchange data between function and ISR 
static   volatile MfCmdInfo     *MpIsrInfo = 0;  
        // ISR send buffer 
static   volatile unsigned char *MpIsrOut  = 0;  
         // ISR receive buffer 
static   volatile unsigned char *MpIsrIn   = 0;      
 
// storage of the last selected serial number including check byte. 
//For multi level serial numbers, only the first 4 bytes are stored. 
unsigned char MLastSelectedSnr[5]; 
 
// Timer 2 
bit		T2IR	 	= 0;	// Timer2 timeout flag 
unsigned int 	CountDown	= 0;	// Timeout counter with 50us resolution 
 
sbit    RC500RST        	= P3^5; 
sbit    LED	        	= P3^4; 
 
/////////////////////////////////////////////////////////////////////////////// 
//             Prototypes for local functions  
/////////////////////////////////////////////////////////////////////////////// 
 
void start_timeout(unsigned int _50us); 
void stop_timeout(void); 
 
// _____________________________________________________________________________ 
// 
//  FUNCTION: M500PcdSetTmo 
//        IN: tmoLength   1  ... 1.0 ms timeout periode 
//                        2  ... 1.5 ms timeout periode 
//                        3  ... 6.0 ms timeout periode 
//        		  4  ... 9.6 ms timeout period 
//                        5  ... 38.5 ms timeout period 
//                        6  ... 154 ms timeout period 
//                        7  ... 616.2 ms timeout period 
//       OUT: - 
//    RETURN:  
//   COMMENT: Set timeout length of the reader internal timer. 
//                
void M500PcdSetTmo(unsigned char tmoLength); 
 
// _____________________________________________________________________________ 
// 
//  FUNCTION: M500PcdCmd 
//        IN: cmd  PCD_IDLE 
//                   PCD_WRITEE2 
//                   PCD_READE2 
//                   PCD_LOADCONFIG 
//                   PCD_LOADKEYE2 
//                   PCD_AUTHENT1 
//                   PCD_CALCCRC 
//                   PCD_AUTHENT2 
//                   PCD_RECEIVE 
//                   PCD_LOADKEY 
//                   PCD_TRANSMIT 
//                   PCD_TRANSCEIVE 
//                   PCD_RESETPHASE 
//                   for a detailed description of the parameter values, please 
//                   have a look on the header file of the reader register 
//                   definitions. 
//            send      byte stream of variable length, which should be send to 
//                      the PICC, the length of stream has to be specified 
//                      in the info - structure 
//       OUT: rcv    byte stream of variable length, which was received  
//                      from the PICC or PCD 
//            info      communication and status structure 
//    RETURN:  
//   COMMENT: This function provides the central interface to the reader module. 
//            Depending on the "cmd"-value, all necessary interrupts are enabled 
//            and the communication is started. While the processing is done by 
//            the reader module, this function waits for its completion. 
//            It's notable, that the data in the "send byte stream" is written  
//            to the FIFO of the reader module by the ISR. Immediate after  
//            enabling the interrupts, the LoAlert interrupt is activated. 
//            The ISR writes the data to the FIFO. This function is not involved 
//            in writing or fetching data from FIFO, all work is done by the  
//            ISR.After command completion, the error status is evaluated and  
//            returned to the calling function. 
// 
char M500PcdCmd(unsigned char cmd, 
                volatile unsigned char* send,  
                volatile unsigned char* rcv, 
                volatile MfCmdInfo *info); 
 
// _____________________________________________________________________________ 
// 
//  FUNCTION: SetBitMask 
//        IN: reg      register address 
//            mask     bit mask to set 
//       OUT: - 
//    RETURN:  
//   COMMENT:  This function performs a read - modify - write sequence 
//             on the specified register. All bits with a 1 in the mask 
//             are set - all other bits keep their original value. 
// 
char SetBitMask(unsigned char reg,unsigned char mask); 
 
// _____________________________________________________________________________ 
// 
//  FUNCTION: ClearBitMask 
//        IN: reg      register address 
//            mask     bit mask to clear 
//       OUT: - 
//    RETURN:  
//   COMMENT:  This function performs a read - modify - write sequence 
//             on the specified register. All bits with a 1 in the mask 
//             are cleared - all other bits keep their original value. 
// 
char ClearBitMask(unsigned char reg,unsigned char mask); 
 
// _____________________________________________________________________________ 
// 
//  FUNCTION: FlushFIFO 
//        IN: - 
//       OUT: - 
//    RETURN:  
//   COMMENT: All remaining date in the FIFO of the reader module is  
//            erased by this function. Before wrinting new data or 
//            starting a new command, all remaining data from former  
//            commands should be deleted. Please note, that in  
//            normal operation, never data should be left, that means 
//            that a call to this function should not be necessary. 
// 
void FlushFIFO(void); 
 
// _____________________________________________________________________________ 
// 
//  FUNCTION: M500PiccAuthState 
//        IN: auth_mode 
//            snr 
//            sector 
//       OUT: - 
//    RETURN:  
//   COMMENT:  
// 
char M500PiccAuthState(unsigned char auth_mode,// PICC_AUTHENT1A, PICC_AUTHENT1B 
                       unsigned char *snr,    // 4 byte serial number 
                       unsigned char sector); // 0 <= sector <= 15   
                                            // sector address for authentication 
 
////////////////////////////////////////////////////////////////////// 
//           E X C H A N G E   B Y T E   S T R E A M 
/////////////////////////////////////////////////////////////////////// 
char ExchangeByteStream(unsigned char Cmd, 
                        unsigned char *send_data, 
                        unsigned char send_bytelen, 
                        unsigned char *rec_data,   
                        unsigned char *rec_bytelen); 
 
/////////////////////////////////////////////////////////////////////////////// 
//                  Interrupt Handler RC500 
/////////////////////////////////////////////////////////////////////////////// 
void RC500ISR (void) interrupt 0 using 1    //Ext0 interrupt 
{ 
   static unsigned char idata irqBits; 
   static unsigned char idata irqMask;             
   static unsigned char idata nbytes; 
   static unsigned char idata cnt; 
 
   IE0 = 0; 	// Clear interrupt request flag 
 
   if (MpIsrInfo && MpIsrOut && MpIsrIn)  // transfer pointers have to be set 
                                          // correctly 
   { 
      while( ReadRawIO(RegPrimaryStatus) & 0x08) // loop while IRQ pending 
                                                // Attention: IRQ bit is  
                                                // inverted when used with 
                                                // low activ IRQ 
      { 
         irqMask = ReadRawIO(RegInterruptEn); // read enabled interrupts 
         // read pending interrupts 
         irqBits = ReadRawIO(RegInterruptRq) & irqMask; 
         MpIsrInfo->irqSource |= irqBits; // save pending interrupts 
         //************ LoAlertIRQ ****************** 
         if (irqBits & 0x01)    // LoAlert 
         {   
            nbytes = MFIFOLength - ReadRawIO(RegFIFOLength); 
            // less bytes to send, than space in FIFO 
            if ((MpIsrInfo->nBytesToSend - MpIsrInfo->nBytesSent) <= nbytes) 
            { 
               nbytes = MpIsrInfo->nBytesToSend - MpIsrInfo->nBytesSent; 
               WriteRawIO(RegInterruptEn,0x01); // disable LoAlert IRQ 
            } 
            // write remaining data to the FIFO 
            for ( cnt = 0;cnt < nbytes;cnt++) 
            { 
               WriteRawIO(RegFIFOData,MpIsrOut[MpIsrInfo->nBytesSent]); 
               MpIsrInfo->nBytesSent++; 
            } 
            WriteRawIO(RegInterruptRq,0x01);  // reset IRQ bit 
         } 
       
         //************* TxIRQ Handling ************** 
         if (irqBits & 0x10)       // TxIRQ 
         { 
            WriteRawIO(RegInterruptRq,0x10);    // reset IRQ bit  
            WriteRawIO(RegInterruptEn,0x82);    // enable HiAlert Irq for 
                                           // response 
            if (MpIsrInfo->cmd == PICC_ANTICOLL1) 	// if cmd is anticollision 
	    {                                           // switch off parity generation 
               WriteRawIO(RegChannelRedundancy,0x02);	// RXCRC and TXCRC disable, parity disable 
	    }	 
         } 
 
         //************* HiAlertIRQ or RxIRQ Handling ****************** 
         if (irqBits & 0x0E) // HiAlert, Idle or RxIRQ 
         { 
            // read some bytes ( length of FIFO queue)               
            // into the receive buffer 
            nbytes = ReadRawIO(RegFIFOLength); 
            // read date from the FIFO and store them in the receive buffer 
            for ( cnt = 0; cnt < nbytes; cnt++)                
            { 
               MpIsrIn[MpIsrInfo->nBytesReceived] = ReadRawIO(RegFIFOData); 
               MpIsrInfo->nBytesReceived++; 
            } 
            WriteRawIO(RegInterruptRq,0x0A & irqBits);   
                                       // reset IRQ bit - idle irq will 
                                       // be deleted in a seperate section 
         }    
    
         //************** IdleIRQ Handling *********** 
         if (irqBits & 0x04)     // Idle IRQ 
         { 
            WriteRawIO(RegInterruptEn,0x20); // disable Timer IRQ 
            WriteRawIO(RegInterruptRq,0x20); // disable Timer IRQ request 
            irqBits &= ~0x20;   // clear Timer IRQ in local var 
            MpIsrInfo->irqSource &= ~0x20; // clear Timer IRQ in info var 
                                        // when idle received, then cancel 
                                        // timeout 
            WriteRawIO(RegInterruptRq,0x04);  // reset IRQ bit  
            // status should still be MI_OK 
            // no error - only used for wake up 
         } 
        
         //************* TimerIRQ Handling *********** 
         if (irqBits & 0x20)       // timer IRQ 
         { 
            WriteRawIO(RegInterruptRq,0x20); // reset IRQ bit  
            MpIsrInfo->status = MI_NOTAGERR; // timeout error 
                                             // otherwise ignore the interrupt 
         } 
          
      } 
   } 
} 
 
/////////////////////////////////////////////////////////////////////// 
//         S e t   T i m e o u t   L E N G T H 
/////////////////////////////////////////////////////////////////////// 
void M500PcdSetTmo(unsigned char tmoLength) 
{ 
   switch(tmoLength) 
   {  // timer clock frequency 13,56 MHz 
      case 1:                       // short timeout (1,0 ms) 
         WriteIO(RegTimerClock,0x07); // TAutoRestart=0,TPrescale=128 
         WriteIO(RegTimerReload,0x6a);// TReloadVal = 'h6a =106(dec)  
         break; 
      case 2:                       // medium timeout (1,5 ms) 
         WriteIO(RegTimerClock,0x07); // TAutoRestart=0,TPrescale=128 
         WriteIO(RegTimerReload,0xa0);// TReloadVal = 'ha0 =160(dec)  
         break; 
      case 3:                       // medium timeout (6 ms) 
         WriteIO(RegTimerClock,0x09); // TAutoRestart=0,TPrescale=4*128 
         WriteIO(RegTimerReload,0xa0);// TReloadVal = 'ha0 =160(dec)  
         break; 
      case 4:                       // long timeout (9.6 ms) 
         WriteIO(RegTimerClock,0x09); // TAutoRestart=0,TPrescale=4*128 
         WriteIO(RegTimerReload,0xff);// TReloadVal = 'hff =255(dec)  
         break; 
      case 5:                       // long timeout (38.5 ms) 
         WriteIO(RegTimerClock,0x0b); // TAutoRestart=0,TPrescale=16*128 
         WriteIO(RegTimerReload,0xff);// TReloadVal = 'hff =255(dec)  
         break; 
      case 6:                       // long timeout (154 ms) 
         WriteIO(RegTimerClock,0x0d); // TAutoRestart=0,TPrescale=64*128 
         WriteIO(RegTimerReload,0xff);// TReloadVal = 'hff =255(dec)  
         break; 
      case 7:                       // long timeout (616.2 ms) 
         WriteIO(RegTimerClock,0x0f); // TAutoRestart=0,TPrescale=256*128 
         WriteIO(RegTimerReload,0xff);// TReloadVal = 'hff =255(dec)  
         break; 
      default:                       //  
         WriteIO(RegTimerClock,0x07); // TAutoRestart=0,TPrescale=128 
         WriteIO(RegTimerReload,tmoLength);// TReloadVal = 'h6a =tmoLength(dec)  
         break; 
   }      
} 
 
////////////////////////////////////////////////////////////////////// 
//       W R I T E   A   P C D   C O M M A N D  
/////////////////////////////////////////////////////////////////////// 
char  M500PcdCmd(unsigned char cmd, 
               volatile unsigned char* send,  
               volatile unsigned char* rcv, 
               volatile MfCmdInfo *info) 
{      
   char          idata status    = MI_OK; 
   char          idata tmpStatus ; 
   unsigned char idata lastBits; 
 
   unsigned char idata irqEn     = 0x00; 
   unsigned char idata waitFor   = 0x00; 
   unsigned char idata timerCtl  = 0x00; 
 
   WriteIO(RegInterruptEn,0x7F); // disable all interrupts 
   WriteIO(RegInterruptRq,0x7F); // reset interrupt requests 
   WriteIO(RegCommand,PCD_IDLE); // terminate probably running command 
 
   FlushFIFO();            // flush FIFO buffer 
 
   // save info structures to module pointers 
   MpIsrInfo = info;   
   MpIsrOut  = send; 
   MpIsrIn   = rcv; 
 
   info->irqSource = 0x0; // reset interrupt flags 
   // depending on the command code, appropriate interrupts are enabled (irqEn) 
   // and the commit interrupt is choosen (waitFor). 
   switch(cmd) 
   { 
      case PCD_IDLE:                   // nothing else required 
         irqEn = 0x00; 
         waitFor = 0x00; 
         break; 
      case PCD_WRITEE2:                // LoAlert and TxIRq 
         irqEn = 0x11; 
         waitFor = 0x10; 
         break; 
      case PCD_READE2:                 // HiAlert, LoAlert and IdleIRq 
         irqEn = 0x07; 
         waitFor = 0x04; 
         break; 
      case PCD_LOADCONFIG:             // IdleIRq 
      case PCD_LOADKEYE2:              // IdleIRq 
      case PCD_AUTHENT1:               // IdleIRq 
         irqEn = 0x05; 
         waitFor = 0x04; 
         break; 
      case PCD_CALCCRC:                // LoAlert and TxIRq 
         irqEn = 0x11; 
         waitFor = 0x10; 
         break; 
      case PCD_AUTHENT2:               // IdleIRq 
         irqEn = 0x04; 
         waitFor = 0x04; 
         break; 
      case PCD_RECEIVE:                // HiAlert and IdleIRq 
         info->nBitsReceived = -(ReadIO(RegBitFraming) >> 4); 
         irqEn = 0x06; 
         waitFor = 0x04; 
         break; 
      case PCD_LOADKEY:                // IdleIRq 
         irqEn = 0x05; 
         waitFor = 0x04; 
         break; 
      case PCD_TRANSMIT:               // LoAlert and IdleIRq 
         irqEn = 0x05; 
         waitFor = 0x04; 
         break; 
      case PCD_TRANSCEIVE:             // TxIrq, RxIrq, IdleIRq and LoAlert 
	 info->nBitsReceived = -(ReadIO(RegBitFraming) >> 4); 
         irqEn = 0x3D; 
         waitFor = 0x04; 
         break; 
      default: 
         status = MI_UNKNOWN_COMMAND; 
   }         
   if (status == MI_OK) 
   { 
      // Initialize uC Timer for global Timeout management 
      irqEn |= 0x20;                        // always enable timout irq 
      waitFor |= 0x20;                      // always wait for timeout  
 
      start_timeout(4000);          // initialise and start guard timer for reader 
      				    // 50us resolution, 200ms 
       
      WriteIO(RegInterruptEn,irqEn | 0x80);  //necessary interrupts are enabled 
      WriteIO(RegCommand,cmd);               //start command    
 
      // wait for commmand completion 
      // a command is completed, if the corresponding interrupt occurs 
      // or a timeout is signaled   
 
      while (!(MpIsrInfo->irqSource & waitFor 
               || T2IR));                // wait for cmd completion or timeout 
 
      WriteIO(RegInterruptEn,0x7F);          // disable all interrupts 
      WriteIO(RegInterruptRq,0x7F);          // clear all interrupt requests 
      SetBitMask(RegControl,0x04);           // stop timer now 
 
      stop_timeout();  			// stop timeout for reader 
      WriteIO(RegCommand,PCD_IDLE);          // reset command register 
 
 
      if (!(MpIsrInfo->irqSource & waitFor))   // reader has not terminated 
      {                                // timer 2 expired 
         status = MI_ACCESSTIMEOUT; 
      } 
      else 
         status = MpIsrInfo->status;           // set status 
 
      if (status == MI_OK)                     // no timeout error occured 
      { 
         if (tmpStatus = (ReadIO(RegErrorFlag) & 0x17)) // error occured 
         { 
            if (tmpStatus & 0x01)   // collision detected 
            { 
               info->collPos = ReadIO(RegCollpos); // read collision position 
               status = MI_COLLERR; 
            } 
            else 
            { 
               info->collPos = 0; 
               if (tmpStatus & 0x02)   // parity error 
               { 
                  status = MI_PARITYERR; 
               } 
            } 
            if (tmpStatus & 0x04)   // framing error 
            { 
               status = MI_FRAMINGERR; 
            } 
            if (tmpStatus & 0x10)   // FIFO overflow 
            { 
               FlushFIFO(); 
               status = MI_OVFLERR; 
            } 
 	    if (tmpStatus & 0x08) //CRC error 
	    { 
               status = MI_CRCERR; 
	    }	 
            if (status == MI_OK) 
               status = MI_NY_IMPLEMENTED; 
            // key error occures always, because of  
            // missing crypto 1 keys loaded 
         } 
         // if the last command was TRANSCEIVE, the number of  
         // received bits must be calculated - even if an error occured 
         if (cmd == PCD_TRANSCEIVE) 
         { 
            // number of bits in the last byte 
            lastBits = ReadIO(RegSecondaryStatus) & 0x07; 
            if (lastBits) 
               info->nBitsReceived += (info->nBytesReceived-1) * 8 + lastBits; 
            else 
               info->nBitsReceived += info->nBytesReceived * 8; 
         } 
      } 
      else 
      { 
         info->collPos = 0x00; 
      } 
   } 
   MpIsrInfo = 0;         // reset interface variables for ISR 
   MpIsrOut  = 0; 
   MpIsrIn   = 0;  
   return status; 
}    
 
////////////////////////////////////////////////////////////////////// 
//   S E T   A   B I T   M A S K  
/////////////////////////////////////////////////////////////////////// 
char SetBitMask(unsigned char reg,unsigned char mask) //  
{ 
   char idata tmp = 0x0; 
 
   tmp = ReadIO(reg); 
   WriteIO(reg,tmp | mask);  // set bit mask 
   return 0x0; 
} 
 
////////////////////////////////////////////////////////////////////// 
//   C L E A R   A   B I T   M A S K  
/////////////////////////////////////////////////////////////////////// 
char ClearBitMask(unsigned char reg,unsigned char mask) //  
{ 
   char idata tmp = 0x0; 
 
   tmp = ReadIO(reg); 
   WriteIO(reg,tmp & ~mask);  // clear bit mask 
   return 0x0; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//                  F L U S H    F I F O 
/////////////////////////////////////////////////////////////////////// 
void FlushFIFO(void) 
{   
   SetBitMask(RegControl,0x01); 
} 
 
/////////////////////////////////////////////////////////////////////// 
//      M I F A R E   M O D U L E   R E S E T  
/////////////////////////////////////////////////////////////////////// 
char M500PcdReset(void) 
{ 
   char idata status = MI_OK; 
    
   RC500RST = 0;  // clear reset pin 
   delay_1ms(25);  // wait for 25ms     
   RC500RST = 1;   // reset RC500 
   delay_50us(50);  // wait for 2.5ms 
   RC500RST = 0;  // clear reset pin 
 
   start_timeout(42000); 	// count down with a period of 50 us 
   			        // 42000 * 50 us = 2.1 s 
    
  // wait until reset command recognized 
   while (((ReadRawIO(RegCommand) & 0x3F) != 0x3F) && !T2IR); 
   // while reset sequence in progress 
   while ((ReadRawIO(RegCommand) & 0x3F) && !T2IR);  
    
   stop_timeout();  		// stop timeout counter 
 
   if (T2IR) 		// If reader timeout occurs 
   { 
      status = MI_RESETERR; // respose of reader IC is not correct 
      T2IR   = 0; 
   } 
   else 
   { 
      WriteRawIO(RegPage,0x80); // Dummy access in order to determine the bus  
                                // configuration 
      // necessary read access  
      // after first write access, the returned value 
      // should be zero ==> interface recognized 
      if (ReadRawIO(RegCommand) != 0x00) 
      {                            
          status = MI_INTERFACEERR; 
      } 
      WriteRawIO(RegPage,0x00); // configure to linear address mode 
      	 
   } 
   return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//      M I F A R E   M O D U L E   C O N F I G U R A T I O N 
/////////////////////////////////////////////////////////////////////// 
char M500PcdConfig(void) 
{ 
   char idata status; 
   char idata i; 
   char idata j; 
 
   if ((status = M500PcdReset()) == MI_OK) 
   { 
     // test clock Q calibration - value in the range of 0x46 expected 
     WriteIO(RegClockQControl,0x0); 
     WriteIO(RegClockQControl,0x40); 
     delay_50us(2);  // wait approximately 100 us - calibration in progress 
     ClearBitMask(RegClockQControl,0x40); // clear bit ClkQCalib for  
                                          // further calibration 
 
     // The following values for RegBitPhase and 
     // RegRxThreshold represents an optimal 
     // value for our demo package. For user 
     // implementation some changes could be 
     // necessary 
     // initialize bit phase 
     WriteIO(RegBitPhase,0xAD);       
 
     // initialize minlevel 
     WriteIO(RegRxThreshold,0xFF);    
   
     // disable auto power down 
     WriteIO(RegRxControl2,0x01); 
 
     // Depending on the processing speed of the 
     // operation environment, the waterlevel  
     // can be adapted. (not very critical for 
     // mifare applications) 
     // initialize waterlevel to value 4 
     WriteIO(RegFIFOLevel,0x04);    
      
     //Timer configuration 
     WriteIO(RegTimerControl,0x02);  // TStopRxEnd=0,TStopRxBeg=0, 
                                     // TStartTxEnd=1,TStartTxBeg=0   
                           // timer must be stopped manually 
     M500PcdSetTmo(1);               // short timeout 
 
     WriteIO(RegIRqPinConfig,0x03); // interrupt active low enable 
 
     M500PcdRfReset(1);            // Rf - reset and enable output driver    
 
     // initialize internal key memory      
     for (i = 0; i < 16; i++) 
        for (j = 0; j < 12; j++) 
           MKeys[i][j] = 0xff; 
   } 
   return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E   R E M O T E   A N T E N N A 
//  Configuration of slave module 
/////////////////////////////////////////////////////////////////////// 
char M500PcdMfInOutSlaveConfig(void) 
{ 
   char idata status = MI_OK; 
 
   FlushFIFO();    // empty FIFO 
   ResetInfo(MInfo);    
   MSndBuffer[0] = 0x10; // addr low byte 
   MSndBuffer[1] = 0x00; // addr high byte 
 
   MSndBuffer[2] = 0x00; // Page 
   MSndBuffer[3] = 0x7B; // RegTxControl modsource 11,InvTx2,Tx2RFEn,TX1RFEn 
   MSndBuffer[4] = 0x3F; // RegCwConductance 
   MSndBuffer[5] = 0x3F; // RFU13 
   MSndBuffer[6] = 0x19; // RFU14 
   MSndBuffer[7] = 0x13; // RegModWidth      
   MSndBuffer[8] = 0x00; // RFU16 
   MSndBuffer[9] = 0x00; // RFU17 
  
   MSndBuffer[10] = 0x00; // Page 
   MSndBuffer[11] = 0x73; // RegRxControl1  
   MSndBuffer[12] = 0x08; // RegDecoderControl 
   MSndBuffer[13] = 0x6c; // RegBitPhase      
   MSndBuffer[14] = 0xFF; // RegRxThreshold   
   MSndBuffer[15] = 0x00; // RFU1D 
   MSndBuffer[16] = 0x00; // RegRxControl2    
   MSndBuffer[17] = 0x00; // RegClockQControl 
 
   MSndBuffer[18] = 0x00; // Page 
   MSndBuffer[19] = 0x06; // RegRxWait 
   MSndBuffer[20] = 0x03; // RegChannelRedundancy 
   MSndBuffer[21] = 0x63; // RegCRCPresetLSB     
   MSndBuffer[22] = 0x63; // RegCRCPresetMSB     
   MSndBuffer[23] = 0x0;  // RFU25 
   MSndBuffer[24] = 0x04; // RegMfOutSelect enable mfout = manchester HT 
   MSndBuffer[25] = 0x00; // RFU27 
      
   // PAGE 5      FIFO, Timer and IRQ-Pin Configuration 
   MSndBuffer[26] = 0x00; // Page 
   MSndBuffer[27] = 0x08; // RegFIFOLevel        
   MSndBuffer[28] = 0x07; // RegTimerClock       
   MSndBuffer[29] = 0x06; // RegTimerControl     
   MSndBuffer[30] = 0x0A; // RegTimerReload      
   MSndBuffer[31] = 0x02; // RegIRqPinConfig     
   MSndBuffer[32] = 0x00; // RFU     
   MSndBuffer[33] = 0x00; // RFU 
   MInfo.nBytesToSend   = 34; 
          
   status = M500PcdCmd(PCD_WRITEE2, 
                       MSndBuffer, 
                       MRcvBuffer, 
                       &MInfo); // write e2 
   return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E   R E M O T E   A N T E N N A 
//  Configuration of master module 
/////////////////////////////////////////////////////////////////////// 
char M500PcdMfInOutMasterConfig(void) 
{ 
   WriteIO(RegRxControl2,0x42); 
   WriteIO(RegTxControl,0x10); 
   WriteIO(RegBitPhase,0x11); 
 
   return MI_OK; 
}      
 
/////////////////////////////////////////////////////////////////////// 
//          M A S T E R   K E Y   L O A D  
/////////////////////////////////////////////////////////////////////// 
char M500PcdLoadMk(unsigned char  auth_mode,  // KEYA or KEYB 
                   unsigned char  key_addr,   // 0 <= key_addr <= 15 
                   unsigned char  *mk)        // 6 bytes uncoded master key 
{ 
   unsigned char idata offset = (auth_mode == PICC_AUTHENT1A) ? 0 : 6; 
 
   memcpy(MKeys[key_addr] + offset,mk,6); 
   return MI_OK; 
} 
                   
/////////////////////////////////////////////////////////////////////// 
//          E E P R O M   M A S T E R   K E Y   L O A D  
/////////////////////////////////////////////////////////////////////// 
char M500PcdLoadKeyE2(unsigned char key_type, 
                          unsigned char sector, 
                          unsigned char *uncoded_keys) 
{ 
   char idata status = MI_OK; 
   // eeprom address calculation 
   // 0x80 ... offset 
   // key_sector ... sector 
   // 0x18 ... 2 * 12 = 24 = 0x18 
   unsigned short idata e2addr = 0x80 + sector * 0x18; 
   unsigned char idata *e2addrbuf = (unsigned char*)&e2addr; 
   unsigned char idata keycoded[12]; 
 
   if (key_type == PICC_AUTHENT1B) 
      e2addr += 12; // key B offset    
    
   FlushFIFO();    // empty FIFO 
   ResetInfo(MInfo); 
 
   M500HostCodeKey(uncoded_keys,keycoded); 
   memcpy(MSndBuffer,e2addrbuf,2); // write low and high byte of address 
   MSndBuffer[2] = MSndBuffer[0];    // Move the LSB of the 2-bytes 
   MSndBuffer[0] = MSndBuffer[1];    // address to the first byte 
   MSndBuffer[1] = MSndBuffer[2]; 
   memcpy(&MSndBuffer[2],keycoded,12); // write 12 bytes of coded keys 
   MInfo.nBytesToSend   = 14; 
    
   // write load command 
   status = M500PcdCmd(PCD_WRITEE2, 
			MSndBuffer, 
			MRcvBuffer, 
			&MInfo);          
    
   return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          E E P R O M   R E A D    
/////////////////////////////////////////////////////////////////////// 
char PcdReadE2(unsigned short startaddr, 
               unsigned char length, 
               unsigned char* _data) 
{ 
   char status = MI_OK; 
 
     // ************* Cmd Sequence **********************************  
     ResetInfo(MInfo);    
     MSndBuffer[0] = startaddr & 0xFF; 
     MSndBuffer[1] = (startaddr >> 8) & 0xFF; 
     MSndBuffer[2] = length; 
     MInfo.nBytesToSend   = 3; 
     status = M500PcdCmd(PCD_READE2, 
                         MSndBuffer, 
                         MRcvBuffer, 
                         &MInfo); 
    if (status == MI_OK) 
    { 
       memcpy(_data,MRcvBuffer,length); 
    } 
    else   // Response Processing 
    { 
       _data[0] = 0; 
    } 
    return status ; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          E E P R O M   W R I T E  
/////////////////////////////////////////////////////////////////////// 
char PcdWriteE2(unsigned short startaddr, 
                unsigned char length, 
                unsigned char* _data) 
{ 
   char status = MI_OK; 
 
     // ************* Cmd Sequence **********************************  
   ResetInfo(MInfo);    
   MSndBuffer[0] = startaddr & 0xFF; 
   MSndBuffer[1] = (startaddr >> 8) & 0xFF; 
   memcpy(MSndBuffer + 2,_data,length); 
 
   MInfo.nBytesToSend   = length + 2; 
          
   status = M500PcdCmd(PCD_WRITEE2, 
                       MSndBuffer, 
                       MRcvBuffer, 
                       &MInfo); // write e2 
   return status; 
}    
 
/////////////////////////////////////////////////////////////////////// 
//          C O N F I G   M F O U T   S E L E C T  
/////////////////////////////////////////////////////////////////////// 
char M500PcdMfOutSelect(unsigned char type) 
{ 
   WriteIO(RegMfOutSelect,type&0x7); 
   return MI_OK; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          W R I T E   R E G I S T E R  
/////////////////////////////////////////////////////////////////////// 
char M500PcdWriteRegister(unsigned char Reg, unsigned char value) 
{ 
   WriteIO(Reg,value); 
   return MI_OK; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          R E A D   R E G I S T E R  
/////////////////////////////////////////////////////////////////////// 
char M500PcdReadRegister(unsigned char Reg) 
{ 
   char value; 
	 
   value = ReadIO(Reg); 
   return (value); 
} 
      
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E    R E Q U E S T  
/////////////////////////////////////////////////////////////////////// 
char M500PiccRequest(unsigned char req_code, // request code ALL = 0x52  
                                             // or IDLE = 0x26  
                   unsigned char *atq)     // answer to request 
{ 
   return M500PiccCommonRequest(req_code,atq); 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E   C O M M O N   R E Q U E S T  
/////////////////////////////////////////////////////////////////////// 
char M500PiccCommonRequest(unsigned char req_code,  
                         unsigned char *atq) 
{ 
   char idata status = MI_OK; 
 
    //************* initialize ****************************** 
   WriteIO(RegChannelRedundancy,0x03); // RxCRC and TxCRC disable, parity enable 
   ClearBitMask(RegControl,0x08);    // disable crypto 1 unit    
   WriteIO(RegBitFraming,0x07);        // set TxLastBits to 7  
   SetBitMask(RegTxControl,0x03);    // Tx2RF-En, Tx1RF-En enable 
    
   ResetInfo(MInfo);    
   MSndBuffer[0] = req_code; 
   MInfo.nBytesToSend   = 1;    
   status = M500PcdCmd(PCD_TRANSCEIVE, 
                      MSndBuffer, 
                      MRcvBuffer, 
                      &MInfo); 
   
   if (status)      // error occured 
   { 
      *atq = 0; 
   }  
   else  
   { 
      if (MInfo.nBitsReceived != 16) // 2 bytes expected 
      { 
         *atq = 0; 
         status = MI_BITCOUNTERR; 
      }  
      else  
      { 
         status = MI_OK; 
         memcpy(atq,MRcvBuffer,2); 
      } 
   } 
   return status;  
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E    A N T I C O L L I S I O N 
// for standard select 
/////////////////////////////////////////////////////////////////////// 
char M500PiccAnticoll (unsigned char bcnt, 
                     unsigned char *snr) 
{ 
   return M500PiccCascAnticoll(0x93,bcnt,snr); // first cascade level 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E    A N T I C O L L I S I O N 
// for extended serial numbers 
/////////////////////////////////////////////////////////////////////// 
char M500PiccCascAnticoll (unsigned char select_code, 
                           unsigned char bcnt,        
                           unsigned char *snr)        
{ 
   char idata status = MI_OK; 
   char idata snr_in[4];     // copy of the input parameter snr 
   char idata nbytes = 0; 
   char idata nbits = 0; 
   char idata complete = 0; 
   char idata i        = 0; 
   char idata byteOffset = 0; 
   unsigned char idata snr_crc; 
   unsigned char idata snr_check; 
   unsigned char dummyShift1;       // dummy byte for snr shift 
   unsigned char dummyShift2;       // dummy byte for snr shift    
  
   //************* Initialisation ****************************** 
   M500PcdSetTmo(106); 
   memcpy(snr_in,snr,4);    
    
   WriteIO(RegDecoderControl,0x28); // ZeroAfterColl aktivieren    
   ClearBitMask(RegControl,0x08);    // disable crypto 1 unit 
       
   //************** Anticollision Loop *************************** 
   complete = 0; 
//   bcnt = 0;   // no part of the snr is known 
   while (!complete && (status == MI_OK) ) 
   { 
      ResetInfo(MInfo);            
      WriteIO(RegChannelRedundancy,0x03); // RxCRC and TxCRC disable, parity enable 
      nbits = bcnt % 8;   // remaining number of bits 
      if (nbits) 
      { 
         WriteIO(RegBitFraming,nbits << 4 | nbits); // TxLastBits/RxAlign auf nb_bi 
         nbytes = bcnt / 8 + 1;    
         // number of bytes known 
 
         // in order to solve an inconsistancy in the anticollision sequence 
         // (will be solved soon), the case of 7 bits has to be treated in a 
         // separate way - please note the errata sheet 
         if (nbits == 7) 
         { 
            MInfo.cmd = PICC_ANTICOLL1;   // pass command flag to ISR         
            WriteIO(RegBitFraming,nbits); // reset RxAlign to zero 
         } 
      }  
      else 
      { 
         nbytes = bcnt / 8; 
      } 
 
      MSndBuffer[0] = select_code; 
      MSndBuffer[1] = 0x20 + ((bcnt/8) << 4) + nbits; //number of bytes send 
                
      for (i = 0; i < nbytes; i++)  // Sende Buffer beschreiben 
      { 
         MSndBuffer[i + 2] = snr_in[i]; 
      } 
      MInfo.nBytesToSend   = 2 + nbytes;    
  
      status = M500PcdCmd(PCD_TRANSCEIVE, 
                         MSndBuffer, 
                         MRcvBuffer, 
                         &MInfo); 
    
     
      // in order to solve an inconsistancy in the anticollision sequence 
      // (will be solved soon), the case of 7 bits has to be treated in a 
      // separate way  
      if (nbits == 7) 
      { 
         // reorder received bits 
         dummyShift1 = 0x00; 
         for (i = 0; i < MInfo.nBytesReceived; i++) 
         { 
            dummyShift2 = MRcvBuffer[i]; 
            MRcvBuffer[i] = (dummyShift1 >> (i+1)) | (MRcvBuffer[i] << (7-i)); 
            dummyShift1 = dummyShift2; 
         } 
         MInfo.nBitsReceived -= MInfo.nBytesReceived; // subtract received parity bits 
         // recalculation of collision position 
         if ( MInfo.collPos ) MInfo.collPos += 7 - (MInfo.collPos + 6) / 9; 
      } 
          
      if ( status == MI_OK || status == MI_COLLERR)    // no other occured 
      { 
         // R e s p o n s e   P r o c e s s i n g    
         if ( MInfo.nBitsReceived != (40 - bcnt) ) // not 5 bytes answered 
         { 
            status = MI_BITCOUNTERR; // Exit with error 
         }  
         else  
         { 
            byteOffset = 0; 
            if( nbits != 0 ) // last byte was not complete 
            { 
                snr_in[nbytes - 1] = snr_in[nbytes - 1] | MRcvBuffer[0]; 
                byteOffset = 1; 
            } 
 
            for ( i =0; i < (4 - nbytes); i++)      
            { 
               snr_in[nbytes + i] = MRcvBuffer[i + byteOffset]; 
            } 
   
            if (status != MI_COLLERR ) // no error and no collision 
            { 
               // SerCh check 
               snr_crc = snr_in[0] ^ snr_in[1] ^ snr_in[2] ^ snr_in[3]; 
               snr_check = MRcvBuffer[MInfo.nBytesReceived - 1]; 
               if (snr_crc != snr_check) 
               { 
                  status = MI_SERNRERR; 
               }  
               else    
               { 
                  complete = 1; 
               } 
            } 
            else                   // collision occured 
            { 
               bcnt = bcnt + MInfo.collPos - nbits; 
               status = MI_OK; 
            } 
         } 
      } 
   } 
   if (status == MI_OK) 
   { 
      // transfer snr_in to snr 
      memcpy(snr,snr_in,4); 
   } 
   else 
   { 
      memcpy(snr,"0000",4); 
   } 
 
   //----------------------Einstellungen aus Initialisierung ruecksetzen  
   ClearBitMask(RegDecoderControl,0x20); // ZeroAfterColl disable 
    
   return status;   
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E    S E L E C T  
// for std. select 
/////////////////////////////////////////////////////////////////////// 
char M500PiccSelect(unsigned char *snr,  
                  unsigned char *sak) 
{ 
   return M500PiccCascSelect(0x93,snr,sak); // first cascade level 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E    C A S C A D E D   S E L E C T  
//  for extended serial number 
/////////////////////////////////////////////////////////////////////// 
char M500PiccCascSelect(unsigned char select_code,  
                        unsigned char *snr, 
                        unsigned char *sak) 
{ 
   char idata status = MI_OK;  
  
   M500PcdSetTmo(106); 
	 
   WriteIO(RegChannelRedundancy,0x0F); // RxCRC,TxCRC, Parity enable 
   ClearBitMask(RegControl,0x08);    // disable crypto 1 unit 
 
   //************* Cmd Sequence **********************************  
   ResetInfo(MInfo);    
   MSndBuffer[0] = select_code; 
   MSndBuffer[1] = 0x70;         // number of bytes send 
    
   memcpy(MSndBuffer + 2,snr,4); 
   MSndBuffer[6] = MSndBuffer[2]  
                   ^ MSndBuffer[3]  
                   ^ MSndBuffer[4]  
                   ^ MSndBuffer[5]; 
   MInfo.nBytesToSend   = 7; 
   status = M500PcdCmd(PCD_TRANSCEIVE, 
                       MSndBuffer, 
                       MRcvBuffer, 
                       &MInfo); 
 
   *sak = 0;    
   if (status == MI_OK)    // no timeout occured 
   { 
      if (MInfo.nBitsReceived != 8)    // last byte is not complete 
      { 
         status = MI_BITCOUNTERR; 
      } 
      else 
      { 
	 *sak = MRcvBuffer[0]; 
         memcpy(MLastSelectedSnr,snr,4);             
      }	 
   } 
   
   return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E      A U T H E N T I C A T I O N 
//   calling compatible version     
/////////////////////////////////////////////////////////////////////// 
char M500PiccAuth(unsigned char keyAB,       // KEYA or KEYB 
               unsigned char *snr,        // 4 bytes card serial number  
               unsigned char key_addr,    // key address in reader storage 
               unsigned char block)       // block number which should be  
                                          // authenticated 
{ 
   char          idata  status = MI_OK; 
   unsigned char xdata * key = 0; 
   unsigned char idata  keycoded[12]; 
   unsigned char idata  offset = (keyAB == PICC_AUTHENT1A) ? 0 : 6;    
 
   key = MKeys[key_addr] + offset; 
   M500HostCodeKey(key,keycoded); 
   status = M500PiccAuthKey(keyAB, 
                            snr, 
                            keycoded, 
                            block);   
  return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//                  A U T H E N T I C A T I O N    
//             W I T H   K E Y S   F R O M   E 2 P R O M 
/////////////////////////////////////////////////////////////////////// 
char M500PiccAuthE2( unsigned char auth_mode,   // KEYA, KEYB 
                     unsigned char *snr,        // 4 bytes card serial number 
                     unsigned char key_sector,  // key address in reader storage,  
						// 0 <= key_sector <= 15                      
                     unsigned char block)      // block number which should be  
                                               // authenticated 
					       // 0 <= block <= 256 
{ 
   char idata status = MI_OK; 
   // eeprom address calculation 
   // 0x80 ... offset 
   // key_sector ... sector 
   // 0x18 ... 2 * 12 = 24 = 0x18 
   unsigned short e2addr = 0x80 + key_sector * 0x18; 
   unsigned char *e2addrbuf = (unsigned char*)&e2addr; 
    
 
   if (auth_mode == PICC_AUTHENT1B) 
      e2addr += 12; // key B offset    
   FlushFIFO();    // empty FIFO 
   ResetInfo(MInfo); 
 
   memcpy(MSndBuffer,e2addrbuf,2); // write low and high byte of address 
   MSndBuffer[2] = MSndBuffer[0];          // Move the LSB of the 2-bytes 
   MSndBuffer[0] = MSndBuffer[1];  // address to the first byte 
   MSndBuffer[1] = MSndBuffer[2]; 
   MInfo.nBytesToSend   = 2; 
    // write load command 
   if ((status=M500PcdCmd(PCD_LOADKEYE2,MSndBuffer,MRcvBuffer,&MInfo)) == MI_OK) 
   {       
      // execute authentication 
      status = M500PiccAuthState(auth_mode,snr,block);   
   } 
   return status; 
}                         
 
/////////////////////////////////////////////////////////////////////// 
//                      C O D E   K E Y S   
/////////////////////////////////////////////////////////////////////// 
char M500HostCodeKey(  unsigned char *uncoded, // 6 bytes key value uncoded 
                     unsigned char *coded)   // 12 bytes key value coded 
{ 
   char idata status = MI_OK; 
   unsigned char idata cnt = 0; 
   unsigned char idata ln  = 0;     // low nibble 
   unsigned char idata hn  = 0;     // high nibble 
    
   for (cnt = 0; cnt < 6; cnt++) 
   { 
      ln = uncoded[cnt] & 0x0F; 
      hn = uncoded[cnt] >> 4; 
      coded[cnt * 2 + 1] = (~ln << 4) | ln; 
      coded[cnt * 2 ] = (~hn << 4) | hn; 
 
   } 
   return MI_OK; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//                  A U T H E N T I C A T I O N    
//             W I T H   P R O V I D E D   K E Y S 
/////////////////////////////////////////////////////////////////////// 
char M500PiccAuthKey(  unsigned char auth_mode, 
                     unsigned char *snr,        
                     unsigned char *keys,       
                     unsigned char block)       
{ 
   char idata status = MI_OK; 
   unsigned char idata i = 0; 
    
   FlushFIFO();    // empty FIFO 
   ResetInfo(MInfo); 
   memcpy(MSndBuffer,keys,12);                  // write 12 bytes of the key 
   MInfo.nBytesToSend = 12; 
   // write load command 
   if ((status=M500PcdCmd(PCD_LOADKEY,MSndBuffer,MRcvBuffer,&MInfo)) == MI_OK) 
   {       
      // execute authentication 
      status = M500PiccAuthState(auth_mode,snr,block);  
   } 
   return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//        A U T H E N T I C A T I O N   S T A T E S 
/////////////////////////////////////////////////////////////////////// 
char M500PiccAuthState( unsigned char auth_mode, 
                        unsigned char *snr, 
                        unsigned char block) 
{ 
   char idata status = MI_OK; 
   unsigned char idata i = 0; 
    
   status = ReadIO(RegErrorFlag);   // read error flags of the previous 
                                    // key load 
   if (status != MI_OK) 
   { 
      if (status & 0x40)            // key error flag set 
         status = MI_KEYERR; 
      else 
         status = MI_AUTHERR;       // generic authentication error  
   } 
   else 
   { 
      MSndBuffer[0] = auth_mode;        // write authentication command 
 
      MSndBuffer[1] = block;    // write block number for authentication 
      memcpy(MSndBuffer + 2,snr,4); // write 4 bytes card serial number  
      ResetInfo(MInfo); 
      MInfo.nBytesToSend = 6; 
      if ((status = M500PcdCmd(PCD_AUTHENT1, 
                               MSndBuffer, 
                               MRcvBuffer, 
                               &MInfo)) == MI_OK) 
      { 
         if (ReadIO(RegSecondaryStatus) & 0x07) // Check RxLastBits for error 
         { 
            status = MI_BITCOUNTERR; 
         } 
         else 
         { 
            ResetInfo(MInfo); 
            MInfo.nBytesToSend = 0; 
            if ((status = M500PcdCmd(PCD_AUTHENT2, 
                                     MSndBuffer, 
                                     MRcvBuffer, 
                                     &MInfo)) == MI_OK)  
            { 
               if ( ReadIO(RegControl) & 0x08 ) // Crypto1 activated 
               { 
                   status = MI_OK; 
               } 
               else 
               { 
                   status = MI_AUTHERR; 
               } 
            } 
         } 
      } 
   } 
   return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E   R E A D    
/////////////////////////////////////////////////////////////////////// 
char M500PiccRead(  unsigned char addr, 
                  unsigned char *_data) 
{ 
   char idata status = MI_OK; 
   char idata tmp    = 0; 
 
   FlushFIFO();    // empty FIFO 
 
   M500PcdSetTmo(3);     // long timeout  
 
   WriteIO(RegChannelRedundancy,0x0F); // RxCRC, TxCRC, Parity enable 
    
   // ************* Cmd Sequence **********************************  
   ResetInfo(MInfo);    
   MSndBuffer[0] = PICC_READ;   // read command code 
   MSndBuffer[1] = addr; 
   MInfo.nBytesToSend   = 2;    
   status = M500PcdCmd(PCD_TRANSCEIVE, 
                       MSndBuffer, 
                       MRcvBuffer, 
                       &MInfo); 
 
   if (status != MI_OK) 
   { 
      if (status != MI_NOTAGERR ) // no timeout occured 
      { 
         if (MInfo.nBitsReceived == 4)  // NACK 
         { 
             MRcvBuffer[0] &= 0x0f;  // mask out upper nibble 
             if ((MRcvBuffer[0] & 0x0a) == 0) 
             { 
                status = MI_NOTAUTHERR; 
             } 
             else 
             { 
                status = MI_CODEERR; 
             } 
          } 
      } 
      memcpy(_data,"0000000000000000",16); // in case of an error initialise  
                                          // data 
   } 
   else   // Response Processing 
   { 
      if (MInfo.nBytesReceived != 16) 
      { 
         status = MI_BYTECOUNTERR; 
         memcpy(_data,"0000000000000000",16); 
      } 
      else 
      { 
         memcpy(_data,MRcvBuffer,16); 
      } 
   } 
   M500PcdSetTmo(1);               // short timeout    
   return status;  
} 
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E   W R I T E      
/////////////////////////////////////////////////////////////////////// 
char M500PiccWrite( unsigned char addr, 
                  unsigned char *_data) 
{ 
   char idata status = MI_OK; 
 
     // ************* Cmd Sequence **********************************  
     ResetInfo(MInfo);    
     MSndBuffer[0] = PICC_WRITE;        // Write command code 
     MSndBuffer[1] = addr; 
     MInfo.nBytesToSend   = 2; 
     status = M500PcdCmd(PCD_TRANSCEIVE, 
                         MSndBuffer, 
                         MRcvBuffer, 
                         &MInfo); 
 
     if (status != MI_NOTAGERR)   // no timeout error 
     { 
        if (MInfo.nBitsReceived != 4)   // 4 bits are necessary 
        { 
           status = MI_BITCOUNTERR; 
        } 
        else                     // 4 bit received 
        { 
           MRcvBuffer[0] &= 0x0f; // mask out upper nibble 
           if ((MRcvBuffer[0] & 0x0a) == 0) 
           { 
              status = MI_NOTAUTHERR;                    
           } 
           else 
           { 
              if (MRcvBuffer[0] == 0x0a) 
              { 
                 status = MI_OK; 
              } 
              else  
              { 
                 status = MI_CODEERR; 
              } 
           } 
        } 
     } 
 
     if ( status == MI_OK) 
     { 
 
        M500PcdSetTmo(3);     // long timeout  
 
        ResetInfo(MInfo);    
        memcpy(MSndBuffer,_data,16); 
        MInfo.nBytesToSend   = 16; 
        status = M500PcdCmd(PCD_TRANSCEIVE, 
                            MSndBuffer, 
                            MRcvBuffer, 
                            &MInfo); 
         
        if (status & 0x80)    // timeout occured 
        { 
           status = MI_NOTAGERR; 
        } 
        else 
        { 
          
           if (MInfo.nBitsReceived != 4)  // 4 bits are necessary 
           { 
              status = MI_BITCOUNTERR; 
           } 
           else                     // 4 bit received 
           { 
              MRcvBuffer[0] &= 0x0f; // mask out upper nibble 
              if ((MRcvBuffer[0] & 0x0a) == 0) 
              { 
                 status = MI_WRITEERR; 
              } 
              else 
              { 
                 if (MRcvBuffer[0] == 0x0a) 
                 { 
                    status = MI_OK; 
                 } 
                 else  
                 { 
                    status = MI_CODEERR; 
                 } 
              }      
           } 
        }         
        M500PcdSetTmo(1);    // short timeout 
     } 
  return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//                V A L U E   M A N I P U L A T I O N  
/////////////////////////////////////////////////////////////////////// 
char M500PiccValue(unsigned char dd_mode,  
                    unsigned char addr,  
                    unsigned char *value, 
                    unsigned char trans_addr) 
{ 
   char status = MI_OK; 
 
   M500PcdSetTmo(1);    // short timeout 
   // ************* Cmd Sequence **********************************  
   ResetInfo(MInfo);    
   MSndBuffer[0] = dd_mode;        // Inc,Dec command code 
   MSndBuffer[1] = addr; 
   MInfo.nBytesToSend   = 2; 
   status = M500PcdCmd(PCD_TRANSCEIVE, 
                       MSndBuffer, 
                       MRcvBuffer, 
                       &MInfo); 
 
   if (status != MI_NOTAGERR)   // no timeout error 
   { 
        if (MInfo.nBitsReceived != 4)   // 4 bits are necessary 
        { 
           status = MI_BITCOUNTERR; 
        } 
        else                     // 4 bit received 
        { 
           MRcvBuffer[0] &= 0x0f; // mask out upper nibble 
           switch(MRcvBuffer[0]) 
           { 
              case 0x00:  
                 status = MI_NOTAUTHERR; 
                 break; 
              case 0x0a: 
                 status = MI_OK; 
                 break; 
              case 0x01: 
                 status = MI_VALERR; 
                 break; 
              default: 
                 status = MI_CODEERR; 
                 break; 
           } 
        } 
     } 
 
     if ( status == MI_OK) 
     { 
        M500PcdSetTmo(3);     // long timeout  
 
        ResetInfo(MInfo);    
        memcpy(MSndBuffer,value,4); 
        MInfo.nBytesToSend   = 4; 
        status = M500PcdCmd(PCD_TRANSCEIVE, 
                            MSndBuffer, 
                            MRcvBuffer, 
                            &MInfo); 
         
        if (status == MI_OK)    // no timeout occured 
        { 
            if (MInfo.nBitsReceived != 4)   // 4 bits are necessary 
            { 
               status = MI_BITCOUNTERR; 
            } 
            else                     // 4 bit received 
            { 
               MRcvBuffer[0] &= 0x0f; // mask out upper nibble 
               switch(MRcvBuffer[0]) 
               { 
                  case 0x00:  
                     status = MI_NOTAUTHERR; 
                     break; 
                  case 0x01: 
                     status = MI_VALERR; 
                     break; 
                  default: 
                     status = MI_CODEERR; 
                     break; 
               } 
            } 
         }         
         else 
         if (status == MI_NOTAGERR ) 
            status = MI_OK;  // no response after 4 byte value -  
                             // transfer command has to follow 
     } 
     if ( status == MI_OK) 
     { 
        ResetInfo(MInfo);    
        MSndBuffer[0] = PICC_TRANSFER;        // transfer command code 
        MSndBuffer[1] = trans_addr; 
        MInfo.nBytesToSend   = 2; 
        status = M500PcdCmd(PCD_TRANSCEIVE, 
                            MSndBuffer, 
                            MRcvBuffer, 
                            &MInfo); 
         
        if (status != MI_NOTAGERR)    // timeout occured 
        { 
            if (MInfo.nBitsReceived != 4)   // 4 bits are necessary 
            { 
               status = MI_BITCOUNTERR; 
            } 
            else                     // 4 bit received 
            { 
               MRcvBuffer[0] &= 0x0f; // mask out upper nibble 
               switch(MRcvBuffer[0]) 
               { 
                  case 0x00:  
                     status = MI_NOTAUTHERR; 
                     break; 
                  case 0x0a: 
                     status = MI_OK; 
                     break; 
                  case 0x01: 
                     status = MI_VALERR; 
                     break; 
                  default: 
                     status = MI_CODEERR; 
                     break; 
               } 
            } 
        }         
     } 
   return status; 
} 
 
/////////////////////////////////////////////////////////////////////// 
//   V A L U E   M A N I P U L A T I O N   W I T H   B A C K U P 
/////////////////////////////////////////////////////////////////////// 
char M500PiccValueDebit(unsigned char dd_mode,  
                         unsigned char addr,  
                         unsigned char *value) 
{ 
   char status = MI_OK; 
    
   M500PcdSetTmo(1);    // short timeout 
   ResetInfo(MInfo);    
   MSndBuffer[0] = dd_mode;        // Inc,Dec command code 
   MSndBuffer[1] = addr; 
   MInfo.nBytesToSend   = 2; 
   status = M500PcdCmd(PCD_TRANSCEIVE, 
                       MSndBuffer, 
                       MRcvBuffer, 
                       &MInfo); 
 
   if (status != MI_NOTAGERR)   // no timeout error 
   { 
        if (MInfo.nBitsReceived != 4)   // 4 bits are necessary 
        { 
           status = MI_BITCOUNTERR; 
        } 
        else                     // 4 bit received 
        { 
           MRcvBuffer[0] &= 0x0f; // mask out upper nibble 
           switch(MRcvBuffer[0]) 
           { 
              case 0x00:  
                 status = MI_NOTAUTHERR; 
                 break; 
              case 0x0a: 
                 status = MI_OK; 
                 break; 
              case 0x01: 
                 status = MI_VALERR; 
                 break; 
              default: 
                 status = MI_CODEERR; 
                 break; 
           } 
        } 
     } 
 
     if ( status == MI_OK) 
     { 
        M500PcdSetTmo(3);     // long timeout  
 
        ResetInfo(MInfo);    
        memcpy(MSndBuffer,value,4); 
        MInfo.nBytesToSend   = 4; 
        status = M500PcdCmd(PCD_TRANSCEIVE, 
                            MSndBuffer, 
                            MRcvBuffer, 
                            &MInfo); 
         
        if (status == MI_OK)    // no timeout occured 
        { 
            if (MInfo.nBitsReceived != 4)   // 4 bits are necessary 
            { 
               status = MI_BITCOUNTERR; 
            } 
            else                     // 4 bit received 
            { 
               MRcvBuffer[0] &= 0x0f; // mask out upper nibble 
               switch(MRcvBuffer[0]) 
               { 
                  case 0x00:  
                     status = MI_NOTAUTHERR; 
                     break; 
                  case 0x0a: 
                     status = MI_OK; 
                     break; 
                  case 0x05: 
                  case 0x01: 
                     status = MI_VALERR; 
                     break; 
                  default: 
                     status = MI_CODEERR; 
                     break; 
               } 
            } 
        }         
     } 
 
   return status; 
} 
 
////////////////////////////////////////////////////////////////////// 
//           P I C C   E X C H A N G E   B L O C K 
/////////////////////////////////////////////////////////////////////// 
char M500PiccExchangeBlock(unsigned char *send_data, 
                           unsigned char send_bytelen, 
                           unsigned char *rec_data,   
                           unsigned char *rec_bytelen, 
                           unsigned char append_crc,  
                           unsigned char timeout ) 
{ 
   char   status = MI_OK; 
 
   if (append_crc) 
   { 
      // RxCRC and TxCRC enable, parity enable 
      WriteIO(RegChannelRedundancy,0x0F);  
      send_bytelen -= 2; 
   } 
   else 
   { 
      // RxCRC and TxCRC disable, parity enable 
      WriteIO(RegChannelRedundancy,0x03);  
   } 
   M500PcdSetTmo(timeout); 
 
   status = ExchangeByteStream(PCD_TRANSCEIVE, 
                               send_data, 
                               send_bytelen, 
                               rec_data, 
                               rec_bytelen); 
 
   if ( status == MI_OK ) 
   { 
      if (append_crc) 
      { 
         *rec_bytelen += 2; // for two CRC bytes 
         rec_data[*rec_bytelen - 2] = 0x00; 
         rec_data[*rec_bytelen - 1] = 0x00; 
      } 
   } 
   else 
   { 
      *rec_bytelen = 0; 
   } 
   return status; 
}                       
 
////////////////////////////////////////////////////////////////////// 
//           E X C H A N G E   B Y T E   S T R E A M 
/////////////////////////////////////////////////////////////////////// 
char ExchangeByteStream(unsigned char Cmd, 
                        unsigned char *send_data, 
                        unsigned char send_bytelen, 
                        unsigned char *rec_data,   
                        unsigned char *rec_bytelen) 
{ 
   signed char status = MI_OK; 
   signed char state; 
 
   FlushFIFO();    // empty FIFO 
   ResetInfo(MInfo); // initialise ISR Info structure 
 
   if (send_bytelen > 0) 
   { 
      memcpy(MSndBuffer,send_data,send_bytelen); // write n bytes  
      MInfo.nBytesToSend = send_bytelen; 
      // write load command 
      state = status = M500PcdCmd(Cmd, 
                      MSndBuffer, 
                      MRcvBuffer, 
                      &MInfo); 
      if ( status == MI_OK ) 
      { 
         *rec_bytelen = MInfo.nBytesReceived; 
         if (*rec_bytelen) 
         { 
            memcpy(rec_data,MRcvBuffer,MInfo.nBytesReceived); 
         } 
      } 
   } 
   else 
   { 
      status = MI_WRONG_PARAMETER_VALUE; 
   } 
   return status; 
}                                    
 
/////////////////////////////////////////////////////////////////////// 
//          M I F A R E     H A L T 
/////////////////////////////////////////////////////////////////////// 
char M500PiccHalt(void) 
{ 
   char idata status = MI_CODEERR; 
 
   // ************* Cmd Sequence **********************************  
   ResetInfo(MInfo);    
   MSndBuffer[0] = PICC_HALT ;     // Halt command code 
   MSndBuffer[1] = 0x00;         // dummy address 
   MInfo.nBytesToSend   = 2; 
   status = M500PcdCmd(PCD_TRANSCEIVE, 
                       MSndBuffer, 
                       MRcvBuffer, 
                       &MInfo);    
   if (status) 
   { 
     // timeout error ==> no NAK received ==> OK 
     if (status == MI_NOTAGERR || status == MI_ACCESSTIMEOUT) 
        status = MI_OK; 
   } 
   //reset command register - no response from tag 
   WriteIO(RegCommand,PCD_IDLE); 
   return status;  
} 
 
////////////////////////////////////////////////////////////////////// 
//                 M I F A R E    R E S E T  
/////////////////////////////////////////////////////////////////////// 
char M500PcdRfReset(unsigned char ms) 
{ 
   char idata status = MI_OK; 
 
   if(ms) 
   { 
     ClearBitMask(RegTxControl,0x03);  // Tx2RF-En, Tx1RF-En disablen 
     delay_1ms(ms);                // Delay for 1 ms 
     SetBitMask(RegTxControl,0x03);    // Tx2RF-En, Tx1RF-En enable 
   } 
   else 
     ClearBitMask(RegTxControl,0x03);  // Tx2RF-En, Tx1RF-En disablen 
      
   return status; 
} 
 
void idle_request(void) 
{ 
  uchar status, _10ms; 
 
  auto_baud(); 
 
  WriteIO(RegChannelRedundancy,0x03); // RxCRC and TxCRC disable, parity enable 
  auto_baud(); 
  ClearBitMask(RegControl,0x08);    // disable crypto 1 unit    
  auto_baud(); 
  WriteIO(RegBitFraming,0x07);        // set TxLastBits to 7  
  auto_baud(); 
  SetBitMask(RegTxControl,0x03);    // Tx2RF-En, Tx1RF-En enable 
  auto_baud(); 
    	 
  ResetInfo(MInfo);    
  auto_baud(); 
  MSndBuffer[0] = PICC_REQALL; 
  auto_baud(); 
  MInfo.nBytesToSend   = 1;    
  auto_baud(); 
  status = M500PcdCmd(PCD_TRANSCEIVE, 
                      	MSndBuffer, 
                      	MRcvBuffer, 
                      	&MInfo); 
   
  auto_baud(); 
  if (status == MI_OK) 
  { 
    auto_baud(); 
    LED = OFF; 
    auto_baud(); 
  }  
   
  RCAP2LH = RCAP2_10ms; 
  auto_baud(); 
  T2LH    = RCAP2_10ms; 
  auto_baud(); 
  T2CON = 0x04;	// 16-bit auto-reload, clear TF2, start timer 
  auto_baud(); 
  _10ms = 10; 
  auto_baud(); 
  do 
  { 
	auto_baud(); 
	while (!TF2) 
	{ 
	  auto_baud(); 
	  if (CmdValid) 
	  { 
		TR2 = FALSE; 
		TF2 = FALSE; 
		LED = ON; 
		return; 
	  } 
	  auto_baud(); 
	  if (CmdReceived) 
	  { 
		TR2 = FALSE; 
		TF2 = FALSE; 
		LED = ON; 
		return; 
	  } 
	  auto_baud(); 
	} 
	auto_baud(); 
	TF2 = FALSE; 
	auto_baud(); 
	_10ms--; 
	auto_baud(); 
  } while (_10ms); 
  LED = ON; 
  auto_baud(); 
  TR2 = FALSE; 
  auto_baud(); 
} 
 
//---------------------------------------------------------------------------- 
//                                                                            
// FUNCTION:     start_timeout                                                    
//                                                                            
// IN:	         _50us                                                         
// OUT:	       	 -                                                            
//                                                                            
// COMMENT:	 Using Timer2 to generate timeout with a resolution of 50 us. 
//		 Timeout is calculated in the interrupt routine. 
//		 Max Timeout = 65535 x 50us = 3.277s                       
//                                                                            
//---------------------------------------------------------------------------- 
void start_timeout(unsigned int _50us) 
{ 
  ET2 = 0; 	// Disable Timer2 interrupt 
  TR2 = 0; 
  RCAP2LH = RCAP2_50us; 
  T2LH    = RCAP2_50us; 
  CountDown = _50us; 
  T2IR = 0;	// Reset timeout state 
  T2CON = 0x04;	// 16-bit auto-reload, clear TF2, start timer 
  ET2 = 1; 	// Enable Timer2 interrupt 
} 
 
//---------------------------------------------------------------------------- 
//                                                                            
// FUNCTION:     stop_timeout                                                    
//                                                                            
// IN:        	-                                                         
// OUT:       	-                                                            
//                                                                            
// COMMENT:  	Stop Timer2 and clear timeout state                        
//                                                                            
//---------------------------------------------------------------------------- 
void stop_timeout(void) 
{ 
  ET2 = 0; 	// disable Timer2 interrupt 
  CountDown = 0; 
  T2CON = 0x00;	// 16-bit auto-reload, clear TF2, stop timer 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
//                  Interrupt Handler TIMER2 
/////////////////////////////////////////////////////////////////////////////// 
void TIMEOUTISR (void) interrupt 5 using 2    //Timer2 interrupt 
{ 
   if(CountDown)  
     	CountDown--;	// Decrease timeout counter 
   if(!CountDown)  
   { 
	T2IR = 1;	// Set timeout state 
   	TR2 = 0; 
   } 
   TF2 = 0; 
}