Search

2013年9月26日 星期四

Arduino - RFID NXP MFRC522

  MFRC522是飛利浦的13.56Mhz的RFID Solution,悠遊卡或者高雄一卡通用的,本來想說要自己Layout洗板,不過因為沒熱風槍不知道怎麼黏MFRC522,總之只要按照Document由SPI傳操作暫存器跟指令就可以用了,Mifare One卡的Block 0前面4byte就是UID,第5byte是校驗和由UID的4byte依序XOR沒錯誤就會得到和第5byte相同的值,另外就是一開始很那悶為什麼得到的UID和iBon不同,原來要轉換成8H10D的格式,將Block 0前面4byte十六進位值反向然後直接視為一個4byte的十進位值就是8H10D了,例如得到:0x12 0x34 0x56 0x78 -> 8H10D:0x78563412 = 2018915346,如果不滿十位數就在開頭補零,最後在網路上看到很多Library不過都很亂所以我重寫整理一下和加入一個8H10D格式的example。


                 Arduino                     MFRC522
            Digital 10            ---->     SDA
            Digital 11(MOSI)  ----> MOSI(DI)
            Digital 12(MISO)  ----> MISO(DO)
            Digital 13(SCK) ----> SCLK(CK)
            Digital 5              ---->      RST
                VCC 3.3V  ---->      VCC
                    GND    ---->      GND

MFRC522.h

#ifndef __MFRC522__
#define __MFRC522__


#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

#include <SPI.h>

//data array maxium length
const unsigned int MAX_LEN = 16;

//MFRC522 command bits
const unsigned char PCD_IDLE = 0x00; //NO action; cancel current commands
const unsigned char PCD_AUTHENT = 0x0E; //verify password key
const unsigned char PCD_RECEIVE = 0x08; //receive data
const unsigned char PCD_TRANSMIT = 0x04; //send data
const unsigned char PCD_TRANSCEIVE = 0x0C; //send and receive data
const unsigned char PCD_RESETPHASE = 0x0F; //reset
const unsigned char PCD_CALCCRC = 0x03; //CRC check and caculation

//Mifare_One card command bits
const unsigned char PICC_REQIDL = 0x26; //Search the cards that not into sleep mode in the antenna area
const unsigned char PICC_REQALL = 0x52; //Search all the cards in the antenna area
const unsigned char PICC_ANTICOLL = 0x93; //prevent conflict
const unsigned char PICC_SElECTTAG = 0x93; //select card
const unsigned char PICC_AUTHENT1A = 0x60; //verify A password key
const unsigned char PICC_AUTHENT1B = 0x61; //verify B password key
const unsigned char PICC_READ = 0x30; //read
const unsigned char PICC_WRITE = 0xA0; //write
const unsigned char PICC_DECREMENT = 0xC0; //deduct value
const unsigned char PICC_INCREMENT = 0xC1; //charge up value
const unsigned char PICC_RESTORE = 0xC2; //Restore data into buffer
const unsigned char PICC_TRANSFER = 0xB0; //Save data into buffer
const unsigned char PICC_HALT = 0x50; //sleep mode


//THe mistake code that return when communicate with MFRC522
const unsigned char MI_OK = 0;
const unsigned char MI_NOTAGERR = 1;
const unsigned char MI_ERR = 2;


