本篇文章内容总结下来就是
- 读卡 使用默认密码读卡所有扇区所有块的数据
- 写ID 使用默认密码读取卡一的0扇区的第一块数据并写入到卡二的0扇区的第一块里
- 密码读卡 不同厂家的初始密码不同,整理了一些默认密码,如果有收集到新的也可以补充进去
- 写全卡 使用默认密码读取卡一的全部扇区全部块数据并写入到卡二的全部扇区的全部块里
目录
从来不按套路出牌的我
一.前期准备
STM32f103c8t6开发板
Keil5编译器
串口工具
我就问你们什么叫专业
二.代码
简介RC522
代码功能
代码
总结
三.源码
没用的前言,看干货跳过这段
想必看到这篇文章的人都是已经翻阅了大量的文章资料,已经厌倦了那种通篇都是百度百科,通篇都是复制官方资料,通篇都是怎样点亮led灯的没有多少营养的文章
本篇文章除了插科打诨与耐人寻味的笑点之外尽可能的把干货提供给大家学习,我在学习RC522时大量的时间都花在查找资料上了,真正的编程时间可能只有找资料的三分之一,所以为了不让同样抱有学习热情的人因为找不到合适的资料而失去耐心最终失去学习的热情,我准备把我整理的干货拿出来与大家分享
没错,不务正业的我又回来了,这回是玩一玩Stm32单片机
当然也是从基础的C语言开始学习,大概自学了小半个月的C语言就开始上手Stm32了
自学是在B站看的视频,一边写笔记一边敲代码,好在有java的基础,不然我就和这行无缘了
好了废话不多说直接干货
RC522模块
RC522相关资料:
链接: https://pan.baidu.com/s/1Qob5o4tv0_gBXrRk6AvLSQ
提取码: fkjt
接线图
每个人接线的引脚不同,我这里是因为接入了其他无关紧要的模块才分开接线,不影响单独RC522连接STM32
RC522接线
RC522_RST ====== GPIOA_Pin_0
RC522_MISO ====== GPIOA_Pin_1
RC522_MOSI ====== GPIOA_Pin_2
RC522_SCK ====== GPIOA_Pin_3
RC522_SDA(NSS) == GPIOA_Pin_4
GND ====== GND
ACC ====== ACC
按钮接线
KEY1_ACC === GPIOB_Pin_0
KEY2_ACC === GPIOB_Pin_1
GND === GND
灯接线(也可以使用板载灯,这里使用板载灯)
LED5_ACC === GPIOA_Pin_5 //模式灯 在选择模式时用于显示当前模式用
LED6_ACC === GPIOA_Pin_6 //确认灯 在识别到卡1时会亮起提示换卡
GND === GND
Keil5编译器
链接: https://pan.baidu.com/s/10jR0g10jxdosawc49IOazQ
提取码: uizh
自己收集整理并添加一些自己常用的方法的Stm32f103c8t6开发环境,解压即用
Keil5开发环境
链接: https://pan.baidu.com/s/1U5sOv9lvs3wwKkisiAFYAA
提取码: z1ih
串口调试程序 串口烧录程序
链接: https://pan.baidu.com/s/11VCAXnfmO1FCQQVjirIkrQ
提取码: h1uz
失效私信我
硬件配置,开发环境详细到这个地步,初学者能够直接上手学习
这就是专业
这就是一条龙服务,以后你们给其他人分享这篇文章时都可以说被一条龙服务过
官方文档一共有109页接下来都是复制这个文档所以大家可以走了[dog]
我就简单总结一下白话文:
1.RC522能读卡
2.RC522能写卡
3.RC522能... 就这样,就是能读卡能写卡,我们要它的功能也就是读卡写卡,我没必要为了实现一个读卡功能把这一百多也的文档看完,也没有必要为了实现一个功能就把它的原理全部吃透
介绍完毕
- 读卡 使用默认密码读卡所有扇区所有块的数据
- 写ID 使用默认密码读取卡一的0扇区的第一块数据并写入到卡二的0扇区的第一块里
- 密码读卡 不同厂家初始密码不同,整理了一些初始密码,如果有收集到新的也可以补充进去
- 写全卡 使用默认密码读取卡一的全部扇区全部块数据并写入到卡二的全部扇区的全部块里
怎么样,干不干,全是干货,没有废话
这是一张CUID白卡,一会复制可以用作对比
有人会问扇区啥意思 区块啥意思,这个就百度去吧好多文章在讲RC522或者复制卡的文章都会用很大篇幅讲什么是卡什么是扇区什么是区块什么是密码段,之后文章就结束了也不说怎么读怎么写怎么复制,最后来一句未完待续或者下一篇文章再详细的写,之后尸沉东京湾留下一段佳话,在这里因为我是旱鸭子就不写了
SQ代表扇区 0~15扇区
QK代表区块 0~3区块
这是一张目标卡,我们要将这张卡的0扇区的0块数据写到上面那张空白卡中
1.读取卡1
进行写操作 放上卡1=> 读取卡1=> 识别到卡1后打印卡唯一id,并提示可进行复制=> 放上卡2=> 写入卡2
读取卡2
已经写到卡2中了
每个扇区的3号区块存储的就是该扇区的密码,只有读取时携带相同的密码才能看到并修改该扇区的数据,如果没有密码都不会读到该扇区
因为厂家为了卡片的数据安全各厂会定义自己的读卡密码
个人收集了一部分密码分享一下
unsigned char KEY[14][6]={
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
{0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5},
{0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5},
{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
{0x4D, 0x3A, 0x99, 0xC3, 0x51, 0xDD},
{0x1A, 0x98, 0x2C, 0x7E, 0x45, 0x9A},
{0x71, 0x4C, 0x5C, 0x88, 0x6E, 0x97},
{0x58, 0x7E, 0xE5, 0xF9, 0x35, 0x0F},
{0xA0, 0x47, 0x8C, 0xC3, 0x90, 0x91},
{0x53, 0x3C, 0xB6, 0xC7, 0x23, 0xf6},
{0x8F, 0xD0, 0xA4, 0xF2, 0x56, 0xE9},
{0x66, 0x55, 0x44, 0x11, 0x22, 0x33},
{0x66, 0x55, 0x44, 0x33, 0x22, 0x11}
};
效果就是两张一模一样的卡
磨叽了半天终于到了重点
代码里出现的 if(EN) if(CN) if(OLED) 代表是否打印对应语言,
EN 代表打印英文
CN 代表打印中文
OLED 代表在屏幕显示(因为接入一块OLED屏幕)
要点:每次对卡进行操作时调用,两个方法一模一样,就是第二种去掉了打印
验证卡片信息,防冲撞,选卡
//
// 寻卡方法
//
char Pcdmain(){
u8 status;
u8 ID_num[9];
u8 *ID; //卡号
status= PcdRequest(REQ_ALL,TagType);//寻卡
if(!status){
status = PcdAnticoll(SelectedSnr);//防冲撞
if(!status){
status=PcdSelect(SelectedSnr);//选卡
if(!status){
if(EN)printf("Read the success !! \r\n");
if(CN)printf("寻找卡片成功 !! \r\n");
if(OLED)GUI_ShowString(0,16,"1",16,1);
ID = Card_ID(SelectedSnr,ID_num); //获取卡ID
if(EN)printf("ID:%s !! \r\n",ID_num);
if(CN)printf("卡号:%s !! \r\n",ID_num);
if(OLED)GUI_ShowString(16,16,"ID:",16,1);
if(OLED)GUI_ShowString(40,16,ID,16,1);
return status;
}else{
if(EN)printf("The selected card \r\n");
if(CN)printf("卡片未找到 \r\n");
if(OLED)GUI_ShowString(0,16,"2",16,1);
return status;
}
}else{
if(EN)printf("Anti-collision \r\n");
if(CN)printf("卡片冲突 \r\n");
if(OLED)GUI_ShowString(0,16,"3",16,1);
return status;
}
}else{
if(EN)printf("Looking for failure \r\n");
if(CN)printf("卡片未找到 \r\n");
if(OLED)GUI_ShowString(0,16,"4",16,1);
return status;
}
}
//
// 寻卡方法
//
char Pcdmain_3(){
u8 status;
status= PcdRequest(REQ_ALL,TagType);//寻卡
if(!status){
status = PcdAnticoll(SelectedSnr);//防冲撞
if(!status){
status=PcdSelect(SelectedSnr);//选卡
if(!status){
return status;
}else{
return status;
}
}else{
return status;
}
}else{
return status;
}
}
要点:密码密码密码 如果密码不对那就结束了,初始密码需要在成员变量中定义
//
// notarize = 1 读卡
//
unsigned char DefaultKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; //默认密码
void notarize_type1(){
u8 status;
u8 section=0;
u8 block = 0;
unsigned char block_value[16];
u8 i=0;
status = Pcdmain();
if(!status){
if(EN)printf("function NO1 Read the card ...\r\n");
if(CN)printf("功能1 读取卡片 \r\n");
if(OLED)GUI_ShowString(48,0,"NO_1",16,1);
for( section = 0 ; section <16 ; section++ ){
status = PcdAuthState(KEYA, (section*4+3), DefaultKey, SelectedSnr);// 校验1扇区密码,密码位于每一扇区第3块
if(!status)
{
for(block = 0; block< 4 ; block++){
status = PcdRead((section*4+block), block_value); // 读卡,读取1扇区0块数据到buf[0]-buf[16]
if(EN)printf("SQ:%d , QK:%d ",section,block);
if(EN)printf("passA ");
for (i = 0 ; i<6; i++)
{
if(EN)printf("%X ",block_value[i]);
}
if(EN)printf(" Value ");
for (i = 6; i<10; i++)
{
if(EN)printf("%X ",block_value[i]);
}
if(EN)printf(" passB ");
for (i = 10; i<16; i++)
{
if(EN)printf("%X ",block_value[i]);
}
if(EN)printf("\r\n");
}
}else {
if(EN)printf("wrong password \r\n");
if(CN)printf("密码错误 \r\n");
if(OLED)GUI_ShowString(0,16,"5",16,1);
}
}
if(EN)printf("\r\n");
if(EN)printf("\r\n");
if(EN)printf("\r\n");
}else{
if(EN)printf("Looking for failure \r\n");
if(CN)printf("卡片未找到 \r\n");
if(OLED)GUI_ShowString(0,16,"4",16,1);
}
}
ID的重要性就不用我多说了,因为通常的ID卡扇区0是出厂就定义好的ID不能再修改,所以也是唯一性的保障,大多数的系统也只会记录ID作为验证使用
而除了0扇区之外的扇区多为详细信息存储使用,如取电卡的电量值,水卡的水量值,饭卡的余额之类,因为这部分可以读写所以密码会与出厂密码不同
//
// notarize = 2 复制卡id
//
void notarize_type2(){
u8 status;
u8 test_status = 0; //id在0扇区 , 测试使用其他扇区
u8 block_num=0;
u8 copy_tf = 1;
unsigned char copy_value[16];
status = Pcdmain_3();
if(!status){
if(EN)printf("function NO1 Read the card ...\r\n");
if(CN)printf("功能2 复制卡片 \r\n");
if(OLED)GUI_ShowString(48,0,"NO_2",16,1);
//读取并复制卡1 指定扇区指定块数据
status = PcdAuthState(KEYA, (test_status*4+3), DefaultKey, SelectedSnr);// 校验1扇区密码,密码位于每一扇区第3块 DefaultKey为默认密码
if(!status)
{
status = PcdRead((test_status*4+0), copy_value); // 读卡,读取1扇区0块数据到buf[0]-buf[16]
if(!status)
{
if(EN)printf("COPY: SQ:%d , QK:%d ",test_status,0);
if(EN)printf("passA ");
for (block_num = 0; block_num<6; block_num++)
{
if(EN)printf("%X ",copy_value[block_num]);
}
if(EN)printf(" Value ");
for (block_num = 6; block_num<10; block_num++)
{
if(EN)printf("%X ",copy_value[block_num]);
}
if(EN)printf(" passB ");
for (block_num = 10; block_num<16; block_num++)
{
if(EN)printf("%X ",copy_value[block_num]);
}
if(EN)printf("\r\n");
}
if(EN)printf("Read over \r\n");
if(CN)printf("复制成功 替换目标卡片 \r\n");
delay_ms(500);
if(OLED)GUI_ShowString(0,16,"COPY OK, CARD_2",16,1);
//读取第二张卡片
do{
LED6S; //PC13输出低电平
//delay_ms(200);
//LED6S; //PC13输出高电平
if(KEY1==0){
status = Pcdmain_3();
if(!status){
if(EN)printf("Ready to write card 2 ...\r\n");
if(CN)printf("写卡2\r\n");
if(OLED)GUI_ShowString(0,0,"CARD2...",16,1);
status = PcdAuthState(KEYA, (test_status*4+3), DefaultKey, SelectedSnr);// 校验1扇区密码,密码位于每一扇区第3块
if(!status){
status = PcdWrite((test_status*4+0), copy_value); // 写卡,将buf[0]-buf[16]写入1扇区0块
if(!status){
LED6R; //PC13输出高电平
delay_ms(20);
LED6S; //PC13输出低电平
delay_ms(20);
LED6R; //PC13输出高电平
delay_ms(20);
LED6S; //PC13输出低电平
delay_ms(20);
LED6R; //PC13输出高电平
if(EN)printf("Copy success (*^▽^*) \r\n");
if(CN)printf("写入成功 (*^▽^*) \r\n");
if(OLED)GUI_ShowString(0,16,"Copy SUCC (*^▽^*)",16,1);
}else{
if(EN)printf("Copy the failure (╯‵□′)╯︵┻━┻ \r\n");
if(CN)printf("复制失败 (╯‵□′)╯︵┻━┻ \r\n");
if(OLED)GUI_ShowString(0,16,"Copy the failure",16,1);
}
}else{
if(EN)printf("card 2 wrong password \r\n");
if(CN)printf("卡2密码错误 \r\n");
if(OLED)GUI_ShowString(0,16,"card 2 PASS WRONG",16,1);
}
LED6R; //PC13输出高电平
}else{
if(EN)printf("Looking for failure \r\n");
if(CN)printf("卡片2未找到 \r\n");
if(OLED)GUI_ShowString(0,16,"4",16,1);
LED6R; //PC13输出高电平
}
LED6R; //PC13输出高电平
copy_tf = 0;
}
}while(copy_tf);
}else{
if(EN)printf("wrong password \r\n");
if(CN)printf("密码错误 \r\n");
if(OLED)GUI_ShowString(0,16,"5",16,1);
}
}else{
if(EN)printf("Looking for failure \r\n");
if(CN)printf("卡片未找到 \r\n");
if(OLED)GUI_ShowString(0,16,"4",16,1);
}
}
测试每个扇区的第3区块密码,当然幸运的话就是初始密码,点背的话也没关系只需要255*255*255*255*255*255 = 274,941,996,890,625次尝试也可以得到密码
//
// notarize = 3 常规密码测试
//
void notarize_type3(){
u8 status;
u8 section=0;
u8 block_num=0;
u8 KEY_num = 0 ;
unsigned char block_value[16];
u8 i=0;
status = Pcdmain_3();
if(!status){
if(EN)printf("function NO3 Password test ...\r\n");
if(CN)printf("功能3 常规密码测试 \r\n");
if(OLED)GUI_ShowString(48,0,"NO_3",16,1);
for( section = 0 ; section <16 ; section++ ){
for( KEY_num =0 ; KEY_num < 13 ; KEY_num++){
status = Pcdmain_3();
if(!status){
//printf("section:%d KEY:%d %d %d %d %d %d ",section,KEY[KEY_num][0],KEY[KEY_num][1],KEY[KEY_num][2],KEY[KEY_num][3],KEY[KEY_num][4],KEY[KEY_num][5]);
status = PcdAuthState(KEYA, (section*4+3), KEY[KEY_num], SelectedSnr);// 校验1扇区密码,密码位于每一扇区第3块
if(!status)
{
status = 1;
//for(block_num = 0; block_num< 4 ; block_num++){
status = PcdRead((section*4+3), block_value); // 读卡,读取1扇区0块数据到buf[0]-buf[16]
if(EN)printf("SQ:%d , QK:%d KEY:%X %X %X %X %X %X ",section,3,KEY[KEY_num][0],KEY[KEY_num][1],KEY[KEY_num][2],KEY[KEY_num][3],KEY[KEY_num][4],KEY[KEY_num][5]);
if(EN)printf("passA ");
for (i = 0 ; i<6; i++)
{
if(EN)printf("%X ",block_value[i]);
}
if(EN)printf(" Value ");
for (i = 6; i<10; i++)
{
if(EN)printf("%X ",block_value[i]);
}
if(EN)printf(" passB ");
for (i = 10; i<16; i++)
{
if(EN)printf("%X ",block_value[i]);
}
if(EN)printf("\r\n");
//}
}else {
}
}
status = 1;
}
}
if(EN)printf("\r\n");
if(EN)printf("\r\n");
if(EN)printf("\r\n");
}else{
if(EN)printf("Looking for failure \r\n");
if(CN)printf("卡片未找到 \r\n");
if(OLED)GUI_ShowString(0,16,"4",16,1);
}
}
由于全卡每个扇区的密码不同所以复制全卡用处不大
//
// notarize = 4 复制全卡
//
void notarize_type4(){
u8 status;
u8 section=0;
u8 block = 0;
u8 block_num=0;
u8 copy_tf = 1;
unsigned char block_value[16];
unsigned char copy_value[16][3][16];
status = Pcdmain_3();
if(!status){
if(EN)printf("function NO1 Read the card ...\r\n");
if(CN)printf("功能1 读取卡片 \r\n");
if(OLED)GUI_ShowString(48,0,"NO_4",16,1);
for( section = 0; section <16 ; section++ ){
status = PcdAuthState(KEYA, (section*4+3), DefaultKey, SelectedSnr);// 校验1扇区密码,密码位于每一扇区第3块
if(!status)
{
for(block = 0; block< 3 ; block++){
status = PcdRead((section*4+block), block_value); // 读卡,读取1扇区0块数据到buf[0]-buf[16]
for( block_num = 0; block_num < 16 ; block_num++){
copy_value[section][block][block_num] = block_value[block_num];
}
}
}
}
if(EN)printf("\r\n");
if(EN)printf("\r\n");
if(EN)printf("\r\n");
if(EN)printf("\r\n");
for(section = 0 ; section < 16 ; section++){
for(block = 0 ; block < 3 ; block++){
//for(block_num = 0 ; block_num < 16 ; block_num++){
if(EN)printf("SQ:%d , QK:%d ",section,block);
if(EN)printf("passA ");
for (block_num = 0; block_num<6; block_num++)
{
if(EN)printf("%X ",copy_value[section][block][block_num]);
}
if(EN)printf(" Value ");
for (block_num = 6; block_num<10; block_num++)
{
if(EN)printf("%X ",copy_value[section][block][block_num]);
}
if(EN)printf(" passB ");
for (block_num = 10; block_num<16; block_num++)
{
if(EN)printf("%X ",copy_value[section][block][block_num]);
}
if(EN)printf("\r\n");
//}
}
if(EN)printf("\r\n");
}
if(EN)printf("\r\n");
if(EN)printf("\r\n");
if(EN)printf("\r\n");
if(EN)printf("Read over \r\n");
if(CN)printf("复制成功 替换目标卡片 \r\n");
if(OLED)GUI_ShowString(0,16,"COPY OK, CARD_2",16,1);
//读取第二张卡片
do{
if(KEY1==0){
status = Pcdmain_3();
if(!status){
if(EN)printf("Ready to write card 2 ...\r\n");
if(CN)printf("写卡2\r\n");
if(OLED)GUI_ShowString(0,0,"CARD2...",16,1);
for(section = 0 ; section < 16 ; section++){
//status = Pcdmain_3();
//if(!status){
status = PcdAuthState(KEYA, (section*4+3), DefaultKey, SelectedSnr);// 校验1扇区密码,密码位于每一扇区第3块
if(!status){
for(block = 0 ; block < 3 ; block++){
//status = PcdWrite((section*4+block), copy_value[section][block]); // 写卡,将buf[0]-buf[16]写入1扇区0块
//status = PcdWrite((section*4+block), zero); // 写卡,将buf[0]-buf[16]写入1扇区0块
status = PcdWrite((4*4+3), setkey); // 写卡,将buf[0]-buf[16]写入1扇区0块
if(EN)printf("SQ:%d , QK:%d ",section,block);
if(EN)printf("passA ");
for (block_num = 0; block_num<6; block_num++)
{
if(EN)printf("%X ",copy_value[section][block][block_num]);
}
if(EN)printf(" Value ");
for (block_num = 6; block_num<10; block_num++)
{
if(EN)printf("%X ",copy_value[section][block][block_num]);
}
if(EN)printf(" passB ");
for (block_num = 10; block_num<16; block_num++)
{
if(EN)printf("%X ",copy_value[section][block][block_num]);
}
if(EN)printf("\r\n");
if(!status){
if(EN)printf("SQ:%d , QK:%d Copy success (*^▽^*) \r\n",section,block);
if(CN)printf("SQ:%d , QK:%d 写入成功 (*^▽^*) \r\n",section,block);
if(OLED)GUI_ShowString(0,16,"Copy SUCC ",16,1);
}else{
if(EN)printf("SQ:%d , QK:%d Copy the failure (* ̄︿ ̄) \r\n",section,block);
if(CN)printf("SQ:%d , QK:%d 复制失败 (* ̄︿ ̄) \r\n",section,block);
if(OLED)GUI_ShowString(0,16,"Copy the failure",16,1);
}
if(CN)printf("SQ:%d , QK:%d (*^▽^*) \r\n",section,block);
}
}else{
if(EN)printf("card 2 wrong password \r\n");
if(CN)printf("卡2密码错误 \r\n");
if(OLED)GUI_ShowString(0,16,"5",16,1);
}
}
}else{
if(EN)printf("Looking for failure \r\n");
if(CN)printf("卡片2未找到 \r\n");
if(OLED)GUI_ShowString(0,16,"4",16,1);
}
copy_tf = 0;
}
}while(copy_tf);
}else{
if(EN)printf("Looking for failure \r\n");
if(CN)printf("卡片未找到 \r\n");
if(OLED)GUI_ShowString(0,16,"4",16,1);
}
}
最后显摆一下我的手工
这篇文章写一半的时候就停了下来,在草稿中存了小半年,最后在年末穿上秋裤的时候想了想还是补全发出来吧
让我写文章的动力是因为C语言可以对这类单片机进行编程,进而读取环境数据,简单的温湿度模块,人体红外感应模块,触摸模块,光敏模块等等
而这与网络爬虫的作用很相似,一个是获取网络信息的自动程序,一个是获取现实世界信息的自动程序,这也是我为什么写爬虫写着写着就学起C的原因
当然爬虫这方面也在找一些有趣有挑战的网站,如果有人能分享有挑战的项目我也会尝试的
那么终于能完结这篇文章了,主要是我太懒,没有什么动力能刺激到我创作
预告一下,下次我要做......等一下有人敲门 我去看看......
完