# NFC Module 这个模块从当前 `custom_app` 中抽取 DP1312EA + NTAG213 UID 读取功能,目标是后续可以直接移植到别的业务项目。 核心原则: - NFC 模块不依赖业务层。 - 业务层通过回调接入 NFC 事件。 - I2C/GPIO/中断/RTOS/sleep/log 全部走平台适配层。 - Quectel SDK 调用只允许出现在 `platform/quectel/`。 ## 目录结构 ```text portable_modules/nfc/ include/ nfc_service.h nfc_platform.h src/ nfc_service.c nfc_dp1312ea.c nfc_ntag213.c platform/quectel/ nfc_platform_ql.c vendor/dp1312ea/ TypeA.c LPCD.c reg.h ... examples/ banban_adapter.c module.mk ``` ## 分层说明 - `include/nfc_service.h`: 业务层接口。业务只需要关心初始化、启动、UID 回调。 - `include/nfc_platform.h`: 平台适配接口。移植到新 SDK 时实现这里的函数。 - `src/nfc_service.c`: NFC 服务状态机,包含 LPCD 等待、中断唤醒、轮询读卡、失败回退。 - `src/nfc_dp1312ea.c`: DP1312EA 寄存器、reset、transceive 等芯片封装。 - `src/nfc_ntag213.c`: NTAG213 兼容 UID 读取。 - `platform/quectel/nfc_platform_ql.c`: 当前移远 SDK 适配。 - `vendor/dp1312ea/`: 原 DP1312EA TypeA/LPCD/寄存器等底层代码。 - `examples/banban_adapter.c`: 当前 Banban 业务接入示例。 ## 当前能力 - 初始化 DP1312EA。 - 进入 LPCD 低功耗检卡。 - IRQ 唤醒后轮询读卡。 - 读取 NTAG213/Ultralight 兼容标签 UID。 - 将 UID 以二进制和十六进制字符串形式回调给业务层。 暂未实现完整写 NFC 标签功能。`vendor` 中保留了 MIFARE 风格的 16 字节块写底层函数,但 NTAG213 页写入和 NDEF 写入需要后续单独补。 ## 业务层怎么使用 业务层只包含: ```c #include "nfc_service.h" ``` 然后实现两个核心回调: ```c static int app_can_poll(void *ctx) { // 返回 1 表示当前允许读卡,返回 0 表示暂时不读。 return 1; } static void app_on_uid(const nfc_uid_t *uid, void *ctx) { // uid->value 是十六进制字符串,例如 "04A1B2C3D4E5F6"。 // 业务层在这里决定绑定、触发 MQTT、播放提示音等。 } ``` 启动 NFC: ```c nfc_service_config_t config; nfc_service_callbacks_t callbacks = { .can_poll = app_can_poll, .on_uid = app_on_uid, .on_error = 0, .ctx = 0, }; nfc_service_default_config(&config); nfc_service_init(&config, &callbacks); nfc_service_start(); ``` `can_poll` 可以为空;为空时模块默认一直允许读卡。`on_uid` 必须提供。 ## 当前 Banban 项目接入方式 参考: ```text examples/banban_adapter.c ``` 这个示例把原项目中的业务判断放回业务层: - FOTA 中不读卡。 - 休眠中不读卡。 - MQTT 未连接不读卡。 - 录音/TTS/播放队列忙时不读卡。 - 测试模式允许读卡。 - 读到 UID 后,根据 `g_nfc_bind_status` 决定调用绑定还是触发上报。 在当前项目中正式切换时,应用层可以把原来的: ```c DP1312EA_Init(); ``` 替换为: ```c banban_nfc_start(); ``` 并把 `examples/banban_adapter.c` 加入业务工程源码。 ## Quectel SDK 工程怎么接入 如果工程的 Makefile 支持 include 片段,可以引入: ```make include ../portable_modules/nfc/module.mk ``` 或者手动加入这些源码: ```make SRC_FILES += \ ../portable_modules/nfc/src/nfc_service.c \ ../portable_modules/nfc/src/nfc_dp1312ea.c \ ../portable_modules/nfc/src/nfc_ntag213.c \ ../portable_modules/nfc/platform/quectel/nfc_platform_ql.c \ ../portable_modules/nfc/vendor/dp1312ea/LPCD.c \ ../portable_modules/nfc/vendor/dp1312ea/TypeA.c INC_DIRS += \ -I../portable_modules/nfc/include \ -I../portable_modules/nfc/src \ -I../portable_modules/nfc/vendor/dp1312ea ``` 如果要使用 Banban 示例,还需要加入: ```make SRC_FILES += ../portable_modules/nfc/examples/banban_adapter.c ``` ## 默认硬件配置 - I2C bus: `1` - DP1312EA I2C slave address: `0x50 >> 1` - Reset pin: `86` - IRQ pin: `87` 如硬件不同,在初始化前修改 `nfc_service_config_t`: ```c nfc_service_default_config(&config); config.i2c_bus = 1; config.reset_pin = 86; config.irq_pin = 87; nfc_service_init(&config, &callbacks); ``` ## 移植到其他平台 移植新平台时,不需要改 `src/` 和 `vendor/`。只需要新增一个平台实现,例如: ```text platform/my_sdk/nfc_platform_my_sdk.c ``` 实现 `include/nfc_platform.h` 中的函数: - `nfc_platform_i2c_init` - `nfc_platform_i2c_read` - `nfc_platform_i2c_write` - `nfc_platform_gpio_init` - `nfc_platform_gpio_set_level` - `nfc_platform_gpio_get_level` - `nfc_platform_irq_register` - `nfc_platform_irq_enable_wakeup` - `nfc_platform_irq_disable_wakeup` - `nfc_platform_sem_create` - `nfc_platform_sem_wait` - `nfc_platform_sem_release` - `nfc_platform_task_create` - `nfc_platform_sleep_ms` - `nfc_platform_log` ## 分层约束 维护时请保持这些边界: - `src/` 和 `vendor/` 不能 include `ql_*.h`。 - `src/` 和 `vendor/` 不能 include `app_inc.h`。 - `src/` 和 `vendor/` 不能调用 MQTT、绑定、播放、录音、FOTA 等业务函数。 - 业务判断必须放在 `can_poll` 回调。 - UID 后续动作必须放在 `on_uid` 回调。