//------------------MFRC522 register ---------------
//Page 0:Command and Status
const unsigned char Reserved00 = 0x00;
const unsigned char CommandReg = 0x01;
const unsigned char CommIEnReg = 0x02;
const unsigned char DivlEnReg = 0x03;
const unsigned char CommIrqReg = 0x04;
const unsigned char DivIrqReg = 0x05;
const unsigned char ErrorReg = 0x06;
const unsigned char Status1Reg = 0x07;
const unsigned char Status2Reg = 0x08;
const unsigned char FIFODataReg = 0x09;
const unsigned char FIFOLevelReg = 0x0A;
const unsigned char WaterLevelReg = 0x0B;
const unsigned char ControlReg = 0x0C;
const unsigned char BitFramingReg = 0x0D;
const unsigned char CollReg = 0x0E;
const unsigned char Reserved01 = 0x0F;
//Page 1:Command
const unsigned char Reserved10 = 0x10;
const unsigned char ModeReg = 0x11;
const unsigned char TxModeReg = 0x12;
const unsigned char RxModeReg = 0x13;
const unsigned char TxControlReg = 0x14;
const unsigned char TxAutoReg = 0x15;
const unsigned char TxSelReg = 0x16;
const unsigned char RxSelReg = 0x17;
const unsigned char RxThresholdReg = 0x18;
const unsigned char DemodReg = 0x19;
const unsigned char Reserved11 = 0x1A;
const unsigned char Reserved12 = 0x1B;
const unsigned char MifareReg = 0x1C;
const unsigned char Reserved13 = 0x1D;
const unsigned char Reserved14 = 0x1E;
const unsigned char SerialSpeedReg = 0x1F;
//Page 2:CFG
const unsigned char Reserved20 = 0x20;
const unsigned char CRCResultRegM = 0x21;
const unsigned char CRCResultRegL = 0x22;
const unsigned char Reserved21 = 0x23;
const unsigned char ModWidthReg = 0x24;
const unsigned char Reserved22 = 0x25;
const unsigned char RFCfgReg = 0x26;
const unsigned char GsNReg = 0x27;
const unsigned char CWGsPReg = 0x28;
const unsigned char ModGsPReg = 0x29;
const unsigned char TModeReg = 0x2A;
const unsigned char TPrescalerReg = 0x2B;
const unsigned char TReloadRegH = 0x2C;
const unsigned char TReloadRegL = 0x2D;
const unsigned char TCounterValueRegH = 0x2E;
const unsigned char TCounterValueRegL = 0x2F;
//Page 3:TestRegister
const unsigned char Reserved30 = 0x30;
const unsigned char TestSel1Reg = 0x31;
const unsigned char TestSel2Reg = 0x32;
const unsigned char TestPinEnReg = 0x33;
const unsigned char TestPinValueReg = 0x34;
const unsigned char TestBusReg = 0x35;
const unsigned char AutoTestReg = 0x36;
const unsigned char VersionReg = 0x37;
const unsigned char AnalogTestReg = 0x38;
const unsigned char TestDAC1Reg = 0x39;
const unsigned char TestDAC2Reg = 0x3A;
const unsigned char TestADCReg = 0x3B;
const unsigned char Reserved31 = 0x3C;
const unsigned char Reserved32 = 0x3D;
const unsigned char Reserved33 = 0x3E;
const unsigned char Reserved34 = 0x3F;
//-----------------------------------------------

class MFRC522
{
public:
MFRC522();

void Write(const unsigned char,const unsigned char);
unsigned char Read(const unsigned char);
void SetBitMask(const unsigned char,const unsigned char);
void ClearBitMask(const unsigned char,const unsigned char);
void AntennaOn();
void AntennaOff();
void Reset();
void Init();

unsigned char MFRC522ToCard(const unsigned char,
const unsigned char*,
const unsigned char,
unsigned char*,
unsigned int*);
unsigned char WriteBlock(unsigned char,unsigned char*);
unsigned char Request(unsigned char,unsigned char*);
unsigned char Anticoll(unsigned char*);
void CalulateCRC(unsigned char*,unsigned char,unsigned char*);
unsigned char SelectTag(unsigned char*);
void Halt();
private:
const static int chip_select_pin = 10;
const static int NRSTPD = 5;
};

#endif

MFRC522.cpp

#include <MFRC522.h>

#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

MFRC522::MFRC522()
{
pinMode(chip_select_pin,OUTPUT);
digitalWrite(chip_select_pin,LOW);

    pinMode(NRSTPD,OUTPUT);
digitalWrite(NRSTPD,HIGH);
}

void MFRC522::Write(const unsigned char address,const unsigned char value)
{
digitalWrite(chip_select_pin,LOW);
SPI.transfer((address << 1) & 0x7E);
    SPI.transfer(value);
digitalWrite(chip_select_pin,HIGH);
}

unsigned char MFRC522::Read(const unsigned char address)
{
unsigned char value;

digitalWrite(chip_select_pin,LOW);
    SPI.transfer(((address << 1) & 0x7E) | 0x80);
    value = SPI.transfer(0x00);
digitalWrite(chip_select_pin,HIGH);

return value;
}

void MFRC522::SetBitMask(const unsigned char reg,const unsigned char mask)
{
    unsigned char temp;
    temp = Read(reg);
    Write(reg,temp | mask);
}

void MFRC522::ClearBitMask(const unsigned char reg,const unsigned char mask)
{
    unsigned char temp;
    temp = Read(reg);
    Write(reg,temp & (~mask)); // clear bit mask
}

void MFRC522::AntennaOn()
{
    unsigned char temp;

    temp = Read(TxControlReg);
    if(!(temp & 0x03)){
SetBitMask(TxControlReg,0x03);
}
}

void MFRC522::AntennaOff()
{
ClearBitMask(TxControlReg,0x03);
}

void MFRC522::Reset()
{
Write(CommandReg,PCD_RESETPHASE);
}

void MFRC522::Init()
{
Reset();

    Write(TModeReg,0x8D);
    Write(TPrescalerReg,0x3E);
    Write(TReloadRegL,30);
    Write(TReloadRegH,0);
   
    Write(TxAutoReg,0x40);
    Write(ModeReg,0x3D);

AntennaOn();
}

