我在学习的过程中,在网上找不到window系统下的libnfc库下nfc卡读写的例子,所以在我做完这个例子后上传到网上,希望以后能帮到和我同样初学libnfc的人。

所使用的硬件:一个pn532模块+usb转接器

PN532支持多种工作模式,包括读写模式、卡模拟模式和点对点模式等,我这次只使用了读写模式

例子用到的卡,一个mifare卡

使用的环境是libnfc1.7,这个资源网上也有,直接在vs上运行即可

里面的端口号是usb插入的端口号

#include <iostream>
#include <windows.h>  // 提供 Sleep 函数
#include <nfc.h>      // libnfc库头文件


#include <cstdint>
#include <vector>
#include <algorithm>
#include <string>

// 延时函数(Windows API)
void delayMs(int ms = 5) {
    Sleep(ms);
}

// 等待用户输入1
void waitForUserInput() {
    int input;
    do {
        std::cout << "请输入1继续下一步操作: ";
        std::cin >> input;
    } while (input != 1);
}

// MIFARE Classic默认密钥A(出厂默认值FF FF FF FF FF FF)
const uint8_t DEFAULT_KEY[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

/**
 * @brief 打印块数据
 */
void printBlockData(uint8_t sector, uint8_t block, const uint8_t* data) {
    printf("Sector %2d, Block %2d: ", sector, block);
    for (int i = 0; i < 16; i++) {
        printf("%02X ", data[i]);
        if (i == 7) printf(" ");  // 每8字节用空格分隔
    }
    printf("\n");
}

/**
 * @brief 认证扇区
 */
bool authenticateSector(nfc_device* pnd, uint8_t block,
    const uint8_t* uid, uint8_t uid_len, bool useKeyA = true) {

    // 1. 增加认证前延时(500ms)
    delayMs(100);

    uint8_t auth_cmd[] = {
        useKeyA ? 0x60 : 0x61, block,   // 0x60=KeyA, 0x61=KeyB
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 默认密钥 FFFFFFFFFFFF
        uid[0], uid[1], uid[2], uid[3]  // UID 后4字节
    };

    uint8_t response[256];
        int res = nfc_initiator_transceive_bytes(pnd, auth_cmd, sizeof(auth_cmd),
            response, sizeof(response), 1000);

    // 2. 增加认证后延时(500ms)
    delayMs(100);

    if (res < 0) {
        printf("认证失败: %s\n", nfc_strerror(pnd));
        return false;
    }
   // printf("认证成功!\n");
    return true;
}

/**
 * @brief 读取块数据
 */
bool readBlock(nfc_device* pnd, uint8_t block, uint8_t* data) {
    uint8_t read_cmd[] = { 0x30, block };  // 读命令+块号
    uint8_t response[256];

    //printf("尝试读取块数据...\n");
    delayMs(100);  // 发送前延时

    int res = nfc_initiator_transceive_bytes(pnd, read_cmd, sizeof(read_cmd),
        response, sizeof(response), NULL);

    delayMs(100);  // 发送后延时

    if (res == 16) {
        memcpy(data, response, 16);
        //printf("读取成功!\n");
        return true;
    }
    printf("读取失败! 返回码: %d\n", res);
    return false;
}

/**
 * @brief 写入数据到指定块
 * @param pnd NFC设备句柄
 * @param block 绝对块号(0~63)
 * @param data 要写入的16字节数据
 * @return 是否写入成功
 */
bool writeblock(nfc_device* pnd, uint8_t block, const uint8_t* data) {
    // MIFARE Classic 写命令格式: 0xA0 + 块号 + 16字节数据
    uint8_t write_cmd[18] = { 0xA0, block };
    memcpy(write_cmd + 2, data, 16);  // 填充数据

    //printf("尝试写入块 %d...\n", block);
    delayMs(100);  // 写入前延时

    uint8_t response[256];
    int res = nfc_initiator_transceive_bytes(pnd, write_cmd, sizeof(write_cmd),
        response, sizeof(response), 1000);

    delayMs(100);  // 写入后延时

    if (res < 0) {
        printf("写入失败! 错误: %s\n", nfc_strerror(pnd));
        return false;
    }
   // printf("写入成功!\n");
    return true;
}

/**
 * @brief 读取指定扇区
 */
void readSector(nfc_device* pnd, nfc_target& target, int sectorToRead) {
    printf("\n=========== 正在读取扇区 %2d ===========\n", sectorToRead);

    for (uint8_t blockInSector = 0; blockInSector < 4; blockInSector++) {
        uint8_t currentBlock = sectorToRead * 4 + blockInSector;
        uint8_t data[16] = { 0 };

        printf("\n---- 块 %d ----\n", blockInSector);

        // 认证当前块
        //std::cout << "认证中...";
        if (!authenticateSector(pnd, currentBlock, target.nti.nai.abtUid, target.nti.nai.szUidLen, true)) {
            std::cout << "认证失败!跳过此块\n";
            continue;
        }

        // 读取块数据
        //std::cout << "读取中...";
        if (readBlock(pnd, currentBlock, data)) {
            printBlockData(sectorToRead, blockInSector, data);
        }
        else {
            std::cout << "读取失败\n";
        }

        delayMs(100);  // 块操作间隔
    }

    std::cout << "\n=========== 扇区 " << sectorToRead << " 读取完成 ===========\n";
}

/**
 * @brief 写入指定扇区块
 */
void writeSectorBlock(nfc_device* pnd, nfc_target& target)
{
    // 选择要写入的扇区和块
    int writeSector, writeBlock;
    do {
        std::cout << "请输入要写入的扇区号(0-15): ";
        std::cin >> writeSector;
        if (writeSector < 0 || writeSector > 15) {
            std::cout << "无效的扇区号!\n";
        }
    } while (writeSector < 0 || writeSector > 15);

    do {
        std::cout << "请输入要写入的块号(0-3): ";
        std::cin >> writeBlock;
        if (writeBlock < 0 || writeBlock > 3) {
            std::cout << "无效的块号!\n";
        }
    } while (writeBlock < 0 || writeBlock > 3);

    // 检查是否是扇区尾块(块3)
    if (writeBlock == 3) {
        std::cout << "警告:这是扇区尾块,包含密钥和访问控制位!\n";
        std::cout << "确定要写入吗?(1=是, 0=否): ";
        char confirm;
        std::cin >> confirm;
        if (confirm != '1') {
            std::cout << "已取消写入尾块操作\n";
            return;
        }
    }

    // 输入要写入的数据
    uint8_t writeData[16] = { 0 };
    std::cout << "\n请输入16字节十六进制数据(用空格分隔,示例: 01 02 03...):\n";
    std::cout << "示例数据: 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10\n";
    std::cout << "你的输入: ";

    for (int i = 0; i < 16; i++) {
        unsigned int byte;
        std::cin >> std::hex >> byte;
        writeData[i] = static_cast<uint8_t>(byte);
    }

    // 执行写入
    uint8_t currentBlock = writeSector * 4 + writeBlock;
    std::cout << "\n准备写入扇区 " << writeSector << " 块 " << writeBlock
        << " (绝对块号 " << (int)currentBlock << ")\n";
    waitForUserInput();

    // 认证
    if (authenticateSector(pnd, currentBlock, target.nti.nai.abtUid, target.nti.nai.szUidLen)) {
        // 写入
        if (writeblock(pnd, currentBlock, writeData)) {
            // 验证写入
            uint8_t verifyData[16];
            if (readBlock(pnd, currentBlock, verifyData)) {
                std::cout << "\n写入验证:\n";
                std::cout << "原始数据: ";
                for (int i = 0; i < 16; i++) printf("%02X ", writeData[i]);
                std::cout << "\n卡片数据: ";
                for (int i = 0; i < 16; i++) printf("%02X ", verifyData[i]);
                std::cout << "\n";

                if (memcmp(writeData, verifyData, 16) == 0) {
                    std::cout << "验证成功: 数据匹配!\n";
                }
                else {
                    std::cout << "验证失败: 数据不匹配!\n";
                }
            }
        }
    }
}

int main() {
    nfc_device* pnd;
    nfc_context* context;
    std::string a ="pn532_uart:COM";
    std::string b;
   
    // 初始化NFC库
    std::cout << "正在初始化NFC库..." << std::endl;
    nfc_init(&context);
    if (!context) {
        std::cerr << "错误: 初始化NFC上下文失败" << std::endl;
        return 1;
    }
    std::cout << "NFC库初始化成功!" << std::endl;
    waitForUserInput();

    // 打开PN532设备(通过COM1串口)
    std::cout << "请输入使用的端口号" << std::endl;
    std::cin >> b;
    a = a + b+":115200";
    std::cout << "正在尝试打开"<<a<<"..." << std::endl;
    const char* c_str_a = a.c_str();
    pnd = nfc_open(context, c_str_a);
    if (!pnd) {
        std::cerr << "错误: 找不到NFC设备 - " << nfc_strerror(pnd) << std::endl;
        nfc_exit(context);
        return 1;
    }
    std::cout << "NFC设备打开成功!" << std::endl;
    waitForUserInput();

    delayMs(1000);
    // 配置读卡器参数
    std::cout << "正在配置读卡器参数..." << std::endl;
    nfc_initiator_init(pnd);
    nfc_device_set_property_int(pnd, NP_TIMEOUT_COMMAND, 2000);  // 1秒超时
    nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, true);      // 启用CRC校验
    std::cout << "读卡器参数配置完成!" << std::endl;
    waitForUserInput();

    std::cout << "等待检测MIFARE Classic卡片..." << std::endl;

    // 检测卡片(ISO14443A协议)
    nfc_target target;

    std::cout << "正在尝试检测卡片..." << std::endl;
    // 1. 确保天线已开启(某些PN532固件可能需要手动开启)
    nfc_device_set_property_bool(pnd, NP_ACTIVATE_FIELD, true);  // 开启RF场
    delayMs(1000);  // 等待天线稳定,卡片上电(关键!)

    const nfc_modulation nm = { NMT_ISO14443A, NBR_106 };
    int res = nfc_initiator_select_passive_target(pnd, nm, nullptr, 0, &target);
    if (res > 0) {
        std::cout << "卡片检测成功!" << std::endl;
        waitForUserInput();

        delayMs(100);  // 卡片初始化延时(重要!)

        printf("卡片信息:\n");
        printf("ATQA: %02X %02X\n", target.nti.nai.abtAtqa[0], target.nti.nai.abtAtqa[1]);
        printf("SAK: %02X\n", target.nti.nai.btSak);
        std::cout << "UID: ";
        for (uint8_t i = 0; i < target.nti.nai.szUidLen; i++) {
            printf("%02X", target.nti.nai.abtUid[i]);
            if (i < target.nti.nai.szUidLen - 1) printf(":");
        }
        std::cout << "\n\n";
        waitForUserInput();

        // 主操作循环
        int operation = 0;
        while (operation != 3) {
            std::cout << "\n请选择操作:\n";
            std::cout << "1. 读卡\n";
            std::cout << "2. 写卡\n";
            std::cout << "3. 退出\n";
            std::cout << "请输入选择(1-3): ";
            std::cin >> operation;

            switch (operation) {
            case 1: {
                // 读卡操作
                int sectorToRead;
                do {
                    std::cout << "请输入要读取的扇区号(0-15): ";
                    std::cin >> sectorToRead;
                    if (sectorToRead < 0 || sectorToRead > 15) {
                        std::cout << "无效的扇区号,请输入0-15之间的数字!\n";
                    }
                } while (sectorToRead < 0 || sectorToRead > 15);

                readSector(pnd, target, sectorToRead);
                waitForUserInput();
                break;
            }
            case 2: {
                // 写卡操作
                writeSectorBlock(pnd, target);
                waitForUserInput();
                break;
            }
            case 3: {
                // 退出
                std::cout << "正在退出程序...\n";
                break;
            }
            default: {
                std::cout << "无效的选择,请重新输入!\n";
                break;
            }
            }
        }

        std::cout << "正在取消选择目标..." << std::endl;
        nfc_initiator_deselect_target(pnd);
        std::cout << "目标取消选择成功!" << std::endl;
    }
    else {
        std::cout << "未检测到卡片! 错误码: " << res << std::endl;
    }

    // 释放资源
    std::cout << "正在关闭NFC设备..." << std::endl;
    nfc_close(pnd);
    std::cout << "正在释放NFC上下文..." << std::endl;
    nfc_exit(context);
    std::cout << "NFC操作完成!" << std::endl;

    return 0;
}




Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