Funpack2-6的基于Nordic7002DK的NFC的按键选择APP唤醒
基于Nordic7002DK的板卡完成了任务三,使用vscode插件+nrf的sdk环境进行编程,使用板卡上的NFC卡片,在其中存储信息,同时配置了两个button按键,以led2监测按键状态,从而实现多个app的可选择唤醒。
标签
嵌入式系统
Funpack活动
LED
按键控制
NFC
Nordic
ZHAO
更新2023-10-10
南京邮电大学
652

一、项目描述

1、项目介绍

本次参加的是funpack第二季第六期的活动,所实现的是任务三,一个基于基于Nordic7002DK板卡的,使用nrf sdk和vscode环境进行设计的简易NFC信息读取和APP唤醒功能。使用板卡上自带的NFC存储APP的包信息,使用手机读取其信息,并且使用按键去更改其中存储的包信息,从而实现唤醒不同app。

2、设计思路

本次设计思路比较的简单,其实就是一个简单的板卡自带的NFC的信息使用。

  1. 首先是硬件配置,项目依赖于板卡上的NFC硬件,所以首先需要进行硬件初始
  2. 化和配置,才能保证NFC功能的正常配置,保证后续的正常使用。
  3. 然后是NFC协议支持,使用Zephyr操作系统和Nordic Semiconductor提供的NFC库,该项目支持NFC Type 2 Tag协议(NFC_T2T),这是一种常见的NFC通信协议,然后这里使用官方例程里面的那个设置去操作进行初始化即可。
  4. 这里把NFC的初始化放在主函数的开始,再在while里面根据按键的值进行不同的编码信息选择。
  5. 先简单说一下nfc卡片存储信息的一个过程,具体为:初始化成功后,在while里面进行消息的编码,编码后把信息设置为负载,然后进入模拟启动。
  6. 然后是NDEF消息编码,代码里面涉及NFC数据交换格式(NDEF)消息的编码,在代码初始的定义的时候,要确保一些基本的参数,比如存储的文本内容,存储的编码格式,比如中文的在后续的使用中,UTF8实现不了,去查阅资料和测试之后,最终选择了UTF16,这样子就可以正常显示中文了,同时多种语言的方式都不一样,虽然大部分基于UTF8,但是需要根据需求去确定这些参数。
  7. 同时在while循环中,因为是多个APP的唤醒,板卡自带的两个按键需要进行编码操作,这里对两个按键分别赋予+1和-1的功能,同时设定一个CNT计数,从初始化的1开始,根据两个按键的变化获得不同的取值,同时在其中加入保险,如按键按下时若超出了5个app的数量,则返回到最高的值,规避按下按键时出现两次变换的情况。
  8. 数据准备好之后,就是NFC事件处理,首先是NFC事件处理函数,也就是nfc_callback,这里按照官方例程来,以便在检测到NFC场时控制板卡上的LED状态,当NFC接触感应时,相应的LED灯会被点亮。
  9. 代码的关键部分在于CNT提供的选择,在进入选择部分,也就是编码信息部分,这里有两个点要注意,第一个点是多个包名无法共享一个数组,若设置为一个,则会默认选择最开始的,因此要多个数组分别存储编码信息。第二个点为,在每次编码的初始化时,若不先关闭NFC,则会导致payload部分的失败,也就是说无法更新选择的编码信息,所以每一次进行编码时,都需要先停止,待编码完毕后再度开启NFC。
  10. 由于数据数量多,若使用短延时会导致payload部分速度太快卡住,所以循环延时改为1s,而且考虑到1s的按键延时会导致按键出现按下没反应的问题,所以添加一个警示灯,用于随时监测按键是否成功按下。

3、硬件介绍

  • 首先这个板卡可以说是诚意满满(个人感觉蛤),首先两个nrf5340加上一个7002,这个配置这个价位,我觉得挺不错的,再加上各司其职,主控,蓝牙,jlink调试,然后还有这个一个 Wi-Fi 双频段天线和一个低功耗蓝牙天线,这个也很有意思。
  • 然后这个套件有nRF5340和7002,主要就是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和三个按键,当然其中一个是reset。
  • 其中5340主要就是,高性能128MHz Arm Cortex-M33应用内核和超低功耗64MHz Arm Cortex-M33网络内核。然后7002是一个Wi-Fi配套IC,可为主控制器提供Wi-Fi连接功能
  • 然后这里主要讲讲这次活动,或者说是任务三用到的,其实也就是一个NFC的接口,然后主控的5340,还有一个led,基本就没了,但是这个板卡资源还是很多的,可以开发挺多东西的。
  • 两个5340和一个7002,这里微距大概拍了一下,还要一个关键的NFC

