DW1000 SFDLED Configuration

Hello!

I am developing a project based on STM32F4 + DWM 1000.

The system consists of one anchor and two tags:

  1. The Anchor performs TOF measurements for each Tag;
  2. The Anchor transmits an appropriate TOF measurements to each tag;
  3. The Anchor sends a synchro message to synchronize the local time of the Tags STM32;

TOF measurement and transmission works perfectly, but to synchronize the STM32 timers on the tag side, I want also use the SFDLED signal.

The problem is in follows: the DWM1000 SFDLED signals of both of my tags have a sufficiently large delay between them:


Although as I understand it, if the Tags are nearby, this signal should be generated simultaneously.

Tag and Anchor applications are based on dw1000_api_rev2p14_stsw API. Firmware for both tags is identical.

There is no SFDLED pin configuration function in the API, so I had to write it separately:

#define GPIO_PIN1_SFD         0x00000100UL    /* The pin operates as SFD LED output */

void dwt_setSFDgpio1(void)
{
    // Set GPIO Mode
    uint32 gpio_mode = dwt_read32bitoffsetreg(GPIO_CTRL_ID, GPIO_MODE_OFFSET);
    gpio_mode &= ~(GPIO_MSGP1_MASK);
    gpio_mode |= GPIO_PIN1_SFD;
    dwt_write32bitoffsetreg(GPIO_CTRL_ID, GPIO_MODE_OFFSET, gpio_mode);
    gpio_mode = dwt_read32bitoffsetreg(GPIO_CTRL_ID, GPIO_MODE_OFFSET);
    
    // Enable LP Oscillator to run from counter and turn on de-bounce clock.
    uint32 reg;
    reg = dwt_read32bitoffsetreg(PMSC_ID, PMSC_CTRL0_OFFSET);
    reg |= (PMSC_CTRL0_GPDCE | PMSC_CTRL0_KHZCLEN);
    dwt_write32bitoffsetreg(PMSC_ID, PMSC_CTRL0_OFFSET, reg);
    
    // Enable LEDs to blink and set default blink time.
    reg = PMSC_LEDC_BLNKEN | 0x01;
    dwt_write32bitoffsetreg(PMSC_ID, PMSC_LEDC_OFFSET, reg);
}

Initial DW1000 setup:

 
  static dwt_config_t config = {
    2,               /* Channel number. */
    DWT_PRF_64M,     /* Pulse repetition frequency. */
    DWT_PLEN_128,    /* Preamble length. Used in TX only. */
    DWT_PAC8,        /* Preamble acquisition chunk size. Used in RX only. */
    9,               /* TX preamble code. Used in TX only. */
    9,               /* RX preamble code. Used in RX only. */
    0,               /* 0 to use standard SFD, 1 to use non-standard SFD. */
    DWT_BR_6M8,      /* Data rate. */
    DWT_PHRMODE_STD, /* PHY header mode. */
    (129 + 8 - 8)    /* SFD timeout (preamble length + 1 + SFD length - PAC size). Used in RX only. */
  };
  
  static dwt_txconfig_t txconfig = {
    0xC2,            /* PG delay. */
    0x67676767,      /* TX power. */
  };

  reset_DW1000(); /* Target specific drive of RSTn line into DW1000 low for a period. */
  port_set_dw1000_slowrate();
  HAL_Delay(1000);
  if (dwt_initialise(DWT_LOADUCODE) == DWT_ERROR)
  {
    while (1)
      ;
  }
  port_set_dw1000_fastrate();
  dwt_configure(&config);
  dwt_configuretxrf(&txconfig);
  dwt_enablegpioclocks();
  dwt_setSFDgpio1();

