Funpack2-6 基于nRF7002 DK的NFC卡片
通过nRF7002 DK板载的NRF5340片上的NFC功能,模拟出一个NFC卡片,使带有NFC功能的设备靠近后,可以通过nRF7002 DK上的tag获取到对应信息
标签
Funpack活动
NFC
Nordic
21st
更新2023-10-13
573

一、项目介绍

本次funpack第二季第六期使用的板卡是来自nordic半导体的nRF7002 DK,可用于nRF7002 Wi-Fi 6双频辅助IC的开发套件,采用nRF5340多协议ic作为主处理器,配合nRF7002 Wi-Fi协同芯片,可以同时支持低功耗蓝牙和Wi-Fi 应用的开发,并实现如 OFDMA、波束成形和目标唤醒时间等多项 Wi-Fi 6 功能。

本项目使用板上的nRF5340实现一个自定义NFC卡片的功能,nRF5340是支持低功耗蓝牙、蓝牙Mesh、NFC、Thread和Zigbee的双核蓝牙5.3 SoC,非常适合用来开发无线连接应用。

提到NFC,大家肯定都不陌生,这几年越来越多的手机和智能手环上都加入了NFC功能。NFC即近距离无线通讯技术(Near Field Communication),可以在两个设备之间进行无线短距离传输通信。目前在智能手机上的NFC应用大致可以分为接触通过、接触支付、接触浏览、接触连接、下载接触,应用模式分为卡模式、读卡器式、点对点式。

本项目将采用读卡器式应用模式,即具备读写功能的NFC手机只能从tag中采集数据,不能向tag中写入数据(目前NFC中的Type 2 Tag协议支持读写功能,但是nordic官方提供参考的Type 2 Tag库只支持读功能,不支持外部设备写入的功能)。当具备NFC功能的手机设备接触到板卡上的tag,可以从该tag中获取到当前商品的出厂日期、官网、活动名称等信息。

二、设计思路

参考nordic官方提供的Libraries for NFC,其中包括NFC Data Exchange Format(NDEF)、Type 2 Tag、Type 4 Tag、Tag NDEF Exchange Protocol(TNEP)四个库,本项目主要使用NDEF和Type 2 Tag两个库,其中NDEF用于将需要传输的数据转换成NFC通讯时用到的NDEF格式数据,使用Type 2 Tag完成使能NFC Tag并配置NFC Tag。

根据nordic提供的Type 2 Tag库使用说明,编程一个tag,并使其存储有对应的信息主要分为三步:1.声明一个回调函数并且处理从Type 2 Tag库内部产生的事件,并调用

nfc_t2t_steup(nfc_callback,NULL)

2.配置tag中保存的数据,可以使用NDEF库直接将原始数据编码成NDEF格式的数据,也可以设置一个TLV格式的原始数据,推荐直接使用NDEF库,方便省事不用深入理解协议。

nfc_t2t_payload_set(ndef_msg_buf,len)

3.激活启动NFC tag,此时tag开始感应附近(<10cm)是否有其他的NFC域,如果有响应它,并且将tag中的数据发送到对应的NFC设备上。

nfc_t2t_emulation_start()

三、硬件介绍

关于NFC部分的硬件原理图部分比较简单,关键在于如何进行PCB布线,以及tag的画法,这些最终会实际影响到NFC识别的灵敏度等。

关于板卡上NFC部分的硬件电路如下

Fmra2v5W37NCHrzN3Txw6kVzHXfp

FkjG4RBn5q0mpOKPm85gaKDv_3bb

nRF5340的NFC功能引脚NFC1、NFC2分别通过两个0Ω电阻和300pF的匹配电容连接到NFC天线的连接器上,在这个连接器上插上对应的天线即可

FjYZWA-aTv0fqIizGKiNDUBopMgR

四、软件介绍

4.1 NFC文本标签

结合nordic提供的Type 2 Tag库使用说明以及官方例程,可以很轻松实现一个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);
		printk("NFC field is exist!\n");
		break;
	case NFC_T2T_EVENT_FIELD_OFF:
		dk_set_led_off(NFC_FIELD_LED);
		printk("NFC field doesn't exist!\n");
		break;
	default:
		break;
	}
}

