RFID-MFRC522射频识别模块,S50卡M1

RFID-MFRC522射频识别模块,S50卡M1射频识别模块什么是RFIDMFRC522S50-M1卡1、主要指标2、存储结构RC522与ArduinoUNO的接线什么是RFID无线射频识别即射频识别技术(RadioFrequencyIdentification,RFID),是自动识别技术的一种,通过无线射频方式进行非接触双向数据通信,利用无线射频方式对记录媒体(电子标签或射频卡)进行读写,从而达到识别目标和数据交换的目的。MF…

大家好,又见面了,我是你们的朋友全栈君。

什么是RFID

无线射频识别即射频识别技术(Radio Frequency Identification,RFID),是自动识别技术的一种,通过无线射频方式进行非接触双向数据通信,利用无线射频方式对记录媒体(电子标签或射频卡)进行读写,从而达到识别目标和数据交换的目的。

MFRC522

MF RC522 利用了先进的调制和解调概念,完全集成了在13.56MHz 下所有类型的被动非接触式通信方式和协议。支持 ISO14443A 的多层应用。其内部发送器部分可驱动读写器天线与ISO 14443A/MIFARE卡和应答机的通信,无需其它的电路。接收器部分提供一个坚固而有效的解调和解码电路,用于处理ISO14443A 兼容的应答器信号。数字部分处理ISO14443A 帧和错误检测(奇偶 &CRC)。此外,它还支持快速CRYPTO1 加密算法,用于验证MIFARE 系列产品。MFRC522 支持MIFARE?更高速的非接触式通信,双向数据传输速率高达424kbit/s。
在这里插入图片描述

S50-M1卡

1、主要指标

  • 容量为8K位(1KByte)EEPROM
  • 分为16个扇区,每个扇区为4块,每块16个字节,以块为存取单位
  • 每个扇区有独立的一组密码及访问控制
  • 每张卡有唯一序列号,为32位
  • 数据保存期为10年,可改写10万次,读无限次
  • 工作温度:-20℃~50℃(湿度为90%)
  • 工作频率:13.56MHZ
  • 通信速率:106 KBPS
  • 读写距离:10 cm以内(与读写器有关)

2、存储结构

