开源NFC模块示例(三):Arduino NFC模块使用方法分享,PN532模块,S50卡

前言

NFC技术近年来得到了极大的应用,但可查的教程很少,国内做开源硬件的公司dfrobot也在出过一个NFC模块——NFC近场通讯模块 Arduino兼容。
个人用过这块板子,感觉很顺手,搜罗了几篇DF社区的文章,大家可以作为实例参考。

详情

近场通信(Near Field Communication,NFC),又称近距离无线通信,是一种短距离的高频无线通信技术,允许电子设备之间进行非接触式点对点数据传输(在十厘米内)交换数据。这个技术由免接触式射频识别(RFID)演变而来,并向下兼容RFID------这世道,写个教程还要想想怎么开头------这世道,写个开头还是抄来的------Attention:题主使用的是PN532模块和S50卡。
(╯‵□′)╯︵┴─┴

好了,言归正传。首先,完成一个S50卡读写操作的步骤:
唤醒----识别卡----密码验证------读写

By the way:波特率115200,数据位8,停止位1,奇偶校验none

首先说唤醒:

应该是PN532带了一个休眠功能,要使用PN532对NFC卡片进行读写的时候要先唤醒一下。过程很简单,写程序的时候加在setup里就可以了,一般就只运行一遍就好。

看看发送的命令(十六进制):
55 55 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 03 FD D4 14 01 17 00

成功的话PN532就会返回
00 00 FF 00 FF 00 00 00 FF 02 FE D5 15 16 00

基本唤醒没什么好说的,这个步骤还不管卡片上面事情,所以你要是没得到相应的回复,不要怀疑你的nfc卡出了问题。检查下接线吧。。

接下来就是识别卡片:
相比较唤醒,识别卡片的步骤就重要的多了,现实中那种对不起砍错人了的事是不会在nfc的世界出现的。nfc对卡的操作都是要先认识卡的。因为读写的操作中不会对卡进行身份确认。
识别命令:
00 00 ff 04 fc d4 4a 02 00 e0 00

上面数据中,4a—命令代码,02----卡数量,一般选1就好,最大是2,

返回(举个栗子):

00 00 FF 00 FF 00 00 00 FF C F4 D5 4B 01 01 00 04 08 04 D1 AA 40 EA 29 00
//0 0 FF 0 FF 0 ----ACK
//0 0 FF C F4
//D5—数据方向是PN532 to Arduino
//4B----响应命令
//1 1----目标卡1,目标卡数量
//0 4----atq
//8----capacity of the card is 8K
//4 ---- 4 numbers of the UID
//D1 AA 40 EA----UID
//29 0------DCS POST— DCS=0xff-0xff&(SUM(0 0 FF C F4 D5 4B 1 1 0 4 8 4 D1 AA 40 EA))

粉红色的就是我们找到的卡的UID,也就是卡的身份证号码,这个号码可以用来识别是哪张卡。DCS的计算就是前面数加起来得到一个和SUM,然后取SUM的后两位(二进制的低八位)
0xFF-SUM=DCS, 是用来校验数据传输的。

当然,如果识别出了问题可能会有其他乱码,至于乱码是什么意思,求你去看看pn532datasheet和使用说明,其实只要不是这个格式的基本就是你程序问题,和卡以及模块无关。。。

好了,来到了激动人心的密码验证环节了
先来看看s50卡的存储机制吧,来张性感照片:
在这里插入图片描述

1024 x 8 bit EEPROM存储器分为16区,每区4块,每块16字节,已图上红色框框里的14号区为例,每个区的第四块(块编号3,程序员是从0数数的)负责存放这个区的密码,
在第四块内有密码A,控制为,密码B,一般默认的是密码A,控制为是控制某个块的读写,增减,传输,储存的。详细信息参见这里

一般你就知道密码是在第放哪里的就好了,其它的不用管。

所以,一个区里四个块的密码是一样的

所以,你在第7块用的密码是和6块一样的,但是第7块用的密码和第8块就不是一个了,虽然出厂都是一个。

验证要发送的数据为:
0x00,0x00,0xFF,0x0F,0xF1,0xD4,0x40,0x01,0x60,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xD1,0xAA,0x40,0xEA,0xC2,0x00

