Electrical – AVR ATmega328 with DWM1000

arduinorfidspi

I recently asked about this over in the Arduino stack, but it was a broad question, and I have made progress and narrowed down the issues.

I have two Arduino Nano's, each attached to a DWM1000. This is on a custom breakout board and a breadboard. There is an Arduino DWM1000 library here that I use to test with the boards. The basic tx/rx sketches work perfect and can ping-pong messages.

This library is not complete, and I need more advanced features for a project I am working on. So DecaWave (the maker of the DWM1000) has a fully written driver for it: (DW1000 Application Programming Interface with STM32F10x Application examples) I ported the necessary functions to use the ATmega chip.

Using the DecaWave driver, I can read/write all registers but am having issues with the basic tx/rx examples. It seems the transmission is not being properly sent. I am able to send code from my one board using the Arduino library example, and receive it with the DecaWave basic rx example with no errors at all.

If I send from the DecaWave basic tx example, and receive it from the DecaWave basic rx example I receive it, but with errors.

Sometimes these bits get set:

CPLOCK, RXPRD, RXSFDD, RXPHE, SLP2INIT, CLKPLL_LL

Other times, these are set:

CPLOCK, RXPRD, RXSFDD, RXPHD, RXRFSL, SLP2INIT, CLKPLL_LL

From the sending code, the proper bits are getting set (TXFRB, TXPRS, TXPHS, TXFRS)

My DecaWave driver tx code:

#include <Arduino.h>
#include <SPI.h>
#include <prescaler.h>
#include <deca_device_api.h>
#include <deca_regs.h>
//#include "DW1000.h"
//#include "usart.h"


 static dwt_config_t config = {
     5,               /* Channel number. */
     DWT_PRF_16M,     /* Pulse repetition frequency. */
     DWT_PLEN_1024,   /* Preamble length. Used in TX only. */
     DWT_PAC32,       /* Preamble acquisition chunk size. Used in RX only. */
     4,               /* TX preamble code. Used in TX only. */
     4,               /* RX preamble code. Used in RX only. */
     0,               /* 0 to use standard SFD, 1 to use non-standard SFD. */
     DWT_BR_110K,     /* Data rate. */
     DWT_PHRMODE_STD, /* PHY header mode. */
     (1025 + 64 - 32) /* SFD timeout (preamble length + 1 + SFD length - PAC size). Used in RX only. */
};

 static uint8 tx_msg[] = {0xC5, 0, 'D', 'E', 'C', 'A', 'W', 'A', 'V', 'E', 0, 0};
 /* Index to access to sequence number of the blink frame in the tx_msg array. */
 #define BLINK_FRAME_SN_IDX 1

 /* Inter-frame delay period, in milliseconds. */
 #define TX_DELAY_MS 1000

void reset_DW1000()
{
  digitalWrite(5, LOW);
  pinMode(5, OUTPUT);
  delay(10);
  pinMode(5, INPUT);
  delay(10);
}

void setup()
{
  SPI.begin();
  Serial.begin(9600);
  while (!Serial) { ; };
  delay(50);

  reset_DW1000();
  setClockPrescaler(CLOCK_PRESCALER_8);
  if (dwt_initialise(DWT_LOADNONE) == DWT_ERROR)
  {
      setClockPrescaler(CLOCK_PRESCALER_1);
      Serial.println("INIT FAILED");
      while (1)
      { };
  }
  setClockPrescaler(CLOCK_PRESCALER_1);

  dwt_configure(&config);
}

void loop()
{
  /* Write frame data to DW1000 and prepare transmission. See NOTE 4 below.*/
  dwt_writetxdata(sizeof(tx_msg), tx_msg, 0); /* Zero offset in TX buffer. */
  dwt_writetxfctrl(sizeof(tx_msg), 0, 0); /* Zero offset in TX buffer, no ranging. */

  /* Start transmission. */
  dwt_starttx(DWT_START_TX_IMMEDIATE);

  while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS))
  { };

  /* Clear TX frame sent event. */
  dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS);

  /* Execute a delay between transmissions. */
  delay(TX_DELAY_MS);

  /* Increment the blink frame sequence number (modulo 256). */
  tx_msg[BLINK_FRAME_SN_IDX]++;
}