1、M1卡分为16个扇区,每个扇区由4块(块0、块1、块2、块3)组成,(我们也将16个扇区的64个块按绝对地址编号为0~63,存贮结构如下图所示:

在这里插入图片描述
2、第0扇区的块0(即绝对地址0块),它用于存放厂商代码,已经固化,不可更改。
3、每个扇区的块0、块1、块2为数据块,可用于存贮数据。
数据块 可作两种应用:
★用作一般的数据保存,可以进行读、写操作。
★用作数据值,可以进行初始化值、加值、减值、读值操作。
4、每个扇区的块3为控制块,包括了密码A、存取控制、密码B。具体结构如下:

A0 A1 A2 A3 A4 A5 FF 07 80 69 B0 B1 B2 B3 B4 B5
密码A(6字节) 存取控制(4字节) 密码B(6字节)

5、每个扇区的密码和存取控制都是独立的,可以根据实际需要设定各自的密码及存取控制。存取控制为4个字节,共32位,扇区中的每个块(包括数据块和控制块)的存取条件是由密码和存取控制共同决定的,在存取控制中每个块都有相应的三个控制位,定义如下:

控制位1 控制位2 控制位3
块0: C10 C20 C30
块1: C11 C21 C31
块2: C12 C22 C32
块3: C13 C23 C33

三个控制位以正和反两种形式存在于存取控制字节中,决定了该块的访问权限(如
进行减值操作必须验证KEY A,进行加值操作必须验证KEY B,等等)。

存取控制(4字节,其中字节9为备用字节)结构如下所示:

7 6 5 4 3 2 1 0
字节6 C23_b C22_b C21_b C20_b C13_b C12_b C11_b C10_b
字节7 C13 C12 C11 C10 C33_b C32_b C31_b C30_b
字节8 C33 C32 C31 C30 C23 C22 C21 C20
字节9

( 注: _b表示取反 )

6、数据块(块0、块1、块2)的存取控制如下:

控制位1 控制位2 控制位3 访 问 条 件
C1X C2X C3X 增加 减少,传输,存储
0 0 0 KeyA/B KeyA/B KeyA/B KeyA/B
0 1 0 KeyA/B Never Never Never
1 0 0 KeyA/B KeyB Never Never
1 1 0 KeyA/B KeyB KeyB KeyA/B
0 0 1 KeyA/B Never Never KeyA/B
0 1 1 KeyB KeyB Never Never
1 0 1 KeyB Never Never Never
1 1 1 Never Never Never Never

KeyA/|B 表示密码A或密码B,Never表示任何条件下不能实现,x=0,1,2
例如:当块0的存取控制位C10 C20 C30=1 0 0时,验证密码A或密码B正确后可读;验证密码B正确后可写;不能进行加值、减值操作。

7、控制块-块3的存取控制与数据块(块0、1、2)不同,它的存取控制如下:

      控制位       |       密码A      |       存取控制    |      密码B
C13 C23 C33 Read Write Read Write Read Write
0 0 0 Never KeyA/B KeyA/B Never KeyA/B KeyA/B
0 1 0 Never Never KeyA/B Never KeyA/B Never
1 0 0 Never KeyB KeyA/B Never Never KeyB
1 1 0 Never Never KeyA/B Never Never Never
0 0 1 Never KeyA/B KeyA/B KeyA/B KeyA/B KeyA/B
0 1 1 Never KeyB KeyA/B KeyB Never KeyB
1 0 1 Never Never KeyA/B KeyB Never Never
1 1 1 Never Never KeyA/B Never Never Never

例如:当块3的存取控制位C13 C23 C33=001时,表示:
密码A:不可读,验证KEYA或KEYB正确后,可写(更改)。
存取控制:验证KEYA或KEYB正确后,可读、可写。
密码B:验证KEYA或KEYB正确后,可读、可写。

3、AB密码一些问题

在这里插入图片描述
控制字的默认值是“FF078069”,此时
A密钥:不可被读出,有全部权限
B密钥:可被读出,没有任何权限

在这里插入图片描述
在大多数使用B密钥的系统中,控制字 = 08778F00, 此时
A密钥:不可被读出,有读取数据可扣款权限
B密钥:不可被读出,有全部权限

原装的Philps S50芯片在出厂时设置每个分区的的第四块A密钥是“FFFFFFFFFFFF”,控制字是:“FF078069”,B密钥是:“FFFFFFFFFFFF”,A密钥是供用户读写操作的,利用A密钥可对对除0区外其它所有扇区块进行读写操作。B密钥不可操作,这些用的都是逻逻加密算法加密,而且密钥都是不可见,我们在读时能看到的A密钥都是显示为“000000000000”,B密钥显示:“FFFFFFFFFFFF”, 这些都是出厂时厂家设定的默认值。
如果用户要使用B密钥,如“公交一卡通的公交卡”,那先要把中间控制改了,如果改错那所改的那个分区就被加密没用了。比如先把控制改成“08778F00”,A密钥改成“111111111111”,B密钥改成“222222222222”,改完之后再用我们的测试DEMO对块三进行写,写操作成功后,这样您就可以利用B密钥对您所改的扇区进行读写操作了,这时A密钥也就不起作用。

M1卡密钥控制字算法程序2.0

RC522与Arduino UNO的接线

一般库文件中有接线定义

RC522 Arduino
SDA 10
SCK 13
MOSI 11
MISO 12
IRQ 空置
GND GND
RST 9
3.3 3.3

在这里插入图片描述

MFRC522库的使用

首先要在ArduinoIDE或者vscode platformIO中下载MFRC522库

examples

1、ReadUID 读取卡的UID


#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 10
#define RST_PIN 9
MFRC522 rfid(SS_PIN, RST_PIN); // Instance of the class
MFRC522::MIFARE_Key key; 
// Init array that will store new NUID 
byte nuidPICC[4];
void setup() { 
 
Serial.begin(9600);
SPI.begin(); // Init SPI bus
rfid.PCD_Init(); // Init MFRC522 
for (byte i = 0; i < 6; i++) { 

key.keyByte[i] = 0xFF;
}
Serial.println(F("This code scan the MIFARE Classsic NUID."));
Serial.print(F("Using the following key:"));
printHex(key.keyByte, MFRC522::MF_KEY_SIZE);
}
void loop() { 

// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if ( ! rfid.PICC_IsNewCardPresent())
return;
// Verify if the NUID has been readed
if ( ! rfid.PICC_ReadCardSerial())
return;
Serial.print(F("PICC type: "));
MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
Serial.println(rfid.PICC_GetTypeName(piccType));
// Check is the PICC of Classic MIFARE type
if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI &&  
piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
piccType != MFRC522::PICC_TYPE_MIFARE_4K) { 

Serial.println(F("Your tag is not of type MIFARE Classic."));
return;
}
if (rfid.uid.uidByte[0] != nuidPICC[0] || 
rfid.uid.uidByte[1] != nuidPICC[1] || 
rfid.uid.uidByte[2] != nuidPICC[2] || 
rfid.uid.uidByte[3] != nuidPICC[3] ) { 

Serial.println(F("A new card has been detected."));
// Store NUID into nuidPICC array
for (byte i = 0; i < 4; i++) { 

nuidPICC[i] = rfid.uid.uidByte[i];
}
Serial.println(F("The NUID tag is:"));
Serial.print(F("In hex: "));
printHex(rfid.uid.uidByte, rfid.uid.size);
Serial.println();
Serial.print(F("In dec: "));
printDec(rfid.uid.uidByte, rfid.uid.size);
Serial.println();
}
else Serial.println(F("Card read previously."));
// Halt PICC
rfid.PICC_HaltA();
// Stop encryption on PCD
rfid.PCD_StopCrypto1();
}
/** * Helper routine to dump a byte array as hex values to Serial. */
void printHex(byte *buffer, byte bufferSize) { 

for (byte i = 0; i < bufferSize; i++) { 

Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], HEX);
}
}
/** * Helper routine to dump a byte array as dec values to Serial. */
void printDec(byte *buffer, byte bufferSize) { 

for (byte i = 0; i < bufferSize; i++) { 

Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], DEC);
}
}

