### Basic CMake Project Setup Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/lib/qmath/utest/CMakeLists.txt Initializes a CMake project, specifying the minimum required version and project name. Ensure the project is run from the correct directory to find necessary tools. ```cmake cmake_minimum_required(VERSION 3.13) project(test_qmath) ``` -------------------------------- ### Initialize DW3000 Driver with dwt_probe and dwt_initialise Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Probes and selects the correct driver for the detected chip, then initializes the device by loading calibration values from OTP memory. Requires platform-specific SPI implementation and external driver references. ```c #include "dw3000.h" // External driver references extern const struct dwt_driver_s dw3000_driver; extern const struct dwt_driver_s dw3720_driver; // Driver list for auto-detection static struct dwt_driver_s *driver_list[] = { (struct dwt_driver_s *)&dw3000_driver, (struct dwt_driver_s *)&dw3720_driver, NULL }; // SPI interface structure (platform-specific implementation) extern struct dwt_spi_s dw3000_spi; int init_dw3000_driver(void) { // Setup probe interface struct dwt_probe_s probe = { .dw = NULL, // Use internal dwchip structure .spi = &dw3000_spi, // Platform-specific SPI implementation .wakeup_device_with_io = dw3000_hw_wakeup, .driver_list = driver_list, .dw_driver_num = 2 }; // Probe and select appropriate driver int ret = dwt_probe(&probe); if (ret != DWT_SUCCESS) { LOG_ERR("dwt_probe failed"); return DWT_ERROR; } // Initialize device with OTP loading ret = dwt_initialise(DWT_READ_OTP_PID | DWT_READ_OTP_LID | DWT_READ_OTP_BAT | DWT_READ_OTP_TMP); if (ret != DWT_SUCCESS) { LOG_ERR("dwt_initialise failed"); return DWT_ERROR; } // Read device information uint32_t part_id = dwt_getpartid(); uint64_t lot_id = dwt_getlotid(); LOG_INF("Part ID: 0x%08X, Lot ID: 0x%016llX", part_id, lot_id); return DWT_SUCCESS; } ``` -------------------------------- ### Basic DW3000 Functionality Check Source: https://github.com/br101/dw3000-decadriver-source/blob/master/README.md Initialize the hardware, perform a reset, and read the device ID to verify basic DW3000 functionality. Ensure LOG_INF is defined. ```c dw3000_hw_init(); dw3000_hw_reset(); uint32_t dev_id = dwt_readdevid(); LOG_INF("DEVID %x", devid); ``` -------------------------------- ### Adding QMath Library and Executable Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/lib/qmath/utest/CMakeLists.txt Adds the QMath library and the main test executable to the build. Specifies source files for the executable. ```cmake add_subdirectory(.. qmath) add_executable(test_qmath test_qmath.cc ) ``` -------------------------------- ### Initialize DW3000 Hardware and Driver Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Initializes the DW3000 hardware and the Decadriver. Ensure correct GPIO configurations in sdkconfig. The driver automatically loads calibration values from OTP memory. ```c #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "dw3000.h" static const char *TAG = "DW3000"; void app_main(void) { ESP_LOGI(TAG, "DW3000 ESP-IDF Example"); // Initialize hardware if (dw3000_hw_init() != 0) { ESP_LOGE(TAG, "HW init failed"); return; } // Reset device dw3000_hw_reset(); vTaskDelay(pdMS_TO_TICKS(2)); // Wait for IDLE_RC while (!dwt_checkidlerc()) { vTaskDelay(pdMS_TO_TICKS(1)); } // Initialize driver if (dwt_initialise(DWT_READ_OTP_PID | DWT_READ_OTP_LID) != DWT_SUCCESS) { ESP_LOGE(TAG, "Driver init failed"); return; } ESP_LOGI(TAG, "Device ID: 0x%08lX", dwt_readdevid()); ESP_LOGI(TAG, "Driver: %s", dwt_version_string()); // Main loop while (1) { // Your ranging/communication logic here vTaskDelay(pdMS_TO_TICKS(1000)); } } ``` -------------------------------- ### Linking Libraries and Compiler Options Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/lib/qmath/utest/CMakeLists.txt Links the test executable against the QMath library and Google Mock, and sets common compiler warnings. ```cmake target_link_libraries(test_qmath PUBLIC qmath gmock_main) target_compile_options(test_qmath PUBLIC -Wall -Werror -Wextra) ``` -------------------------------- ### Configure DW3000 Interrupt Callbacks Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Sets up callback functions for TX and RX events and enables corresponding hardware interrupts. Requires a prior call to dw3000_hw_init_interrupt to complete the hardware configuration. ```c #include "dw3000.h" // Callback function prototypes static void tx_done_cb(const dwt_cb_data_t *cb_data); static void rx_ok_cb(const dwt_cb_data_t *cb_data); static void rx_timeout_cb(const dwt_cb_data_t *cb_data); static void rx_error_cb(const dwt_cb_data_t *cb_data); // Reception state static volatile bool frame_received = false; static uint8_t rx_buffer[128]; static uint16_t rx_frame_len; int setup_interrupt_callbacks(void) { // Setup callbacks structure dwt_callbacks_s callbacks = { .cbTxDone = tx_done_cb, .cbRxOk = rx_ok_cb, .cbRxTo = rx_timeout_cb, .cbRxErr = rx_error_cb, .cbSPIErr = NULL, .cbSPIRDErr = NULL, .cbSPIRdy = NULL, .cbDualSPIEv = NULL }; dwt_setcallbacks(&callbacks); // Enable interrupts dwt_setinterrupt( DWT_INT_TXFRS_BIT_MASK | // TX frame sent DWT_INT_RXFCG_BIT_MASK | // RX frame CRC good DWT_INT_RXFTO_BIT_MASK | // RX frame wait timeout DWT_INT_RXPTO_BIT_MASK | // Preamble timeout DWT_INT_RXPHE_BIT_MASK | // PHY header error DWT_INT_RXFCE_BIT_MASK | // RX CRC error DWT_INT_RXFSL_BIT_MASK | // RX sync loss DWT_INT_RXSTO_BIT_MASK, // SFD timeout 0, // High word interrupts DWT_ENABLE_INT ); // Initialize hardware interrupt dw3000_hw_init_interrupt(); LOG_INF("Interrupt callbacks configured"); return DWT_SUCCESS; } static void tx_done_cb(const dwt_cb_data_t *cb_data) { LOG_DBG("TX done"); } static void rx_ok_cb(const dwt_cb_data_t *cb_data) { rx_frame_len = cb_data->datalength; dwt_readrxdata(rx_buffer, rx_frame_len - 2, 0); frame_received = true; LOG_DBG("RX OK: %d bytes", rx_frame_len); } static void rx_timeout_cb(const dwt_cb_data_t *cb_data) { LOG_DBG("RX timeout"); } static void rx_error_cb(const dwt_cb_data_t *cb_data) { LOG_DBG("RX error: status=0x%08X", cb_data->status); dwt_forcetrxoff(); } ``` -------------------------------- ### CMake Build Configuration for UWB Driver Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/CMakeLists.txt This CMake script sets up the build for the UWB driver. It defines static and interface libraries, includes directories, and conditionally adds subdirectories for DW3000 and DW3720 devices. It also links against the qmath library. ```cmake cmake_minimum_required(VERSION 3.13.1) project(dw3xxx) add_library(uwb_driver_itf INTERFACE) target_include_directories(uwb_driver_itf INTERFACE .) add_library(uwb_driver STATIC deca_interface.c deca_compat.c deca_rsl.c) target_link_libraries(uwb_driver PUBLIC uwb_driver_itf ) if(DWT_DW3000) message("Building drivers for DW3000 devices") add_subdirectory(dw3000) target_link_libraries(uwb_driver PRIVATE dw3000_uwb_driver) endif() if(DWT_DW3720) message("Building drivers for DW3720 devices") add_subdirectory(dw3720) target_link_libraries(uwb_driver PRIVATE dw3720_uwb_driver) endif() add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/qmath) target_link_libraries(uwb_driver PRIVATE qmath) ``` -------------------------------- ### Add NRF SDK Source Files to Makefile Source: https://github.com/br101/dw3000-decadriver-source/blob/master/README.md Include the necessary DW3000 driver and platform-specific source files in your NRF SDK project's Makefile. Ensure correct include paths are set. ```makefile INCLUDES += -Ilib/decadriver/decadriver INCLUDES += -Ilib/decadriver/platform/nrf-sdk SRC += lib/decadriver/decadriver/deca_device.c SRC += lib/decadriver/platform/nrf-sdk/deca_port.c SRC += lib/decadriver/platform/nrf-sdk/dw3000_hw.c SRC += lib/decadriver/platform/nrf-sdk/dw3000_spi.c SRC += lib/decadriver/platform/nrf-sdk/dw3000_spi_trace.c ``` -------------------------------- ### Configure GPIO for Status LEDs Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Configures GPIO pins 2 and 3 for RX/TX status LEDs and enables them with an initial blink pattern. Ensure GPIO clocks are enabled before configuration. ```c #include "dw3000.h" int configure_leds(void) { // Enable GPIO clocks dwt_enablegpioclocks(); // Configure GPIO 2 and 3 for RX/TX LEDs dwt_setgpiomode( GPIO2_FUNC_MASK | GPIO3_FUNC_MASK, GPIO_PIN2_RXLED | GPIO_PIN3_TXLED ); // Enable LEDs with initial blink dwt_setleds(DWT_LEDS_ENABLE | DWT_LEDS_INIT_BLINK); LOG_INF("LEDs configured"); return DWT_SUCCESS; } ``` -------------------------------- ### Initialize DW3000 Hardware and Reset Chip Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Initializes the hardware abstraction layer and resets the DW3000 chip. Ensure the device is ready by checking the IDLE_RC state before proceeding. Reads the device ID to verify communication. ```c #include "dw3000.h" int main(void) { // Initialize hardware layer (SPI, GPIO pins) int ret = dw3000_hw_init(); if (ret != 0) { LOG_ERR("Hardware init failed: %d", ret); return ret; } // Reset the DW3000 chip dw3000_hw_reset(); // Wait for device to be ready (IDLE_RC state) while (!dwt_checkidlerc()) { // Device not ready yet } // Read device ID to verify communication uint32_t dev_id = dwt_readdevid(); LOG_INF("Device ID: 0x%08X", dev_id); // Expected: 0xDECA0302 (DW3000), 0xDECA0312 (DW3000 with PDOA) // 0xDECA0304 (QM33110), 0xDECA0314 (QM33120 with PDOA) return 0; } ``` -------------------------------- ### Defining a Test Case Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/lib/qmath/utest/CMakeLists.txt Defines a test case named 'test_qmath' that will execute the compiled test_qmath program. ```cmake add_test(NAME test_qmath COMMAND test_qmath) ``` -------------------------------- ### Configure Power Management and Sleep Modes Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Sets up low-power sleep modes, including oscillator calibration and wake-up triggers. Includes functions for entering deep sleep, waking up, and enabling auto-sleep after radio events. ```c #include "dw3000.h" int configure_sleep_mode(void) { // Calibrate low-power oscillator for accurate sleep timing uint16_t lp_osc_cal = dwt_calibratesleepcnt(); LOG_INF("LP OSC calibration: %d (freq = %.1f kHz)", lp_osc_cal, 38400.0f / lp_osc_cal); // Configure sleep count (wake-up time) // Sleep time = sleepcnt * lp_osc_period * 4096 // For 1 second with ~19kHz LP OSC: sleepcnt = 19000 / 4096 ~= 5 uint16_t sleep_cnt = 5; // Approximately 1 second dwt_configuresleepcnt(sleep_cnt); // Configure sleep mode uint16_t on_wake_mode = DWT_CONFIG | // Download AON config on wake DWT_LOADLDO | // Load LDO tune on wake DWT_LOADBIAS | // Load bias tune on wake DWT_LOADDGC | // Load DGC config on wake DWT_GOTOIDLE; // Go to IDLE state after wake uint8_t wake_mode = DWT_SLP_EN | // Enable sleep DWT_WAKE_CSN | // Wake on SPI chip select DWT_WAKE_WUP | // Wake on WAKEUP pin DWT_BROUT_EN; // Enable brownout detection dwt_configuresleep(on_wake_mode, wake_mode); LOG_INF("Sleep mode configured"); return DWT_SUCCESS; } int enter_deep_sleep(void) { // Clear AON configuration if needed dwt_clearaonconfig(); // Re-configure sleep parameters configure_sleep_mode(); // Enter sleep (0 = deep sleep, DWT_DW_IDLE_RC = stay in IDLE_RC after wake) dwt_entersleep(0); LOG_INF("Entered deep sleep"); return DWT_SUCCESS; } int wakeup_from_sleep(void) { // Wake up using hardware pin dw3000_hw_wakeup(); // Wait for device to be ready k_msleep(5); // Platform-specific delay // Check device is in IDLE_RC if (!dwt_checkidlerc()) { LOG_ERR("Device not ready after wakeup"); return DWT_ERROR; } // Restore configuration (calibrations, etc.) dwt_restoreconfig(1); // 1 = full restore including PGF cal LOG_INF("Wakeup complete"); return DWT_SUCCESS; } // Auto-sleep after TX/RX (for tag applications) void configure_auto_sleep(void) { configure_sleep_mode(); // Enable auto-sleep after TX dwt_entersleepafter(DWT_TX_COMPLETE); // Or after both TX and RX // dwt_entersleepafter(DWT_TX_COMPLETE | DWT_RX_COMPLETE); } ``` -------------------------------- ### CMake Build Configuration Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/utest/CMakeLists.txt Sets up the CMake build system for the DecaDriver project. Ensures necessary tools are available and configures build options. ```cmake cmake_minimum_required(VERSION 3.13) project(test_deca) ``` ```cmake if (NOT EXISTS ${PROJECT_SOURCE_DIR}/../../../tools/cmake) message(FATAL_ERROR "\n Unit tests and coverage uses tools from uwb-stack. \n You currently need to run this from uwb-stack/deps/dwt_uwb_driver. ") endif() ``` ```cmake set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../../tools/cmake) ``` ```cmake option(ENABLE_TEST_COVERAGE "Enable test coverage" OFF) ``` ```cmake add_subdirectory(../../../deps/googletest/googletest gtest EXCLUDE_FROM_ALL) ``` ```cmake set(DWT_DW3000 ON) ``` ```cmake add_subdirectory(.. uwb_driver) ``` ```cmake add_executable(utest src/test_rsl.cc src/test_tx_power.cc ) ``` ```cmake target_link_libraries(utest PUBLIC qmath gmock_main uwb_driver) ``` ```cmake target_compile_options(utest PUBLIC -Wall -Werror -Wextra) ``` ```cmake target_include_directories(utest PRIVATE ${PROJECT_SOURCE_DIR}/../dw3000) ``` ```cmake add_test(NAME utest COMMAND utest) ``` -------------------------------- ### CMake Build Commands for Unit Tests Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/utest/CMakeLists.txt Commands to build and run unit tests using CMake. Assumes a Debug build type and Ninja generator. ```bash # $ cmake -S utest -B ./build-san -G Ninja -DCMAKE_BUILD_TYPE=Debug # $ cmake --build ./build-san # $ ./build-san/utest ``` -------------------------------- ### Configure DW3000 Driver CMake Build Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/dw3000/CMakeLists.txt Defines the static library target, include paths, and required dependencies for the DW3000 driver. ```cmake # # SPDX-FileCopyrightText: Copyright (c) 2024 Qorvo US, Inc. # SPDX-License-Identifier: LicenseRef-QORVO-2 # cmake_minimum_required(VERSION 3.13.1) add_library(dw3000_uwb_driver STATIC dw3000_device.c) target_include_directories(dw3000_uwb_driver PRIVATE .) target_link_libraries(dw3000_uwb_driver PRIVATE uwb_driver ) ``` -------------------------------- ### Configure and Execute AES-128/CCM Encryption Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Sets up the AES hardware core and performs encryption on a frame payload before transmission. Requires a 128-bit key and a 13-byte nonce. ```c #include "dw3000.h" // 128-bit AES key static dwt_aes_key_t aes_key = { .key0 = 0x00010203, .key1 = 0x04050607, .key2 = 0x08090A0B, .key3 = 0x0C0D0E0F, .key4 = 0, .key5 = 0, .key6 = 0, .key7 = 0 }; // 13-byte nonce for CCM mode static uint8_t aes_nonce[13] = { 0xAC, 0xDE, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x06 }; int setup_aes_encryption(void) { // Load AES key to register dwt_set_keyreg_128(&aes_key); // Configure AES block dwt_aes_config_t aes_config = { .aes_core_type = AES_core_type_CCM, // CCM mode .mic = MIC_8, // 8-byte MIC .key_src = AES_KEY_Src_Register, // Key from registers .key_load = AES_KEY_No_Load, // Key already loaded .key_addr = 0, .key_size = AES_KEY_128bit, .mode = AES_Encrypt, .aes_key_otp_type = AES_key_RAM, .aes_otp_sel_key_block = AES_key_otp_sel_1st_128 }; dwt_configure_aes(&aes_config); LOG_INF("AES encryption configured"); return DWT_SUCCESS; } int encrypt_and_transmit(uint8_t *header, uint8_t hdr_len, uint8_t *payload, uint16_t payload_len) { // Write plaintext to TX buffer dwt_writetxdata(hdr_len, header, 0); dwt_writetxdata(payload_len, payload, hdr_len); // Setup AES job for encryption dwt_aes_job_t aes_job = { .nonce = aes_nonce, .header = header, .payload = payload, .header_len = hdr_len, .payload_len = payload_len, .src_port = AES_Src_Tx_buf, .dst_port = AES_Dst_Tx_buf, .mode = AES_Encrypt, .mic_size = 8 // 8-byte MIC }; // Perform encryption int8_t aes_result = dwt_do_aes(&aes_job, AES_core_type_CCM); if (aes_result < 0) { LOG_ERR("AES encryption failed: %d", aes_result); return DWT_ERROR; } // Transmit encrypted frame (payload_len + MIC) uint16_t total_len = hdr_len + payload_len + 8 + 2; // +MIC +CRC dwt_writetxfctrl(total_len, 0, 0); dwt_starttx(DWT_START_TX_IMMEDIATE); // Increment nonce for next transmission aes_nonce[12]++; return DWT_SUCCESS; } ``` -------------------------------- ### Configure GPIO for External PA/LNA Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Sets up GPIO pins for external Power Amplifier (PA) and Low Noise Amplifier (LNA) control. This requires disabling fine-grain TX sequencing and configuring specific GPIOs for PA enable, TX enable, and RX enable signals. ```c #include "dw3000.h" int configure_external_pa_lna(void) { // Disable fine grain TX sequencing (required for PA mode) dwt_setfinegraintxseq(0); // Enable external PA and LNA control dwt_setlnapamode(DWT_LNA_ENABLE | DWT_PA_ENABLE); // Configure GPIOs for PA/LNA // GPIO4: EXTDA (PA enable), GPIO5: EXTTXE (TX enable), GPIO6: EXTRXE (RX enable) dwt_setgpiomode( GPIO4_FUNC_MASK | GPIO5_FUNC_MASK | GPIO6_FUNC_MASK, DW3000_GPIO_PIN4_EXTPA | DW3000_GPIO_PIN5_EXTTXE | DW3000_GPIO_PIN6_EXTRXE ); LOG_INF("External PA/LNA configured"); return DWT_SUCCESS; } ``` -------------------------------- ### Zephyr RTOS DW3000 Initialization Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Initializes the DW3000 driver within a Zephyr RTOS environment. This involves hardware initialization, reset, and driver initialization using specific flags. Ensure necessary Kconfig options and device tree configurations are set. ```c #include #include "dw3000.h" int main(void) { LOG_INF("DW3000 Zephyr Example"); // Initialize hardware if (dw3000_hw_init() != 0) { LOG_ERR("HW init failed"); return -1; } // Reset and initialize dw3000_hw_reset(); k_msleep(2); while (!dwt_checkidlerc()) { k_msleep(1); } if (dwt_initialise(DWT_READ_OTP_PID | DWT_READ_OTP_LID) != DWT_SUCCESS) { LOG_ERR("Driver init failed"); return -1; } LOG_INF("Device ID: 0x%08X", dwt_readdevid()); LOG_INF("Driver: %s", dwt_version_string()); // Continue with configuration... return 0; } ``` -------------------------------- ### CMake Build Commands for Coverage Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/utest/CMakeLists.txt Commands to build and run code coverage tests using CMake. Enables test coverage and generates HTML reports. ```bash # $ cmake -S utest -B ./build/cov -G Ninja -DCMAKE_BUILD_TYPE=Debug -DENABLE_TEST_COVERAGE=ON # $ cmake --build ./build/cov # $ ninja -C ./build/cov ./test_deca_coverage # $ open build/cov/test_deca_coverage_html/index.html ``` -------------------------------- ### Add Zephyr Module to CMakeLists.txt Source: https://github.com/br101/dw3000-decadriver-source/blob/master/README.md Integrate the DW3000 driver as a Zephyr module by adding its path to ZEPHYR_EXTRA_MODULES in your main CMakeLists.txt. ```cmake list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/lib/decadriver/platform) ``` -------------------------------- ### Configure DW3000 Radio Parameters Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Sets up standard UWB radio parameters like channel, preamble length, data rate, and SFD type. Ensure correct antenna delays are set for accurate ranging. ```c #include "dw3000.h" // Standard configuration for TWR ranging static dwt_config_t config = { .chan = 5, // Channel 5 (6.5 GHz) or 9 (8 GHz) .txPreambLength = DWT_PLEN_128, // Preamble length 128 symbols .rxPAC = DWT_PAC8, // PAC size 8 (for preamble <= 128) .txCode = 9, // TX preamble code (9 = 64 MHz PRF) .rxCode = 9, // RX preamble code .sfdType = DWT_SFD_DW_8, // DW 8-symbol SFD .dataRate = DWT_BR_6M8, // 6.8 Mbps data rate .phrMode = DWT_PHRMODE_STD, // Standard PHR mode .phrRate = DWT_PHRRATE_STD, // Standard PHR rate .sfdTO = 129, // SFD timeout (preamble + 1) .stsMode = DWT_STS_MODE_OFF, // STS disabled .stsLength = DWT_STS_LEN_64, // STS length (if enabled) .pdoaMode = DWT_PDOA_M0 // PDOA disabled }; int configure_radio(void) { // Apply configuration int ret = dwt_configure(&config); if (ret != DWT_SUCCESS) { LOG_ERR("dwt_configure failed: %d", ret); return ret; } // Configure TX power (channel 5 typical values) dwt_txconfig_t txconfig = { .PGdly = 0x34, // Pulse generator delay .power = 0xFDFDFDFDUL, // TX power for all frame parts .PGcount = 0 }; dwt_configuretxrf(&txconfig); // Set antenna delays for accurate ranging uint16_t tx_ant_dly = 16385; // Calibrated TX antenna delay uint16_t rx_ant_dly = 16385; // Calibrated RX antenna delay dwt_settxantennadelay(tx_ant_dly); dwt_setrxantennadelay(rx_ant_dly); LOG_INF("Radio configured: CH%d, %s, %s PRF", config.chan, config.dataRate == DWT_BR_6M8 ? "6.8M" : "850K", config.txCode >= 9 ? "64MHz" : "16MHz"); return DWT_SUCCESS; } ``` -------------------------------- ### Configure DW3000 in Zephyr prj.conf Source: https://github.com/br101/dw3000-decadriver-source/blob/master/README.md Enable DW3000 support and related configurations like SPI and GPIO in your Zephyr project's prj.conf file. ```config CONFIG_DW3000=y CONFIG_DW3000_CHIP_DW3000=y CONFIG_SPI=y CONFIG_GPIO=y ``` -------------------------------- ### Dependency Check and Path Configuration Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/lib/qmath/utest/CMakeLists.txt Checks for the existence of required tools in a specific path and configures the CMake module path. This is crucial for locating external modules. ```cmake if (NOT EXISTS ${PROJECT_SOURCE_DIR}/../../../../../tools/cmake) message(FATAL_ERROR "\n Unit tests and coverage uses tools from uwb-stack. \n You currently need to run this from uwb-stack/deps/dwt_uwb_driver. ") endif() set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../../../../tools/cmake) ``` -------------------------------- ### Receive UWB Frame with dwt_rxenable Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Enable the receiver using `dwt_rxenable` and poll for frame reception or errors. Frame length and timestamp are extracted upon successful reception. ```c #include "dw3000.h" #define RX_BUF_LEN 127 static uint8_t rx_buffer[RX_BUF_LEN]; int receive_frame(uint32_t timeout_uus) { // Set receive timeout (0 = no timeout) dwt_setrxtimeout(timeout_uus); // Enable receiver immediately int ret = dwt_rxenable(DWT_START_RX_IMMEDIATE); if (ret != DWT_SUCCESS) { LOG_ERR("dwt_rxenable failed"); return ret; } // Poll for RX events uint32_t status; while (!((status = dwt_readsysstatuslo()) & (DWT_INT_RXFCG_BIT_MASK | SYS_STATUS_ALL_RX_ERR | SYS_STATUS_ALL_RX_TO))) { // Waiting for RX event } if (status & DWT_INT_RXFCG_BIT_MASK) { // Good frame received uint8_t ranging; uint16_t frame_len = dwt_getframelength(&ranging); if (frame_len <= RX_BUF_LEN) { dwt_readrxdata(rx_buffer, frame_len - 2, 0); // -2 excludes CRC // Read RX timestamp uint8_t rx_ts[5]; dwt_readrxtimestamp(rx_ts, DWT_COMPAT_NONE); uint64_t rx_time = 0; for (int i = 4; i >= 0; i--) { rx_time = (rx_time << 8) | rx_ts[i]; } LOG_INF("RX frame: %d bytes, ts: 0x%010llX, ranging: %d", frame_len, rx_time, ranging); // Clear RX events dwt_writesysstatuslo(DWT_INT_RXFCG_BIT_MASK | DWT_INT_RXFR_BIT_MASK); return frame_len; } } else if (status & SYS_STATUS_ALL_RX_TO) { LOG_WRN("RX timeout"); dwt_writesysstatuslo(SYS_STATUS_ALL_RX_TO); return -1; } else { LOG_ERR("RX error: 0x%08X", status & SYS_STATUS_ALL_RX_ERR); dwt_writesysstatuslo(SYS_STATUS_ALL_RX_ERR); dwt_forcetrxoff(); dwt_rxenable(DWT_START_RX_IMMEDIATE); return -2; } return 0; } ``` -------------------------------- ### Configure Secure Ranging with STS Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Enables IEEE 802.15.4z compliant secure ranging by configuring STS Mode 1. Requires setting the STS key and IV, and loading the IV into hardware. ```c #include "dw3000.h" // STS key (128-bit AES key) static dwt_sts_cp_key_t sts_key = { .key0 = 0x14EB220F, .key1 = 0xF86050A8, .key2 = 0xD1D336AA, .key3 = 0x14148674 }; // STS IV (initialization vector / counter) static dwt_sts_cp_iv_t sts_iv = { .iv0 = 0x1F9A3DE4, .iv1 = 0xD37EC3CA, .iv2 = 0xC44FA8FB, .iv3 = 0x362EEB34 }; int configure_secure_ranging(void) { // Configure for STS Mode 1 (STS after SFD, before PHR) dwt_config_t config_sts = { .chan = 9, .txPreambLength = DWT_PLEN_64, .rxPAC = DWT_PAC8, .txCode = 9, .rxCode = 9, .sfdType = DWT_SFD_IEEE_4Z, // IEEE 4z SFD for secure ranging .dataRate = DWT_BR_6M8, .phrMode = DWT_PHRMODE_STD, .phrRate = DWT_PHRRATE_STD, .sfdTO = 65, .stsMode = DWT_STS_MODE_1, // STS Mode 1 .stsLength = DWT_STS_LEN_64, // 64-symbol STS .pdoaMode = DWT_PDOA_M0 }; int ret = dwt_configure(&config_sts); if (ret != DWT_SUCCESS) { return ret; } // Configure STS key and IV dwt_configurestskey(&sts_key); dwt_configurestsiv(&sts_iv); dwt_configurestsloadiv(); // Load IV into hardware LOG_INF("Secure ranging configured with STS Mode 1"); return DWT_SUCCESS; } // Increment STS IV counter for each ranging exchange void increment_sts_counter(void) { sts_iv.iv0++; if (sts_iv.iv0 == 0) { sts_iv.iv1++; } dwt_configurestsiv(&sts_iv); dwt_configurestsloadiv(); } ``` -------------------------------- ### Monitor Sensors and Compensate Crystal Trim Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Reads on-chip temperature and voltage sensors and applies temperature compensation to the crystal trim value. ```c #include "dw3000.h" int read_temperature_voltage(void) { // Read raw temperature and voltage uint16_t temp_vbat = dwt_readtempvbat(); uint8_t raw_temp = (temp_vbat >> 8) & 0xFF; uint8_t raw_vbat = temp_vbat & 0xFF; // Convert to actual values using factory calibration float temperature = dwt_convertrawtemperature(raw_temp); float voltage = dwt_convertrawvoltage(raw_vbat); LOG_INF("Temperature: %.1f C", temperature); LOG_INF("Voltage: %.2f V", voltage); // Read factory calibration references uint8_t ref_temp = dwt_geticreftemp(); uint8_t ref_volt = dwt_geticrefvolt(); LOG_INF("Factory refs - Temp: %d, Volt: %d", ref_temp, ref_volt); return DWT_SUCCESS; } // Temperature compensation for crystal trim int apply_xtal_temperature_compensation(void) { dwt_xtal_trim_t params = { .temperature = TEMP_INIT, // Use on-chip sensor (-127 = auto) .crystal_trim = 0, // Use OTP calibration value .crystal_trim_temperature = TEMP_INIT, // Assume 25C if unknown .crystal_alpha = 0, // Crystal-specific coefficient .crystal_beta = 0 // Crystal-specific coefficient }; uint8_t new_trim; int ret = dwt_xtal_temperature_compensation(¶ms, &new_trim); if (ret == DWT_SUCCESS) { LOG_INF("Crystal trim adjusted to: %d", new_trim); } return ret; } ``` -------------------------------- ### Calculate distance using SS-TWR in C Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Uses initiator and responder timestamps to compute time-of-flight and distance. Assumes symmetric delays; for higher accuracy, consider DS-TWR. ```c #include "dw3000.h" #define SPEED_OF_LIGHT 299702547.0 // m/s in air #define DWT_TIME_UNITS 15.65e-12 // ~15.65 picoseconds per DW time unit // Calculate distance from timestamps (SS-TWR) double calculate_distance_ss_twr(uint64_t poll_tx_ts, uint64_t poll_rx_ts, uint64_t resp_tx_ts, uint64_t resp_rx_ts) { // Round-trip time at initiator int64_t rtd_init = resp_rx_ts - poll_tx_ts; // Reply time at responder int64_t rtd_resp = resp_tx_ts - poll_rx_ts; // Time of flight (ToF) - simplified SS-TWR formula // Note: This assumes symmetric delays; DS-TWR is more accurate double tof = ((double)rtd_init - (double)rtd_resp) / 2.0 * DWT_TIME_UNITS; // Distance in meters double distance = tof * SPEED_OF_LIGHT; return distance; } // Responder: Wait for poll, send response with embedded timestamps int twr_responder(void) { static uint8_t resp_msg[] = {0x41, 0x88, 0x00, 0xCA, 0xDE, 'R', 'E', 'S', 'P', 0, 0, 0, 0, // Poll RX timestamp (4 bytes) 0, 0, 0, 0, // Response TX timestamp (4 bytes) 0, 0}; // CRC // Enable RX and wait for poll dwt_setrxtimeout(0); // No timeout dwt_rxenable(DWT_START_RX_IMMEDIATE); // Wait for good frame (polling) while (!(dwt_readsysstatuslo() & DWT_INT_RXFCG_BIT_MASK)) { if (dwt_readsysstatuslo() & SYS_STATUS_ALL_RX_ERR) { dwt_writesysstatuslo(SYS_STATUS_ALL_RX_ERR); dwt_rxenable(DWT_START_RX_IMMEDIATE); } } // Get poll RX timestamp uint32_t poll_rx_ts = dwt_readrxtimestamplo32(DWT_COMPAT_NONE); // Calculate response TX time (poll RX + fixed delay) uint32_t resp_tx_time = (poll_rx_ts + (3000 * 65536 / 1000000)) & 0xFFFFFFFEUL; // Get predicted response TX timestamp uint32_t resp_tx_ts = (resp_tx_time | 0x1FF) + 1; // Embed timestamps in response resp_msg[9] = (poll_rx_ts >> 0) & 0xFF; resp_msg[10] = (poll_rx_ts >> 8) & 0xFF; resp_msg[11] = (poll_rx_ts >> 16) & 0xFF; resp_msg[12] = (poll_rx_ts >> 24) & 0xFF; resp_msg[13] = (resp_tx_ts >> 0) & 0xFF; resp_msg[14] = (resp_tx_ts >> 8) & 0xFF; resp_msg[15] = (resp_tx_ts >> 16) & 0xFF; resp_msg[16] = (resp_tx_ts >> 24) & 0xFF; // Clear RX status dwt_writesysstatuslo(DWT_INT_RXFCG_BIT_MASK); // Send delayed response dwt_writetxdata(sizeof(resp_msg), resp_msg, 0); dwt_writetxfctrl(sizeof(resp_msg), 0, 1); dwt_setdelayedtrxtime(resp_tx_time); int ret = dwt_starttx(DWT_START_TX_DELAYED); if (ret == DWT_SUCCESS) { while (!(dwt_readsysstatuslo() & DWT_INT_TXFRS_BIT_MASK)) {} dwt_writesysstatuslo(DWT_INT_TXFRS_BIT_MASK); LOG_INF("Response sent"); } return ret; } ``` -------------------------------- ### Subdirectory Inclusion for Google Test Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/lib/qmath/utest/CMakeLists.txt Includes the Google Test framework as a subdirectory for testing purposes. It is excluded from the main build targets. ```cmake add_subdirectory(../../../../../deps/googletest/googletest gtest EXCLUDE_FROM_ALL) ``` -------------------------------- ### CMake Conditional Build Configuration Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/utest/CMakeLists.txt Configures build targets based on the ENABLE_TEST_COVERAGE option. Includes coverage or sanitization tools. ```cmake if(ENABLE_TEST_COVERAGE) include(Coverage) target_coverage(uwb_driver) target_coverage(qmath) add_coverage(NAME utest GTEST_JUNIT) else() include(Sanitize) target_sanitize(utest) endif() ``` -------------------------------- ### Configure IEEE 802.15.4 Frame Filtering Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Configures the device addresses (PAN ID, short address, EUI64) and enables IEEE 802.15.4 frame filtering. The filter can be configured to accept data, ACK, and coordinator frames. ```c #include "dw3000.h" int configure_frame_filtering(void) { // Set device addresses uint16_t pan_id = 0xDECA; uint16_t short_addr = 0x1234; uint8_t eui64[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; dwt_setpanid(pan_id); dwt_setaddress16(short_addr); dwt_seteui(eui64); // Enable frame filtering uint16_t filter_cfg = DWT_FF_DATA_EN | // Accept data frames DWT_FF_ACK_EN | // Accept ACK frames DWT_FF_COORD_EN; // Act as coordinator (accept no-dest frames) dwt_configureframefilter(DWT_FF_ENABLE_802_15_4, filter_cfg); LOG_INF("Frame filtering enabled"); LOG_INF("PAN: 0x%04X, Short: 0x%04X", pan_id, short_addr); // Read back EUI to verify uint8_t read_eui[8]; dwt_geteui(read_eui); LOG_INF("EUI: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", read_eui[7], read_eui[6], read_eui[5], read_eui[4], read_eui[3], read_eui[2], read_eui[1], read_eui[0]); return DWT_SUCCESS; } ``` -------------------------------- ### Enable WiFi Coexistence Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Enables WiFi coexistence functionality on a specified GPIO pin. The function `dwt_wifi_coex_set` takes the enable flag and the GPIO pin index (0 for GPIO5, 1 for GPIO4). ```c #include "dw3000.h" int configure_wifi_coexistence(void) { // Enable WiFi coexistence on GPIO5 dwt_wifi_coex_set(DWT_EN_WIFI_COEX, 0); // 0 = GPIO5, 1 = GPIO4 LOG_INF("WiFi coexistence enabled on GPIO5"); return DWT_SUCCESS; } ``` -------------------------------- ### Conditional Build Configuration for Coverage or Sanitization Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/lib/qmath/utest/CMakeLists.txt Conditionally includes CMake modules for test coverage or sanitization based on the ENABLE_TEST_COVERAGE option. This allows for flexible build configurations. ```cmake if(ENABLE_TEST_COVERAGE) include(Coverage) target_coverage(qmath) add_coverage(NAME test_qmath GTEST_JUNIT) else() include(Sanitize) target_sanitize(qmath) endif() ``` -------------------------------- ### Test Coverage Option Source: https://github.com/br101/dw3000-decadriver-source/blob/master/dwt_uwb_driver/lib/qmath/utest/CMakeLists.txt Defines a CMake option to enable or disable test coverage. Defaults to OFF. ```cmake option(ENABLE_TEST_COVERAGE "Enable test coverage" OFF) ``` -------------------------------- ### Transmit UWB Frame with dwt_writetxdata Source: https://context7.com/br101/dw3000-decadriver-source/llms.txt Use `dwt_writetxdata` to send frame data and `dwt_starttx` for immediate transmission. Ensure TX completion by polling `dwt_readsysstatuslo`. ```c #include "dw3000.h" // Simple blink frame static uint8_t tx_msg[] = {0x41, 0x88, 0x00, 0xCA, 0xDE, 'W', 'A', 'V', 'E', 0x00, 0x00}; #define TX_MSG_LEN sizeof(tx_msg) int transmit_frame(void) { // Write frame data to TX buffer int ret = dwt_writetxdata(TX_MSG_LEN, tx_msg, 0); if (ret != DWT_SUCCESS) { LOG_ERR("dwt_writetxdata failed"); return ret; } // Configure frame control (length + offset + ranging bit) dwt_writetxfctrl(TX_MSG_LEN + 2, 0, 0); // +2 for auto-added CRC // Start immediate transmission ret = dwt_starttx(DWT_START_TX_IMMEDIATE); if (ret != DWT_SUCCESS) { LOG_ERR("dwt_starttx failed"); return ret; } // Wait for TX complete (polling mode) while (!(dwt_readsysstatuslo() & DWT_INT_TXFRS_BIT_MASK)) { // Waiting for transmission complete } // Clear TX status dwt_writesysstatuslo(DWT_INT_TXFRS_BIT_MASK); // Read TX timestamp uint8_t tx_ts[5]; dwt_readtxtimestamp(tx_ts); uint64_t tx_time = 0; for (int i = 4; i >= 0; i--) { tx_time = (tx_time << 8) | tx_ts[i]; } LOG_INF("TX timestamp: 0x%010llX", tx_time); return DWT_SUCCESS; } ```