• 基于nRF7002-DK的NFC功能切换系统(nRF Connect SDK+NFC)


    👉 【Funpack2-6】基于nRF7002-DK的NFC功能切换系统
    👉 Github: EmbeddedCamerata/nRF7002-DK-nfc-function-switching

    项目介绍

    本项目基于nRF7002-DK,使用nRF Connect SDK v2.4.2 开发,使用NFC外设,实现NFC记录英文文本信息、中文文本信息与打开安卓应用三个功能,并可通过按键切换,通过手机NFC触碰即可触发。

    👉 官网:nRF Connect SDK

    硬件介绍

    nRF7002-DK是用于nRF7002 Wi-Fi 6协同IC的开发套件,该开发套件采用nRF5340多协议片上系统 (SoC) 作为nRF7002的主处理器,在单一的电路板上包含了开发工作所需的一切,可让开发人员轻松开启基于nRF7002 的物联网项目。该 DK 包括 Arduino 连接器、两个可编程按钮、一个 Wi-Fi 双频段天线和一个低功耗蓝牙天线,以及电流测量引脚。

    这款DK支持低功耗 Wi-Fi 应用开发,并实现了多项 Wi-Fi 6 功能,比如 OFDMA、波束成型和 TWT。nRF7002 Wi-Fi 6配套IC为另一个主机添加了低功耗Wi-Fi 6功能,提供无缝连接和基于Wi-Fi的定位(本地Wi-Fi集线器的SSID嗅探)功能。该IC设计用于搭配Nordic现有的nRF52®和nRF53®系列多协议片上系统 (SoC) 和nRF91®系列蜂窝物联网系统级封装 (SiP) 使用。nRF7002 IC 还可与非nordic主机器件搭配使用。通过SPI或QSPI与主机通信,并带有额外的共存功能,可与其他协议如蓝牙、Thread或Zigbee无缝共存。

    nRF7002在Nordic的nRF Connect SDK中提供集成和支持。

    板卡特性:

    • 用于nRF7002双频带Wi-Fi 6配套IC的开发套件
    • nRF5340 SoC主机器件
    • Wi-Fi 6 (IEEE 802.11 a/b/g/n/ac/ax)、蓝牙低功耗(LE)、蓝牙网状网络、802.15.4、Thread、Zigbee®、ANT、2.4GHz专有和NFC无线协议支持2.4GHz、5GHz芯片和NFC天线
    • SWF射频连接器
    • SEGGER J-Link板载编程器/调试器
    • 用户可编程LED (2x) 和按钮 (2x)
    • 用于测量功耗的引脚
    • 来自USB、外部或锂聚合物电池的2.9V至5.0V电源
    • Arduino连接器

    nRF7002结构框图

    项目设计

    开发环境及工程目录

    根据 官网文档 手动安装SDK等依赖,nRF Connect SDK 版本 v2.4.2 。由于笔者是Linux环境,且有一定的洁癖,因此在安装 west 的时候单独为其使用Poetry,创建了一个Python虚拟环境。并在此基础上,安装额外的Python依赖。每次使用 west 先激活Python虚拟环境即可。

    👉 Poetry

    工程目录可根据 ncs/nrf/samples 中的sample复制一份,并在此基础上添加自己的代码或配置。例如:

    ├ build(编译时所产生的构建文件)
    ├ src
    │ ├ main.c
    │ └ ...(如有需要,其他的源代码)
    ├ CMakeLists.txt
    ├ prj.conf
    ├ sample.yaml
    ├ README.md
    └ ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    prj.conf 中配置需要启用的库,从而在编译程序的时候编译对应的头文件。sample.yaml 仅做描述示例程序之用。此外,Kconfig 是为了用户可在menuconfig内手动配置某些选项(主要是可选功能),在本工程中省略。

    CMakeLists.txt 内,额外添加 set(BOARD nrf7002dk_nrf5340_cpuapp) ,从而在 west build 时可省略 -b nrf7002dk_nrf5340_cpuapp) 编译选项。

    cmake_minimum_required(VERSION 3.20.0)
    set(BOARD nrf7002dk_nrf5340_cpuapp)
    find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
    project(nfc_function_switching)
    FILE(GLOB app_sources src/*.c)
    # NORDIC SDK APP START
    target_sources(app PRIVATE ${app_sources})
    # NORDIC SDK APP END
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    总体流程图

    工程流程图

    硬件初始化

    在此初始化板卡上的LED与按键。并将LED2打开以表示板卡正常工作。

    dk_buttons_init(NULL);
    dk_leds_init();
    dk_set_led_on(SYSTEM_ON_LED);
    
    • 1
    • 2
    • 3

    NFC功能实现

    首先通过 nfc_t2t_setup() 注册NFC事件的回调函数,其回调函数内主要响应NFC标签检测到外部NFC场时与移除时的事件。当检测到外部NFC场时,LED1亮起,移除时灭。

    nfc_t2t_setup(nfc_callback, NULL)
    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;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    文本记录

    主要使用nRF提供的nfc库。先生成NDEF文本记录描述符,再将这个记录加入NDEF消息中,之后 nfc_ndef_msg_encode 编码,并存入 buffer 内,长度为 len

    static int nfc_text_encode(uint8_t *buffer, uint32_t *len)
    {
    	NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_text_rec,
    								  UTF_8,
    								  en_code,
    								  sizeof(en_code),
    								  en_payload,
    								  sizeof(en_payload));
    
    	NFC_NDEF_MSG_DEF(nfc_text_msg, MAX_REC_COUNT);
    
    	/* Add record */
    	if (nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_text_msg),
    								&NFC_NDEF_TEXT_RECORD_DESC(nfc_text_rec)) < 0)
    	{
    		printk("Cannot add record!\n");
    		return -1;
    	}
    
    	/* Encode text message */
    	if (nfc_ndef_msg_encode(&NFC_NDEF_MSG(nfc_text_msg), buffer, len) < 0)
    	{
    		printk("Cannot encode message!\n");
    		return -1;
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    需要注意的是如何编码中英文文本信息至NDEF。对于英文使用UTF-8编码,直接编码ASCII字符,例如“Hello, World!”:

    static const uint8_t en_payload[] = {
    	'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
    static const uint8_t en_code[] = {'e', 'n'};
    
    NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_text_rec,
    						      UTF_8,
    							  en_code,
    							  sizeof(en_code),
    							  en_payload,
    							  sizeof(en_payload));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    而对于中文信息,每个中文字符需要UTF-16编码,但是 NFC_NDEF_TEXT_RECORD_DESC_DEF 宏内传入的数据的类型却是 uint8_t const* ,因此需要将“遥遥领先!”对应十六进制“\u6590\u6590\u8698\u4851\u01ff”拆分,先写低位再高位,使用UTF-16编码:

    static const uint8_t zh_payload[] = {
    	'\x90', '\x65', '\x90', '\x65', '\x98', '\x86', '\x51', '\x48', '\xff', '\x01'};
    static const uint8_t zh_code[] = {'z', 'h'};
    
    NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_text_rec,
    							  UTF_16,
    							  zh_code,
    							  sizeof(zh_code),
    							  zh_payload,
    							  sizeof(zh_payload));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    安卓应用打开

    所需要的是安卓应用的名称 android_pkg_name 。在此,将打开明日方舟 com.hypergryph.arknights 。并通过 nfc_launchapp_msg_encode 完成该类型消息的创建与编码,并存到 buffer,长度为 len

    /* Package: com.hypergryph.arknights */
    static const uint8_t android_pkg_name[] = {
    	'c', 'o', 'm', '.',
    	'h', 'y', 'p', 'e', 'r', 'g', 'r', 'y', 'p', 'h', '.',
    	'a', 'r', 'k', 'n', 'i', 'g', 'h', 't', 's'};
    static int nfc_launchapp_encode(uint8_t *buffer, uint32_t *len)
    {
    	/* Encode launch app data  */
    	if (nfc_launchapp_msg_encode(android_pkg_name,
    								 sizeof(android_pkg_name), NULL, 0, buffer, len) < 0)
    	{
    		printk("Cannot encode message!\n");
    		return -1;
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    按键切换NFC功能

    实现该功能,需要一个 enum 类型变量 nfc_app_t 存储当前功能的类别。并在初始化NFC功能后,在 while (true) 循环内扫描按键,判断 DK_BTN1 是否按下,按下则切换功能类别,同时根据 nfc_app 重新生成NFC payload并设置。

    typedef enum
    {
    	NFC_APP_TEXT = 0U,
    	NFC_APP_LAUNCHAPP
    } nfc_app_t;
    
    // In main() function
    while (true)
    {
    	dk_read_buttons(&button_state, NULL);
    
    	if (button_state & DK_BTN1_MSK)
    	{
    		nfc_app = 1 - nfc_app;
    		/* Stop sensing NFC field */
    		if (nfc_t2t_emulation_stop() < 0)
    		{
    			printk("Cannot stop emulation!\n");
    			return -1;
    		}
    		if (nfc_payload_set(nfc_app) < 0)
    		{
    			printk("NFC payload set failed!\n");
    			goto fail;
    		}
    	}
    	k_sleep(K_MSEC(200));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    需要注意的是,两种功能的payload,长度、内容不同,如果它们共用同一个buffer存储NDEF消息,例如 static uint8_t buffer[256] ,实测会出错。因此,单独为两个功能各自设置一个buffer用于存储NDEF消息。那么,根据 nfc_app 的值,编码与设置不同类别的NDEF消息:

    /* Buffer used to hold an NFC NDEF message. */
    #define NDEF_MSG_BUF_SIZE 256
    static uint8_t text_msg_buf[NDEF_MSG_BUF_SIZE];
    static uint8_t launch_app_msg_buf[NDEF_MSG_BUF_SIZE];
    
    // When nfc_app_t == NFC_APP_TEXT
    len = sizeof(text_msg_buf);
    nfc_text_encode(text_msg_buf, &len);
    /* Set created message as the NFC payload */
    nfc_t2t_payload_set(text_msg_buf, len);
    
    // When nfc_app_t == NFC_APP_LAUNCHAPP
    len = sizeof(launch_app_msg_buf);
    nfc_launchapp_encode(launch_app_msg_buf, &len);
    /* Set created message as the NFC payload */
    nfc_t2t_payload_set(launch_app_msg_buf, len);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    功能展示

    板卡:

    板卡全貌
    通过按键切换NFC功能,串口打印提示语句:

    串口打印
    当功能为记录英文文本信息时,手机NFC触碰后,可读取UTF-8编码的文本信息“Hello, World!”。

    NFC读取英文文本信息
    当功能为记录中文文本信息时,手机NFC触碰后,可读取UTF-16编码的文本信息“遥遥领先!”。

    NFC读取中文文本信息
    当功能为记录安卓应用信息时,手机NFC触碰后,可读取应用信息“com.hypergryph.arknights”,即明日方舟包名。此外,当不用NFC标签助手等APP读取NFC信息,而直接触碰,则会唤起应用。

    NFC读取安卓应用信息
    在手机NFC触碰后,LED2将亮起,手机移开后,NFC连接移除,LED2灭。

    👉 详细展示参见:B站:基于nRF7002-DK的NFC功能切换系统

    项目总结

    本次项目使用nRF7002-DK开发板,使我接触了Nordic家产品的开发环境,N家的开发环境如果使用VSCode插件全套的话,体验还算不错。但只是在笔者Linux端上VSCode插件识别不到板子,因此只能手动安装SDK、Zypher等工具,这个流程上倒是不难,只不过west工具还需要依赖Python,但隔离得不如esp-idf优雅。在编译流程上,有esp-idf的经验那么对于N家的流程就差不多。

    工程上,借助NFC实现了多个功能切换,这让我学习了有关NFC的概念,对NFC功能了解更为深入,同时还让我接触到安卓设备投屏至Linux端的软件(推荐scrcpy)。希望日后再有机会用N家的开发板,能更深入地学习设备树、Kconfig、prj.conf、Zypher 等知识。

  • 相关阅读:
    学习笔记(9)JavaScript元素、节点
    协程知识点总结
    C#中使用list封装多个同类型对象以及组合拓展实体的应用
    034-用Swing组件综合应用(实现QQ登录与注册界面功能)
    leetcode 63. 不同路径 II(dp)
    文件上传下载
    【java学习—九】类的成员之四:初始化块(1)
    小熊家务帮day13-day14 门户管理(ES搜索,Canal+MQ同步,索引同步)
    [Linux打怪升级之路]-冯诺依曼体系结构和对操作系统的认识
    【逐步剖C++】-第二章-C++类和对象(中)
  • 原文地址:https://blog.csdn.net/weixin_46422143/article/details/132958016