DWM1000 with AtMega328p host: Polled reciever looping

Hello,

I am trying to setup the DWM1000 with an AVR AtMega 328P (8MHz, 3.3V). I have set up SPI and am able to read the device ID and have confirmed SPI is working via the suggestion in the aps022 debugging manual. I have tried using the SimpleTx example and that program seems to run fine. However, the SimpleRx example gets stuck in the loop checking if RXFCG or ALL_RX_ERR gets set in the SYS_STATUS. Neither bit ever gets set. Reading the status register, it always reads 0x2. The aps022 manual (as well as other threads on this forum on similar topics) suggest reading the SYS_STATE register. I have done that and read 0, which according to aps022 means the DWM1000 is still in INIT mode. Interestingly, when I use the arduino library for the DWM1000, communication between the 2 modules works.

My Rx code is as follows:

#define DW1000_RST_DIR  DDRD 
#define DW1000_RST_PORT PORTD 
#define DW1000_RST_PIN  PD3 

#define LOG(str) uart::writeBytes((uint8_t*) str, sizeof(str));

static dwt_config_t config = {
    .chan           = 2,               /* Channel number. */
    .prf            = DWT_PRF_64M,     /* Pulse repetition frequency. */
    .txPreambLength = DWT_PLEN_1024,   /* Preamble length. Used in TX only. */
    .rxPAC          = DWT_PAC32,       /* Preamble acquisition chunk size. Used in RX only. */
    .txCode         = 9,               /* TX preamble code. Used in TX only. */
    .rxCode         = 9,               /* RX preamble code. Used in RX only. */
    .nsSFD          = 1,               /* 0 to use standard SFD, 1 to use non-standard SFD. */
    .dataRate       = DWT_BR_110K,     /* Data rate. */
    .phrMode        = DWT_PHRMODE_STD, /* PHY header mode. */
    .sfdTO          = (1025 + 64 - 32) /* SFD timeout (preamble length + 1 + SFD length - PAC size). Used in RX only. */
};

/* Buffer to store received frame. See NOTE 1 below. */
#define FRAME_LEN_MAX 127
static uint8 rx_buffer[FRAME_LEN_MAX];

/* Hold copy of status register state here for reference so that it can be examined at a debug breakpoint. */
static uint32 status_reg = 0;

/* Hold copy of frame length of frame received (if good) so that it can be examined at a debug breakpoint. */
static uint16 frame_len = 0;

void init_hardware();

void reset_dw1000();

/**
 * Application entry point.
 */