My DecaWave driver rx code:

#include <Arduino.h>
#include <SPI.h>
#include <prescaler.h>
#include <deca_device_api.h>
#include <deca_regs.h>
//#include "USART.h"
/*! ----------------------------------------------------------------------------
 *  @file    main.c
 *  @brief   Simple RX example code
 *
 * @attention
 *
 * Copyright 2015 (c) Decawave Ltd, Dublin, Ireland.
 *
 * All rights reserved.
 *
 * @author Decawave
 */

 static dwt_config_t config = {
     5,               /* Channel number. */
     DWT_PRF_16M,     /* Pulse repetition frequency. */
     DWT_PLEN_1024,   /* Preamble length. Used in TX only. */
     DWT_PAC32,       /* Preamble acquisition chunk size. Used in RX only. */
     4,               /* TX preamble code. Used in TX only. */
     4,               /* RX preamble code. Used in RX only. */
     0,               /* 0 to use standard SFD, 1 to use non-standard SFD. */
     DWT_BR_110K,     /* Data rate. */
     DWT_PHRMODE_STD, /* PHY header mode. */
     (1025 + 64 - 32) /* SFD timeout (preamble length + 1 + SFD length - PAC size). Used in RX only. */
};

#define FRAME_LEN_MAX 127
static uint8 rx_buffer[FRAME_LEN_MAX];

static uint32 status_reg = 0;

static uint16 frame_len = 0;

void reset_DW1000()
{
  digitalWrite(5, LOW);
  pinMode(5, OUTPUT);
  delay(10);
  pinMode(5, INPUT);
  delay(10);
}

void debug_register(uint16_t reg, uint8_t len)
{
  uint8_t ar[len];
  dwt_readfromdevice (reg, 0, len, ar) ;
  for (int i=0; i<len; i++)
  {
    Serial.print(i); Serial.print(" = "); Serial.println(ar[i]);
  }
}

void setup()
{
  SPI.begin();
  Serial.begin(9600);
  //initUSART();
  while (!Serial) { ; };
  delay(50);
  Serial.println("Hi hello hi hello fdaffasd?");

  reset_DW1000();
  setClockPrescaler(CLOCK_PRESCALER_8);
  if (dwt_initialise(DWT_LOADNONE) == DWT_ERROR)
  {
      setClockPrescaler(CLOCK_PRESCALER_1);
      Serial.println("INIT FAILED");
      while (1)
      { };
  }
  setClockPrescaler(CLOCK_PRESCALER_1);
  delay(50);

  dwt_configure(&config);
}
void loop()
{
  int i;

  for (i = 0 ; i < FRAME_LEN_MAX; i++ )
  {
      rx_buffer[i] = 0;
  }
  dwt_rxenable(DWT_START_RX_IMMEDIATE);

  while (!((status_reg = dwt_read32bitreg(SYS_STATUS_ID)) & (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR))) { ; }

  if (status_reg & SYS_STATUS_RXFCG)
  {
      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);
      }
      dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG);
  }
  else
  {
    dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
  }
}

EDIT:
So, I'm having a tough time now thinking that it is something on my end, and now thinking there could be something wrong with the driver api? At least with the dwm1000 specifically.

I now have two DWM1000s hooked up with STM32s. I am now using the board architecture that the api was designed for. I have the exact same results..

If I comment out the "dwt_configure(&config);" I am able to receive messages with the aforementioned error bits set, the rx stm32 prints "err".

If I use any configuration, I never receive any messages at all.

These modules still work fine using the arduino library, so there is nothing wrong with them, and I have 4 modules that I have tested with each.

Please if anyone has any suggestions let me know. If not it looks like I will be moving on to a new UWB chip.

Thanks!

STM32 tx code:

#include <mbed.h>
#include "decadriver/deca_device_api.h"
#include "decadriver/deca_regs.h"


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

DigitalOut myled(PC_13);


static uint8 tx_msg[] = {0xC5, 0, 'D', 'E', 'C', 'A', 'W', 'A', 'V', 'E', 0, 0};

#define BLINK_FRAME_SN_IDX 1

#define TX_DELAY_MS 1000

DigitalInOut pin_rst(PA_10);

