Add portable NFC module

This commit is contained in:
2026-07-20 10:48:38 +08:00
commit 4ed123f87c
25 changed files with 3965 additions and 0 deletions

211
nfc/README.md Normal file
View File

@@ -0,0 +1,211 @@
# 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` 回调。