What's the difference between DWM1000 and DWM1001 DEVELOPMENT BOARD

I was looking for a board compatible with Raspberry to implement ranging with UWB and I’ve come across the DWM1000 and DWM1001.

I’ve understood the DWM1000 is just the “UWB compliant wireless transceiver module based on Decawave’s DW1000 IC” and the DWM1001 development board includes the DW1000, a Nordic Semiconductor nRF52832 MCU, and a 3-axis accelerometer soldered into a Raspberry compatible board.

Now, I’m reading the API function on the DWM1000 and I can’t understand if I can use it on the DWM1001 development board. For instance, in Section 5 of https://forum.qorvo.com/uploads/default/original/1X/8b220e1e26fea4ebd83f0b0e5ef42eb9a251310d.pdf is written that

functions are implemented in the device driver source code file “deca_device.c”

but I can’t find the file anywhere.

Hi ,
The examples in the API have been designed to run on an EVB1000 board with an stm32f105rc Processor.
To have these examples run on a DWM1001 these examples need to be ported to the MCU on the DWM1001 (nRF52832).
Some examples have been ported. You can go to https://github.com/Decawave/dwm1001-examples to access a couple of very basic software routines for implementing a single-sided TWR exchange between two nodes using the DWM1001.

So basically, if I want to read registers such as, for instance, the accumulator after a correct reception, I need to rewrite the functions written for the stm32f105rc in the deca_device.c for the nRF52832

Am I right?

In theory yes. The underlying interface is standard SPI so you’ll need to configure the SPI interface either by direct access to the hardware or more likely through some supplied SPI device drivers.
That same driver should have a simple read/write function (on SPI you do both at the same time). The only slightly odd bit is how the address setup is done.

e.g. the code below is taken from my DW1000 driver.

void DW1000::readRegister(uint8_t reg, uint16_t subaddress, uint8_t *buffer, int length)
    setupTransaction(reg, subaddress, false);
    for(int i=0; i<length; i++)                             // get data
        buffer[i] = spi.write(0x00);

void DW1000::writeRegister(uint8_t reg, uint16_t subaddress, uint8_t *buffer, int length)
    setupTransaction(reg, subaddress, true);
    for(int i=0; i<length; i++)                             // put data

void DW1000::setupTransaction(uint8_t reg, uint16_t subaddress, bool write)
    reg |=  (write * DW1000_WRITE_FLAG);                                        // set read/write flag
    if (subaddress > 0) {                                                       // there's a subadress, we need to set flag and send second header byte
        spi.write(reg | DW1000_SUBADDRESS_FLAG);
        if (subaddress > 0x7F) {                                                // sub address too long, we need to set flag and send third header byte
            spi.write((uint8_t)(subaddress & 0x7F) | DW1000_2_SUBADDRESS_FLAG); // and
            spi.write((uint8_t)(subaddress >> 7));
        } else {
    } else {
        spi.write(reg);                                                         // say which register address we want to access

void DW1000::select() {       // always called to start an SPI transmission
    irq.disable_irq();      // disable interrupts from DW1000 during SPI
    cs = 0;                 // set Select pin low to start transmission

void DW1000::deselect() }    // always called to end an SPI transmission
    cs = 1;                 // set Select pin high to stop transmission
    irq.enable_irq();       // reenable the interrupt handler

I then define a load of constants for the various register addresses and have functions like:

void DW1000::startRX() {
    writeRegister(DW1000_SYS_CTRL, 0x01, 0x01, 1);                    // start listening by setting the RXENAB bit
uint64_t DW1000::getTXTimestamp() {
    return readRegister(DW1000_TX_TIME, 0, 5); // 40 bit read
1 Like