通过nrf_callback判断当前的事件为什么,当有NFC域存在时,触发打印print;当NFC域离开时,触发打印print。关于nrf_callback返回的事件分别有

FnTH4gEwjk_EE_-xyf5MslJJzvhc

static int nrf5340_msg_encode(uint8_t *buffer, uint32_t *len)
{
	int err;

	/* Create NFC NDEF text record description in English */
	NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_eetree_text_rec,
				      UTF_8,
				      en_code,
				      sizeof(en_code),
				      eetree_payload,
				      sizeof(eetree_payload));

	/* Create NFC NDEF text record description in Norwegian */
	NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_funpack_text_rec,
				      UTF_8,
				      en_code,
				      sizeof(en_code),
				      funpack_payload,
				      sizeof(funpack_payload));

	/* Create NFC NDEF text record description in Polish */
	NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_board_text_rec,
				      UTF_8,
				      en_code,
				      sizeof(en_code),
				      board_payload,
				      sizeof(board_payload));

	/* Create NFC NDEF message description, capacity - MAX_REC_COUNT
	 * records
	 */
	NFC_NDEF_MSG_DEF(nfc_text_msg, MAX_REC_COUNT);

	/* Add text records to NDEF text message */
	err = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_text_msg),
				   &NFC_NDEF_TEXT_RECORD_DESC(nfc_eetree_text_rec));
	if (err < 0) {
		printk("Cannot add first record!\n");
		return err;
	}
	err = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_text_msg),
				   &NFC_NDEF_TEXT_RECORD_DESC(nfc_funpack_text_rec));
	if (err < 0) {
		printk("Cannot add second record!\n");
		return err;
	}
	err = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_text_msg),
				   &NFC_NDEF_TEXT_RECORD_DESC(nfc_board_text_rec));
	if (err < 0) {
		printk("Cannot add third record!\n");
		return err;
	}

	err = nfc_ndef_msg_encode(&NFC_NDEF_MSG(nfc_text_msg),
				      buffer,
				      len);
	if (err < 0) {
		printk("Cannot encode message!\n");
	}

	return err;
}

使用nrf5340_msg_encode对tag中保存的信息编码为NDEF数据

int main(void)
{
	uint32_t len = sizeof(ndef_msg_buf);

	printk("Starting NFC Text Record example\n");

	/* Configure LED-pins as outputs */
	if (dk_leds_init() < 0) {
		printk("Cannot init LEDs!\n");
		goto fail;
	}

	/* Set up NFC */
	if (nfc_t2t_setup(nfc_callback, NULL) < 0) {
		printk("Cannot setup NFC T2T library!\n");
		goto fail;
	}



	if (nrf5340_msg_encode(ndef_msg_buf, &len) < 0) {
		printk("Cannot encode message!\n");
		goto fail;
	}

	/* Set created message as the NFC payload */
	if (nfc_t2t_payload_set(ndef_msg_buf, len) < 0) {
		printk("Cannot set payload!\n");
		goto fail;
	}


	/* Start sensing NFC field */
	if (nfc_t2t_emulation_start() < 0) {
		printk("Cannot start emulation!\n");
		goto fail;
	}
	printk("NFC configuration done\n");

	return 0;

fail:
#if CONFIG_REBOOT
	sys_reboot(SYS_REBOOT_COLD);
#endif /* CONFIG_REBOOT */

	return -EIO;
}

main函数中依次进行使能NFC——>将要保存的tag信息编码成NDEF格式——>将NDEF数据填充到payload里面——>开始感应NFC域。当调用nfc_t2t_emulation_start后,当感应到NFC域后,库内部自己将编码后的NDEF数据发送给对应的NFC手机端。

4.2 NFC拨打紧急电话(紧急联系人可自定义)