FoGpuVOGjyMNxIpF-kH_aRI5RXcqFtFg7jjBYScE56zPIfmVRLUOVW29

FlHezctOSnrChfsYUGXrO43tFc-VFnnc3ew-gGGKV8nL7rVLA9N7z7Mo

二、软件流程图及各功能对应的主要代码片段及说明

1、流程图

FttEGvUL9RzPXScbBniMS4b1H9es

  • 说明
  • 程序开始执行 main 函数。
  • main 函数调用 button_init 函数来初始化按钮和LED。
  • 在 button_init 函数中,首先检查按钮1和按钮2是否准备就绪。这是通过 gpio_is_ready_dt 函数来检查的。如果有任何一个按钮未准备就绪,将打印错误消息并返回。
  • 然后,配置按钮1和按钮2的引脚为输入,并配置它们的中断触发方式为边缘触发。这是通过 gpio_pin_configure_dt 和 gpio_pin_interrupt_configure_dt 函数来实现的。同时,初始化按钮1和按钮2的回调函数,并将它们与相应的GPIO引脚关联。
  • 如果LED2(led2)准备就绪(可通过 device_is_ready 函数检查),则配置LED2的引脚为输出,以便可以控制LED2的状态。
  • 然后,调用 nfc_init 函数来初始化NFC。
  • 进入无限循环 (while(1)),程序将不断重复以下步骤:
  • 检测按钮1和按钮2的状态,通过 gpio_pin_get_dt 函数获取按钮状态。
  • 如果按钮1按下(val1等于1),则增加计数器 cnt 的值,并根据计数器的值选择不同的NFC消息进行编码和模拟。如果 cnt 大于等于6,则将其设置为5,并打印 "outtttt!" 消息。同时,点亮LED2。
  • 如果按钮2按下(val2等于1),则减少计数器 cnt 的值,并根据计数器的值选择不同的NFC消息进行编码和模拟。如果 cnt 等于0,则将其设置为1,并打印 "lesssss!" 消息。同时,点亮LED2。
  • 每次循环都会在NFC消息编码和模拟之前停止当前的NFC模拟,这是通过 nfc_t2t_emulation_stop 函数来实现的,并在选择了新的NFC消息后重新开始模拟,这是通过 nfc_t2t_emulation_start 函数来实现的。
  • 打印当前的 cnt 值,以显示当前的操作状态。
  • 循环结束后,延时1S,并关闭LED2。
  • 回到无限循环的开头,继续检测按钮状态并处理NFC消息。

2、代码片段说明

  • 主函数
