DW3000 problem with interrupt handling

Hi :slight_smile:

I’m currently working on a project for my university and I’m having trouble using interrupts with the dw3000 board with an ESP32 wrover B.

I’m using the legacy Arduino IDE. The Arduino version is 1.8.19 and the gcc version is 11.4.0.

I cloned the repository of the lib from GitHub - Makerfabs/Makerfabs-ESP32-UWB-DW3000. Added it to the Arduino library dir.

Now I wanted to use deep sleep when sending some data every x seconds to reduce power consumption in my scenario. To do this I wanted to use interrupts to complete the transmission and wake up the system.

I used the example for sending data in the repository (simple_tx) and added two callbacks to handle the interrupts.

dwt_setcallbacks(&txDoneHandler, NULL, NULL, NULL, NULL, &readyHandler);

Next I called

dwt_setinterrupt(SYS_ENABLE_LO_SPIRDY_ENABLE_BIT_MASK | SYS_ENABLE_LO_TXFRS_ENABLE_BIT_MASK, 0 , DWT_ENABLE_INT );

I then cleared the status bits and sent data, which works. But the callbacks are never called. My first problem was that when I called dwt_setinterrupt I got an Illegal Instruction Exception and the system rebooted. With the help of debugging tools I found out that the problem must be in the function decamutexon(), which disables interrupts and saves the current state. The function is declared to return a value, but it doesn’t. If I change it to return something like NULL, the crash is gone, but the interrupts still don’t work. So there seems to be something wrong with the compiler?

Ignoring this error, what am I doing wrong in handling interrupts, is there a flag that needs to be set ?

I have no experience with the makerfabs boards or drivers but those look like standard decawave driver function calls so they have probably simply ported them (badly by the sound of it).

I’ve only ever used the Tx and Rx interrupts using the standard drivers but looking at my code for that are you sure it’s supposed to be &txDoneHandler? I think you may want just txDoneHandler without the &.

Also after enabling the interrupts it’s worth writing to the status register to clear the interrupt flag, if that bit is already set for any reason then you won’t get an further interrupts.

You might be right. But still no interrupts are issued. Maybe it’s more useful if I display the code here :slight_smile:

const uint8_t PIN_RST = 27; // reset pin
const uint8_t PIN_IRQ = 34; // irq pin
const uint8_t PIN_SS = 4; // spi select pin

static dwt_config_t config = {
  5,               /* Channel number. */
  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. */
  1,               /* 0 to use standard 8 symbol SFD, 1 to use non-standard 8 symbol, 2 for non-standard 16 symbol SFD and 3 for 4z 8 symbol SDF type */
  DWT_BR_6M8,      /* Data rate. */
  DWT_PHRMODE_STD, /* PHY header mode. */
  DWT_PHRRATE_STD, /* PHY header rate. */
  (129 + 8 - 8),   /* SFD timeout (preamble length + 1 + SFD length - PAC size). Used in RX only. */
  DWT_STS_MODE_OFF,
  DWT_STS_LEN_64, /* STS length, see allowed values in Enum dwt_sts_lengths_e */
  DWT_PDOA_M0     /* PDOA mode off */
};
void setup() {
  UART_init();
  Serial.print("Initialse... ");

  spiBegin(PIN_IRQ, PIN_RST);
  spiSelect(PIN_SS);

  delay(200);
  while (!dwt_checkidlerc())
  {
    Serial.println("IDLE FAILED 1 ... Retry");
    while (100);
  }

  dwt_softreset();
  delay(200);

  while (!dwt_checkidlerc())
  {
    Serial.println("IDLE FAILED 2 ... Retry");
    while (100);
  }

  if (dwt_initialise(DWT_DW_INIT) == DWT_ERROR)
  {
    Serial.println("INIT FAILED ... Retry");
    while (100);
  }

  dwt_setleds(DWT_LEDS_ENABLE | DWT_LEDS_INIT_BLINK);
  
  if (dwt_configure(&config))
  {
    Serial.println("Config FAILED ... Retry");
    while (100);
  }
  
  Serial.println("Install interrupthandlers");
  dwt_setcallbacks(doneTxIrq, NULL, NULL, NULL, NULL, wakeUpIrq);
  
  dwt_configuretxrf(&txconfig_options);

  Serial.println("Enable interrupts");
  dwt_setinterrupt(SYS_ENABLE_LO_SPIRDY_ENABLE_BIT_MASK | SYS_ENABLE_LO_TXFRS_ENABLE_BIT_MASK, 1 , DWT_ENABLE_INT );
  Serial.println("Configure sleep");
  dwt_configuresleepcnt(1000);
  dwt_configuresleep(DWT_CONFIG, DWT_PRES_SLEEP | 0x40 | DWT_SLP_EN);
  
  dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS_BIT_MASK | SYS_STATUS_SPIRDY_BIT_MASK);
  Serial.println("Ready");
}