根据官方的NFC Launch app例程进行修改,首先将手机上的安卓应用名字找到,放置到android_pkg_name_tel中,然后将对应的紧急联系人电话添加到m_url_tel中,main函数中通过nfc_launchapp_msg_encode函数将nfc_launchapp_msg_encodenfc_launchapp_msg_encode编码为NDEF格式的数据。

对于nfc_launchapp_msg_encode函数,首先创建两个记录描述符,一个用来保存nfc_launchapp_msg_encode,一个用来保存m_url_tel,然后判断传入的实参是否为1,可以对应url和android_pkg_name都有,也可以二者中包含任意一个。实参为1之后,调用nfc_ndef_msg_record_add将record添加到msg中,最后调用nfc_ndef_msg_encode进行编码,将msg编码为NDEF数据。

关于如何要手机将url的信息识别为电话号码,关键需要在创建uri记录描述符的宏中将URI 记录类型修改为NFC_URI_TEL

代码片段如下:

static const uint8_t android_pkg_name_tel[] = {
	'c', 'o', 'm', '.', 'a', 'n', 'd', 'r', 'o', 'i', 'd', '.', 'p', 'h', 'o', 'n', 'e'};
static const uint8_t m_url_tel[] = {'1','2','3','1','5'};
	NFC_NDEF_MSG_DEF(nfc_launchapp_msg, 2);

	/* Create NFC NDEF Android Application Record description */
	NFC_NDEF_ANDROID_LAUNCHAPP_RECORD_DESC_DEF(nfc_and_launchapp_rec,
						   android_package_name,
						   android_package_name_len);

	/* Create NFC NDEF URI Record description */
	NFC_NDEF_URI_RECORD_DESC_DEF(nrf_universal_link_rec,
				     NFC_URI_TEL,
				     universal_link,
				     universal_link_len);

4.3 NFC启动高德地图

需要更改的地方与4.2基本相同,但是这里在高德开放平台上找到一个URI,将其添加到程序中对应m_url_map

FpOkzuolwsrwfX0jXChmFMm41ywA

高德地图URI APP

高德地图URI Web

代码片段如下:

static const uint8_t android_pkg_name_map[] = {
	'c', 'o', 'm', '.', 'a', 'u', 't', 'o', 'n', 'a', 'v', 'i', '.', 'm', 'i', 'n', 'i','m','a','p'};

static const uint8_t m_url_map[] = "androidamap://navi?sourceApplication=amap&poiname=fangheng&lat=36.547901&lon=104.258354&dev=1&style=2";
err = nfc_launchapp_msg_encode(android_pkg_name_map,
				       sizeof(android_pkg_name_map),
				       m_url_map,
				       sizeof(m_url_map),
				       m_ndef_msg_buf,
				       &len);

五、功能展示

功能展示 4.1

Fmdgp_ICmiqWCpiyaeF9pVFX2yvq

板卡复位后,通过手机端的NFC功能可以读取出板上tag中保留的三条record信息

Fh1RzoVrYpZs6UEWLvZxyQq5IwLl

同时,当手机靠近板上的tag,串口也会有对应的信息输出。

功能展示 4.2

这里不知道为什么,明明添加的是电话的pkg_name,靠近nfc tag时,总是会唤醒手机上的google商店,很奇怪。先把pkg_name设为NULL,只使用URI实现。效果如下:

FkQwFU6F0H1aytfmlsYSQxk7_vRj

如果没有将URI类型设置为NFC_URI_TEL,手机只会把“12315”识别为文本。

功能展示 4.3

靠近nfc tag会启动手机上的高德地图APP,并跳转到导航页面。

FjYtbL4P2FeXATvxKoEEmBTKHyxP

六、心得体会

非常感谢硬禾和得捷提供的大厂板卡学习(白嫖)机会,这块板子的可玩性非常多,特别是关于zephyr的应用,基本上大部分例程都用到了,有时候多看些大厂提供的代码示例也会开拓许多思维。这次应该不是股东了!不过这些大厂的板卡有时候搭建环境很麻烦,windows下从github拉源码总是拉取不完,一直编译失败(已经开启科学上网了),换到linux下拉源码就很顺利。

附件下载
code.c
main
团队介绍
21st
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号