int main(void)
{
//cnt是计数,设置为1是默认第一个APP的包信息,在后续循环中可以变动
//两个初始化函数,一个是按键的初始化,一个是nfc的初始化配置。
	int cnt=1;
	button_init();
	int err;
	nfc_init();

while(1)
{
//两个val存放button的值,若button按下,则为1
	int val1 = gpio_pin_get_dt(&button1);
    int val2 = gpio_pin_get_dt(&button2);
	
	if(val1 == 1)
	{
    	cnt = cnt + 1;//若第一个按键按下,则+1
			if(cnt>=6)//保险,因为只有五个包信息,为了确保不超出,即为按键按下时可能变成两个,所以加个限制
			{
				cnt=5;
				printk("outtttt!\n");
			}
    	        printk("button1 open,cnt = %d\n", cnt);//确认CNT的值
                gpio_pin_set_dt(&led2, 1);
	}

	if(val2 == 1)
	{
    	cnt = cnt - 1;//同理button1
			if(cnt<=0)
			{
				cnt=1;
				printk("lesssss!\n");
			}
		        printk("button2 open,cnt = %d\n", cnt);
                gpio_pin_set_dt(&led2, 1);
	}

	size_t len;
    err = 0; // 用于在while循环中重置错误状态
	if(cnt ==1)
	{
		nfc_t2t_emulation_stop();//先封闭,也就是先停止模拟
		size_t len = sizeof(ndef_msg_buf1);//获取包信息的大小
		err = nfc_launchapp_msg_encode(android_pkg_name1,
				    sizeof(android_pkg_name1),
				    NULL,
				    0,
				    ndef_msg_buf1,
				    &len);  // 编码启动应用程序消息	
						err = nfc_t2t_payload_set(ndef_msg_buf1, len);  // 设置 NFC 负载
						err = nfc_t2t_emulation_start();  // 开始模拟 NFC Type 2 Tag
	}
	if(cnt ==2)
	{
		nfc_t2t_emulation_stop();
		size_t len = sizeof(ndef_msg_buf2);
		err = nfc_launchapp_msg_encode(android_pkg_name2,
				    sizeof(android_pkg_name2),
				    NULL,
				    0,
				    ndef_msg_buf2,
				    &len);  // 编码启动应用程序消息	
					err = nfc_t2t_payload_set(ndef_msg_buf2, len);  // 设置 NFC 负载
					err = nfc_t2t_emulation_start();  // 开始模拟 NFC Type 2 Tag
	}	
	if(cnt ==3)
	{
		nfc_t2t_emulation_stop();
		size_t len = sizeof(ndef_msg_buf3);
		err = nfc_launchapp_msg_encode(android_pkg_name3,
				    sizeof(android_pkg_name3),
				    NULL,
				    0,
				    ndef_msg_buf3,
				    &len);
					err = nfc_t2t_payload_set(ndef_msg_buf3, len);  // 设置 NFC 负载
					err = nfc_t2t_emulation_start();  // 开始模拟 NFC Type 2 Tag
	}	
	if(cnt ==4)
	{
		nfc_t2t_emulation_stop();
		size_t len = sizeof(ndef_msg_buf4);
		err = nfc_launchapp_msg_encode(android_pkg_name4,
				    sizeof(android_pkg_name4),
				    NULL,
				    0,
				    ndef_msg_buf4,
				    &len);  // 编码启动应用程序消息	
					err = nfc_t2t_payload_set(ndef_msg_buf4, len);  // 设置 NFC 负载
					err = nfc_t2t_emulation_start();  // 开始模拟 NFC Type 2 Tag
	}
	if(cnt ==5)
	{
		nfc_t2t_emulation_stop();
		size_t len = sizeof(ndef_msg_buf5);
		err = nfc_launchapp_msg_encode(android_pkg_name5,
				    sizeof(android_pkg_name5),
				    NULL,
				    0,
				    ndef_msg_buf5,
				    &len);  // 编码启动应用程序消息	
					err = nfc_t2t_payload_set(ndef_msg_buf5, len);  // 设置 NFC 负载
					err = nfc_t2t_emulation_start();  // 开始模拟 NFC Type 2 Tag
	}		

	printk("cnt = %d\n", cnt);//时刻检测CNT的值,也就是第几个包信息的反馈

	printk("NFC configuration done\n");  // 打印配置完成消息
	k_msleep(SLEEP_TIME_MS);//延时1s
	gpio_pin_set_dt(&led2, 0);
//前面的定义里面有#define SLEEP_TIME_MS	1000
}
	
	return 0;  // 返回 0,表示成功


fail:
#if CONFIG_REBOOT
	sys_reboot(SYS_REBOOT_COLD);  // 冷启动系统
#endif /* CONFIG_REBOOT */

	return err;  // 返回错误码
}
  • 按键button的初始化,放在botton_init函数里面
void button_init()
{
	int button_Set;

	if (!gpio_is_ready_dt(&button1) || !gpio_is_ready_dt(&button2)) {
    	printk("Error: button device is not ready\n");
    	return 0;
	}


	button_Set = gpio_pin_configure_dt(&button1, GPIO_INPUT);
	if (button_Set != 0) {
		printk("Error %d: failed to configure %s pin %d\n",
		       button_Set, button1.port->name, button1.pin);
		return 0;
	}
	button_Set = gpio_pin_configure_dt(&button2, GPIO_INPUT);
	if (button_Set != 0) {
    	printk("Error %d: failed to configure %s pin %d\n",
           button_Set, button2.port->name, button2.pin);
    	return 0;
	}

	button_Set = gpio_pin_interrupt_configure_dt(&button1,
					      GPIO_INT_EDGE_TO_ACTIVE);
	if (button_Set != 0) {
		printk("Error %d: failed to configure interrupt on %s pin %d\n",
			button_Set, button1.port->name, button1.pin);
		return 0;
	}
	button_Set = gpio_pin_interrupt_configure_dt(&button2, GPIO_INT_EDGE_TO_ACTIVE);
	if (button_Set != 0) {
    	printk("Error %d: failed to configure interrupt on %s pin %d\n",
        	button_Set, button2.port->name, button2.pin);
	    return 0;
	}

	gpio_init_callback(&button_cb_data, button_pressed, BIT(button1.pin));
	gpio_add_callback(button1.port, &button_cb_data);
	printk("Set up button at %s pin %d\n", button1.port->name, button1.pin);


	gpio_init_callback(&button2_cb_data, button_pressed, BIT(button2.pin));
	gpio_add_callback(button2.port, &button2_cb_data);
	printk("Set up button2 at %s pin %d\n", button2.port->name, button2.pin);
		
	if (led2.port && !device_is_ready(led2.port)) {
		printk("Error %d: LED device %s is not ready; ignoring it\n",
		       button_Set, led2.port->name);
		led2.port = NULL;
	}
	if (led2.port) {
		button_Set = gpio_pin_configure_dt(&led2, GPIO_OUTPUT);
		if (button_Set != 0) {
			printk("Error %d: failed to configure LED device %s pin %d\n",
			       button_Set, led2.port->name, led2.pin);
			led2.port = NULL;
		} else {
			printk("Set up LED at %s pin %d\n", led2.port->name, led2.pin);
		}
	}
	printk("Press the button\n");
}
  • void nfc_init()//NFC的初始化
