DW1000 UWB driver based on work of Matthias Grob & Manuel Stalder - ETH Zürich - 2015

Dependencies:   BurstSPI

DW1000.h

Committer:
AndyA
Date:
2016-04-20
Revision:
9:326bf149c8bc
Parent:
8:0b408e77b701
Child:
10:f1e3c04080d6

File content as of revision 9:326bf149c8bc:

// by Matthias Grob & Manuel Stalder - ETH Zürich - 2015

#ifndef DW1000_H
#define DW1000_H

#include "mbed.h"
#include "BurstSPI.h"
#include "DW1000Registers.h"
#include "DW1000Setup.h"

#define TIMEUNITS_TO_US       (1/(128*499.2))               // conversion between the decawave timeunits (ca 15.65ps) to microseconds.
#define US_TO_TIMEUNITS       ((uint32_t)(128*499.2))                   // conversion between microseconds to the decawave timeunits (ca 15.65ps).
#define c_mPerS     299792458
#define c_mmPerTick (c_mPerS * TIMEUNITS_TO_US / 1000)
#define c_mPerTick  (c_mmPerTick/1000)



/** A DW1000 driver
*
* It is expected that the protocol implimentation above this will inherit this object.
* If not using this structure then move the protected functions to being public.
* 
*/
class DW1000
{
public:

    /** Constructor.
    *
    *  The radio will default to DW1000Setup::tunedDefault until you call applySetup() with a new configuration.
    */
    DW1000(PinName MOSI, PinName MISO, PinName SCLK, PinName CS, PinName IRQ);              // constructor, uses SPI class

    /** Read the device ID
    * @return the device ID (0xDECA0130)
    */
    uint32_t getDeviceID();                                                                 // gets the Device ID which should be 0xDECA0130 (good for testing SPI!)

    /** Read the Extended Unique ID
    * @return The device EUI as stored in the system registers
    */
    uint64_t getEUI();

    /** Set the Extended Unique ID
    * @param EUI The EUID to use
    *
    * Note - ID is only valid until the next power cycle and overrides the value in the OTP memory.
    * To set a value that is automatically loaded on startup set OTP memory addresses 0 and 1.
    */
    void setEUI(uint64_t EUI);                                                              // sets 64 bit Extended Unique Identifier according to IEEE standard

    /** Read voltage input

    @return the current device voltage

        For accurate ranging the voltage of the device should be taken into account.
        User manual give variation as ~5.35cm / V
    */
    float getVoltage();                                                                     // gets the current chip voltage measurement form the A/D converter

    /** Read on board temperature sensor
    @return The temperature in C

    For accurate ranging the temperature of the device should be taken into account.
    User manual give variation as ~2.15mm / C
    */
    float getTemperature();                                                                 // gets the current chip temperature measurement form the A/D converter

    /** Get the status register
    * @return The system status register
    *
    * See user manual section 7.2.17 for details
    */
    uint64_t getStatus();                                                                   // get the 40 bit device status


    /** Set receive antenna delay
    * @param ticks Delay in system clock cycles
    */
    void setRxDelay(uint16_t ticks);
    /** Set transmit antenna delay
    * @param ticks Delay in system clock cycles
    */
    void setTxDelay(uint16_t ticks);

    /** Set receive antenna delay in meters
    * @param errorDistance Delay in meters at speed of light
    */
    void setRxDelayDistance(double errorDistance) {
        setRxDelay(errorDistance/c_mPerTick);
    };

    /** Set transmit antenna delay  in meters
    * @param errorDistance Delay in meters at speed of light
    */
    void setTxDelayDistance(double errorDistance) {
        setTxDelay(errorDistance/c_mPerTick);
    };

    /** Read a value from the OTP memory
    * @param word_address The OTP memory address to read.
    * @return The 32 bit value at that address.
    *
    * See Section 6.3.1 of the user manual for the memory map.
    */
    uint32_t readOTP (uint16_t word_address);

    /** Write a value to the OTP memory
    * @param word_address The OTP memory address to read.
    * @param data The value to write
    * @return True if the write was sucessful.
    *
    * Writes the supplied data to the OTP memory and then reads it back to verify it was sucessfully programmed.
    * WARNING - this is a one time operation for each memory address.
    * See Section 6.3.1 of the user manual for the memory map.
    * It is recommened that the device is reset or power cycled after programing.
    */
    bool writeOTP(uint16_t word_address,uint32_t data);                                          // program a value in the OTP. It is recommended to reset afterwards.

    /** get the current radio configuration
    * @return A pointer to a DW1000Setup object of the current setup.
    *
    * Note to change the setup you must make a copy of the current setup and then pass that to applySetup().
    */
    DW1000Setup *getSetup();

    /** Get the current Transmit gain settings.
    *
    * @param power Optional, is set to the first power in dBm
    * @param boost500 Optional, is set to the second power in dBm
    * @param boost250 Optional, is set to the third power in dBm
    * @param boost125 Optional, is set to the forth power in dBm
    * @return The raw transmit gain register value
    *
    * If smart power is on then power represents the normal transmit power,
    * boost500-boost125 indicates the power used for packets of that number of us or less.
    *
    * If smart power is off then boost500 represents the gain for the PHY header, boost250 the gain for the main message.
    * power and boost125 are not used.
    */
    uint32_t getTxPower(float *power = NULL, float *boost500 = NULL, float *boost250 = NULL, float *boost125 = NULL);

