板卡介绍:
nRF7002 DK 是用于 nRF7002 Wi-Fi 6 协同 IC 的开发套件,它包含了在单板上启动开发工作所需的一切。这款开发板带有一个 nRF5340 多协议系统级芯片(SoC),用作 nRF7002 的主处理器。
板载的nRF5340拥有两个Arm® Cortex®-M33处理器,一个为具有FPU和DSP指令的128 MHz Arm Cortex-M33作为高性能应用处理器,一个64 MHz Arm Cortex-M33作为网络处理器。
Arm® Cortex®-M33内核采用Armv8-M架构,采用TrustZone技术,具有相对Arm® Cortex®-M3更好的性能和安全性。
该 DK 支持低功耗 Wi-Fi 应用的开发,并实现多项 Wi-Fi 6 功能,如 OFDMA、波束成形和目标唤醒时间 (TWT)。并具有一个开拆卸的NFC天线用于进行nfc开发。
开发环境Zephyr是一个开源的实时操作系统(RTOS),它专为连接和安全的未来设备而设计。支持多种硬件架构,包括 ARC、ARM、RISC-V 和 X86。Zephyr OS 是当今产品中使用的经过验证的 RTOS。Zephyr OS是模块化的,支持多种架构,开发人员可以轻松定制最佳解决方案来满足他们的需求。Zephyr OS 应用广泛,从简单的连接传感器到复杂的边缘系统。
得益于这些优良硬软件的配合,我实现了对该板卡的快速开发。
任务选择:
因为对nordic板子和zepyhr不是很熟悉,我选择了任务二使用板卡的NFC功能,模拟出一个自定义功能的卡片,使用手机靠近并能读取卡片信息。可以显示文本信息或打卡指定应用。
任务实现:
对nrf系列芯片开发,依赖于nRF Connect SDK 库,Zephyr 库等。本任务使用sdk-nrfxlib,nRF Connect SDK libraries中的NDEF messages,Text records,同时也使用zephyr库中的一些设备树和gpio接口。
使用nrf系列芯片,首先需要配置nrf connect sdk环境。
首先需要配置环境,下载nrf connect for desktop
图1
随后选择toolchain下载nrf connect sdk下载环境
图2
选择最新版本下载完成后,打卡。ncs基于vs code编辑器。
本项目主要用到本开发的nrf5430的主要处理器核心,较少的用到其他计算性能。
本项目思路主要集中于NFC的调用和写入,我采用NFC T2T库,将NDEF文本记录作为荷载提供与nfc使用。中文文本采用utf8编码格式,我选择输出电子森林四个字,他们的编码是0xE7, 0x94, 0xB5, 0xE5, 0xAD, 0x90, 0xE6, 0xA3, 0xAE, 0xE6, 0x9E, 0x97。
启动应用同理,NFC NDEF传入的消息不同。我这里启用nrf toolbox。
NDEF (NFC Data Exchange Format) 是NFC通信中使用的标准数据格式。
NDEF记录的主要格式有:
- TNF (Type Name Format): 表示记录的类型,例如 URI、MIME 类型、外部类型等。
- Payload: 存储数据的部分,例如文本、URL、二进制数据等。
- Type: 描述有效载荷的格式或意图,例如 "T" 代表文本,"U" 代表 URI 等。
- ID: 可选字段,为记录提供一个唯一标识符。
编辑完程序后,可以直接使用Devicetree board file对设备树进行编辑,十分优雅。例如设置led灯,串口通信十分方便。经过翻看PCB layout,我大概了解了本板子的组成和功能分区。
代码解读:
static const uint8_t zh_payload[] = {
0xE7, 0x94, 0xB5, 0xE5, 0xAD, 0x90, 0xE6, 0xA3, 0xAE, 0xE6, 0x9E, 0x97,'e', 'e', 't', 'r', 'e', 'e'
};
使用一个uint8_t存储我需要的字符串 电子森林eetree 保存为utf8编码格式
我使用了一个简单的python程序进行处理(人生苦短我用python)
# 输入要转换的字符
input_char = input("请输入字符串: ")
# 将字符转换为UTF-8编码
utf8_bytes = input_char.encode('utf-8')
# 将UTF-8编码转换为16进制表示形式
hex_representation = ', '.join(['0x{:02X}'.format(b) for b in utf8_bytes])
# 输出UTF-8编码的16进制表示形式
print("UTF-8编码的16进制表示形式:", hex_representation)
因为我使用的是utf8编码 所以更改NFC NDEF 消息的缓冲区的宏定义,以适应中文编码
NDEF消息包括以下两个主要部分记录和消息头
NFC_NDEF_TEXT_RECORD_DESC_DEF(nfc_zh_text_rec,
UTF_8,
zh_code,
sizeof(zh_code),
zh_payload,
sizeof(zh_payload));
如果是调用软件则是使用这个常量
/* Package: no.nordicsemi.android.nrftoolbox */
static const uint8_t android_pkg_name[] = {
'n', 'o', '.', 'n', 'o', 'r', 'd', 'i', 'c', 's', 'e', 'm', 'i', '.', 'a', 'n', 'd', 'r',
'o', 'i', 'd', '.', 'n', 'r', 'f', 't', 'o', 'o', 'l', 'b', 'o', 'x' };
def convert_string(s):
return [char for char in s]
package_name = "no.nordicsemi.android.nrftoolbox"
formatted_string = convert_string(package_name)
print(formatted_string)
/* Package: no.nordicsemi.android.nrftoolbox */
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'};
/* Configure LED-pins as outpts */
err = dk_leds_init();
if (err) {
printk("Cannot init LEDs!\n");
goto fail;
}
/* Set up NFC */
err = nfc_t2t_setup(nfc_callback, NULL);
if (err) {
printk("Cannot setup NFC T2T library!\n");
goto fail;
}
/* Encode launch app data */
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");
goto fail;
}
/* Set created message as the NFC payload */
err = nfc_t2t_payload_set(ndef_msg_buf, len);
if (err) {
printk("Cannot set payload!\n");
goto fail;
}
/* Start sensing NFC field */
err = nfc_t2t_emulation_start();
if (err) {
printk("Cannot start emulation!\n");
goto fail;
}
printk("NFC configuration done\n");
return 0;
调用dk库和nfc_t2t_lib进行初始化,构造一个回调函数,通过不断判断event,确定nfc的状态,改变led的状态,确保用户能感知到时候在使用nfc。
// 定义一个用于存储NFC NDEF消息的缓冲区。其大小由NDEF_MSG_BUF_SIZE决定。
static uint8_t ndef_msg_buf[NDEF_MSG_BUF_SIZE];
// 此函数是NFC Type 2 Tag(T2T)事件的回调函数。
// 参数:- context: 指向可能在回调中使用的用户数据的指针。- event: 已触发的NFC T2T事件。- data: 指向与NFC事件相关的数据的指针。- data_length: 数据的长度。
static void nfc_callback(void *context,
nfc_t2t_event_t event,
const uint8_t *data,
size_t data_length)
{
// 这些宏函数确保在此函数中未使用提供的参数。
ARG_UNUSED(context); // 标记context为未使用的参数。
ARG_UNUSED(data); // 标记data为未使用的参数。
ARG_UNUSED(data_length); // 标记data_length为未使用的参数。
// switch语句用于处理不同的NFC事件。
switch (event) {
// 当检测到NFC场或打开时的事件。
case NFC_T2T_EVENT_FIELD_ON:
dk_set_led_on(NFC_FIELD_LED); // 打开与NFC_FIELD关联的LED。
break;
// 当NFC场失去或关闭时的事件。
case NFC_T2T_EVENT_FIELD_OFF:
dk_set_led_off(NFC_FIELD_LED); // 关闭与NFC_FIELD关联的LED。
break;
// 默认情况下用于其他未特别处理的事件。
default:
break;
}
}
/* 创建 NFC NDEF 消息描述,容量为 MAX_REC_COUNT 条记录 */
NFC_NDEF_MSG_DEF(nfc_text_msg, MAX_REC_COUNT);
/* 将文本记录添加到 NDEF 文本消息中 */
err = nfc_ndef_msg_record_add(&NFC_NDEF_MSG(nfc_text_msg),
&NFC_NDEF_TEXT_RECORD_DESC(nfc_zh_text_rec));
if (err < 0) {
printk("Cannot add record!\n");
return err;
}
配置设备树 对于nrf7002dk系列开发板 较为新颖的一个特性就是其设备树功能。
我在代码中不需要定义具体引脚,而是用一个诸如LED 0的名称代替,再统一在设备树中进行更改,如果我更改了硬件的引脚,我只需要简单的调整设备树即可。
图 设备树
逻辑框图:
成果展示:
图3 串口回调,确保成功打开nfc和设备树
图4 模拟nfc卡片显示文本
心得体会:
本次是第一次参与funpack活动,自评任务难度是适中的,适合第一次接触zepyter的同学。我这次也是第一次使用digikey购买电子元器件,拥有不错的体验,除了没预估好美国仓库发货的时间。Nordic为nrf7002dk_nrf5430提供的工具链很不错,开箱即用,demo丰富,图形化配置设备树(太优雅了)。