本项目的设计目的是借助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!":
     title=
  • 原神,启动!
     title=

标签: WiFi-6, C/C++, NFC, 无线通信

添加新评论