解释一下
// 数据头 卡1 密码验证命令 块号 密码 UID(身份证号) DCS POST
//00 00 FF 0F F1 D4 40 01 60 07 FF FF FF FF FF FF 02 F5 13 BE C2 00

TIPS:
你要改的部分是块号,密码,和UID,DCS,其它的别改就好。DCS每次在程序里自动计算就好

如果成功,返回:
out: 00 00 FF 00 FF 00 00 00 FF 03 FD D5 41 00 EA 00
其中:
41 00 表示正确状态,很多时候你会收到 41 17之类的数据,你多半是瞎改过这个区的密码,要不就是没唤醒和寻卡。抠鼻。。。

终于到读写了
刚才说的区和块的注意点最好看懂了再操作写这个功能,不然手贱锁芯片的事不要太容易就发生,不过好的情况是,
锁了一个区,还有其它区可以用

读取数据的命令:
0x00,0x00,0xff,0x05,0xfb,0xD4,0x40,0x01,0x30,0x07,0xB4,0x00

各数据的解释:
// ----head--------- cmd card 1 readcmd block number DCS+POST
//00 00 ff 05 f b D4 40 01 30 07 B4 00 //读第7块

命令发送没什么好说的,成功的话你会看到回复:

00 00 FF 00 FF 00 00 00 FF 13 ED D5 41 00 01 01 02 02 03 03 04 04 05 05 06 06 07 07 08 08 A2 00
其中:
00 00 FF 00 FF 00-------数据头
00 00 FF 13 ED D5-----写东西的时候天气不好,所以没去查什么意思
41 00----------去读成功标志位,你要是总出现 41 03,please check your code,多半是操作问题,按照唤醒,寻卡,密码验证,读写的顺序准没错
01 01 02 02 03 03 04 04 05 05 06 06 07 07 08 08-----------之前我写进去的数据
A2 00------------DCS和POST

来看看写卡:
00 00 ff 15 EB D4 40 01 A0 06 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F CD 00
各数据解释:
00 00 ff 15 EB D4-------Just ignore its ***king meaning
40------------写命令
01------------卡1
06------------块号
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F------------要写进去的数据
CD 00-----------DCS+POST

如果成功,回复:
00 00 00 FF 00 FF 00 00 00 FF 03 FD D5 41 00 EA 00

这个和前面类似,基本写操作完成就是这几个数据,成功的标志是 41 00, 做好DCS的校验基本不会出什么问题。

附件是NFC的UID读取代码和NFC的读写代码例子,严禁吐槽代码写得不漂亮,红字表严肃。

代码是基于Leonardo控制板写的,因为Leonardo两个串口,做调试什么的比较方便。如果您身边只有UNO,可以参照附件的程序,
同时参照@Cain 的NFC示例(一)的帖子,你要是说没有IIC1602显示器,(╯‵□′)╯︵┴─┴

nfc卡片上0区有个地方是写这个卡身份证号码的地方,那个地方不能写,只能读。

贴一下代码吧,方便以后复制。

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#define print1Byte(args) Serial1.write(args)
#define print1lnByte(args)  Serial1.write(args),Serial1.println()
#else
#include "WProgram.h"
#define print1Byte(args) Serial1.print(args,BYTE)
#define print1lnByte(args)  Serial1.println(args,BYTE)
#endif

