DW1000 UWB driver based on work of Matthias Grob & Manuel Stalder - ETH Zürich - 2015
DW1000.h
- Committer:
- AndyA
- Date:
- 2017-03-28
- Revision:
- 12:da4b09aff6af
- Parent:
- 10:f1e3c04080d6
- Child:
- 13:8718966cd81e
File content as of revision 12:da4b09aff6af:
// 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. * * @note 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); void getFullQualityMetrics(uint16_t *std_noise, uint16_t *fp_amp1, uint16_t *fp_amp2, uint16_t *fp_amp3, uint16_t *cir_pwr, uint16_t *preAmbleAcc, uint16_t *preAmbleAcc_NoSat); 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); /** Get the first path amplitude values * @param fp_amp2 Will be set to first path second peak amplitude * @param fp_amp3 Will be set to first path third peak amplitude * * Reads the two registers for the last packet recieved. Used for quality metrics. */ void getFirstPath(uint16_t *fp_amp2,uint16_t *fp_amp3); /** Get the LDE threshold value * @return the Leading edge detection threshold register value */ inline uint16_t getLDE_THRESH() { return readRegister16(DW1000_LDE_CTRL,DWLDE_LDE_THRESH); } /** Get the LDE Peak path amplitude * @return the Leading edge detection peak path amplitude value */ inline uint16_t getLDE_PPAMPL() { return readRegister16(DW1000_LDE_CTRL,DWLDE_LDE_PPAMPL); } 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