unsigned char MFRC522::MFRC522ToCard(const unsigned char command,const unsigned char *send_data,const unsigned char send_len,unsigned char *back_data,unsigned int *back_len)
{
    unsigned char status = MI_ERR;
    unsigned char irqEn = 0x00;
    unsigned char waitIRq = 0x00;
    unsigned char lastBits;
    unsigned char n;
    unsigned int i;

    switch(command)
    {
        case PCD_AUTHENT: //verify card password
        {
            irqEn = 0x12;
            waitIRq = 0x10;
            break;
        }
        case PCD_TRANSCEIVE: //send data in the FIFO
        {
            irqEn = 0x77;
            waitIRq = 0x30;
            break;
        }
        default:
            break;
    }
 
    Write(CommIEnReg,irqEn | 0x80); //Allow interruption
    ClearBitMask(CommIrqReg,0x80); //Clear all the interrupt bits
    SetBitMask(FIFOLevelReg,0x80); //FlushBuffer=1, FIFO initilizate
   
    Write(CommandReg,PCD_IDLE); //NO action;cancel current command ???

    //write data into FIFO
    for (i = 0; i < send_len;++i){
        Write(FIFODataReg,send_data[i]);
    }

    //procceed it
    Write(CommandReg,command);
    if (command == PCD_TRANSCEIVE){
SetBitMask(BitFramingReg,0x80); //StartSend=1,transmission of data starts
    }
   
    //waite receive data is finished
    i = 2000; //i should adjust according the clock, the maxium the waiting time should be 25 ms???
    do {
        //CommIrqReg[7..0]
        //Set1 TxIRq RxIRq IdleIRq HiAlerIRq LoAlertIRq ErrIRq TimerIRq
        n = Read(CommIrqReg);
        --i;
    }

    while ((i != 0) && !(n & 0x01) && !(n & waitIRq));

    ClearBitMask(BitFramingReg,0x80); //StartSend=0
   
    if (i != 0){
if(!(Read(ErrorReg) & 0x1B)){ //BufferOvfl Collerr CRCErr ProtecolErr
            status = MI_OK;
            if(n & irqEn & 0x01){
                status = MI_NOTAGERR; //??
            }
           
            if(command == PCD_TRANSCEIVE){
                n = Read(FIFOLevelReg);
                lastBits = Read(ControlReg) & 0x07;
                if (lastBits){
                    *back_len = (n - 1) * 8 + lastBits;
                }
                else{
                    *back_len = n*8;
                }
               
                if(n == 0){
                    n = 1;
                }
                if (n > MAX_LEN){
                    n = MAX_LEN;
                }
               
                //read the data from FIFO
                for (i=0; i < n;++i){
                    back_data[i] = Read(FIFODataReg);
                }
            }
        }
        else{
            status = MI_ERR;
        }
       
    }
   
    //SetBitMask(ControlReg,0x80); //timer stops
    //Write(CommandReg, PCD_IDLE);

    return status;
}

unsigned char MFRC522::WriteBlock(unsigned char block_address,unsigned char  *write_data)
{
    unsigned char status;
    unsigned int recvBits;
    unsigned char i;
    unsigned char buff[18];
   
    buff[0] = PICC_WRITE;
    buff[1] = block_address;
    CalulateCRC(buff,2,&buff[2]);
    status = MFRC522ToCard(PCD_TRANSCEIVE,buff,4,buff,&recvBits);

    if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A)){
        status = MI_ERR;
    }
       
    if (status == MI_OK){
for (i=0;i < 16;++i){ //Write 16 bytes data into FIFO
            buff[i] = *(write_data+i);
        }
        CalulateCRC(buff,16,&buff[16]);
        status = MFRC522ToCard(PCD_TRANSCEIVE,buff,18,buff,&recvBits);
       
        if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A)){
            status = MI_ERR;
        }
    }
   
    return status;
}

unsigned char MFRC522::Request(unsigned char mode,unsigned char *tag_type)
{
    unsigned char status;
    unsigned int backBits; //the data bits that received

    Write(BitFramingReg,0x07); //TxLastBists = BitFramingReg[2..0] ???
   
    tag_type[0] = mode;
    status = MFRC522ToCard(PCD_TRANSCEIVE,tag_type,1,tag_type,&backBits);

    if ((status != MI_OK) || (backBits != 0x10)){
        status = MI_ERR;
    }
 
    return status;
}

unsigned char MFRC522::Anticoll(unsigned char *uid)
{
    unsigned char status;
    unsigned char i;
    unsigned char serNumCheck=0;
    unsigned int unLen;
   
    //ClearBitMask(Status2Reg, 0x08); //strSensclear
    //ClearBitMask(CollReg,0x80); //ValuesAfterColl
    Write(BitFramingReg,0x00); //TxLastBists = BitFramingReg[2..0]

    uid[0] = PICC_ANTICOLL;
    uid[1] = 0x20;
    status = MFRC522ToCard(PCD_TRANSCEIVE,uid,2,uid,&unLen);

    if (status == MI_OK){
        //Verify card UID
        for (i=0;i < 4;++i){
            serNumCheck ^= uid[i];
        }
        if (serNumCheck != uid[i]){
            status = MI_ERR;
        }
    }

    //SetBitMask(CollReg, 0x80); //ValuesAfterColl=1

    return status;
}