And both event handlers. I don’t assume the error to be here.

void wakeUpIrq(const dwt_cb_data_t * data){
  Serial.println("Wake up");
  while (!dwt_checkidlerc()){};
  dwt_restoreconfig();
  tx_msg.message_id++;
  dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_SPIRDY_BIT_MASK);
  txData();
}

void doneTxIrq(const dwt_cb_data_t * data){
  Serial.println("TX done");
  dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS_BIT_MASK);
  dwt_entersleep(DWT_DW_IDLE);  
}

So the plan is that you put the DW3000 to sleep for a fixed time. It then wakes up and interrupts your processor. The processor transmits a packet and then on the Tx done interrupt puts the DW3000 back to sleep.

Are you putting the processor to sleep too? If not then the power saving of the DW3000 being asleep may not be that much. Personally I’d put the DW3000 into deep sleep, put the processor into sleep and use a CPU timer to wake the processor which then wakes the DW3000. That way the processor is always in control.

Having said that I can’t think of anything fundamentally wrong with what you’re doing. Serial prints and infinite loops are generally a very bad idea from within an interrupt, ideally wakeUp would set a flag (make sure it’s defied as volatile) and then the main background loop would check for that flag and do the actual restore transmit. But while not ideal I don’t think the way you have it would break things in this situation.

Rather than going to sleep at the end of your initialisation can you set it to instead transmit. That will let you test the transmit done interrupt. If that’s not working then fix that first before you worry about the sleep side. If you aren’t getting that then first check the physical interrupt pin is changing (this is a university project so I’d expect some sort of basic oscilloscope is available somewhere). If the firmware isn’t responding to an external signal it’s always first checking that the external signal is actually where you expect it :slight_smile:
If the Tx interrupt isn’t working then wait a short while (enough time for the Tx to complete) and then read the status register. Does it claim a transmit was completed? That should help work out where the issue is.

Once you’ve got Tx working correctly then if sleep still isn’t working you at least know it’s something specific to the sleep configuration that’s wrong.

Thanks for your reply. I’m at home at the moment and will have a look at the interrupt pin itself tomorrow. What I can say for sure is that the status register responds correctly after sending a message. The SYS_STATUS_TXFRS_BIT_MASK bit is set a few ms after the tx request is initialised. When using a simple loop i can send more than one message using the status register.

Hi @chris91, do you have any updates on this? I am starting to look into using interrupts with my DW3000 and it would be helpful to know if you managed to get this working (and what changes you had to make to the code you posted on an earlier reply)

There seems to be an error in the function decamutexon. It’s not returning a value that is requested by the function prototype. The compiler does something weird then causing an illegal instruction on my esp32 rover.

I got it working by ignoring the dw3000 library and simple used attachInterrupt. attachInterrupt() - Arduino-Referenz

See Code below:

dwt_setinterrupt( SYS_ENABLE_LO_TXFRS_ENABLE_BIT_MASK | SYS_ENABLE_LO_RXFCG_ENABLE_BIT_MASK | SYS_ENABLE_LO_RXFCE_ENABLE_BIT_MASK, 0x0 , DWT_ENABLE_INT_ONLY );
  attachInterrupt(PIN_IRQ, isr, RISING);

Hope that’s somehow helpful. Another idea could be to take a closer look at the Challenger RP2040 UWB modules. They have a github with a cleaned up dw3000 lib.