void nfc_init()
{
	int err;
	printk("Starting NFC Launch app example\n"); 
	err = dk_leds_init();
	if (err) {
		printk("Cannot init LEDs!\n");
		return 0;
	}
	err = nfc_t2t_setup(nfc_callback, NULL);//初始化
	if (err) {
		printk("Cannot setup NFC T2T library!\n");  
		return 0; 
	}
	if (err) {
		printk("Cannot encode message!\n");
		return 0;
	}
}
  • APP的信息包的配置和相应的存储数组
// packname1
static const uint8_t android_pkg_name1[] = {
	't', 'v', '.',
	'd', 'a', 'n', 'm', 'a', 'k', 'u','.',
	'b', 'i', 'l', 'i'};

//packname2
static const uint8_t android_pkg_name2[] = {
	'c', 'o', 'm', '.',
	'm', 'i', 'H', 'o', 'Y', 'o', '.',
	'h', 'k', 'r', 'p', 'g'};

//packname3
static const uint8_t android_pkg_name3[] = {
	'c', 'o', 'm', '.',
	'm', 'i', 'u', 'i', '.',
	'g', 'a', 'l', 'l', 'e','r','y'};

//packname4
static const uint8_t android_pkg_name4[] = {
	'c', 'o', 'm', '.',
	't', 'e', 'n','c', 'e','n','t','.',
	'm','m'};

//packname5
static const uint8_t android_pkg_name5[] = {
	'c', 'o', 'm', '.',
	't', 'e', 'n','c', 'e','n','t','.',
	't','i','m'};	
//static uint8_t ndef_msg_buf[NDEF_MSG_BUF_SIZE];
static uint8_t ndef_msg_buf1[NDEF_MSG_BUF_SIZE];
static uint8_t ndef_msg_buf2[NDEF_MSG_BUF_SIZE];
static uint8_t ndef_msg_buf3[NDEF_MSG_BUF_SIZE];
static uint8_t ndef_msg_buf4[NDEF_MSG_BUF_SIZE];
static uint8_t ndef_msg_buf5[NDEF_MSG_BUF_SIZE];
  • 两个按钮的配置
/*两个按钮的初始化配置*/
#define SW0_NODE	DT_ALIAS(sw0)
#if !DT_NODE_HAS_STATUS(SW0_NODE, okay)
#error "Unsupported board: sw0 devicetree alias is not defined"
#endif
static const struct gpio_dt_spec button1 = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios,
							      {0});
static struct gpio_callback button_cb_data;

#define SW1_NODE DT_ALIAS(sw1)
#if !DT_NODE_HAS_STATUS(SW1_NODE, okay)
#error "Unsupported board: sw1 devicetree alias is not defined"
#endif

static const struct gpio_dt_spec button2 = GPIO_DT_SPEC_GET_OR(SW1_NODE, gpios,
                                                             {0});
static struct gpio_callback button2_cb_data;

