361 lines
11 KiB
C
361 lines
11 KiB
C
/********************************************************************************************************
|
||
* @file rfid_main.c
|
||
* @brief RFID 读卡器应用层主逻辑文件
|
||
* @details
|
||
* 本文件包含了RFID读卡器的主要应用逻辑,负责初始化RFID模块,并根据不同的卡片类型(Type A, B, V, F)
|
||
* 执行相应的寻卡、激活和数据交换流程。
|
||
* 原代码是为51单片机编写,现已进行重构,移除了所有硬件相关的代码(如GPIO、SPI、UART的直接操作),
|
||
* 并引入了硬件抽象层(HAL)的概念。所有底层硬件操作都通过 `rfid_hal.h` 中定义的接口函数完成,
|
||
* 以便于在STM32等不同平台上进行移植。
|
||
*
|
||
* @author Kilo Code
|
||
* @date 2025-11-24
|
||
* @version 1.0
|
||
*
|
||
* @note
|
||
* - 您需要在使用本模块前,在您的平台上实现 `rfid_hal.h` 中声明的所有硬件接口函数。
|
||
* - `rfid_task()` 函数是一个示例性的主任务循环,您可以根据您的实际应用需求进行修改或集成。
|
||
* - 文件中保留了对不同卡片类型事件的处理函数(`TYPE_A_EVENT`, `TYPE_B_EVENT`等),
|
||
* 这些是RFID协议的核心逻辑。
|
||
********************************************************************************************************/
|
||
|
||
#include "../include/rfid_main.h"
|
||
#include <stdarg.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#include "../include/READER.h"
|
||
#include "../include/READER_REG.h"
|
||
#include "../include/MIFARE.h"
|
||
#include "../include/NTAG.h"
|
||
#include "rfid_hal.h" // 引入硬件抽象层头文件
|
||
|
||
// 宏定义,用于调试信息输出。您需要实现 rfid_hal.h 中的 rfid_log_debug 函数
|
||
#define rfid_printf rfid_log_debug
|
||
|
||
/**
|
||
* @brief 将一个字节的整数转换为两位十六进制字符串。
|
||
* @param buf [out] 存储转换后字符串的缓冲区,长度至少为3。
|
||
* @param num [in] 要转换的无符号字符。
|
||
* @return 无。
|
||
*/
|
||
static void IntToHex(unsigned char *buf, unsigned char num)
|
||
{
|
||
const unsigned char digits[] = "0123456789ABCDEF";
|
||
buf[0] = digits[(num >> 4) & 0x0F]; // 高4位
|
||
buf[1] = digits[num & 0x0F]; // 低4位
|
||
buf[2] = '\0';
|
||
}
|
||
|
||
/**
|
||
* @brief 通过调试接口打印一个字节的十六进制值。
|
||
* @param num [in] 要打印的无符号字符。
|
||
* @return 无。
|
||
*/
|
||
static void printHex(unsigned char num)
|
||
{
|
||
unsigned char buf[3];
|
||
IntToHex(buf, num);
|
||
rfid_printf((const char *)buf);
|
||
}
|
||
|
||
/**
|
||
* @brief 处理Type A卡片事件。
|
||
* @details
|
||
* 该函数执行ISO/IEC 14443 Type A卡片的完整激活流程,包括:
|
||
* 1. 初始化读卡器以支持Type A协议。
|
||
* 2. 打开RF场。
|
||
* 3. 请求(Request)和防冲突(Anticollision),最终激活卡片。
|
||
* 4. 根据卡片的SAK(Select Acknowledge)值,判断卡片具体类型(如Mifare, NTAG, CPU卡)并调用相应的处理函数。
|
||
* 5. 操作结束后关闭RF场。
|
||
* @return 无。
|
||
*/
|
||
void TYPE_A_EVENT(void)
|
||
{
|
||
unsigned char result;
|
||
rfid_printf("TYPE_A_EVENT begin\n");
|
||
|
||
// 初始化读卡器为Type A模式
|
||
result = ReaderA_Initial();
|
||
if (result != SUCCESS)
|
||
{
|
||
rfid_printf("INIT_ERROR\r\n");
|
||
SetCW(DISABLE);
|
||
return;
|
||
}
|
||
|
||
// 打开RF场(载波)
|
||
result = SetCW(ENABLE);
|
||
if (result != SUCCESS)
|
||
{
|
||
rfid_printf("CW_ERROR\r\n");
|
||
SetCW(DISABLE);
|
||
return;
|
||
}
|
||
|
||
// 激活Type A卡片
|
||
result = ReaderA_CardActivate(&PICC_A);
|
||
if (result != SUCCESS)
|
||
{
|
||
// rfid_printf("ReaderA_CardActivate_ERROR\r\n");
|
||
SetCW(DISABLE);
|
||
return;
|
||
}
|
||
|
||
rfid_printf("************* TYPE A CARD ************* \r\n");
|
||
rfid_printf("-> ATQA = %02X%02X\r\n", PICC_A.ATQA[0], PICC_A.ATQA[1]);
|
||
|
||
if (PICC_A.UID_Length == 4)
|
||
{
|
||
rfid_printf("-> UID = %02X%02X%02X%02X\r\n", PICC_A.UID[0], PICC_A.UID[1], PICC_A.UID[2], PICC_A.UID[3]);
|
||
}
|
||
else if (PICC_A.UID_Length > 4) // 支持更长的UID
|
||
{
|
||
rfid_printf("-> UID = ");
|
||
for (int i = 0; i < PICC_A.UID_Length; i++)
|
||
{
|
||
rfid_printf("%02X", PICC_A.UID[i]);
|
||
}
|
||
rfid_printf("\r\n");
|
||
}
|
||
rfid_printf("-> SAK = %02X\r\n", PICC_A.SAK[0]);
|
||
|
||
// 根据SAK值判断卡片类型
|
||
if (PICC_A.SAK[0] == 0x08)
|
||
{
|
||
rfid_printf("************* Mifare CARD ************* \r\n");
|
||
result = MIFARE_CARD_EVENT();
|
||
}
|
||
else if ((PICC_A.SAK[0] == 0x28) || (PICC_A.SAK[0] == 0x20))
|
||
{
|
||
rfid_printf("************* CPU CARD ************* \r\n");
|
||
// result = CPU_CARD_EVENT(); // CPU卡处理函数,暂未实现
|
||
}
|
||
else if (PICC_A.SAK[0] == 0x04)
|
||
{
|
||
rfid_printf("************* NTAG CARD ************* \r\n");
|
||
result = NTAG_EVENT();
|
||
}
|
||
|
||
SetCW(DISABLE); // 关闭RF场
|
||
}
|
||
|
||
/**
|
||
* @brief 处理Type B卡片事件。
|
||
* @details
|
||
* 该函数执行ISO/IEC 14443 Type B卡片的激活流程,包括:
|
||
* 1. 初始化读卡器以支持Type B协议。
|
||
* 2. 打开RF场。
|
||
* 3. 发送REQB/WUPB命令寻卡。
|
||
* 4. 发送ATTRIB命令选卡。
|
||
* 5. 获取卡片序列号(SN)。
|
||
* 6. 操作结束后关闭RF场。
|
||
* @return 无。
|
||
*/
|
||
void TYPE_B_EVENT(void)
|
||
{
|
||
unsigned char result;
|
||
rfid_printf("TYPE_B_EVENT begin\n");
|
||
|
||
ReaderB_Initial();
|
||
SetCW(ENABLE);
|
||
|
||
result = ReaderB_Request(&PICC_B);
|
||
if (result != SUCCESS)
|
||
{
|
||
SetCW(DISABLE);
|
||
return;
|
||
}
|
||
|
||
rfid_printf("************* TYPE B CARD ************* \r\n");
|
||
// 打印ATQB信息
|
||
rfid_printf("-> ATQB = ");
|
||
for(int i=0; i<12; i++) rfid_printf("%02X", PICC_B.ATQB[i]);
|
||
rfid_printf("\r\n");
|
||
|
||
result = ReaderB_Attrib(&PICC_B);
|
||
if (result != SUCCESS)
|
||
{
|
||
SetCW(DISABLE);
|
||
return;
|
||
}
|
||
rfid_printf("-> ATTRIB = %02X\r\n", PICC_B.CID);
|
||
|
||
result = ReaderB_Get_SN(&PICC_B);
|
||
if (result != SUCCESS)
|
||
{
|
||
SetCW(DISABLE);
|
||
return;
|
||
}
|
||
rfid_printf("-> SN = ");
|
||
for(int i=0; i<8; i++) rfid_printf("%02X", PICC_B.SN[i]);
|
||
rfid_printf("\r\n");
|
||
|
||
SetCW(DISABLE);
|
||
}
|
||
|
||
/**
|
||
* @brief 处理Type V (ISO/IEC 15693) 卡片事件。
|
||
* @details
|
||
* 该函数执行ISO/IEC 15693 Vicinity卡片的交互流程,包括:
|
||
* 1. 初始化读卡器以支持15693协议。
|
||
* 2. 打开RF场。
|
||
* 3. 发送Inventory命令寻卡并获取UID。
|
||
* 4. 发送Select命令选择卡片。
|
||
* 5. 示例性地对第4块进行写操作,然后再读回校验。
|
||
* 6. 操作结束后关闭RF场。
|
||
* @return 无。
|
||
*/
|
||
void TYPE_V_EVENT(void)
|
||
{
|
||
unsigned char result, i;
|
||
rfid_printf("TYPE_V_EVENT begin\n");
|
||
|
||
ReaderV_Initial();
|
||
SetCW(ENABLE);
|
||
|
||
result = ReaderV_Inventory(&PICC_V);
|
||
if (result != SUCCESS)
|
||
{
|
||
SetCW(DISABLE);
|
||
rfid_printf("-> ReaderV Inventory ERROR!\r\n");
|
||
return;
|
||
}
|
||
|
||
rfid_printf("************* TYPE V CARD ************* \r\n");
|
||
rfid_printf("UID=");
|
||
for (i = 0; i < 8; i++)
|
||
{
|
||
printHex(PICC_V.UID[i]);
|
||
}
|
||
rfid_printf("\r\n");
|
||
|
||
result = ReaderV_Select(&PICC_V);
|
||
if (result != SUCCESS)
|
||
{
|
||
SetCW(DISABLE);
|
||
rfid_printf("-> ReaderV Select ERROR!\r\n");
|
||
return;
|
||
}
|
||
|
||
// 示例:写单个块
|
||
memcpy(PICC_V.BLOCK_DATA, "\x11\x22\x33\x44", 4);
|
||
result = ReaderV_WriteSingleBlock(4, &PICC_V);
|
||
if (result != SUCCESS)
|
||
{
|
||
SetCW(DISABLE);
|
||
rfid_printf("-> ReaderV WriteSingleBlock ERROR!\r\n");
|
||
return;
|
||
}
|
||
rfid_printf("WriteSingleBlock SUCCESS\r\n");
|
||
|
||
// 示例:读单个块
|
||
result = ReaderV_ReadSingleBlock(4, &PICC_V);
|
||
if (result != SUCCESS)
|
||
{
|
||
SetCW(DISABLE);
|
||
rfid_printf("-> ReaderV ReadSingleBlock ERROR!\r\n");
|
||
return;
|
||
}
|
||
rfid_printf("BLOCK DATA = %02X%02X%02X%02X \r\n", PICC_V.BLOCK_DATA[0], PICC_V.BLOCK_DATA[1], PICC_V.BLOCK_DATA[2], PICC_V.BLOCK_DATA[3]);
|
||
|
||
SetCW(DISABLE);
|
||
}
|
||
|
||
/**
|
||
* @brief 处理Type F (FeliCa) 卡片事件。
|
||
* @details
|
||
* 该函数执行FeliCa卡片的交互流程,包括:
|
||
* 1. 初始化读卡器以支持FeliCa协议。
|
||
* 2. 打开RF场。
|
||
* 3. 发送Inventory命令寻卡并获取UID。
|
||
* 4. 后续可以添加与FeliCa卡的数据交换命令。
|
||
* 5. 操作结束后关闭RF场。
|
||
* @note 当前实现仅包含寻卡部分,具体的TPDU命令交换被注释掉了,因为它们依赖于一个未提供的 `CPU_TPDU` 函数。
|
||
* @return 无。
|
||
*/
|
||
void TYPE_F_EVENT(void)
|
||
{
|
||
unsigned char result, i;
|
||
// unsigned char SendBuffer[255];
|
||
// unsigned char ReceiveBuffer[255];
|
||
// transmission_struct TPDU;
|
||
// TPDU.pSendBuffer = SendBuffer;
|
||
// TPDU.pReceiveBuffer = ReceiveBuffer;
|
||
|
||
rfid_printf("TYPE_F_EVENT begin\n");
|
||
ReaderF_Initial();
|
||
SetCW(ENABLE);
|
||
|
||
result = ReaderF_Inventory(&PICC_F);
|
||
if (result != SUCCESS)
|
||
{
|
||
SetCW(DISABLE);
|
||
return;
|
||
}
|
||
|
||
rfid_printf("************* TYPE F CARD ************* \r\n");
|
||
rfid_printf("->TYPE F UID = ");
|
||
for(i=0; i<8; i++) rfid_printf("%02X", PICC_F.UID[i]);
|
||
rfid_printf("\r\n");
|
||
|
||
// 此处省略了原代码中复杂的TPDU数据交换部分,
|
||
// 因为它依赖于一个未定义的 CPU_TPDU 函数。
|
||
// 如果需要与CPU卡进行数据交换,您需要实现相关的APDU指令封装和解析。
|
||
|
||
SetCW(DISABLE);
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief RFID模块的主任务函数。
|
||
* @details
|
||
* 这是一个示例性的任务函数,展示了如何初始化RFID芯片并进入一个无限循环来轮询不同类型的卡片。
|
||
* 您可以将此函数作为一个独立的任务运行,或者将其中的逻辑集成到您现有的任务调度中。
|
||
* 1. 调用 `rfid_hal_init()` 初始化底层硬件。
|
||
* 2. 调用 `FM176XX_HardReset()` 硬复位RFID芯片。
|
||
* 3. 检查芯片版本号,确认通信正常。
|
||
* 4. 在主循环中,依次调用不同卡片类型的事件处理函数。
|
||
* @return 无。
|
||
*/
|
||
void rfid_task(void)
|
||
{
|
||
unsigned char result, reg_data;
|
||
|
||
// 1. 初始化底层硬件 (SPI, GPIO, UART, Delay)
|
||
// 这个函数需要您在 rfid_hal.c 中实现
|
||
rfid_hal_init();
|
||
|
||
// 2. 硬复位 FM176XX 芯片
|
||
while (1)
|
||
{
|
||
result = FM176XX_HardReset();
|
||
if (result != SUCCESS)
|
||
{
|
||
rfid_printf("FM176XX HardReset FAIL\r\n");
|
||
rfid_delay_ms(1000); // 延时后重试
|
||
}
|
||
else
|
||
{
|
||
rfid_printf("FM176XX HardReset SUCCESS\r\n");
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 3. 读取芯片版本号,确认通信是否正常
|
||
GetReg(REG_VERSION, ®_data);
|
||
rfid_printf("REG_VERSION = %02X\r\n", reg_data);
|
||
|
||
// 4. 进入主循环,轮询不同类型的卡
|
||
while (1)
|
||
{
|
||
// 您可以根据需要取消注释来测试不同类型的卡
|
||
// TYPE_A_EVENT();
|
||
// TYPE_B_EVENT();
|
||
TYPE_V_EVENT(); // 当前默认只测试 Type V (15693) 卡
|
||
// TYPE_F_EVENT();
|
||
|
||
rfid_delay_ms(500); // 每次轮询后延时
|
||
}
|
||
}
|