Non-blocking RX handler (called as often as possible):

  case DWM_RX:
    if (handler->dwm.pocessing == 0)
    {
      dwt_rxenable(DWT_START_RX_IMMEDIATE);
      handler->dwm.pocessing = 1;
    }
    else
    {
      statusReg = dwt_read32bitreg(SYS_STATUS_ID);
      if (statusReg & SYS_STATUS_RXFCG)
      {
        uint16_t rxPacketSize = dwt_read32bitreg(RX_FINFO_ID) & RX_FINFO_RXFL_MASK_1023;
        if (rxPacketSize <= 0xFF + 4)
        {
          dwt_readrxdata((uint8_t *)&handler->rxPacketBuffer.packet, rxPacketSize - UWB_PROTOCOL_HW_CRC_LENGTH, 0);
          handler->rxPacketBuffer.rxTimestamp = dwt_readrxtimestamplo32() + (((uint64_t)dwt_readrxtimestamphi32() & 0xFF000000) << 8);
          Fifo_pushBack(handler->rxFifo, &handler->rxPacketBuffer);
          dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR);
          dwt_rxenable(DWT_START_RX_IMMEDIATE);
          break;
        }
        dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_RXFCG | SYS_STATUS_ALL_RX_ERR);
        handler->dwm.pocessing = 0;
      }
      else if (statusReg & SYS_STATUS_ALL_RX_ERR)
      {
        dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
        handler->dwm.pocessing = 0;
      }
    }

Do you send messages to 2 tags sequentially?
I.e. Msg to the first tag, then msg to the second tag?

Hello!

I send synchro message once for both tags.

I think, I figured out what was the root of my problem:

SFDLED GPIO functionality needs Kilohertz Clock to be enabled (PMSC_CTRL0_KHZCLEN in PMSC_CTRL0 Sub-register). Kilohertz Clock also controls GPIO De-Bounce clock timing.

  1. I lowered Kilohertz Clock Prescaler (Set KHZCLKDIV in PMSC_CTRL1 Sub-register to 0):

    reg = dwt_read32bitoffsetreg(PMSC_ID, PMSC_CTRL1_OFFSET);
    reg &= ~PMSC_CTRL1_KHZCLKDIV_MASK;
    reg += 0 << 26;
    dwt_write32bitoffsetreg(PMSC_ID, PMSC_CTRL1_OFFSET, reg);
    

    With this configuration SFDLED response difference reduced to ~10-40 ns but multiple SFDLED pulses on single packet receive appeared.

  2. Then I increased Blink time count value (Set BLINK_TIM in PMSC_LEDC Sub-register to 10 instead of 1):

    reg = PMSC_LEDC_BLNKEN | 0x0A;
    dwt_write32bitoffsetreg(PMSC_ID, PMSC_LEDC_OFFSET, reg);
    

    It solves multiple pulses problem, but 10-40 ns difference is still to large

  3. Then I tryed disable De-Bounce function for SFDLED GPIO instead of lowering Kilohertz Clock Prescaler:

    reg = dwt_read32bitoffsetreg(GPIO_CTRL_ID, GPIO_IDBE_OFFSET);
    gpio_mode &= ~(GIDBE1);
    dwt_write32bitoffsetreg(GPIO_CTRL_ID, GPIO_IDBE_OFFSET, reg);
    

    It had no effect.

Also when I disable De-Bounce clocking (GPDCE and GPDRN bits in PMSC_CTRL0 Sub-register) SFDLED stops working at all.

Is there a way to use SFDLED without De-Bounce function?

I think you’re going to hit a fundamental limitation of the part. The LED lines are intended to flash LEDs. They aren’t intended for accurate time indications, at least not that accurate.

Can I ask what you are doing that requires such accurate time synchronisation between the two anchors?
Unless you are also controlling for CPU clock speed differences no matter how accurately you synchronise them you’ll very quickly be that far out of sync again anyway.

1 Like

Hello!

The aim of my experiment is to generate an accurate synchronizing pulse on the STM32.

I assume that knowing the TOF in each tag, I can start a timer on both tags to compensate TOF and generate a pulse at the same time.

There are several articles on the Internet describing this mechanism. It is possible to achieve SFD detection accuracy with an accuracy of ~8 ns.

Keep in mind that on an M4 processor you’re going to have an interrupt latency of 12 clock cycles and a timing quantisation of 1 clock cycle. So at 180MHz you’ve got between 67 and 72ns of interrupt latency on the processor you’re using. Even if the UWB timing is perfect your processors are going to add 5 ns of variability.

My initial question is not about interrupt delays and clocking of STM32. I want to understand how to correctly disable the GPIO De-Bounce function.