    /** Set Transmit gain
    *
    * @param normalPowercB Normal transmit gain to use.
    * @param boost500 Gain to use for 6.8Mb/s packets of under 500ms.
    * @param boost250 Gain to use for 6.8Mb/s packets of under 250ms.
    * @param boost125 Gain to use for 6.8Mb/s packets of under 125ms.
    *
    * All gains are in dB. Gains can be between 0 and 33.5dB.
    * Boost gains are optional, if not specified boost gains are set to the power for the lower rate (e.g. boost125 is set to the boost250 level).
    * If smart power is disabled then the normal gain is used for all settings.
    * The values in the internal DW1000Setup are updated to reflect the configured powers.
    */
    void setTxPower(float normalPowerdB, float boost500 = 0, float boost250 = 0, float boost125 = 0);

    /** Get the rx signal power for the last packet
    *
    * @param direct Is set to the direct path Rx power in dBm
    * @param total Is set to the total Rx power in dBm
    *
    * According to the DW1000 manual if the direct path power is within 6dB of the total then it was probably a LoS measurment.
    * If there is more than 10dB difference then it's probably an indirect path.
    */
    void getRxSignalPower(float *direct, float *total);

    /** Get a metric of timestamp accuracy
    *
    * @param sigAmp Optional location to return the raw signal amplitude
    * @param noiseAmp Optional location to return the raw channel noise level
    * @return Timestamp quality metric
    *
    * The quality metric is a somewhat arbitary number based on channel noise and direct path strength
    */
    float getRxQuality(uint16_t *sigAmp = NULL, uint16_t *noiseAmp = NULL);

protected:

    /**
    * Sets the callbacks on packet Rx and Tx
    * @param callbackRX The function to call on packet Rx complete
    * @param callbackTX The function to call on packet Tx complete
    *
    * set either or both to null to disable the appropriate interupt
    */
    void setCallbacks(void (*callbackRX)(void), void (*callbackTX)(void));                  // setter for callback functions, automatically enables interrupt, if NULL is passed the coresponding interrupt gets disabled

    /**
    * c++ version of setCallbacks()
    * @param tptr object for callbacks
    * @param mptrRX method to call on packet Rx complete
    * @param mptrTX method to call on packet Tx complete
    *
    */
    template<typename T>
    void setCallbacks(T* tptr, void (T::*mptrRX)(void), void (T::*mptrTX)(void)) {      // overloaded setter to treat member function pointers of objects
        callbackRX.attach(tptr, mptrRX);                                                    // possible client code: dw.setCallbacks(this, &A::callbackRX, &A::callbackTX);
        callbackTX.attach(tptr, mptrTX);                                                    // concept seen in line 100 of http://developer.mbed.org/users/mbed_official/code/mbed/docs/4fc01daae5a5/InterruptIn_8h_source.html
        setInterrupt(true,true);
    }

    /** Get the last packet recieve time
    * @return the internal time stamp for the last packet Rx
    *
    * Time is counted on a clock running at 499.2MHz * 128 (~15.65ps)
    * This value is raw time minus user set Rx antenna delay.
    */
    uint64_t getRXTimestamp();

    /** Get the last packet transmit time
    * @return the internal time stamp for the last packet Tx
    *
    * Time is counted on a clock running at 499.2MHz * 128 (~15.65ps)
    * This value is raw time plus user set Tx antenna delay to give time at the antenna.
    */
    uint64_t getTXTimestamp();

    /** Send a packet
    * @param message A buffer containing the data to send
    * @param length The length of the data in bytes.
    *
    * The supplied packet is transmitted as soon as possible and the reciever re-enabled once transmission is complete.
    * Maximum packet size is 125 bytes.
    *
    * The receiver is re-activated as soon as packet transmission is complete.
    */
    void sendFrame(uint8_t* message, uint16_t length);                                      // send a raw frame (length in bytes)

    /** Send a packet at a certain time
    * @param message A buffer containing the data to send
    * @param length The length of the data in bytes.
    * @param TxTimestamp The timestamp to send the packet.
    *
    * The supplied packet is transmitted once the internal clock reaches the specified timestamp.
    * Maximum packet size is 125 bytes.
    * Rx is disabled as soon as this command is issued and re-enabled once transmission is complete.
    * Note - 9 LSBs are ignored so timings are only accurate to ~8ns. For more accurate timing check the
    * tx timestamp after transmission is complete.
    *
    * The receiver is re-activated as soon as packet transmission is complete.
    *
    */
    void sendDelayedFrame(uint8_t* message, uint16_t length, uint64_t TxTimestamp);

    /** Set up data for a transmit on sync
    * @param message A buffer containing the data to send
    * @param length The length of the data in bytes.
    *
    * Data is loaded into the transmit buffer but the transmission is not started.
    * Maximum packet size is 125 bytes.
    */
    void setupSyncedFrame(uint8_t* message, uint16_t length);

