一.项目介绍
本项目使用Funpack2-6活动的开发板,来自Nordic的nRF7002DK。
本项目制作的是活动预设的任务三:“使用板卡的NFC功能,模拟出一个自定义功能的卡片,使用手机靠近并能读取卡片信息”。
我在该题目的基础上增加了按键控制NFC切换卡片、启动停止NFC的功能。
板卡上电默认处于NFC文本模式且关闭NFC使能。button1用于切换NFC工作模式(文本、打开B站个人主页),button2用于启动/停止NFC工作,LED1用于指示NFC是否正在通讯,LED2用于指示NFC是否正在工作
二.设计思路
按照活动任务三的要求,板卡上电后用手机靠近,能读取到NFC信息即为完成。
那么首先第一步就是需要搭建开发环境,我参考了Nordic的官方文档(https://www.nordicsemi.cn/tools/nrfconnectsdk/)、硬禾学堂的直播课程(https://class.eetree.cn/live_pc/l_647d47a9e4b0cf39e6d3d397)以及网上其他人分享的教程
搭建完成开发环境后跑官方的NFC Demo,一共有7个,官方SDK也有这些demo的说明(https://developer.nordicsemi.com/nRF_Connect_SDK/doc/2.4.1/nrf/samples/nfc.html),让我可以更快的熟悉Demo,跑完所有Demo后就开始阅读代码和各个函数的说明,理解其中的运行逻辑和函数各个参数的含义。
其实运行完Demo就基本可以完成任务要求了,但是我还是希望在当前板卡硬件的基础上增加一些内容,于是我使用了板卡自带的2个button和LED灯。用于实现NFC切换卡片、启动停止NFC、LED指示NFC是否处于运行状态。
现在我需要做的就是学习一下如何在现有工程中如何添加GPIO以及GPIO相关函数如何使用。由于没有找到现成的demo,于是我把所有的历程代码都导入vscode,直接查询哪些demo使用了按键和LED,参考他们的代码。再使用到自己的工程中
三.代码解析
本项目的代码主要有以下几个文件main.c、nfc_openApp.c、nfc_openApp.h、nfc_text.c、nfc_text.h。其中main.c存放main函数和切换NFC、启停NFC的代码。我将NFC的具体业务放到了nfc_xxx中。
首先 我们来看一下main函数,流程图如下
int main(void)
{
int ret = -1;
/* 初始化LED灯 */
if (dk_leds_init() < 0) {
printk("Cannot init LEDs!\n");
goto fail;
}
/* 初始化按钮 */
if (dk_buttons_init(button_handler)) {
printk("Cannot init buttons\n");
goto fail;
}
return 0;
fail:
#if CONFIG_REBOOT
sys_reboot(SYS_REBOOT_COLD);
#endif /* CONFIG_REBOOT */
return -EIO;
}
mian对LED和button做了初始化,如果任一初始化失败,系统将重启。在初始化button时注册了回调函数button_handler
static void button_handler(uint32_t button_state, uint32_t has_changed)
{
int ret = -1;
uint32_t button = button_state & has_changed;
if (button & DK_BTN1_MSK)
{
if (nfcMode == NFC_MODE_TEXT)
{
printk("switch nfc mode to open app\r\n");
nfcMode = NFC_MODE_OPEN_APP;
}
else
{
printk("switch nfc mode to text\r\n");
nfcMode = NFC_MODE_TEXT;
}
}
if (button & DK_BTN2_MSK)
{
if (nfcWorkState == true)
{
ret = nfc_t2t_emulation_stop();
if (ret != 0)
{
printk("nfc_t2t_emulation_stop ret = %d\r\n", ret);
return;
}
ret = nfc_t2t_done();
if (ret != 0)
{
printk("nfc_t2t_done ret = %d\r\n", ret);
return;
}
printk("nfc deinit success\r\n");
nfcWorkState = false;
dk_set_led_off(DK_LED2);
}
else
{
switch (nfcMode)
{
case NFC_MODE_TEXT:
/* 设置NFC为一个文本 */
ret = set_nfc_as_text_record();
if (ret == -1)
{
printk("set_nfc_as_text_record fail\r\n");
}
break;
case NFC_MODE_OPEN_APP:
/* 设置NFC为打开一个APP */
ret = set_nfc_as_open_app();
if (ret == -1)
{
printk("set_nfc_as_open_app fail\r\n");
}
break;
}
printk("nfc start work\r\n");
nfcWorkState = true;
dk_set_led_on(DK_LED2);
}
}
}
/**
* @brief 设置NFC为一个文本
* @param[in] none
* @return err 0:正常;-1:异常
*/
int set_nfc_as_text_record()
{
uint32_t len = sizeof(temp_ndef_msg_buffer);
/* 设置NFC为T2T,注册回调 */
if (nfc_t2t_setup(nfc_callback, NULL) < 0) {
printk("Cannot setup NFC T2T library!\n");
return -1;
}
/* 根据文本信息,组装成NDEF格式数据 */
if (text_msg_encode(temp_ndef_msg_buffer, &len) < 0) {
printk("Cannot encode message!\n");
return -1;
}
/* 将组装好的NEDF数据载入NFC payload */
if (nfc_t2t_payload_set(temp_ndef_msg_buffer, len) < 0) {
printk("Cannot set payload!\n");
return -1;
}
/* 启动NFC */
if (nfc_t2t_emulation_start() < 0) {
printk("Cannot start emulation!\n");
return -1;
}
printk("NFC configuration done\n");
return 0;
}
设置一个NFC大致分为以下几步,
1.初始化NFC模式,我这边使用的T2T,还有T4T等,他们有什么区别我这边就不赘述了,有兴趣的可以上网学习一下。
2.组装NDEF数据,什么是NDEF数据,有兴趣的也可以去自学一下,不过不理解也没关系,官方SDK都有对应的函数,我们可以参考demo直接使用
3.将刚才组装好的数据加载到NFC
4.启动NFC
在初始化NFC模式时,还要注册一个回调函数nfc_callback,当NFC有事件时可以进入回调函数
/**
* @brief NFC回调函数
* @param[in] context:回调执行的应用程序上下文
* @param[in] event: 是什么事件触发的回调
* @param[in] data: 要发送到app的数据(特定于事件)
* @param[in] data_length: data的长度
* @return err 0:正常;-1:异常
*/
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) {
/* 检测到有外部NFC */
case NFC_T2T_EVENT_FIELD_ON:
/* 点亮LED1 */
dk_set_led_on(DK_LED1);
break;
/* 检测到外部NFC已移除 */
case NFC_T2T_EVENT_FIELD_OFF:
/* 熄灭LED1 */
dk_set_led_off(DK_LED1);
break;
default:
break;
}
}
这里的回调函数我没有修改 ,直接复用demo的,实现的功能是当有NFC设备于开发板通讯时,LED1亮起,结束通讯时,LED1熄灭
组装一个文本NDEF函数如下
/**
* @brief 组装文本数据为NDEF数据
* @param[out] buffer:存放组装好的NDEF数据
* @param[out] len:组装好的NDEF数据的长度
* @return err 0:正常;-1:异常
*/
static int text_msg_encode(uint8_t *buffer, uint32_t *len)
{
int err;
/* 创建一个 NFC NDEF 文本 */
NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_text_rec,
UTF_8,
text_code,
sizeof(text_code),
text_payload,
sizeof(text_payload));
/* 创建 NFC NDEF 描述 */
NFC_NDEF_MSG_DEF(nfc_text_msg, MAX_REC_COUNT);
/* 新增一条文本信息到NFC NDEF信息中 */
err = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_text_msg),
&NFC_NDEF_TEXT_RECORD_DESC(nfc_text_rec));
if (err < 0) {
printk("Cannot add first 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;
}
这里的函数基本参考demo中的代码,将一条文本信息组装成NDEF数据
如何组装打开APP的NDEF数据我就不再赘述了,见附件代码即可,大同小异
四.功能展示
LED2亮起,指示NFC正在工作中
手机扫描FNC读取文本信息
停止NFC,切换NFC为“打开B站个人主页”模式
手机扫描NFC,打开B站个人主页
五.心得体会
这是我第一次学习使用nordic的新SDK,之前在52832上学习BLE时还是用的Keil环境,也有卖家写好的全套教程,按部就班的学。这次活动让我自己摸索,看官方文档,找其他人的分享,自己一步一步,从环境搭建,到编译成功,到实现功能,其中踩了很多坑,也学习到了很多新的东西,这一个多月的自学时光,让我收获颇丰。非常感谢硬禾学堂举办的funpack活动,让大家免费玩到各种各样的开发板。