Funpack4-1 基于NFC控制的LED效果灯
该项目使用了Qorvo 的 DWM3001CDK,实现了基于NFC控制的LED效果灯的设计,它的主要功能为:使用刷卡设备通过NFC协议控制LED实现不同灯效。
标签
Funpack活动
NFC
NCS
DWM3001CDK
Funpack4-1
LED效果灯
nRF52833
枫雪天
更新2025-04-08
13

任务介绍

    本项目实现了Funpack第4-1期活动Qorvo板卡的任务一,使用了DWM3001CDK开发板实现了基于NFC控制的LED效果灯的设计,支持使用刷卡设备通过NFC协议控制LED实现不同灯效

硬件平台

    首先介绍本次用到的开发板:DWM3001CDK,它是射频方案商Qorvo推出一块基于Nordic nRF52833微控制器的UWB开发板,它主要面向的是UWB定位应用的开发,但由于使用了nRF52833这一片低功耗蓝牙SoC,所以也同时支持蓝牙、NFC等常用功能。开发板分为底板和核心邮票板,底板引出了大量引脚,并集成了常用的外设,如按键、LED、串口、NFC、USB接口和调试器接口。在软件方面,Nordic官方和第三方平台已经适配好了Arduino、nRF5 SDK、NCS等多种开发框架,上手开发会很方便。本次我会使用这块开发板的NFC外设,做一个基于NFC控制的LED效果灯

主控设备Qorvo DWM3001CDK开发板

    • 搭载Nordic nRF52833低功耗蓝牙SoC
    • 集成NFC Type 2标签功能
    • 板载4个可编程LED指示灯
    • 支持多线程实时操作系统(Zephyr RTOS)

控制终端:

  • 支持NFC-A协议的智能手机/读写器
  • ISO/IEC 14443 Type A标准兼容
  • 数据传输速率:106kbps

任务分析与实现

本项目实现了通过NFC协议控制LED灯效的智能交互系统,主要功能包括:

  1. 五种灯效模式
    • 模式0:全灭状态(低功耗模式)
    • 模式1:顺序流水(LED2→LED3→LED4)
    • 模式2:逆序流水(LED4→LED3→LED2)
    • 模式3:三灯同步快闪(200ms周期)
    • 模式4:三灯同步慢闪(1000ms周期)
  2. 双控制通道
    • NFC无线控制:通过NDEF消息'A'-'D'选择模式1-4
    • 物理按钮控制:板载按键循环切换0-4模式
  3. 持久化存储
    • Flash存储最后生效的NDEF消息
    • 长按按钮恢复默认灯效配置


方案框图:

代码详解

整体软件流程图:

本次项目涉及到NFC数据解析、按键与LED控制几个关键部分,接下来结合相关代码来进行讲解


一、多线程架构

K_THREAD_DEFINE(led_tid, LED_STACK_SIZE,  // LED控制线程(优先级-2)
led_control_thread, NULL, NULL, NULL,
LED_PRIORITY, 0, 0);

K_THREAD_DEFINE(button_tid, BUTTON_STACK_SIZE, // 按钮检测线程(优先级-1)
button_thread, NULL, NULL, NULL,
BUTTON_PRIORITY, 0, 0);
  • 主线程:处理NFC协议栈和闪存操作
  • 独立线程1:LED灯光效果控制
  • 独立线程2按钮状态检测

二、NFC数据解析

功能点

  • 快速提取NDEF消息Payload
  • HEX数据打印辅助调试
  • 原子操作保证模式切换的线程安全


手机向nRF52833模拟的NFC设备写入数据,此时会触发 NFC_T4T_EVENT_NDEF_UPDATED 事件,我们在该事件中识别手机写入的数据,转化为本地控制指令。

1. 数据格式

我们先看一看上位机写入数据后,下位机得到的原始数据是怎样的。这里我们分四次向nRF52833写入了'A'、'B'

'C'、'D'四种字符。将它们打印出来:

printk("Raw Data (Hex): ");
for (int i = 0; i < data_length+2; i++) {
    printk("%02X ", data[i]);
}
printk("\n");

image.png

可以初步得到如下的数据

NLEN        | NDEF Record
00 08 | D1 01 06 54 02 7A 68 41
( 8 bytes) | ▲ ▲ ▲ ▲ ▲ ▲ ▲
│ │ │ │ │ │ └─ 数据只有'A'0x41
│ │ │ │ │ └─ 语言码"zh"
│ │ │ │ └─ 状态字节
│ │ │ └─ 类型'T'
│ │ └─ 标称载荷长度6字节
│ └─ 类型长度1字节
└─ NDEF Header

可知当写入单字符时,最后一个字符就是我们写入的Payload。nRF52的NFC库提供了完善的NDEF数据解析函数。但这里场景限定且单一,我们可以写一个最简单的解析方法:

int target_mode = -1;
/* 指令映射 */
switch (data[9]) {
    case 'A': target_mode = 1; break; // LED_SEQ_FORWARD
    case 'B': target_mode = 2; break; // LED_SEQ_BACKWARD
    case 'C': target_mode = 3; break; // LED_SYNC_FAST
    case 'D': target_mode = 4; break; // LED_SYNC_SLOW
    default:  target_mode = 0; break; // LED_OFF
}

/* 控制LED */
if (target_mode != -1) {
    atomic_set(&current_mode, target_mode);
    printk("NFC command '%c' -> Mode %d\n", data[9], target_mode);
}

三、LED控制线程

1. 设备树设定

DWM3001CDK开发板上有一排4个可控的LED。