unsigned char receive_ACK[35];
unsigned char UID[4]={0xD1,0xAA,0x40,0xEA};
unsigned char secret[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
unsigned char dataWriteIntoCard[16]={0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04,0x05,0x05,0x06,0x06,0x07,0x07,0x08,0x08};

int ctr=0;

void setup()
{
  Serial.begin(9600);
  Serial1.begin(115200);

  wakeUp();
  delay(10);
  readAck(15);
  for(int i=0;i<15;i++)  Serial.print(receive_ACK[i]);
  Serial.println();

}

void loop()
{ 
  Scan();
  if(passWordCheck(0x08,UID,secret)==1)
{   
   Serial.println("passed");   
  if(ctr<4)   //  写4次后就不写了,s50卡的使用寿命是写10W次,读不限,放心用
  {  
        writeData(0x08,dataWriteIntoCard);
        Serial.println("written");
        ctr++;
  } 
  delay(2000);
  readData(0x08);    
  } 
  delay(4000);
}

void wakeUp()
{
  const unsigned char wake[24]={
  0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xfd, 0xd4, 0x14, 0x01, 0x17, 0x00};//wake up NFC module
  for(int i=0;i<24;i++) //send command
  {
    UART1_Send_Byte(wake[i]);
  }
}

void Scan()
{
  const unsigned char cmdUID[11]={ 0x00, 0x00, 0xFF, 0x04, 0xFC, 0xD4, 0x4A, 0x01, 0x00, 0xE1, 0x00};
  //0 0 FF 0 FF 0 0 0 FF C F4 D5 4B 1 1 0 4 8 4 D1 AA 40 EA 29 0 
  //0 0 FF 0 FF 0 ----ACK
  //0 0 FF C F4 
  //D5---PN532 to Arduino
  //4B----respond command 
  //1 1----target ID  and target amount
  //0 4----atq 
  //8----capacity of the card  is 8K
  //4 ---- 4 numbers of the UID
  //D1 AA 40 EA----UID 
  //29 0------DCS  POST---  DCS=0xff&(SUM(0 0 FF C F4 D5 4B 1 1 0 4 8 4 D1 AA 40 EA))
  unsigned char NFC_UID[5],count=0; 
  for(int i=0;i<11;i++)  UART1_Send_Byte(cmdUID[i]);
  delay(10); 
  readAck(25);
  delay(10); 
}


int passWordCheck(int block,unsigned char id[],unsigned char st[])
{
  //---------head-------  card 1  check blocknumber  password               UID D1 AA 40 EA      DCS+POST
  //00 00 FF 0F F1 D4 40    01     60    07          FF FF FF FF FF FF       02 F5 13 BE           C2 00
  
  //out: 00 00 FF 00 FF 00 00 00 FF 03 FD D5 41 00 EA 00  ------41 00 //正确状态    16Bytes
  //     0  0  FF 0  FF 0  0  0  FF  3 FD D5 41 27 C3 0 
  unsigned char cmdPassWord[22]={0x00,0x00,0xFF,0x0F,0xF1,0xD4,0x40,0x01,0x60,\
                                 0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xD1,0xAA,0x40,0xEA,0xC2,0x00};                                 
  unsigned char sum=0,count=0;
  cmdPassWord[9]=block;
  for(int i=10;i<16;i++) cmdPassWord[i]=st[i-10];// 密码
  for(int i=16;i<20;i++) cmdPassWord[i]=UID[i-16];// UID
  for(int i=0;i<20;i++) sum+=cmdPassWord[i];
  cmdPassWord[20]=0xff-sum&0xff;
  
  Serial.println("passWordSend: ");
  for(int i=0;i<22;i++)  {Serial.print(cmdPassWord[i],HEX); Serial.print(" ");}
  Serial.println();
  

  while(Serial1.available())   char xx=Serial1.read();//clear the serial data
  
  for (int i=0;i<22;i++)  UART1_Send_Byte(cmdPassWord[i]);
  delay(100);
  while(Serial1.available())
     {
        receive_ACK[count]=Serial1.read();
        count++;
     }
// readAck(16);delay(10);
  Serial.println("passWordRes: ");
  for(int i=0;i<16;i++)  {Serial.print(receive_ACK[i],HEX); Serial.print(" ");}
  Serial.println();
  
  if(checkDCS(16)==1 && receive_ACK[12]==0x41 && receive_ACK[13]==0x00)  return 1;
  else return 0; 
}

void readData(int block)// 读取数据  block---要读取的块
{
  //----head--------- cmd  card 1   readcmd   block 07  DCS+POST
  //00 00 ff 05 fb D4  40    01      30        07        B4 00 //读第7块
  //out: 00 00 FF 00 FF 00 //ACK
  //------------------41 00for right  41 03wrong        data                                           DCS+POST
  //00 00 FF 13 ED D5 41 00                        00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF     01 00  //7块

  //test back:00 00 FF 00 FF 00 00 00 FF 13 ED D5 41 00 01 01 02 02 03 03 04 04 05 05 06 06 07 07 08 08 A2 00 
  unsigned char cmdRead[12]={0x00,0x00,0xff,0x05,0xfb,0xD4,0x40,0x01,0x30,0x07,0xB4,0x00};
  unsigned char sum=0,count=0;
  cmdRead[9]=block;
  for(int i=0;i<10;i++) sum+=cmdRead[i];
  cmdRead[10]=0xff-sum&0xff;
  
  while(Serial1.available())   char xx=Serial1.read();//clear the serial data
  
  //Serial.print("readDCS:");Serial.println(cmdRead[10],HEX);
  for(int i=0;i<12;i++){UART1_Send_Byte(cmdRead[i]);}
  delay(10);
  while(Serial1.available())
     {
        receive_ACK[count]=Serial1.read();
////        Serial.print(count);Serial.print(":"); 
//        Serial.print(receive_ACK[count],HEX);
//        Serial.print(" ");
        count++;
     }
     
  Serial.println("Read data:   ");
  for(int i=0;i<count;i++)  {Serial.print(receive_ACK[i],HEX); Serial.print(" ");}
  Serial.println();
  //    DCS校验是否成功        这两位数据上为0x41和0x00则表示操作成功
  if(checkDCS(32)==1 && receive_ACK[12]==0x41 && receive_ACK[13]==0x00)  Serial.println("Finish Reading");
  
  Serial.println(" ");
}

void writeData(int block,unsigned char dwic[])//  block:待写入数据的块,dwic[]待写入的数据
{
  //00 00 ff 15 EB D4 40 01 A0 06 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F CD 00
  unsigned char cmdWrite[]={0x00,0x00,0xff,0x15,0xEB,0xD4,0x40,0x01,0xA0, \
                            0x06,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, \
                            0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xCD,0x00};
  unsigned char sum=0,count=0;
  cmdWrite[9]=block;
  for(int i=10;i<26;i++) cmdWrite[i]=dwic[i-10];// 待写入的数据
  for(int i=0;i<26;i++) sum+=cmdWrite[i];//加和
  cmdWrite[26]=0xff-sum&0xff;//  计算DCS
  
  while(Serial1.available())   char xx=Serial1.read();//clear the serial data
  
  for(int i=0;i<28;i++) UART1_Send_Byte(cmdWrite[i]);
  while(Serial1.available())
     {
        receive_ACK[count]=Serial1.read();
        count++;
     }
     
  Serial.print("Write respond:   ");
  for(int i=0;i<17;i++)  {Serial.print(receive_ACK[i],HEX); Serial.print(" ");}
  Serial.println("     Write respond End ");
  if(checkDCS(17)==1 && receive_ACK[13]==0x41 && receive_ACK[14]==0x00)
  {
    Serial.println("WriteFinish!");
  }
}

void readAck(int x) //读取x个串口发来的数据
{
  unsigned char i;
  for(i=0;i<x;i++)
  {
    receive_ACK[i]= Serial1.read();
  }
}

void UART1_Send_Byte(unsigned char command_data)  // 按照固定格式发送命令
{
  print1Byte(command_data);
  #if defined(ARDUINO) && ARDUINO >= 100
    Serial1.flush();// complete the transmission of outgoing serial data 
  #endif
} 

char checkDCS(int x)  //  NFC  S50卡  DCS校验检测子函数
{
  unsigned char sum=0,dcs=0;
  for(int i=6;i<x-2;i++)
  {
    sum+=receive_ACK[i];
  }
  dcs=0xff-sum&0xff;
  if(dcs==receive_ACK[x-2])  return 1;
  else   return 0;
}

参考链接:
NFC的PN532 读写命令格式:http://wenku.baidu.com/link?url=erLduczaC8U8HmSE3X503nMa6JVpfPAeXIkbsPiXn_hB1NN3hlEhdfRo6SsqCTqDGAc6FCrTNUWDg6Z9wWRik0yzq8_XO37IfPH4nsuawH_
Mifare1卡说明文档:http://wenku.baidu.com/view/4784445477232f60ddcca1f6.html
以及DFRobot的NPC模块 wiki:http://wiki.dfrobot.com.cn/index.php/(SKU:DFR0231)NFC近场通讯模块_Arduino兼容

作者:Holiday
转载自:DF创客社区

猜你喜欢

转载自blog.csdn.net/qq_29338243/article/details/86626565
NFC