根据前面的学习,我们开始学习在51 系统下对M1卡的扇区块进行读写的操作处理。
读写扇区、块数据,要严格按照以下顺序操作:
1、寻卡 (Request):让RC522模块寻找附近的M1卡,获取卡片类型代码。
2、防冲突 (Anticollision):当有多张卡时,选出一张进行通信,并获得该卡的UID(唯一标识符,4字节)。
3、选卡 (Select Tag):正式选定刚才通过防冲突检测的卡片。
4、认证扇区 (Authentication):这是最关键的一步。在操作某个扇区前,必须用该扇区的密码(KeyA或KeyB)进行身份验证。比如要读取1号扇区,就需要用1号扇区的密码去认证。
5、读/写操作 (Read/Write):认证成功后,才能对同一个扇区内的数据块(块0、1、2)进行读写。
在前面读取M1卡的UID的时候,1~3的操作已经被实现了,我们就在这个基础上,简单修改代码,追加读写操作。
本以为很简单的,有现成的库支持,应该很快就能写好程序、测试,结果出了问题。折腾了两天。导致出问题的点是第三步选卡的动作没有加进去。结果在认证扇区的处理中,一直出现认证失败的情况。为此又是研读Arduino下的RC522支持库源码,又是学习Arduino下读写的额处理,又是尝试按照Arduino下的RC522支持库源码修改本地51系统下的代码。直到今天晚些时候才发现,整体的处理中漏掉了PcdSelect(MLastSelectedSnr);的处理步骤,才彻底解决问题。
现有代码,与RC522的通讯使用的是软件模拟SPI方式通讯。出问题时,一度以为模拟处理中SCK的脉冲时间太短,导致数据、指令没有正确传达给RC522,为此做了好长时间的修改、调试,虽然在处理上有了一些变化,比如认证处理的返回值,从29、39、到0D、0B等发生了变化,却依旧没有从根本上解决问题。而且不同卡的表现还不一样,差点以为是卡的问题。还好,在加入选卡处理之后,所有卡都能正常被扫描到,并完成数据的读写处理。
以下是主体程序的代码(RC522的文件改动不大)。
void main() {
unsigned char i, dat = 0;
char status;
unsigned long t;
unsigned char buff[24]={'\0'};
// 初始化单片机
sys_inti();
// 打开调试信号
LED = 1;
// 初始化定时2,串口波特率发生器
Timer2Init();
// 初始化串口1
Uart1Init();
// 开总中断
EA = 1;
// 关闭调试信号
LED = 0;
// 输出测试日志
printf("Start Test !\r\n");
// 初始化液晶
lcd_25696_init();
delay_ms(10);
// 测试液晶
lcd25696_test();
delay_ms(2000);
// 显示开始处理的标志字符串
clear_screen(); //清屏
display_string_16x16(0, 0, "Test:");
PCD_Init();
M500PcdConfigISOType( 'A' );
t = 0; // 计时
// 打印 UID
display_string_16x16(0, 2, "ID: ");
while(1) {
// 1、寻卡,防冲突,选择卡 返回卡类型(2 bytes)
status= PcdRequest(PICC_REQALL, &RevBuffer[2]);
//printf("PcdRequest 返回信息: %02X %02X \n", RevBuffer[3], RevBuffer[2]);
if(status != STATUS_OK) {
//status= PcdHalt();
} else {
printf("ATQA: 0x%02X 0x%02X\n", RevBuffer[3], RevBuffer[4]); // 0x00 0x04
printf("卡片类型: MIFARE Classic 1K\n");
// 2、防冲突 读卡的系列号
//status = PcdAnticoll(&RevBuffer[2]);
status = PcdAnticoll(MLastSelectedSnr);
if(status != STATUS_OK) {
status= PcdHalt();
}
printf("UID: %02X %02X %02X %02X\n", MLastSelectedSnr[0], MLastSelectedSnr[1], MLastSelectedSnr[2], MLastSelectedSnr[3]);
t++;
sprintf(buff, "%d", t);
display_string_16x16(50, 0, buff);
// 显示卡ID
cardid[0] = ASCII[MLastSelectedSnr[0]/16];
cardid[1] = ASCII[MLastSelectedSnr[0]%16];
cardid[2] = ASCII[MLastSelectedSnr[1]/16];
cardid[3] = ASCII[MLastSelectedSnr[1]%16];
cardid[4] = ASCII[MLastSelectedSnr[2]/16];
cardid[5] = ASCII[MLastSelectedSnr[2]%16];
cardid[6] = ASCII[MLastSelectedSnr[3]/16];
cardid[7] = ASCII[MLastSelectedSnr[3]%16];
cardid[8] = '\0';
display_string_16x16(0, 2, "ID:");
display_string_16x16(30, 2, cardid);
// 3. 选卡 - 确认卡片已选中
status = PcdSelect(MLastSelectedSnr);
if (status == MI_OK) {
printf("选卡成功!\n");
} else {
printf("选卡失败!\n");
}
// 4、认证1号扇区(使用KeyA,认证尾部块地址7),PcdAuthState
// 参数:验证模式, 块地址, 密钥, UID
// 认证1扇区0块,物理地址的第4块
status = PcdAuthState(PICC_AUTHENT1A, BLOCK_1_0, DefaultKey, MLastSelectedSnr);
if (status != STATUS_OK) {
// 认证1扇区0块失败,可能密码错误
printf("认证 1 扇区 0 块失败,返回值=%02x\n", status);
display_string_16x16(100, 0, "Authentication!");
display_string_16x16(0, 4, " ");
display_string_16x16(0, 6, " ");
display_string_16x16(0, 8, " ");
} else {
printf("认证 1 区 0 块成功!\n");
display_string_16x16(100, 0, " ");
// 认证成功后,读取1号扇区的块0、块1、块2
// 注意:操作同一扇区的数据块前,只需认证一次该扇区的尾部块
status = PcdRead(BLOCK_1_0, RevBuffer);
if (status == MI_OK) {
printf("读取 1 区 0 块成功!\n");
// 成功读取块0数据,RevBuffer[0]~RevBuffer[15]即为数据
showBlockData(0, 4, RevBuffer, 10);
printf("Data : %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X \n",
RevBuffer[0], RevBuffer[1], RevBuffer[2], RevBuffer[3], RevBuffer[4], RevBuffer[5], RevBuffer[6], RevBuffer[7], RevBuffer[8], RevBuffer[9]
);
// 前8个数据加16后,回写
for (i=0; i<8; i++) {
RevBuffer[i] += 0x10;
}
status = PcdWrite(BLOCK_1_0, RevBuffer);
if (status == MI_OK) {
printf("回写 1 区 0 块成功!\n");
// 再读
status = PcdRead(BLOCK_1_0, RevBuffer);
if (status == MI_OK) {
printf("再次读取 1 区 0 块成功!\n");
// 再次成功读取块0数据,RevBuffer[0]~RevBuffer[15]即为数据
showBlockData(0, 6, RevBuffer, 10);
printf("Update : %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X \n",
RevBuffer[0], RevBuffer[1], RevBuffer[2], RevBuffer[3], RevBuffer[4], RevBuffer[5], RevBuffer[6], RevBuffer[7], RevBuffer[8], RevBuffer[9]
);
}
}
} else {
printf("读取 1 区 0 块失败,返回值=%02x\n", status);
}
delay_ms(3000);
}
}
PcdHalt();
delay_ms(500);
}
}红色标记的部分,是开始时漏掉的代码。主要处理逻辑是读取扇区数据、修改、回写、再读,显示对比。开发板使用的STC的擎天柱开发板,烧录频率22.1184MHz。端口连接上参照以下设置:
/////////////////////////////////////////////////////////////////////
//端口定义
/////////////////////////////////////////////////////////////////////
//MFRC522
sbit MF522_RST = P0^4; // 复位
sbit MF522_NSS = P0^2; // 片选
sbit MF522_SCK = P0^1; // 时钟
sbit MF522_SI = P0^0; // 接RC522模块的MOSI
sbit MF522_SO = P0^3; // 接RC522模块的MISO

图片中ID为扫描的M1卡的UID。第三行为读取到M1卡第1扇区第0块,也就是物理上的第4块的16进制数据。第四行是取到的数据的前8个加上16后回写给M1卡,在重新读取后的结果。读写是以块(16字节)的方式整体操作的。可以看到,第9、10个数据并没有被改变。
这是串口的输出日志:

至此终于把51系统下M1卡的读写处理完成了。后面准备在此基础上,继续学习自动加减1的处理。
我要赚赏金
