这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 基础知识 » 我的NFC学习之旅(11)---51系统下调通自动增减处理

共1条 1/1 1 跳转至

我的NFC学习之旅(11)---51系统下调通自动增减处理

专家
2026-07-17 20:41:15     打赏

前面使用Arduino铺好了路,这次使用51系统开发M1卡的自动增减处理功能。花了半天时间,疏通了处理程序,中间发生过一些问题,导致自动增减处理失败。

准备数据方面,依旧使用Arduino开发出来的结果。通过程序固定值块给M1卡的第8、9、10块。51测试中实际只用第8、9块。第8块自动加1,第九块自动减1。

图片1.png

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处理。

测试结果很完美。







关键词: 懒猫的学习笔记     NFC    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]