Funpack4-1 - 用DWM3001CDK实现的UWB距离测算和控制
该项目使用了DWM3001CDK,实现了UWB远程测距的设计,它的主要功能为:使用UWB和Iphone11的UWB功能进行测距, 通过不同的距离改变LED灯的颜色,并且将距离信息显示在屏幕上。
标签
嵌入式系统
Funpack活动
开发板
Wang Chong
更新2025-04-08
7

项目描述

当前的任务为自定义任务, 其基础任务是基于任务二。任务二要求使用UWB测算手机的方位,并且根据距离和角度改变板子上RGB LED灯的颜色和亮度, 但是在实际的开发过程中(基于官方提供的nearby interaction SDK)在开发板初始化过程中AOA功能被禁用掉了, 原因是当前的芯片并不支持AOA功能。 AOA功能是获取手机角度信息的重要数据, 因此即无法完成根据距离和角度改变板子上RGB LED等的颜色和亮度的功能。 如若在本任务基础上完成,需要写IOS的程序来解析Apple芯片的UWB数据中的AOA信息然后将数据发送给UWB开发板然后由UWB开发板对数据进行解析才可以完成上述任务。 由于个人水平和时间有限, 在工作人员的指导下决定将当前的任务改为自定义任务, 即使用屏幕显示手机端到UWB开发板的距离,并且根据距离信息来改变开发板上LED灯的状态。

简短的硬件介绍

  • SSD1306 OLED0.92寸屏幕: 采用I2C通讯的显示模块
  • DWM3001CDK开发板:Qorvo 的 DWM3001CDK 是为基于 DW3110 IC 的 DWM3001C 全集成 (UWB) 模块开发的设计套件,可与 Apple U1 和 U2 芯片互操作。DWM3001C 是一款基于Qorvo DW3110 IC的全集成 UWB 收发模块。
  • iphone 11 : 其支持UWB功能的手机即可.


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

未命名绘图.png

核心代码展示


OLED的初始化和队列的创建


/**@brief Function for application main entry.
*/
int main(void) {
// Initialize modules.
clock_init();
reporter_instance.init();

hal_uwb.mcu_sleep_config();

#if NRF_LOG_ENABLED
init_logger_thread();
#endif

// Accessory Nearby Interaction Initialization
niq_init(ResumeUwbTasks,
StopUwbTask,
(const void *)nrf_crypto_init,
(const void *)nrf_crypto_uninit,
(const void *)nrf_crypto_rng_vector_generate);

// Accessory instructed to act as a Responder or Initiator
niq_set_ranging_role(ACCESSORY_RANGING_ROLE);

// Create Default task: it responsible for WDT and starting of "applications"
AppConfigInit(); /**< load the RAM Configuration parameters from NVM block */

// Start BLE
char advertising_name[32];

snprintf(advertising_name,
sizeof(advertising_name),
"%s (%08X)",
(char *)BoardName,
(unsigned int)NRF_FICR->DEVICEADDR[0]);
ble_init(advertising_name);

EventManagerInit();
BoardInit();



if (uwb_init() != DWT_SUCCESS) {
APP_ERROR_HANDLER(NRF_ERROR_RESOURCES);
}
DefaultTaskInit();

// Driver version is available after probing of the DW chip
const char ver[] = FULL_VERSION;
const char *drv_ver = dwt_version_string();
const char *mac_ver = uwbmac_get_version();

char str[256];
int sz;

sz = sprintf(str, "Application: %s\r\n", ApplicationName);
sz += sprintf(&str[sz], "BOARD: %s\r\n", BoardName);
sz += sprintf(&str[sz], "OS: %s\r\n", OsName);
sz += sprintf(&str[sz], "Version: %s\r\n", ver);
sz += sprintf(&str[sz], "%s\r\n", drv_ver);
sz += sprintf(&str[sz], "MAC: %s\r\n", mac_ver);
sz += sprintf(&str[sz], "ACCESSORY_RANGING_ROLE: %s\r\n", (ACCESSORY_RANGING_ROLE) ? "Initiator" : "Responder");
reporter_instance.print(str, sz);

I2C_Init();
OLED_Init();

xQueue = xQueueCreate(5, sizeof(int));
xTaskCreate(oled_display_task, "oled_display_task", 512, NULL, 5, NULL);

// Start FreeRTOS scheduler.
osKernelStart();

for (;;) {
APP_ERROR_HANDLER(NRF_ERROR_FORBIDDEN);
}
}