int main(void)
{
    /* Start with board specific hardware init. */
    uart::init(9600);
    LOG("Simple rx, polling\r\n");
    spi::init();

    init_hardware();

    /* Reset and initialise DW1000. See NOTE 2 below.
     * For initialisation, DW1000 clocks must be temporarily set to crystal speed. After initialisation SPI rate can be increased for optimum
     * performance. */
    reset_dw1000(); /* Target specific drive of RSTn line into DW1000 low for a period. */

    uint32_t devid = dwt_readdevid();
    if (devid == 0xDECA0130) {
        LOG("Correct device id\r\n");
    } else {
        LOG("Incorrect device ID. Broken SPI?\r\n");
        while (1) {}
    }

    spi::set_clock_div(128);
    if (dwt_initialise(DWT_LOADNONE) == DWT_ERROR)
    {
        LOG("Init failed\r\n");
        while (1) { };
    }
    spi::set_clock_div(2);

    /* Configure DW1000. */
    dwt_configure(&config);

    char buf[32];
    uint8_t sz;

    uint32_t partid = dwt_getpartid();
    sz = sprintf(buf, "part id: %d\r\n", partid);
    uart::writeBytes((uint8_t*) buf, sz);

    uint8_t c = 0;

    /* Loop forever receiving frames. */
    while (1)
    {
        sz = sprintf(buf, "[%d]", c);
        uart::writeBytes((uint8_t*) buf, sz);

        int i;

        /* TESTING BREAKPOINT LOCATION #1 */

        /* Clear local RX buffer to avoid having leftovers from previous receptions  This is not necessary but is included here to aid reading
         * the RX buffer.
         * This is a good place to put a breakpoint. Here (after first time through the loop) the local status register will be set for last event
         * and if a good receive has happened the data buffer will have the data in it, and frame_len will be set to the length of the RX frame. */
        for (i = 0 ; i < FRAME_LEN_MAX; i++ )
        {
            rx_buffer[i] = 0;
        }

        /* Activate reception immediately. See NOTE 3 below. */
        dwt_rxenable(DWT_START_RX_IMMEDIATE);

        /* Poll until a frame is properly received or an error/timeout occurs. See NOTE 4 below.
         * STATUS register is 5 bytes long but, as the event we are looking at is in the first byte of the register, we can use this simplest API
         * function to access it. */
        while (!((status_reg = dwt_read32bitreg(SYS_STATUS_ID)) & (SYS_STATUS_RXFCG | SYS_STATUS_RXFCE | SYS_STATUS_ALL_RX_ERR))) { 
            sz = sprintf(buf, "status: 0x%x\r\n", status_reg);
            uart::writeBytes((uint8_t*) buf, sz);
        } // never exits this loop

        if (status_reg & SYS_STATUS_RXFCG)
        {
            LOG("status_reg & SYS_STATUS_RXFCG\r\n");

            /* A frame has been received, copy it to our local buffer. */
            frame_len = dwt_read32bitreg(RX_FINFO_ID) & RX_FINFO_RXFL_MASK_1023;
            if (frame_len <= FRAME_LEN_MAX)
            {
                dwt_readrxdata(rx_buffer, frame_len, 0);
                LOG("Received: ");
                LOG(rx_buffer);
                LOG("\r\n");
            }

            /* Clear good RX frame event in the DW1000 status register. */
            dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG);
        }
        else
        {
            LOG("RX err\r\n");

            /* Clear RX error events in the DW1000 status register. */
            dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
        }
        ++c;
    }
}

void init_hardware() {
    // use DW1000_RST_PIN as pin to reset dw1000. configure as input, high impedance for now
    DW1000_RST_DIR  &= ~(1 << DW1000_RST_PIN);
    DW1000_RST_PORT &= ~(1 << DW1000_RST_PIN);
}

void reset_dw1000() {
    deca_sleep(5);

    DW1000_RST_DIR  |=  (1 << DW1000_RST_PIN); // set DW1000_RST_PIN as output
    DW1000_RST_PORT &= ~(1 << DW1000_RST_PIN); // drive DW1000_RST_PIN low

    // delay minimum 10ns
    deca_sleep(2);
    
    // put DW1000_RST_PIN back to input, high impedance
    DW1000_RST_DIR  &= ~(1 << DW1000_RST_PIN); // set DW1000_RST_PIN as input
    DW1000_RST_PORT &= ~(1 << DW1000_RST_PIN); // set DW1000_RST_PIN to have high impedance

    deca_sleep(10);
}

Hi,
Arduino works… Could I ask where you got the software from? This Arduino SW might be Ok already for a 8bit processor, however the code you’re porting is based on a 32bit STM32 and so based on a faster processor with a faster SPI speed.
I suggest you connect the RX /TX /IRQ to a scope and so check the timings and status and active level of IRQ (high /low)
This is also described the the debug application note, APS022.
Not least important, please have a look at our Application note APS019 "ISSUES TO CONSIDER WHEN PORTING THE DECAWAVE DECARANGING SOURCE CODE TO AN 8-bit MCU. This application note describes what to bwe aware and take care of when porting our source code to a 8bit processor.
APS019 can be found on our website under “UWB System” https://www.decawave.com/application-notes

/Leo

Hello,

The arduino code I am using is from https://github.com/thotro/arduino-dw1000
Unfortunately I do not have access to an oscilloscope but I will look at APS019.
Also, I have not changed anything but now the SYS_STATE register which previously read 0 now reads either 0x280 or 0x80. It sometimes switches between the 2 when i reset the host MCU.