近场通信(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, 是用来校验数据传输的。
当然,如果识别出了问题可能会有其他乱码,至于乱码是什么意思,求你去看看pn532的datasheet 和使用说明 ,其实只要不是这个格式的基本就是你程序问题,和卡以及模块无关。。。
好了,来到了激动人心的密码验证环节了
先来看看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;
}
复制代码
参考链接:
以及DFRobot的产品wiki
嗯,基本就是这么多了。
看帖回帖,是美德
{:5_153:}