前面使用Arduino铺好了路,这次使用51系统开发M1卡的自动增减处理功能。花了半天时间,疏通了处理程序,中间发生过一些问题,导致自动增减处理失败。
准备数据方面,依旧使用Arduino开发出来的结果。通过程序固定值块给M1卡的第8、9、10块。51测试中实际只用第8、9块。第8块自动加1,第九块自动减1。

51主程序如下:
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:");
//PcdReset();
//PcdAntennaOff();
//PcdAntennaOn();
PCD_Init();
M500PcdConfigISOType( 'A' );
t = 0;// 计时
//formatStr(0, 80, "Ver:%02X", PCD_GetVersion());
// 打印 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_2_0, DefaultKey, MLastSelectedSnr);
if (status != STATUS_OK) {
// 认证1扇区0块失败,可能密码错误
printf("认证 2 扇区 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, " ");
display_string_16x16(0, 10, " ");
}else {
printf("认证 2 区 0 块成功!\n");
display_string_16x16(100, 0, " ");
// 认证成功后,读取1号扇区的块0、块1、块2
// 注意:操作同一扇区的数据块前,只需认证一次该扇区的尾部块
status = PcdRead(BLOCK_2_0, RevBuffer);
if (status == MI_OK) {
printf("读取 2 区 0 块成功!\n");
// 成功读取块0数据,RevBuffer[0]~RevBuffer[15]即为数据
showBlockData(0, 4, RevBuffer, 10);
printf("变化前 : %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]
);
// 增加10
printf("增加 2 区 0 块 : +1!\n");
status = MIFARE_Increment(BLOCK_2_0, 1);
if (status == MI_OK) {
printf("增加 2 区 1 块 成功!再读: \n");
status = PcdRead(BLOCK_2_0, RevBuffer);
if (status == MI_OK) {
// 成功读取块0数据,RevBuffer[0]~RevBuffer[15]即为数据
showBlockData(0, 6, RevBuffer, 10);
printf("变化后 : %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("读取 2 区 0 块失败,返回值=%02x\n", status);
}
printf("===============================================================\n");
// 读取1号扇区的块1
// 注意:操作同一扇区的数据块前,只需认证一次该扇区的尾部块
status = PcdRead(BLOCK_2_1, RevBuffer);
if (status == MI_OK) {
printf("读取 2 区 1 块成功!\n");
// 成功读取块0数据,RevBuffer[0]~RevBuffer[15]即为数据
showBlockData(0, 8, RevBuffer, 10);
printf("变化前 : %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]
);
// 减少1
printf("减少 2 区 1 块 : -1!\n");
status = MIFARE_Decrement(BLOCK_2_1, 1);
if (status == MI_OK) {
printf("减少 2 区 1 块 成功!再读: \n");
status = PcdRead(BLOCK_2_1, RevBuffer);
if (status == MI_OK) {
// 成功读取块0数据,RevBuffer[0]~RevBuffer[15]即为数据
showBlockData(0, 10, RevBuffer, 10);
printf("变化后 : %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("读取 2 区 1 块失败,返回值=%02x\n", status);
}
delay_ms(2000);
}
}
PcdHalt();
delay_ms(500);
}
}M1卡的处理程序上追加了两个函数(MIFARE_Decrement和MIFARE_Increment),主要是为了在调用函数上,表达更清晰易懂。
unsigned char MIFARE_Decrement(unsigned char blockAddr, unsigned long delta) {
return PcdValue(PICC_DECREMENT, blockAddr, delta);
} // End MIFARE_Decrement()
char MIFARE_Increment(unsigned char blockAddr, unsigned long delta) {
return PcdValue(PICC_INCREMENT, blockAddr, delta);
} // End MIFARE_Increment()同时还修改了一个处理自动加减处理的函数“PcdValue”,
char PcdValue(unsigned char dd_mode,unsigned char addr, unsigned long pValue) {
char status;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN];
unsigned char val[5]={0,0,0,0,0};
ucComMF522Buf[0] = dd_mode;
ucComMF522Buf[1] = addr;
//printf("PcdValue CRC校验!\n");
CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
//printf(" 校验结果:0x%02X, 0x%02X \n", ucComMF522Buf[2], ucComMF522Buf[3]);
//printf("PcdValue 传递指令给M1卡 \n");
//printf(" 指令数据: 0x%02X, 0x%02X,0x%02X, 0x%02X,0x%02X, 0x%02X \n", ucComMF522Buf[0], ucComMF522Buf[1], ucComMF522Buf[2], ucComMF522Buf[3]);
status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
if ((status != STATUS_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A)) {
status = MI_ERR;
}
ucComMF522Buf[0] = ((char *)pValue)[0];
ucComMF522Buf[1] = ((char *)pValue)[1];
ucComMF522Buf[2] = ((char *)pValue)[2];
ucComMF522Buf[3] = ((char *)pValue)[3];
//printf("变化量变换为Byte: 0x%02X, 0x%02X,0x%02X, 0x%02X,0x%02X, 0x%02X \n", ucComMF522Buf[0], ucComMF522Buf[1], ucComMF522Buf[2], ucComMF522Buf[3]);
if (status == STATUS_OK) {
printf("PcdValue 传递变化值: %d, \n", pValue);
// 将要增减的数据差,拷贝到ucComMF522Buf中0-3字节位置
//memcpy(val, (char *)pValue, 4);
// 调整顺序
ucComMF522Buf[0] = (unsigned char)(pValue & 0xFF); // 最低字节
ucComMF522Buf[1] = (unsigned char)((pValue >> 8) & 0xFF);
ucComMF522Buf[2] = (unsigned char)((pValue >> 16) & 0xFF);
ucComMF522Buf[3] = (unsigned char)((pValue >> 24) & 0xFF); // 最高字节
// 计算CRC,结果或存放到ucComMF522Buf[4]\ucComMF522Buf[5]
CalulateCRC(ucComMF522Buf, 4, &ucComMF522Buf[4]);
//printf("CRC变换 : 0x%02X, 0x%02X,0x%02X, 0x%02X,0x%02X, 0x%02X \n", ucComMF522Buf[0], ucComMF522Buf[1], ucComMF522Buf[2], ucComMF522Buf[3], ucComMF522Buf[4], ucComMF522Buf[5]);
unLen = 0;
//printf("PcdValue 传递数据给M1卡 \n");
status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 6, ucComMF522Buf, &unLen);
if (status != MI_ERR) {
status = STATUS_OK;
}
}
if (status == STATUS_OK) {
ucComMF522Buf[0] = PICC_TRANSFER;
ucComMF522Buf[1] = addr;
CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf,&unLen);
if ((status != STATUS_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A)) {
status = MI_ERR;
}
}
return status;
}修改的点只有一个,就是向M1卡发送指令之后,第二次要发送的修改量那一块中数据传送形式上。修改前的代码:
memcpy(ucComMF522Buf, pValue, 4);
修改后的代码:
ucComMF522Buf[0] = ((char *)pValue)[0]; ucComMF522Buf[1] = ((char *)pValue)[1]; ucComMF522Buf[2] = ((char *)pValue)[2]; ucComMF522Buf[3] = ((char *)pValue)[3];
使用原来的代码,在进行加减处理之后,虽然可以正常执行操作,但结果会很奇怪。原因嘛,对一个unsigned long变量,它有四个字节:
Byte3 Byte2 Byte1 Byte0
它们在51系统中在内存(字节单位的存储单元)中的存储顺序就是3-2-1-0的顺序存储的,而M1卡值块中保存数据的方式是
Byte0 Byte1 Byte2 Byte3
是0-1-2-3的方式保存的,与51系统内存中的保存方式刚好相反。如果按照51 系统内存中的形式传动给RC522,处理结果一定是错的。
另外可以看到PcdValue函数中,自动加减操作需要调用三次 PcdComMF522(PCD_TRANSCEIVE, ...),这是M1卡硬件协议规定的标准流程
步骤 | 命令内容 | 发送数据 | 卡片响应 | 作用 |
第1次 | 加减指令头 | [0xC0/C1, addr, CRC_H, CRC_L] | 0x0A (ACK) | 告诉卡片要执行加/减操作 |
第2次 | 变化量数据 | [val0, val1, val2, val3, CRC_H, CRC_L] | 0x00 (成功) | 传递要增加/减少的数值 |
第3次 | 传输指令 | [0xB0, addr, CRC_H, CRC_L] | 0x0A (ACK) | 将计算结果从缓冲区写入EEPROM |
测试时为了观察数据的变化,使用同一颗M1卡执行了两次刷卡:

第一次刷卡时的情况。第3、5行在自动加减处理之前的M1卡内第8、9块数据。第4、6行在自动加减处理之后的M1卡内第8、9块数据。块数据总共16字节,受屏幕限制,只显示了前10个字节的数据。第8块数据执行自动加1处理,第9块数据执行递减1处理。


测试结果很完美。
我要赚赏金
