Files
99_7018_lmx/apps/earphone/RFID/reader/rfid_main.c
2025-11-24 13:55:10 +08:00

361 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************************************************************
* @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. 根据卡片的SAKSelect 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, &reg_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); // 每次轮询后延时
}
}