我们选择其中一个作为NFC传输指示灯,另外三个作为效果灯。

image.png

使用宏定义和枚举在程序中定义:

#define LED_NUM 3
#define LED1 DK_LED2
#define LED2 DK_LED3
#define LED3 DK_LED4
#define MODE_COUNT 5  // 五种模式(含关闭)


/* 全局变量 */
static enum {
    LED_OFF,          // 模式0:关闭
    LED_SEQ_FORWARD,  // 模式1:顺序流水
    LED_SEQ_BACKWARD, // 模式2:逆序流水
    LED_SYNC_FAST,    // 模式3:快速同步
    LED_SYNC_SLOW     // 模式4:慢速同步
} led_mode = LED_OFF;

2. 灯效控制线程

按照题目要求,我们共设计了四种灯光效果,包含关闭,就是共5种状态:

void led_control_thread(void *arg1, void *arg2, void *arg3)
{
    ARG_UNUSED(arg1);
    ARG_UNUSED(arg2);
    ARG_UNUSED(arg3);
   
    int last_mode = -1;
    bool led_state = false;
    int64_t last_toggle_time = 0;
   
    while (1) {
        int current = atomic_get(&current_mode);
       
        // 模式变化时重置状态
        if (current != last_mode) {
            dk_set_leds(0);
            last_toggle_time = 0;
            last_mode = current;
        }
       
        switch (current) {
        case LED_OFF:
            dk_set_leds(0);
            k_sleep(K_MSEC(100));  // 低功耗等待
            break;
           
        case LED_SEQ_FORWARD:
            dk_set_led(LED1, (current_led == 0));
            dk_set_led(LED2, (current_led == 1));
            dk_set_led(LED3, (current_led == 2));
            current_led = (current_led + 1) % 3;
            k_sleep(K_MSEC(200));
            break;
           
        case LED_SEQ_BACKWARD:
            dk_set_led(LED3, (current_led == 0));
            dk_set_led(LED2, (current_led == 1));
            dk_set_led(LED1, (current_led == 2));
            current_led = (current_led + 1) % 3;
            k_sleep(K_MSEC(200));
            break;
           
        case LED_SYNC_FAST:
            if (k_uptime_get() - last_toggle_time >= 200) {
                led_state = !led_state;
                dk_set_leds(led_state ? (BIT(LED1)|BIT(LED2)|BIT(LED3)) : 0);
                last_toggle_time = k_uptime_get();
            }
            k_sleep(K_MSEC(10));
            break;
           
        case LED_SYNC_SLOW:
            if (k_uptime_get() - last_toggle_time >= 1000) {
                led_state = !led_state;
                dk_set_leds(led_state ? (BIT(LED1)|BIT(LED2)|BIT(LED3)) : 0);
                last_toggle_time = k_uptime_get();
            }
            k_sleep(K_MSEC(10));
            break;
        }
    }
}

三、按键控制线程

1、状态流转

  • 初始状态:LED_OFF(全灭)
  • 转换条件:每次按钮按下触发模式切换
  • 状态特征
    • LED_SEQ_FORWARD:顺序流水(LED2→3→4)
    • LED_SEQ_BACKWARD:逆序流水(LED4→3→2)
    • LED_SYNC_FAST:三灯同步快闪(200ms)
    • LED_SYNC_SLOW:三灯同步慢闪(1000ms)
  • 循环机制:模式4后回到模式0(LED_OFF)

2、按键扫描与控制

nRF52的SDK为我们实现好了按键读取的函数 dk_read_buttons,可以直接使用,为了不影响主线程代码,我们可以单独起一个按键线程,读取按键并控制LED。

void button_thread(void *arg1, void *arg2, void *arg3) 
{
    ARG_UNUSED(arg1);
    ARG_UNUSED(arg2);
    ARG_UNUSED(arg3);
   
    uint32_t last_btn_state = 0;
   
    while (1) {
        uint32_t btn_state;
        dk_read_buttons(&btn_state, NULL);
       
        if ((btn_state & DK_BTN1_MSK) &&
           !(last_btn_state & DK_BTN1_MSK)) {
            int new_mode = (atomic_get(&current_mode) + 1) % MODE_COUNT;
            atomic_set(&current_mode, new_mode);
            printk("Mode changed to: %d\n", new_mode);
        }
       
        last_btn_state = btn_state;
        k_sleep(K_MSEC(50));
    }
}

效果展示

手机控制页面

灯效1:顺序流水灯

灯效2:逆序流水灯

灯效3:同步快闪

灯效4:同步慢闪

遇到的难题与解决办法

问题:NDEF数据包解析

​ 我们获取到的NDEF数据是经过协议编码的,Payload只是其中的一部分。nRF SDK提供了一个库来提供解析功能,但示例很少。

解法

编写一套完善的、稳定的NDEF协议解析代码是不必要的,因为我们的使用场景限定且单一,我们可以写一个最简单的解析方法,直接从RAW数据的特定位置中提取我们想要的字节,这样的解析效率也是最高。

活动感想

通过本项目实践,进一步掌握了Zephyr多线程开发、NFC协议栈集成等关键技术。Qorvo的DWM3001CDK开发套件使用了成熟的nRF52主控方案,使用Nordic完善的NFC库支持与简洁易用的开发环境极大简化了开发流程。期待未来能在物联网设备交互设计领域继续深耕,开发出更多创新应用。

    感谢硬禾学堂和得捷电子联合举办的Funpack活动,祝硬禾的活动越办越好!

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