RC522模块不但可以读取标签中的数据,还能将数据写入标签中,本篇介绍S50卡的写卡操作。
1. S50卡介绍
S50非接触式IC卡的容量为1K字节EEPROM,又称M1卡。内部EEPROM又分为16个扇区,每个扇区分4个块,以块为存取单位,每个块由16个字节组成。
1. M1卡主要指标:
每个扇区有独立的一组密码和访问控制。
每张卡有唯一32位序列号。
无电源,自带天线,内含加密控制逻辑和通讯逻辑电路。
数据保存期为10年,可改写10万次,读无限次
工作频率:13.56MHZ
通信速率:106 KBPS
工作温度:-20℃~50℃(湿度为90%)
2. M1卡存储结构
存储结构如下图,16个扇区,每个扇区4个块,可将16个扇区的64个块按绝对地址编号0-63。
其中第0扇区的块0,用于存放厂商代码,一般前四字节为UID,已经固化,一般不可更改。
每个扇区的块0、块1、块2为数据块,可用于存储数据,块3为控制块,包括了密码A,存取控制,密码B。
3. 读写流程
每个扇区的密码和存取控制都是独立的,可根据实际需要设定各自的密码及存取控制。出厂默认的密码6个字节都为0xFF。
扇区中每个块的存取条件是由密码和存取控制共同决定的,每个块有相应的三个控制位,按照一定规则进行约束,具体可以参照M1卡数据手册。
本篇演示向扇区1的块0,绝对地址为块4中写入数据。主要流程为:模块进行卡扫描读取卡片信息,通过密码进行身份认证,然后读取写入前的块数据,然后再次进行身份认证并写入自定义数据,然后再次身份认证读取写入后的块数据来检测是否写入成功。
2. 实验材料
Uno R3开发板
配套USB数据线
面包板及配套连接线
RFID-RC522模块及配套S50白卡和异形卡
3. 实验步骤
1. 根据原理图搭建电路图。
RC522模块的3.3V、GND分别对应连接开发板的3.3V、GND,模块的MOSI、MISO、SCK分别连接开发板的SPI接口11、12、13,模块的SDA、RST分别连接开发板数字管脚10、9。
实验原理图如下图所示:
实物连接图如下图所示:
2. 新建sketch,拷贝如下代码替换自动生成的代码并进行保存。
#include <SPI.h>
#include <MFRC522.h>
#define RST_PIN 9
#define SS_PIN 10
MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;
void setup() {
Serial.begin(9600);
while (!Serial); // 等待串口打开
SPI.begin();
mfrc522.PCD_Init();
// 出厂默认使用FF FF FF FF FF FF作为密码A和B
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
Serial.println(F("开始扫描卡进行读写..."));
Serial.print(F("使用密码:"));
dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE);
Serial.println();
Serial.println(F("数据将被写入到#1扇区"));
}
void loop() {
//寻找新卡
if ( ! mfrc522.PICC_IsNewCardPresent())
return;
//验证UID是否可读
if ( ! mfrc522.PICC_ReadCardSerial())
return;
//显示卡信息
Serial.print(F("卡 UID:"));
dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);
Serial.println();
Serial.print(F("卡类型: "));
MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
Serial.println(mfrc522.PICC_GetTypeName(piccType));
// 检查是否MIFARE卡类型
if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI
&& piccType != MFRC522::PICC_TYPE_MIFARE_1K
&& piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
Serial.println(F("不支持读取此卡类型"));
return;
}
// 操作扇区1
// 扇区1包括:块4~块7
byte sector = 1;
byte blockAddr = 4;
byte dataBlock[] = {
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x10
};//要写入的数据
byte trailerBlock = 7;
MFRC522::StatusCode status;
byte buffer[18];
byte size = sizeof(buffer);
// 使用密码A进行身份认证
Serial.println(F("使用密码A进行身份认证..."));
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("身份认证失败 "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// 显示当前扇区数据
Serial.println(F("当前扇区数据:"));
mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
Serial.println();
// 读取写入前块数据
Serial.print(F("读取写入前块")); Serial.print(blockAddr);
Serial.println(F("数据..."));
status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("读取失败 "));
Serial.println(mfrc522.GetStatusCodeName(status));
}
Serial.print(F("块")); Serial.print(blockAddr); Serial.println(F("数据:"));
dump_byte_array(buffer, 16); Serial.println();
Serial.println();
// 使用密码B进行身份认证
Serial.println(F("使用密码B进行身份认证..."));
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("身份认证失败 "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
//写入数据
Serial.print(F("写数据到块")); Serial.print(blockAddr);
Serial.println(F("..."));
dump_byte_array(dataBlock, 16); Serial.println();
status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("写入失败 "));
Serial.println(mfrc522.GetStatusCodeName(status));
}
Serial.println();
//读取写入后块数据
Serial.print(F("读取写入后块")); Serial.print(blockAddr);
Serial.println(F("数据..."));
status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("读取失败 "));
Serial.println(mfrc522.GetStatusCodeName(status));
}
Serial.print(F("块")); Serial.print(blockAddr); Serial.println(F("块:"));
dump_byte_array(buffer, 16); Serial.println();
// 显示当前扇区数据
Serial.println(F("当前扇区数据:"));
mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
Serial.println();
//使放置在读卡区的IC卡进入休眠状态,不再重复读卡
mfrc522.PICC_HaltA();
// 停止读卡模块编码
mfrc522.PCD_StopCrypto1();
}
// 十六进制输出
void dump_byte_array(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], HEX);
}
}
3. 连接开发板,设置好对应端口号和开发板类型,进行程序下载。
4. 实验现象
打开串口监视器,波特率设置成与程序中相一致的9600。将卡靠近模块,根据打印信息可看到数据被写入到指定块中。
Arduino学习交流群:672088578
更多内容,欢迎关注我的公众号。 微信扫一扫下方二维码即可关注: