基于NRF7002-DK实现的NFC原神启动器!
本项目的设计目的是借助nRF7002-DK的板载NFC功能实现卡片模拟,当手机靠近NFC天线时能读取到卡片中存储的字符信息,板卡同时点亮LED1灯表示NFC通信正在进行;板载的按键可以在纯文本传输与启动APP之间切换,板卡通过LED2指示当前的工作模式。
硬件信息
nRF7002-DK板载三颗MCU芯片,其中一片nRF5340用作接口MCU,刷写有Jlink固件提供调试下载与虚拟串口的功能;一片nRF5340用作主MCU,用于用户逻辑的实现;一片nRF7002用作协同MCU,处理Wi-Fi相关的事务,并与主MCU之间通过QSPI进行通信。
上图即为本板卡的俯视图,其中U2为接口MCU,U1为主MCU,U8为协同MCU。同样地,板卡上板载了三个天线接口J1 J5 J7与两个板载陶瓷天线A1 A2。其中J5为NFC天线接口、J1为WiFi天线接口、J7为蓝牙天线接口、A1为2.4G/5G双频天线、A2为2.4G单频天线,用户可以根据需求配置射频选择器来选择天线使用。
本设计中的关键内容是NFC部分的硬件连接与配置,通过查阅硬件手册的4.13章节得知NFC天线默认连接在P0.02与P0.03两个引脚。这对引脚的默认功能被配置为NFC,如需使用为GPIO需要配置prj.conf并修改硬件电路。相关电路图如下所示,在此只使用NFC功能不对硬件做修改。
流程框图
本设计的工作流程展示如下,系统启动后会首先进行按键与LED相关GPIO的初始化,同样也会配置默认NFC工作模式为NFC_TEXT。
- Button 1按键用于切换NFC工作模式,可以在NFC_TEXT与NFC_APP状态之间切换,功能分别为纯文本传输与APP打开。其中纯文本传输模式会向手机传输"Hello World!"字符串,APP打开模式将会默认启动原神!;
- LED1用于展示NFC通讯状态,亮起表示NFC正在通信,熄灭表示通信结束;
- LED2用于展示NFC工作模式,亮起表示处于NFC_TEXT模式,熄灭表示处于NFC_APP模式;
- 板载Jlink自带两个虚拟串口,在系统正常运行时将会通过串口打印调试信息。
软件开发
环境配置
Nordic官方推荐的开发工具是VS Code,在其上安装一系列官方插件即可配置出完整的IDE环境,官方文档可以参考How to install the extension。在启动VS Code之前,必须通过官网的Toolchain Manager来安装SDK配置环境,建议不要将安装路径设置在C盘根目录否则容易导致权限问题。一切安装妥当后点击“Open VS Code”启动开发环境,如下图所示当nRF Connect SDK与nRF Connect Toolchain能自当识别到即代表SDK安装正确。
在欢迎界面点击"Create a new application"打开应用创建Freestanding应用,应用正常创建后将会跳转到开发环境,若仍停留在资源管理器界面请自行点击nRF Connect扩展图标打开开发环境。
此时新建的应用工程将提示"No build configurations"信息,配置"build configuartions"的作用类似配置BSP。在主界面的Board中选择核心为“nrf7002dk_nrf5340_cpuapp”即可,该CPU为不带TFM的核心。虽然该开发板不在兼容列表中,但通过查阅硬件手册可得知二者的NFC引脚相同故可直接套用。
配置完"build configuartions"后直接编译可能会提示无法找到“nfc_t2t_lib.h”或者"kernel.h",此时建议重启VS Code重新进行索引。同时如果需要使用NFC启动APP或TEXT功能时,需要在prj.conf中开启相关配置否则会报错未定义,本项目配置情况如下:
CONFIG_NFC_NDEF=y
CONFIG_NFC_NDEF_MSG=y
CONFIG_NFC_NDEF_RECORD=y
CONFIG_NFC_NDEF_URI_REC=y
CONFIG_NFC_NDEF_LAUNCHAPP_MSG=y
CONFIG_NFC_NDEF_TEXT_RECORD=y
代码介绍
- 主函数:dk_leds_init与dk_buttons_init分别对LED与按键进行初始化,并指定按键时间的回调函数button_handler。同时在外设初始化完成之后还通过nfc_record_text指定了NFC默认工作模式NFC_TEXT。
int main(void)
{
int err;
printk("Starting NFC Launch app example\n");
/* 设置LED GPIO为输出 */
err = dk_leds_init();
if (err) {
printk("Cannot init LEDs!\n");
return err;
}
/* 初始化GPIO端口并指定handler */
err = dk_buttons_init(button_handler);
if (err) {
printk("Cannot init KEYs!\n");
return err;
}
/* 默认配置为record_text模式 */
nfc_record_text();
dk_set_led_on(NFC_MODE_LED);
printk("Mode has been changed to NFC_TEXT!\n");
return 0;
}
- 按键事件处理函数:当Button1按下时通过nfc_t2t_emulation_stop与nfc_t2t_done关闭NFC功能,然后切换工作模式并重新配置NFC与LED灯。
static void button_handler(uint32_t button_state, uint32_t has_changed)
{
uint32_t ret;
uint32_t button = button_state & has_changed; //记录按键按下状态
if (button & DK_BTN1_MSK)
{
//NFC状态切换前先关闭NFC功能
if (nfc_t2t_emulation_stop()){
printk("nfc_t2t_emulation_stop ret = %d\r\n", ret);
return;
}
if (nfc_t2t_done()){
printk("nfc_t2t_done ret = %d\r\n", ret);
return;
}
//切换工作模式
if (mode == NFC_TEXT)
{
mode = NFC_APP; //由NFC_TEXT状态转换为NFC_APP
nfc_launch_app();
dk_set_led_off(NFC_MODE_LED);
printk("Mode has been changed to NFC_APP!\n");
}else if(mode == NFC_APP)
{
mode = NFC_TEXT; //由NFC_APP状态转换为NFC_TEXT
nfc_record_text();
dk_set_led_on(NFC_MODE_LED);
printk("Mode has been changed to NFC_TEXT!\n");
}
}
}
- NFC_APP模式下NFC初始化函数:进行NFC的T2T工作状态配置,NDEF信息编码,Payload载入与NFC模拟开始。其中android_pkg_name为需要打开的Android应用程序包名,可以查询手机的包管理器得到。
int nfc_launch_app()
{
int err;
uint32_t len = sizeof(ndef_msg_buf);
/* 设置NFC为T2T */
err = nfc_t2t_setup(nfc_callback, NULL);
if (err) {
printk("Cannot setup NFC T2T library!\n");
return err;
}
/* 编码android_pkg_name、universal_link到ndef_msg_buf */
err = nfc_launchapp_msg_encode(android_pkg_name,
sizeof(android_pkg_name),
universal_link,
sizeof(universal_link),
ndef_msg_buf,
&len);
if (err) {
printk("Cannot encode message!\n");
return err;
}
/* 把已生成的NDEF数据写入NFC Payload */
err = nfc_t2t_payload_set(ndef_msg_buf, len);
if (err) {
printk("Cannot set payload!\n");
return err;
}
/* 开启NFC模拟 */
err = nfc_t2t_emulation_start();
if (err) {
printk("Cannot start emulation!\n");
return err;
}
printk("NFC configuration done\n");
return 0;
}
- NFC回调函数:当NFC状态改变时,系统回调该函数处理相关事务。在本函数中,当NFC状态更改为NFC_T2T_EVENT_FIELD_ON时候开启LED灯,当NFC状态更改为NFC_T2T_EVENT_FIELD_OFF关闭LED灯,这两种事务分别对应NFC活跃与离线。
static void nfc_callback(void *context,
nfc_t2t_event_t event,
const uint8_t *data,
size_t data_length)
{
ARG_UNUSED(context);
ARG_UNUSED(data);
ARG_UNUSED(data_length);
switch (event) {
case NFC_T2T_EVENT_FIELD_ON:
dk_set_led_on(NFC_FIELD_LED);
break;
case NFC_T2T_EVENT_FIELD_OFF:
dk_set_led_off(NFC_FIELD_LED);
break;
default:
break;
}
}
项目展示
分别展示纯文本信息传输与APP打开两个功能:
- NFC纯文本传输"Hello World!":
- 原神,启动!