三、功能展示及说明

  1. 首先把手机接触NFC,可以看到会弹出FiS6wB8OsyjM8JmjeojyBJLcJQbY
  2. 然后按下按钮1,此时串口调试助手中,CNT变为2,且APP也切换FiWpJ9V4-jetia5BXECbpdkZdyvr
  3. 然后按下按钮1,循环此过程到5,下图分别为CNT3、4、5时的APPFgkg1GW-3bB0XlA8nXDjbuek937yFsg0QniRXCE6cPGtb0nARWqLTJEWFs2wbFjC_h4f205mrsR9DkcKUpAV
  4. 在CNT溢出时,串口调试助手会反馈且把cnt控制到5FifSY0BvgE4IMDEH24k5wffCv3uu
  5. 按下按钮2则是反过来实现选择功能,不展示全部,如图3到2跳转FqgPXYdUz59ZZHdmhD_H6-ldWsU0
  6. CNT小于0时反馈为1FpahVU7JmPUdMh0hGyhY8LFSh7We
  7. 按下按钮时,LED2亮灯表示监测状态FrB_ae7ypb8XlhK0pUbnt0UNV8I5

四、对本活动的心得体会

  1. 本次活动有两点需要注意,第一点是这个NFC存储数据的时候,也就是编码的时候,需要先关闭模拟,我的理解是,第一次编码+模拟之后,卡片被占用了,要想重新编码,按照指定顺序的话,需要先关闭,再重新启动,所以在每一个if选语句中加了先关闭,编码再重启的步骤
  2. 第二个点是buf资源的共享,编码之后存放数据的buf不能是同一个,经过测试后发现会默认存储第一个,这个暂时不知道原因,只发现了现象,所以对应的解决方法就是采用多数组分别存储数据。
  3. 然后是环境,这一次比较麻烦的是环境,Zephyr RTOS这个东西我头一次接触,然后不知道为什么环境搭建有问题,west文件夹拉不到数据,这个环境调了两三天吧,一开始要么就是空文件夹,要么就是sdk没有,要么就是toolchain没有,手动操作可能是个人问题,反正挺麻烦的,后面有一篇文章,博主分享了资源,移植一下就成功了,后续的vscode开发倒是没什么问题。
  4. 接着是官方资源,这一次活动代码难度不高,是因为官方给的资料全,NFC其中可以说是有很多原理还是要深究的,我这一次也只是使用了官方库,做了一些简单的功能,官方文档和资料,例程都是很好用的。
  5. 最后感谢群里面的大佬在环境配置初期的帮忙,这一次群里面佬挺多的,交流了很多技术,学到了不少东西,比如蓝牙键鼠的usbhid,还有一些wifi的知识都学了不少,然后还有一些环境和代码架构的知识也都看了个七七八八,毕竟都是看群友大概聊天,没怎么细看,总之还是扩展了不少见识。
  6. 更新:本案例中没用文本去编码,但是一样的,调用文本编码的库,然后在while循环的cnt选择里面添加,再把其中的编码换成文本的就行了。

五、环境配置的补充说明

  1. 最后补充一下环境配置的解决方案,首先打开这个链接:开发你的第一个nRF Connect SDK(NCS)/Zephyr应用程序 - iini - 博客园 (cnblogs.com)
  2. 去里面下载那个百度网盘,然后下载的时候,确保你在nrfconnect里面已经进行到了这一步。FqTkAvEmijY1YC23Q8OqtsDXVmaM
  3. 然后确认你的文件夹路径里面是不是空的,一般在nrF\v2.4.2\.west这个路径,如果环境有问题,多半是空的west,其实正常情况里面有个文件,然后可能还没有其他文件夹,这里只需要把解压的那个文件,全部替换在里面,然后如图。FlOeoUVgHjyFh_L9SI13XzfvkMtj
  4. 这个时候去nrf工具里面更新一下sdk就可以了,基本上就ok了。
  5. 如何查找应用APP的信息,这边以小米的手机为例,先长按应用,然后右上角,点开后,第三张图的圈即为APP包信息,这个就是我们需要存进数组里的。FhRrN0Er3QdyCdFkmvMxezY4_ywJFuOonfuAjyusvaAIuZzrwPtdpzWdFlLOJqwG67OzOSc9pLFcUYIbzYe7
  6. 官方组件自带的vscode的设备树的功能的大概说明,以button和led为例,在设备树里面可以像cubemx一样自定义很多设置,如这里配置的button引脚的1.08,也就是第三张图的原理图里面的按键1对应引脚。Fuv1OJzN51p19wlx59PZiIpG3fSoFpVC4d1rmkviG5sWZcUBzu94E9EzFpdTnSx6Z3oZZnKt62IsU_Z12r91

五、可编译下载的代码

放在附件中

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