    /** Transmit on the next sync pulse
    *
    * On the next rising edge of the sync line the transmitter will be activated.
    * The packet must have previously been set up using setupSyncedFrame()
    *
    * Rx is disabled until transmission is complete and then re-enabled.
    */
    void armSyncedFrame();

    /** Get last packet size
    * @return The length in bytes of the last packet received
    */
    uint16_t getFramelength();                                                              // to get the framelength of the received frame from the PHY header

    /** Get last recieved packet
    * @param buffer The location to put the received data
    * @param length The number of bytes to read
    */
    void readRxBuffer( uint8_t *buffer, int length ) {
        readRegister(DW1000_RX_BUFFER, 0, buffer, length);
    }

    /** Enable reciever
    *
    * This is automatically done after each Tx completes but can also be forced manually
    */
    void startRX();                                                                         // start listening for frames

    /** Disable radio link
    *
    * Disables both the recieve and transmit systems.
    * Any transmissions waiting for a delayed time or sync pulse will be canceled.
    */
    void stopTRX();                                                                         // disable tranceiver go back to idle mode

    /** Reset the reciever logic
    *
    * This should be done after any receive errors
    */
    void resetRX();                                                                         // soft reset only the tranciever part of DW1000

    /** Enable/Disable interrupts
    * @param RX true to enable recieve interrupts
    * @param TX true to enable transmit interrupts
    *
    * For c style callbacks simply set the callback to null to disable it.
    * When using c++ style callbacks both are enabled as default, this allows a method to disabled one or both.
    */
    void setInterrupt(bool RX, bool TX);                                                    // set Interrupt for received a good frame (CRC ok) or transmission done

   
    /** apply a new radio setup to the UWB system
    * @param setup The new settings to use
    * @return true if the setup was applied.
    *
    * The setup object supplied is copied and can be disposed of after the call.
    * If the supplied setup fails DW1000Setup::check() then it is ignored and the function returns false.
    * Note - this will reset the radio. You must re-enable interupts, receiver etc. after calling it. 
    */
    bool applySetup(DW1000Setup *setup);

private:
    void resetAll();                                                                        // soft reset the entire DW1000 (some registers stay as they were see User Manual)

    void setupRadio();

    // system register setup functions
    void setupGPIO();
    void setupAGC();
    void setupRxConfig();
    void setupLDE();
    void setupChannel();
    void setupTxFrameCtrl();
    void setupAnalogRF();
    void setupFreqSynth();
    void setupTxCalibration();
    void setupSystemConfig();
    void setupPower();

    void loadLDE();                                                                         // load the leading edge detection algorithm to RAM, [IMPORTANT because receiving malfunction may occur] see User Manual LDELOAD on p22 & p158
    void loadLDOTUNE();                                                                     // load the LDO tuning as set in the factory

    uint8_t powerToRegValue(float powerdB);
    float regToPowerValue(uint8_t powerVal);

    DW1000Setup systemConfig;

    // Interrupt
    InterruptIn irq;                                                                        // Pin used to handle Events from DW1000 by an Interrupthandler
    FunctionPointer callbackRX;                                                             // function pointer to callback which is called when successfull RX took place
    FunctionPointer callbackTX;                                                             // function pointer to callback which is called when successfull TX took place
    void ISR();                                                                             // interrupt handling method (also calls according callback methods)

    // SPI Inteface
    BurstSPI spi;                                                                                // SPI Bus
    DigitalOut cs;                                                                          // Slave selector for SPI-Bus (here explicitly needed to start and end SPI transactions also usable to wake up DW1000)

    uint8_t readRegister8(uint8_t reg, uint16_t subaddress);                                // expressive methods to read or write the number of bits written in the name
    uint16_t readRegister16(uint8_t reg, uint16_t subaddress);
    uint32_t readRegister32(uint8_t reg, uint16_t subaddress);
    uint64_t readRegister40(uint8_t reg, uint16_t subaddress);
    uint64_t readRegister64(uint8_t reg, uint16_t subaddress);
    void writeRegister8(uint8_t reg, uint16_t subaddress, uint8_t buffer);
    void writeRegister16(uint8_t reg, uint16_t subaddress, uint16_t buffer);
    void writeRegister32(uint8_t reg, uint16_t subaddress, uint32_t buffer);
    void writeRegister40(uint8_t reg, uint16_t subaddress, uint64_t buffer);

    void readRegister(uint8_t reg, uint16_t subaddress, uint8_t *buffer, int length);       // reads the selected part of a slave register into the buffer memory
    void writeRegister(uint8_t reg, uint16_t subaddress, uint8_t *buffer, int length);      // writes the buffer memory to the selected slave register
    void setupTransaction(uint8_t reg, uint16_t subaddress, bool write);                    // sets up an SPI read or write transaction with correct register address and offset
    void select();                                                                          // selects the only slave for a transaction
    void deselect();                                                                        // deselects the only slave after transaction
};

#endif