2、ReadAndWrite 数据读写

#include <SPI.h>
#include <MFRC522.h>
#define RST_PIN 9 // 配置针脚
#define SS_PIN 10 
MFRC522 mfrc522(SS_PIN, RST_PIN);   // 创建新的RFID实例
MFRC522::MIFARE_Key key; //6字节的密码
void dump_byte_array(byte *buffer, byte bufferSize); //声明dump_byte_array函数
void setup() { 

Serial.begin(9600); // 设置串口波特率为9600
while (!Serial);    // 如果串口没有打开,则死循环下去不进行下面的操作
SPI.begin();        // SPI开始
mfrc522.PCD_Init(); // Init MFRC522 card
for (byte i = 0; i < 6; i++) { 
//设置key为:FF FF FF FF FF FF
key.keyByte[i] = 0xFF;
}
Serial.println(F("扫描卡开始进行读或者写"));
Serial.print(F("使用A和B作为键"));
dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE);
Serial.println();
Serial.println(F("注意,会把数据写入到卡在#1"));
}
void loop() { 

// 寻找新卡
if ( ! mfrc522.PICC_IsNewCardPresent())
return;
// 选择一张卡
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("仅仅适合Mifare Classic卡的读写"));
return;
}
// 我们只使用第二个扇区
// 覆盖扇区4
byte sector         = 1;
byte blockAddr      = 4;//第4个块为第二个扇区第一个数据块
byte dataBlock[]    = { 

0x01, 0x02, 0x03, 0x04, // 1, 2, 3, 4,
0x05, 0x06, 0x07, 0x08, // 5, 6, 7, 8,
0x00, 0x00, 0x00, 0x00, // 0,0,0,0
0x00, 0x00, 0x00, 0x00  // 0,0,0,0
};//写入的数据定义
byte trailerBlock   = 7;//第7个块为第二个扇区的控制块
MFRC522::StatusCode status;
byte buffer[18];
byte size = sizeof(buffer);
// 原来的数据
Serial.println(F("显示原本的数据..."));
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));//在uid为mfrc522.uid的卡的trailerBlock块(此块为扇区控制块)验证key是否与A密码相同
if (status != MFRC522::STATUS_OK) { 

Serial.print(F("身份验证失败?或者是卡链接失败"));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// 显示整个扇区
Serial.println(F("显示所有扇区的数据"));
mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);//串行输出uid卡,第sector扇区的数据
Serial.println();
// 从块儿读取数据
Serial.print(F("读取块儿的数据在:")); Serial.print(blockAddr);
Serial.println(F("块 ..."));
status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);//读取size个第blockAddr块的数据到buffer
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();//输出第4块的数据
Serial.println();
//开始进行写入准备
Serial.println(F("开始进行写入的准备..."));
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));//验证密码B
if (status != MFRC522::STATUS_OK) { 

Serial.print(F("写入失败,没有连接上或者没有权限 "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// Write data to the block
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("等待验证结果..."));
byte count = 0;
for (byte i = 0; i < 16; i++) { 

// 比较一下缓存中的数据(我们读出来的数据) = (我们刚刚写的数据)
if (buffer[i] == dataBlock[i])
count++;
}
Serial.print(F("匹配的字节数量 = ")); Serial.println(count);
if (count == 16) { 

Serial.println(F("验证成功 :"));
} else { 

Serial.println(F("失败,数据不匹配"));
Serial.println(F("也许写入的内容不恰当"));
}
Serial.println();
// 转储扇区数据
Serial.println(F("写入后的数据内容为::"));
mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
Serial.println();
// 停止 PICC
mfrc522.PICC_HaltA();
//停止加密PCD
mfrc522.PCD_StopCrypto1();
}
/** * 将字节数组串行输出为16进制字符 */
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);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/159374.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 浙江更新了小学3年级到9年级信息技术课,小学开始学编程

    浙江更新了小学3年级到9年级信息技术课,小学开始学编程据浙江最新消息,今年9月份开始的新学期,三到九年级信息技术课将同步替换新器材。其中,八年级将新增Python课程内容。新高一信息技术编程语言由VB替换为Python,大数据、人工智能、程序设计与算法按照教材规划五六年级开始接触。在最新的教材目录上看到,从小学三年级一直到九年级,内容都有不同程度的调整。三年级新增了“信息社会”和“网络生活”,四上新增了“走进多媒体”、“制作演示文稿”和数字名片(H5),五下加入了“算法和程序设计”,六年级更是出现了“大数据”、“初始人工智能”、“万物互联”。初中阶段新

  • zencart 模板设计「建议收藏」

    zencart 模板设计「建议收藏」ZenCart的模板设计比较复杂,需要一定的时间来熟悉。一旦你了解了它的结构,就会慢慢习惯了。首先要阅读常见问答部分的:如何添加、制作新模板。ZenCart的设计没有什么特别,与以前设计HTML页面是一样的。只是整个页面分成了好几个部分,并加入了PHP代码。通常,页面分为页眉(header),页脚(footer),边框(sideboxes)。所以设计页面的时候,要记住ZenCart是如

  • C++二维数组sort排序问题

    C++二维数组sort排序问题以往遇到行排列问题(按每行的字典序排序)的时候,总是使用结构体来进行排序,但是如何使用二维数组来达到同样的效果呢?实验内容:利用二维数组进行“三级排序”测试1:使用c++内置的普通型二维数组#include&lt;algorithm&gt;#include&lt;iostream&gt;usingnamespacestd;boolcmp(inta[],intb[]){ …

    2022年10月20日
  • 安卓_数据库泄露_安卓数据库app

    安卓_数据库泄露_安卓数据库app今天遇到系统提示数据库泄露了不过找了好久也m

  • Nginx日志管理——了解Nginx日志选项配置以及自定义日志格式使用「建议收藏」

    Nginx日志管理——了解Nginx日志选项配置以及自定义日志格式使用「建议收藏」一、引言不管什么程序,一般都会有日志的。哪怕你在浏览器上网访问了一个网站,也会有记录保存的。在这个里互联网时代,想在网上不留下痕迹那是很难的。在我们开发一个程序,日志功能往往也是不可缺少的,今天我们就来讲讲这个Nginx的日志是怎么样来玩的。二、了解日志管理我们查看nginx安装目录下有个logs,包含了三个文件"access.log、error.log、nginx.pid"。…

  • html js 数组添加,js数组添加数据

    html js 数组添加,js数组添加数据我们在学习python的过程中,会对列表、字符串添加数据。在Javascript中,我们也会对数组添加数据。在不同的位置添加数据有着不同的方法。本文介绍js数组添加数据的三种方法:1、结尾添加push()方法;2、头部添加unshift()方法;3、向/从数组指定位置添加/删除项目,然后返回被删除的项目splice()方法。方式一:结尾添加push()方法1、语法arrayObject.pus…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号