内容介绍
内容介绍
UWB开发板项目实践分享
项目背景
原本的任务是用UWB技术,根据手机的方位信息来改变开发板上RGB LED灯的颜色和亮度。但在开发过程中,我发现芯片不支持AOA(到达角)功能,导致无法获取手机角度信息,原任务没法完成。后来,我调整了任务目标:用屏幕显示手机到UWB开发板的距离,并根据距离信息改变开发板上LED灯的状态。
硬件介绍
- SSD1306 OLED 0.96 寸屏幕 :通过I2C通讯,用于显示距离信息。
- DWM3001CDK 开发板 :基于Qorvo DW3110 IC的全集成UWB模块开发套件,能和Apple U1、U2芯片一起用。
- iPhone 11 :支持UWB功能的手机,用来和开发板配合。
软件流程及核心代码

初始化与任务创建
在主函数里,我初始化了软i2c、log输出、UWB模块等,还创建了FreeRTOS任务和队列。为啥要创建队列和OLED显示任务呢?因为如果在蓝牙的回调函数里更新屏幕显示,软i2c更新时间会影响蓝牙通讯,导致蓝牙断开连接。
/**@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(10, sizeof(int));
xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
// Start FreeRTOS scheduler.
osKernelStart();
for (;;) {
APP_ERROR_HANDLER(NRF_ERROR_FORBIDDEN);
}
}
OLED显示任务
这个任务从队列里接收数据,然后把距离信息刷新到屏幕上,这样就不会干扰蓝牙协议了。
void vTask1(void *pvParameters) {
char *buff = "AppInit: Initializing application...\n";
// 应用程序启动时的初始化逻辑
reporter_instance.print(buff, strlen(buff));
Testing_Display();
while (1) {
int received_value;
if (xQueueReceive(xQueue, &received_value, portMAX_DELAY) == pdPASS) {
Testing_Display5(received_value);
}
vTaskDelay(pdMS_TO_TICKS(200));
}
}
蓝牙回调函数
拿到手机与开发板之间的距离信息后,我对数据进行归一化处理,根据不同的距离区间控制不同的LED灯点亮或熄灭。
///定义灯的状态
int led_states[8][4] = {
{0, 1, 1, 1},
{0, 1, 1, 1},
{0, 0, 1, 1},
{0, 0, 1, 1},
{0, 0, 0, 1},
{0, 0, 0, 1},
{0, 0, 0, 0},
{0, 0, 0, 0}
};
// 定义每个条件对应的LED状态
void set_leds(const int* states) {
for (int i = 0; i < 4; i++) {
if (states[i]) {
bsp_board_led_on(i);
} else {
bsp_board_led_off(i);
}
}
}
/*
* @brief Gets the ranging results and print it
* */
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)); // 发送数据到队列
int condition = distance_cm / 10;
// 检查条件是否在范围内
if (condition >= 0 && condition < 8) {
set_leds(led_states[condition]); ///设置灯
} else {
// 默认情况,开启所有LED
for (int i = 0; i < 4; i++) {
bsp_board_led_on(i);
}
}
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);
}
功能展示:连接蓝牙根据返回距离信息点亮不同的led灯
- :返回距离为0的时候,点亮是所有LED灯-
- 距离小于20大于10的时候,点亮是三个LED灯:
- 大于30的时候点亮两个LED:
- 大于40的时候,所有LED都不点亮
项目中的难题与解决方法
- AOA功能问题 :一开始我不清楚开发板没开启AOA信息,后来还是在朋友的王哥帮助下,才找到AOA的初始化信息。因为他踩过坑了,所以问了下他,他说他踩过坑,所以知道了AOA的初始化信息
- I2C屏幕刷新问题 :在使用屏幕显示的时候,用过彩屏和OLED,SSD1306没反应,最后没法想到是硬I2C 用的不对,只能用软I2C。
- 蓝牙的数据回调:找了很久才找到蓝牙的数据回调方法在哪里。
心得体会
这次活动让我非常感谢主办方提供的宝贵机会,让我得以接触到DWM3001CDK板卡,并亲身体验UWB技术的魅力。这是我第一次接触UWB(超宽带)定位技术,而这次实践让我对它的潜力有了更深刻的认识。尤其是测距的精准度,远超我的预期,误差几乎可以忽略不计,UWB之前一直都在苹果AirTag上有而已,这让我对UWB技术的实际应用充满期待。最后,再次感谢活动方提供的这次宝贵机会,让我能够在实践中学习和成长。未来,我期待UWB技术的进一步普及
附件下载
QANI-All-FreeRTOS_QNI_3_0_0.zip
QANI-All-FreeRTOS_QNI_3_0_0.z06
QANI-All-FreeRTOS_QNI_3_0_0.z05
QANI-All-FreeRTOS_QNI_3_0_0.z04
QANI-All-FreeRTOS_QNI_3_0_0.z03
QANI-All-FreeRTOS_QNI_3_0_0.z02
QANI-All-FreeRTOS_QNI_3_0_0.z01
团队介绍
个人
评论
0 / 100
查看更多
猜你喜欢
Funpack4-1_DWM3001CDK_基于UWB的手机距离测算基于DWM3001CDK评估板实现的通过UWB协议与手机距离测算,并将其通过带屏十二指神探(RP2040)展示出来
vic
27
Funpack4-1 - 用DWM3001CDK实现的UWB距离测算和控制该项目使用了DWM3001CDK,实现了UWB远程测距的设计,它的主要功能为:使用UWB和Iphone11的UWB功能进行测距, 通过不同的距离改变LED灯的颜色,并且将距离信息显示在屏幕上。
Wang Chong
14
Funpack4-1:基于 DWM3001CDK 实现手机距离测算该项目使用了DWM3001CDK,实现了通过超宽带(UWB)技术实现对智能手机相对于开发板的距离测算的设计,它的主要功能为:测距。
topgear
31