void MFRC522::CalulateCRC(unsigned char *input,unsigned char len,unsigned char *output)
{
    unsigned char i,n;

    ClearBitMask(DivIrqReg,0x04); //CRCIrq = 0
    SetBitMask(FIFOLevelReg,0x80); //Clear FIFO pointer
    //Write(CommandReg, PCD_IDLE);

    //Write data into FIFO
    for (i=0;i < len;i++){
        Write(FIFODataReg,*(input + i));
    }
    Write(CommandReg,PCD_CALCCRC);

    //waite CRC caculation to finish
    i = 0xFF;
    do {
        n = Read(DivIrqReg);
        --i;
    }
    while ((i != 0) && !(n & 0x04)); //CRCIrq = 1

    //read CRC caculation result
    output[0] = Read(CRCResultRegL);
    output[1] = Read(CRCResultRegM);
}

unsigned char MFRC522::SelectTag(unsigned char *uid)
{
unsigned char i;
unsigned char status;
unsigned char size;
unsigned int recvBits;
unsigned char buffer[9];

//ClearBitMask(Status2Reg, 0x08);                        //MFCrypto1On=0

buffer[0] = PICC_SElECTTAG;
buffer[1] = 0x70;

for (i=0; i<5; i++){
    buffer[i + 2] = *(uid + i);
}

  CalulateCRC(buffer,7,&buffer[7]);

  status = MFRC522ToCard(PCD_TRANSCEIVE,buffer,9,buffer,&recvBits);
  if ((status == MI_OK) && (recvBits == 0x18)){
    size = buffer[0];
  }
  else{
    size = 0;
  }

  return size;
}

void MFRC522::Halt()
{
    unsigned char status;
    unsigned int unLen;
    unsigned char buff[4];

    buff[0] = PICC_HALT;
    buff[1] = 0;
    CalulateCRC(buff,2,&buff[2]);

    status = MFRC522ToCard(PCD_TRANSCEIVE,buff,4,buff,&unLen);
}


8H10D.ino

#include <MFRC522.h>
#include <SPI.h>

/* 16^0 ~ 16^7 */
unsigned long table[8] = {1,16,256,4096,65536,1048576,16777216,268435456};

MFRC522 mfrc522;

unsigned char uid[5];

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  mfrc522.Init();
}

void loop()
{
  unsigned char status;
  unsigned char str[MAX_LEN];

  status = mfrc522.Request(PICC_REQIDL,str);
  if (status != MI_OK){
    return;
  }

  status = mfrc522.Anticoll(str);
  if (status == MI_OK){
    memcpy(uid,str,5);
    mfrc522.SelectTag(uid); //防止同張卡片持續感應與讀取
 
    Serial.print("Original UID : ");
    unsigned long sum = 0; //將UID每位byte加總(4byte uid & 1byte check)
    for(int i = 3;i >= 0;--i){
      unsigned long temp_h = (uid[i] >> 4) & 0x0000000F;
      Serial.print(temp_h,HEX);
      sum += (temp_h * table[(i*2)+1]); //加上UID每byte的高4bit
      unsigned long temp_l = (uid[i] & 0x0000000F);
      Serial.print(temp_l,HEX);
      sum += (temp_l * table[i*2]); //加上UID每byte的低4bit
    }
    Serial.println("");
 
    Serial.print("8H10D UID : ");
    String _8H10D = String(sum);
    if(_8H10D.length() < 10){
      for(int i = 0;i < (10 - _8H10D.length());++i){
        Serial.print("0"); //8H10D格式未滿十碼開頭補零
      }
    }
    for(int i = 0;i < _8H10D.length();++i){
      Serial.print(_8H10D.charAt(i));
    }
  }
  Serial.println("");

  mfrc522.Halt();
  delay(100);
}

參考

MFRC522 Datasheet

3 則留言:

  1. 作者您好~ 感謝您這麼熱心提供RFID資料,不過有個疑惑請教一下,目前這個範例似乎無法感應悠遊卡,不知道是什麼原因呢? 謝謝

    回覆刪除
  2. 一定可以感應並讀取悠遊卡uid,如果你說的是內容有加密除非你...

    回覆刪除
  3. 作者您好,我也是卡在資料轉換直到看見您這篇文章

    想請教是如何知道由UID轉8H10D是將byte倒序排列呢(有沒有線索可以得知?)

    感謝您:)

    回覆刪除