这里创建队列和OLED显示task的主要原因是因为如果在蓝牙的回调函数里面来更新屏幕的显示的话,由于I2C更新需要一定的时间,所以它会影响到蓝牙的通讯。从而造成蓝牙的断开连接。


OLED display Task

void oled_display_task(void *pvParameters) {
while (1) {
int received_value;
if (xQueueReceive(xQueue, &received_value, portMAX_DELAY) == pdPASS) {
OLED_Fill(0, 128, 0, 64, 0);
OLED_ShowNum(50, 16, received_value, 5, 16);
OLED_RefreshGram();
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}

OLED display task的主要作用就是接收来自消息队列的数据,然后将距离信息刷新到屏幕上, 这样就避免了干扰蓝牙协议的情况出现。


蓝牙回调函数

static void report_cb(const struct ranging_results *results, void *user_data) {
static bool notify = true;

int len = 0;
int distance_cm;
struct string_measurement *str_result = (struct string_measurement *)user_data;
struct ranging_measurements *rm;
fira_param_t *fira_param = get_fira_config();

if (results->stopped_reason != 0xFF)
return;

session_id = fira_param->session_id;

len = sprintf(str_result->str, "{\"Block\":%" PRIu32 ", \"results\":[", results->block_index);

for (int i = 0; i < results->n_measurements; i++) {
if (i > 0) {
len += snprintf(&str_result->str[len], str_result->len - len, ",");
}

rm = (struct ranging_measurements *)(&results->measurements[i]);

len += snprintf(&str_result->str[len],
str_result->len - len,
"{\"Addr\":\"0x%04x\",\"Status\":\"%s\"",
rm->short_addr,
(rm->status) ? ("Err") : ("Ok"));

if (rm->status == 0) {
distance_cm = (int)rm->distance_mm / 10;
xQueueSend(xQueue, &distance_cm, pdMS_TO_TICKS(5)); // 发送数据到队列

switch (distance_cm / 10) { // 归一化到 10 的倍数
case 0: // 0-10 cm
case 1:
bsp_board_led_on(0);
bsp_board_led_on(1);
bsp_board_led_on(2);
bsp_board_led_on(3);


break;

case 2: // 11-30 cm
case 3:

bsp_board_led_on(0);
bsp_board_led_on(1);
bsp_board_led_on(2);
bsp_board_led_off(3);
break;

case 4: // 31-50 cm
case 5:
bsp_board_led_on(0);
bsp_board_led_on(1);
bsp_board_led_off(2);
bsp_board_led_off(3);
break;

case 6: // 51-70 cm
case 7:
bsp_board_led_on(0);
bsp_board_led_off(1);
bsp_board_led_off(2);
bsp_board_led_off(3);
break;

default: // 71 cm 以上
bsp_board_led_off(0);
bsp_board_led_off(1);
bsp_board_led_off(2);
bsp_board_led_off(3);
break;
}

len += snprintf(&str_result->str[len], str_result->len - len, ",\"D_cm\":%d", (int)rm->distance_mm / 10);

if (hal_uwb.is_aoa() == AOA_ENABLED) {
len += snprintf(&str_result->str[len],
str_result->len - len,
",\"LPDoA_deg\":%0.2f,\"LAoA_deg\":%0.2f,\"LFoM\":%d,\"RAoA_deg\":%0.2f",
convert_aoa_2pi_q16_to_deg(rm->local_aoa_measurements[0].pdoa_2pi),
convert_aoa_2pi_q16_to_deg(rm->local_aoa_measurements[0].aoa_2pi),
rm->local_aoa_measurements[0].aoa_fom,
convert_aoa_2pi_q16_to_deg(rm->remote_aoa_azimuth_2pi));
}

len += snprintf(
&str_result->str[len], str_result->len - len, ",\"CFO_100ppm\":%d", (int)fira_uwb_mcps_get_cfo_ppm());

// Take action based on distance
if (notify && (distance_cm < MIN_CM_DISTANCE_THRESHOLD)) {
#if LEDS_NUMBER > 1
// Visual indication on the device for "inside the bubble"
bsp_board_led_on(BSP_BOARD_LED_2); // Red LED On
bsp_board_led_off(BSP_BOARD_LED_0); // Green LED Off
#endif

// Send message to trigger notification on the iOS side
send_ios_notification((uint8_t)distance_cm, "You are in the secure bubble.");
notify = false;
} else if (!notify && (distance_cm > MAX_CM_DISTANCE_THRESHOLD)) {
#if LEDS_NUMBER > 1
// Visual indication on the device for "outside the bubble"
bsp_board_led_off(BSP_BOARD_LED_2); // Red LED Off
bsp_board_led_on(BSP_BOARD_LED_0); // Green LED On
#endif

// Send message to trigger notification on the iOS side
send_ios_notification((uint8_t)distance_cm, "You are out of the secure bubble.");
notify = true;
}
}
len += snprintf(&str_result->str[len], str_result->len - len, "}");
}

len += snprintf(&str_result->str[len], str_result->len - len, "]");

/* Display RSSI, CFO and NLOS */
if (fira_uwb_is_diag_enabled()) {
len = fira_uwb_add_diag(str_result->str, len, str_result->len);
}

len += snprintf(&str_result->str[len], str_result->len - len, "}\r\n");
reporter_instance.print((char *)str_result->str, len);
}

蓝牙回调函数比较长, 当我们拿到手机到开发板之间的距离信息之后。 对数据进行归一化处理,使其距离信息可以在一个switch区间中,从而避免了大量的IF else判断。 然后根据不同的区间来操作不同的LED灯点亮或者熄灭。


功能展示图片及说明

image.png

上图为开发板刚连接到手机上的状态, 其距离信息为13CM, 对应的代码为所有的LED灯全部点亮

 bsp_board_led_on(0);
bsp_board_led_on(1);
bsp_board_led_on(2);
bsp_board_led_on(3);


Snipaste_2025-03-21_23-23-51.png

上图为距离为30CM-50CM的状态, 对应代码如下所示

bsp_board_led_on(0);
bsp_board_led_on(1);
bsp_board_led_off(2);
bsp_board_led_off(3);


Snipaste_2025-03-21_23-27-06.png

上图为大于80CM的状态, 所有的LED全部关闭,对应的代码如下所示

 bsp_board_led_off(0);
bsp_board_led_off(1);
bsp_board_led_off(2);
bsp_board_led_off(3);


项目中遇到的难题和解决方法

项目中主要是遇见了两个难题, 第一个难题就是就AOA功能是否开启的代码trace, 刚开始并不清楚这个开发板没有开启AOA信息,但是一直在用AOA信息做调试,也没有找到为什么AOA功能被关闭了。 后来通过多次的代码trace才找到了AOA的初始化信息。第二个难题就是在移植硬件I2C的时候I2C刷新屏幕的时候SSD1306没有响应的问题, 这个问题至今还没有得到一个好的解决, 最终在妖哥(我的一位朋友的帮助下)使用了软件的I2C来驱动了Oled屏幕。 最终完成了本次项目的制作。


对本活动的心得体会

在这次活动中,我尝试过很多种方法来完成其中的某一项任务。 在最初的项目想法的时候是做NFC刷卡来进行识别点亮LED灯的。 但是我对那个SDK研究之后实在没有一点头绪, 而且手里也并没有支持NFC的设备(卡,或者手机等)。 所以我尝试使用这快开发板来驱动屏幕和传感器等来实现蓝牙传输的功能。 我的微信小程序都已经写好了,并且是nearby interaction的应用程序中已经调试成功了同时可以成功的接收到来自开发板的数据。 但是任务的要求还需要驱动屏幕进行显示。 我在GITHUB上找了很多的SSD1306的驱动并且使用固件中的SDK函数进行替换对应的时序操作, 同时使用逻辑分析仪来对时序进行分析。 其中有一个完成度非常高的驱动, 我在调试的时候所有的时序都和驱动中的数据匹配, 但是在屏幕刷新的时候。 不知道因为什么原因就NCK没有响应了。 后来我便尝试来做根据角度和距离来点亮LED灯的任务,也是研究了两天左右。 经过多次调试才发现板载并不支持AOA数据, 因此没办法在开发板方向解析AOA数据, 所以任务二是没办法完成的。除非在对应的手机端来写一个APP然后接收手机芯片的AOA信息然后通过蓝牙信息发送给开发板再由开发板进行解析和控制。 但是本人没有学习过IOS端的APP开发。 因此在咨询了群内的工作人员ZDS,在他的指导下于是决定来做自定义任务,即不解析AOA信息,使用LED屏幕显示距离信息。 并且控制LED灯的行为。 于是我便动手开始完成这个项目了。 屏幕的驱动部分是在朋友的帮助下移植完成的软件I2C从而完成了对数据进行的显示。 虽然目前现在功能都也实现了,但是那个硬件I2C不刷新的问题还是困扰我到现在。最后感谢电子森林提供的UWB板卡的试用机会, 这块板卡采用的模组好像也是很流行, 我在安信可b站演示的视频上也看到了使用同样的APP和同个公司的模组进行的循迹延时,就是不知道它支不支持AOA了

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