thotro / arduino-dw1000

A library that offers functionality to use Decawave's DW1000 chips/modules with Arduino.
Apache License 2.0
527 stars 288 forks source link

Sleep, Deep sleep, Snooze mode #30

Open thotro opened 9 years ago

thotro commented 9 years ago
greymfm commented 7 years ago

I'm trying (without success) to add section 4.4.1 (Low-Power Listening) to the DWM Arduino library to save battery energy at times the TAG is not used. The idea is that both the host CPU and DWM1000 is set to sleep mode, and the DWM1000 will continue to receive packets at configured time intervals (e.g. automatically awake every 30 seconds, then go back to sleep mode). If a packet is received, the DWM1000 can trigger the IRQ pin, and the host CPU will wake up from sleep mode too.

What could be wrong with this code, the datasheet looks too simple ;) The IRQ pin is not triggered when a packet arrives, actually the DWM1000 is not going into sleep mode at all, and not receiving anything anymore.

// ---DW1000Constants.h---
// PMSC
#define PMSC 0x36
#define PMSC_CTRL0_SUB 0x00
#define PMSC_CTRL1_SUB 0x04
#define PMSC_SNOZT_SUB 0x0C
#define LEN_PMSC_CTRL0 4
#define LEN_PMSC_CTRL1 4
#define LEN_PMSC_SNOZT 1

#define ARXSLP_BIT 12
#define SNOZE_BIT 13
#define SNOZR_BIT 14

// AON
#define AON 0x2C
#define AON_WCFG_SUB 0x00
#define AON_CTRL_SUB 0x02
#define AON_CFG0_SUB 0x06
#define LEN_AON_CFG0 4
#define AON_AON_CTRL 1
#define LEN_AON_WCFG 2

#define ONW_LDC_BIT 6
#define PRES_SLEEP_BIT 8
#define UPL_CFG_BIT 2
---DW1000.cpp---

bool DW1000Class::enterLowPowerListening(){
  // Configure the receiver parameters for channel, data rate, PRF, preamble code, etc. as required for normal receive operation before. 
  // Then enable and configure the Low-Power Listening functionality    

  byte pmscctrl1[LEN_PMSC_CTRL1];    
  memset(pmscctrl1, 0, LEN_PMSC_CTRL1);  
  readBytes(PMSC, PMSC_CTRL1_SUB, pmscctrl1, LEN_PMSC_CTRL1);  
  if (getBit(pmscctrl1, LEN_PMSC_CTRL1, ARXSLP_BIT)) return false;    

  Serial.println("enterLowPowerListening");       
  setBit(pmscctrl1, LEN_PMSC_CTRL1, ARXSLP_BIT, true); 
  writeBytes(PMSC, PMSC_CTRL1_SUB, pmscctrl1, LEN_PMSC_CTRL1);

  byte pmssnozt = 0x40; // SNOZ_TIM (snooze time)
  writeBytes(PMSC, PMSC_SNOZT_SUB, &pmssnozt, 1);  

  setBit(pmscctrl1, LEN_PMSC_CTRL1, SNOZE_BIT, true);
  setBit(pmscctrl1, LEN_PMSC_CTRL1, SNOZR_BIT, true);
  writeBytes(PMSC, PMSC_CTRL1_SUB, pmscctrl1, LEN_PMSC_CTRL1);

  byte aoncfg0[LEN_AON_CFG0];    
  memset(aoncfg0, 0, LEN_AON_CFG0);  
  readBytes(AON, AON_CFG0_SUB, aoncfg0, LEN_AON_CFG0);  
  aoncfg0[2]=24;    
  writeBytes(AON, AON_CFG0_SUB, aoncfg0, LEN_AON_CFG0);

  byte aonwcfg[LEN_AON_WCFG];    
  memset(aonwcfg, 0, LEN_AON_WCFG);  
  readBytes(AON, AON_WCFG_SUB, aonwcfg, LEN_AON_WCFG);    
  setBit(aonwcfg, LEN_AON_WCFG, ONW_LDC_BIT, true);
  setBit(aonwcfg, LEN_AON_WCFG, PRES_SLEEP_BIT, true);
  writeBytes(AON, AON_WCFG_SUB, aonwcfg, LEN_AON_WCFG);

  byte mask[LEN_SYS_MASK];
  memset(mask, 0, LEN_SYS_MASK);    
  setBit(mask, LEN_SYS_MASK, MRXFCG_BIT, true);    
  writeBytes(SYS_MASK, NO_SUB, mask, LEN_SYS_MASK);

  setBit(_sysctrl, LEN_SYS_CTRL, RXENAB_BIT, true);  
  writeBytes(SYS_CTRL, NO_SUB, _sysctrl, LEN_SYS_CTRL);

  return true;
}

bool DW1000Class::leaveLowPowerListening(){    
  byte pmscctrl1[LEN_PMSC_CTRL1];      
  memset(pmscctrl1, 0, LEN_PMSC_CTRL1);  
  readBytes(PMSC, PMSC_CTRL1_SUB, pmscctrl1, LEN_PMSC_CTRL1);  
  if (!getBit(pmscctrl1, LEN_PMSC_CTRL1, ARXSLP_BIT)) return false;     
  Serial.println("leaveLowPowerListening");
  setBit(pmscctrl1, LEN_PMSC_CTRL1, ARXSLP_BIT, false); 
  setBit(pmscctrl1, LEN_PMSC_CTRL1, SNOZE_BIT, false);
  setBit(pmscctrl1, LEN_PMSC_CTRL1, SNOZR_BIT, false);
  writeBytes(PMSC, PMSC_CTRL1_SUB, pmscctrl1, LEN_PMSC_CTRL1);   

  byte aonwcfg[LEN_AON_WCFG];    
  memset(aonwcfg, 0, LEN_AON_WCFG);  
  readBytes(AON, AON_WCFG_SUB, aonwcfg, LEN_AON_WCFG);    
  setBit(aonwcfg, LEN_AON_WCFG, ONW_LDC_BIT, false);
  setBit(aonwcfg, LEN_AON_WCFG, PRES_SLEEP_BIT, false);
  writeBytes(AON, AON_WCFG_SUB, aonwcfg, LEN_AON_WCFG);

  writeSystemEventMaskRegister();  
  return true;
}

For the host Arduino CPU I'm using this code in my Arduino loop (I double-checked it is actually working by triggering the IRQ pin manually):

void loop(){
  if (DW1000Ranging.getNetworkDevicesNumber() == 0){
    // no packets, let go DWM1000 and host CPU into sleep mode
    DW1000.enterLowPowerListening();    
    delay(1000);            
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
    sleep_enable();     
    sleep_mode();   // will enter sleep mode (requires pin interrupt to wake up again)
    //--- this is called on wakeup (rising IRQ by DWM1000) ---
    sleep_disable();  
    power_all_enable();
    Serial.println("wakeup");
    DW1000.leaveLowPowerListening();    
  }
greymfm commented 7 years ago

I have attached my (non-working sleep mode) test code containing the functions above. dw1000_ranging_demo.zip