void reset_DW1000()
{
  pin_rst.write(0);
  wait(0.01);
  pin_rst.write(1);
}

int main(void)
{
    myled = 0;
    Serial serial(PB_6, PB_7);
    wait(0.5);
    serial.baud(9600);

    pin_rst.output();
    pin_rst.mode(OpenDrain);
    SPI bus(PA_7, PA_6, PA_5);//mosi, miso, clk

    serial.printf("Device ID: %u\r\n", dwt_readdevid());

    reset_DW1000();
    bus.frequency(2000000);
    if (dwt_initialise(DWT_LOADNONE) == DWT_ERROR)
    {
        serial.printf("Error on init11\n\r");
        while (1) { ; };
    }
    bus.frequency(20000000);

    //dwt_configure(&config);

    while(1)
    {
      dwt_writetxdata(sizeof(tx_msg), tx_msg, 0); /* Zero offset in TX buffer. */
      dwt_writetxfctrl(sizeof(tx_msg), 0, 0); /* Zero offset in TX buffer, no ranging. */

      dwt_starttx(DWT_START_TX_IMMEDIATE);

      while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS))
      { };

      dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS);

      myled = 1;
      wait(1);
      myled = 0;

      tx_msg[BLINK_FRAME_SN_IDX]++;
    }
}

STM32 rx code:

#include <mbed.h>
#include "decadriver/deca_device_api.h"
#include "decadriver/deca_regs.h"


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

#define FRAME_LEN_MAX 127
static uint8 rx_buffer[FRAME_LEN_MAX];

static uint32 status_reg = 0;

static uint16 frame_len = 0;

DigitalInOut pin_rst(PA_10);

void reset_DW1000()
{
  pin_rst.write(0);
  wait(0.01);
  pin_rst.write(1);
}



int main(void)
{
    //myled = 0;
    Serial serial(PB_6, PB_7);
    wait(0.5);
    serial.baud(9600);

    pin_rst.output();
    pin_rst.mode(OpenDrain);
    SPI bus(PA_7, PA_6, PA_5);//mosi, miso, clk

    serial.printf("Device ID: %u\r\n", dwt_readdevid());

    reset_DW1000();
    bus.frequency(2000000);
    if (dwt_initialise(DWT_LOADNONE) == DWT_ERROR)
    {
        serial.printf("Error on init11\n\r");
        while (1) { ; };
    }
    bus.frequency(20000000);

    //dwt_configure(&config);

    while(1)
    {
      int i;

      for (i = 0 ; i < FRAME_LEN_MAX; i++ )
      {
          rx_buffer[i] = 0;
      }
      dwt_rxenable(DWT_START_RX_IMMEDIATE);

      while (!((status_reg = dwt_read32bitreg(SYS_STATUS_ID)) & (SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR))) { ; }

      if (status_reg & SYS_STATUS_RXFCG)
      {
          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);
              for(int s=0; s<frame_len-3; s++)
              {
                serial.printf("%c", rx_buffer[s]);
              }
              serial.printf("\r\n");
          }
          dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG);
      }
      else
      {
        dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
        serial.printf("Err\r\n");
      }
    }
}

Best Answer

I would recommend that you use more of DecaWave's driver, especially as you're looking for more advanced features. Specifically, you want to use their interrupt framework to handle the details of TX and RX.

For example, look into the dwt_isr() function. There is quite a bit of work going on behind the scenes. It handles multiple complex status bit interactions, checks for overruns, has implemented a few work-arounds for hardware bugs, etc.

To do this, you need to provide two functions (referred to as "callback functions") to their driver: one for RX handling and one for TX handling. You inform their driver of your function names by using dwt_setcallbacks().

Then, you need to set up the Arduino to respond to the DW1000 interrupt pin appropriately. When the DW1000 causes an interrupt, their driver handles everything and then calls the appropriate one of your callback functions.

Finally, when the DecaWave driver calls your function(s), it passes along a data structure which contains information about the transmission or reception. You can analyse this data to ensure that everything went well, or to help figure out what went wrong.

If you use their driver, you should never have to use lower-level functions such as dwt_read32bitreg(). The API is fairly well developed.

Using the DecaWave driver isn't trivial, but it will make sense once you get it going. You should look to their application examples to see how to piece it all together.

Good luck :)