From 0ab114da21ee8bb0ea0cb9aa9e76be1de9b981ee Mon Sep 17 00:00:00 2001 From: Michele Biondi Date: Thu, 7 Feb 2019 15:46:28 +0100 Subject: [PATCH] Added abstraction for Arduino SPI (#137) Now when you change clock mode the SPI speed not changes automatically. --- .../StandardRTLSAnchorB_TWR.ino | 3 +- .../StandardRTLSAnchorC_TWR.ino | 3 +- .../StandardRTLSAnchorMain_TWR.ino | 1 - .../StandardRTLSTag_TWR.ino | 1 - src/DW1000Ng.cpp | 170 ++++++++---------- src/DW1000Ng.hpp | 17 +- src/DW1000NgConstants.hpp | 5 +- src/DW1000NgRegisters.hpp | 12 +- src/DW1000NgUtils.cpp | 2 + src/DW1000NgUtils.hpp | 2 + src/SPIporting.cpp | 111 ++++++++++++ src/SPIporting.hpp | 79 ++++++++ 12 files changed, 290 insertions(+), 116 deletions(-) create mode 100644 src/SPIporting.cpp create mode 100644 src/SPIporting.hpp diff --git a/examples/StandardRTLSAnchorB_TWR/StandardRTLSAnchorB_TWR.ino b/examples/StandardRTLSAnchorB_TWR/StandardRTLSAnchorB_TWR.ino index d485a4f0..d5297caa 100644 --- a/examples/StandardRTLSAnchorB_TWR/StandardRTLSAnchorB_TWR.ino +++ b/examples/StandardRTLSAnchorB_TWR/StandardRTLSAnchorB_TWR.ino @@ -12,7 +12,6 @@ * This is an example slave anchor in a RTLS using two way ranging ISO/IEC 24730-62_2013 messages */ -#include #include #include #include @@ -109,7 +108,7 @@ void transmitRangeReport() { void loop() { RangeAcceptResult result = DW1000NgRTLS::anchorRangeAccept(NextActivity::RANGING_CONFIRM, next_anchor); if(result.success) { - delay(3); // Tweak based on your hardware + delay(2); // Tweak based on your hardware range_self = result.range; transmitRangeReport(); diff --git a/examples/StandardRTLSAnchorC_TWR/StandardRTLSAnchorC_TWR.ino b/examples/StandardRTLSAnchorC_TWR/StandardRTLSAnchorC_TWR.ino index 281aaa92..35042348 100644 --- a/examples/StandardRTLSAnchorC_TWR/StandardRTLSAnchorC_TWR.ino +++ b/examples/StandardRTLSAnchorC_TWR/StandardRTLSAnchorC_TWR.ino @@ -12,7 +12,6 @@ * This is an example slave anchor in a RTLS using two way ranging ISO/IEC 24730-62_2013 messages */ -#include #include #include #include @@ -109,7 +108,7 @@ void transmitRangeReport() { void loop() { RangeAcceptResult result = DW1000NgRTLS::anchorRangeAccept(NextActivity::ACTIVITY_FINISHED, blink_rate); if(result.success) { - delay(1); // Tweak based on your hardware + delay(4); // Tweak based on your hardware range_self = result.range; transmitRangeReport(); diff --git a/examples/StandardRTLSAnchorMain_TWR/StandardRTLSAnchorMain_TWR.ino b/examples/StandardRTLSAnchorMain_TWR/StandardRTLSAnchorMain_TWR.ino index 352300e7..3825610f 100644 --- a/examples/StandardRTLSAnchorMain_TWR/StandardRTLSAnchorMain_TWR.ino +++ b/examples/StandardRTLSAnchorMain_TWR/StandardRTLSAnchorMain_TWR.ino @@ -12,7 +12,6 @@ * This is an example master anchor in a RTLS using two way ranging ISO/IEC 24730-62_2013 messages */ -#include #include #include #include diff --git a/examples/StandardRTLSTag_TWR/StandardRTLSTag_TWR.ino b/examples/StandardRTLSTag_TWR/StandardRTLSTag_TWR.ino index e638da9b..f183f1a4 100644 --- a/examples/StandardRTLSTag_TWR/StandardRTLSTag_TWR.ino +++ b/examples/StandardRTLSTag_TWR/StandardRTLSTag_TWR.ino @@ -12,7 +12,6 @@ * This is an example tag in a RTLS using two way ranging ISO/IEC 24730-62_2013 messages */ -#include #include #include #include diff --git a/src/DW1000Ng.cpp b/src/DW1000Ng.cpp index 878f7cf6..3e727c73 100644 --- a/src/DW1000Ng.cpp +++ b/src/DW1000Ng.cpp @@ -44,12 +44,12 @@ #include #include -#include #include +#include "DW1000Ng.hpp" #include "DW1000NgUtils.hpp" #include "DW1000NgConstants.hpp" #include "DW1000NgRegisters.hpp" -#include "DW1000Ng.hpp" +#include "SPIporting.hpp" namespace DW1000Ng { @@ -103,15 +103,6 @@ namespace DW1000Ng { uint16_t _antennaTxDelay = 0; uint16_t _antennaRxDelay = 0; - /* SPI relative variables */ - #if defined(ESP32) || defined(ESP8266) - const SPISettings _fastSPI = SPISettings(20000000L, MSBFIRST, SPI_MODE0); - #else - const SPISettings _fastSPI = SPISettings(16000000L, MSBFIRST, SPI_MODE0); - #endif - const SPISettings _slowSPI = SPISettings(2000000L, MSBFIRST, SPI_MODE0); - const SPISettings* _currentSPI = &_fastSPI; - /* ############################# PRIVATE METHODS ################################### */ /* * Write bytes to the DW1000Ng. Single bytes can be written to registers via sub-addressing. @@ -130,8 +121,7 @@ namespace DW1000Ng { void _writeBytesToRegister(byte cmd, uint16_t offset, byte data[], uint16_t data_size) { byte header[3]; - uint8_t headerLen = 1; - uint16_t i = 0; + uint8_t headerLen = 1; // TODO proper error handling: address out of bounds // build SPI header @@ -148,17 +138,7 @@ namespace DW1000Ng { headerLen += 2; } } - SPI.beginTransaction(*_currentSPI); - digitalWrite(_ss, LOW); - for(i = 0; i < headerLen; i++) { - SPI.transfer(header[i]); // send header - } - for(i = 0; i < data_size; i++) { - SPI.transfer(data[i]); // write values - } - delayMicroseconds(5); - digitalWrite(_ss, HIGH); - SPI.endTransaction(); + SPIporting::writeToSPI(_ss, headerLen, header, data_size, data); } void _writeToRegister(byte cmd, uint16_t offset, uint32_t data, uint16_t data_size) { @@ -182,10 +162,9 @@ namespace DW1000Ng { * The number of bytes expected to be received. */ // TODO incomplete doc - void _readBytes(byte cmd, uint16_t offset, byte data[], uint16_t n) { + void _readBytes(byte cmd, uint16_t offset, byte data[], uint16_t data_size) { byte header[3]; uint8_t headerLen = 1; - uint16_t i = 0; // build SPI header if(offset == NO_SUB) { @@ -201,17 +180,7 @@ namespace DW1000Ng { headerLen += 2; } } - SPI.beginTransaction(*_currentSPI); - digitalWrite(_ss, LOW); - for(i = 0; i < headerLen; i++) { - SPI.transfer(header[i]); // send header - } - for(i = 0; i < n; i++) { - data[i] = SPI.transfer(0x00); // read values - } - delayMicroseconds(5); - digitalWrite(_ss, HIGH); - SPI.endTransaction(); + SPIporting::readFromSPI(_ss, headerLen, header, data_size, data); } // always 4 bytes @@ -253,6 +222,31 @@ namespace DW1000Ng { _writeBytesToRegister(bitRegister, RegisterOffset+idx, &targetByte, 1); } + + void _enableClock(byte clock) { + byte pmscctrl0[LEN_PMSC_CTRL0]; + memset(pmscctrl0, 0, LEN_PMSC_CTRL0); + _readBytes(PMSC, PMSC_CTRL0_SUB, pmscctrl0, LEN_PMSC_CTRL0); + if(clock == SYS_AUTO_CLOCK) { + pmscctrl0[0] = SYS_AUTO_CLOCK; + pmscctrl0[1] &= 0xFE; + } else if(clock == SYS_XTI_CLOCK) { + pmscctrl0[0] &= 0xFC; + pmscctrl0[0] |= SYS_XTI_CLOCK; + } else if(clock == SYS_PLL_CLOCK) { + pmscctrl0[0] &= 0xFC; + pmscctrl0[0] |= SYS_PLL_CLOCK; + } else if (clock == TX_PLL_CLOCK) { + pmscctrl0[0] &= 0xCF; + pmscctrl0[0] |= TX_PLL_CLOCK; + } else if (clock == LDE_CLOCK) { + pmscctrl0[0] = SYS_XTI_CLOCK; + pmscctrl0[1] = 0x03; + } else { + // TODO deliver proper warning + } + _writeBytesToRegister(PMSC, PMSC_CTRL0_SUB, pmscctrl0, 2); + } /* Steps used to get Temp and Voltage */ void _vbatAndTempSteps() { @@ -756,23 +750,6 @@ namespace DW1000Ng { _writeBytesToRegister(FS_CTRL, FS_PLLCFG_SUB, fspllcfg, LEN_FS_PLLCFG); } - /* Crystal calibration from OTP (if available) - * FS_XTALT - reg:0x2B, sub-reg:0x0E - * OTP(one-time-programmable) memory map - table 10 */ - void _fsxtalt() { - byte fsxtalt[LEN_FS_XTALT]; - byte buf_otp[4]; - _readBytesOTP(0x01E, buf_otp); //0x01E -> byte[0]=XTAL_Trim - if (buf_otp[0] == 0) { - // No trim value available from OTP, use midrange value of 0x10 - DW1000NgUtils::writeValueToBytes(fsxtalt, ((0x10 & 0x1F) | 0x60), LEN_FS_XTALT); - } else { - DW1000NgUtils::writeValueToBytes(fsxtalt, ((buf_otp[0] & 0x1F) | 0x60), LEN_FS_XTALT); - } - // write configuration back to chip - _writeBytesToRegister(FS_CTRL, FS_XTALT_SUB, fsxtalt, LEN_FS_XTALT); - } - void _tune() { // these registers are going to be tuned/configured _agctune1(); @@ -791,7 +768,6 @@ namespace DW1000Ng { _rftxctrl(); if(_autoTCPGDelay) _tcpgdelaytune(); _fspll(); - _fsxtalt(); } void _writeNetworkIdAndDeviceAddress() { @@ -1050,7 +1026,8 @@ namespace DW1000Ng { void _manageLDE() { // transfer any ldo tune values byte ldoTune[LEN_OTP_RDAT]; - _readBytesOTP(0x04, ldoTune); // TODO #define + uint16_t LDOTUNE_ADDRESS = 0x04; + _readBytesOTP(LDOTUNE_ADDRESS, ldoTune); // TODO #define if(ldoTune[0] != 0) { // TODO tuning available, copy over to RAM: use OTP_LDO bit } @@ -1067,38 +1044,33 @@ namespace DW1000Ng { otpctrl[0] = 0x00; otpctrl[1] = 0x80; _writeBytesToRegister(PMSC, PMSC_CTRL0_SUB, pmscctrl0, 2); + // uCode + _enableClock(LDE_CLOCK); + delay(5); _writeBytesToRegister(OTP_IF, OTP_CTRL_SUB, otpctrl, 2); + delay(1); + _enableClock(SYS_AUTO_CLOCK); delay(5); pmscctrl0[0] = 0x00; pmscctrl0[1] &= 0x02; _writeBytesToRegister(PMSC, PMSC_CTRL0_SUB, pmscctrl0, 2); } - void _enableClock(byte clock) { - byte pmscctrl0[LEN_PMSC_CTRL0]; - memset(pmscctrl0, 0, LEN_PMSC_CTRL0); - _readBytes(PMSC, PMSC_CTRL0_SUB, pmscctrl0, LEN_PMSC_CTRL0); - /* SYSCLKS */ - if(clock == SYS_AUTO_CLOCK) { - _currentSPI = &_fastSPI; - pmscctrl0[0] = SYS_AUTO_CLOCK; - pmscctrl0[1] &= 0xFE; - } else if(clock == SYS_XTI_CLOCK) { - _currentSPI = &_slowSPI; - pmscctrl0[0] &= 0xFC; - pmscctrl0[0] |= SYS_XTI_CLOCK; - } else if(clock == SYS_PLL_CLOCK) { - _currentSPI = &_fastSPI; - pmscctrl0[0] &= 0xFC; - pmscctrl0[0] |= SYS_PLL_CLOCK; - } else if (clock == TX_PLL_CLOCK) { /* NOT SYSCLKS but TX */ - _currentSPI = &_fastSPI; - pmscctrl0[0] &= 0xCF; - pmscctrl0[0] |= TX_PLL_CLOCK; + /* Crystal calibration from OTP (if available) + * FS_XTALT - reg:0x2B, sub-reg:0x0E + * OTP(one-time-programmable) memory map - table 10 */ + void _fsxtalt() { + byte fsxtalt[LEN_FS_XTALT]; + byte buf_otp[4]; + _readBytesOTP(0x01E, buf_otp); //0x01E -> byte[0]=XTAL_Trim + if (buf_otp[0] == 0) { + // No trim value available from OTP, use midrange value of 0x10 + DW1000NgUtils::writeValueToBytes(fsxtalt, ((0x10 & 0x1F) | 0x60), LEN_FS_XTALT); } else { - // TODO deliver proper warning + DW1000NgUtils::writeValueToBytes(fsxtalt, ((buf_otp[0] & 0x1F) | 0x60), LEN_FS_XTALT); } - _writeBytesToRegister(PMSC, PMSC_CTRL0_SUB, pmscctrl0, 2); + // write configuration back to chip + _writeBytesToRegister(FS_CTRL, FS_XTALT_SUB, fsxtalt, LEN_FS_XTALT); } void _clearReceiveStatus() { @@ -1237,23 +1209,29 @@ namespace DW1000Ng { // DW1000 data sheet v2.08 §5.6.1 page 20, the RSTn pin should not be driven high but left floating. pinMode(_rst, INPUT); } - // start SPI - SPI.begin(); + + SPIporting::SPIinit(); // pin and basic member setup // attach interrupt // TODO throw error if pin is not a interrupt pin if(_irq != 0xff) attachInterrupt(digitalPinToInterrupt(_irq), interruptServiceRoutine, RISING); - select(); + SPIporting::SPIselect(_ss, _irq); // reset chip (either soft or hard) - reset(); + SPIporting::setSPIspeed(SPIClock::SLOW); _enableClock(SYS_XTI_CLOCK); delay(5); + + // Configure the CPLL lock detect + _writeBitToRegister(EXT_SYNC, EC_CTRL_SUB, LEN_EC_CTRL, PLLLDT_BIT, true); + + // Configure XTAL trim + _fsxtalt(); + // load LDE micro-code _manageLDE(); - delay(5); // read the temp and vbat readings from OTP that were recorded during production test // see 6.3.1 OTP memory map @@ -1265,6 +1243,7 @@ namespace DW1000Ng { _enableClock(SYS_AUTO_CLOCK); delay(5); + SPIporting::setSPIspeed(SPIClock::FAST); _readNetworkIdAndDeviceAddress(); _readSystemConfigurationRegister(); @@ -1274,25 +1253,13 @@ namespace DW1000Ng { /* Cleared AON:CFG1(0x2C:0x0A) for proper operation of deepSleep */ _writeToRegister(AON, AON_CFG1_SUB, 0x00, LEN_AON_CFG1); + } void initializeNoInterrupt(uint8_t ss, uint8_t rst) { initialize(ss, 0xff, rst); } - void select() { - #if !defined(ESP32) && !defined(ESP8266) - if(_irq != 0xff) - SPI.usingInterrupt(digitalPinToInterrupt(_irq)); - #endif - pinMode(_ss, OUTPUT); - digitalWrite(_ss, HIGH); - } - - void end() { - SPI.end(); - } - /* callback handler management. */ void attachErrorHandler(void (* handleError)(void)) { _handleError = handleError; @@ -1479,11 +1446,15 @@ namespace DW1000Ng { } void softwareReset() { + SPIporting::setSPIspeed(SPIClock::SLOW); + /* Disable sequencing and go to state "INIT" - (a) Sets SYSCLKS to 01 */ _disableSequencing(); /* Clear AON and WakeUp configuration */ _writeToRegister(AON, AON_WCFG_SUB, 0x00, LEN_AON_WCFG); _writeToRegister(AON, AON_CFG0_SUB, 0x00, LEN_AON_CFG0); + // TODO change this with uploadToAON + _writeToRegister(AON, AON_CTRL_SUB, 0x00, LEN_AON_CTRL); _writeToRegister(AON, AON_CTRL_SUB, 0x02, LEN_AON_CTRL); /* (b) Clear SOFTRESET to all zero’s */ _writeToRegister(PMSC, PMSC_SOFTRESET_SUB, 0x00, LEN_PMSC_SOFTRESET); @@ -1652,6 +1623,7 @@ namespace DW1000Ng { void getTemperatureAndBatteryVoltage(float& temp, float& vbat) { // follow the procedure from section 6.4 of the User Manual _vbatAndTempSteps(); + delay(1); byte sar_lvbat = 0; _readBytes(TX_CAL, 0x03, &sar_lvbat, 1); byte sar_ltemp = 0; _readBytes(TX_CAL, 0x04, &sar_ltemp, 1); @@ -1869,6 +1841,10 @@ namespace DW1000Ng { } void enableTransmitPowerSpectrumTestMode(int32_t repeat_interval) { + /* DW1000 clocks must be set to crystal speed so SPI rate have to be lowered and will + not be increased again */ + SPIporting::setSPIspeed(SPIClock::SLOW); + _disableSequencing(); _configureRFTransmitPowerSpectrumTestMode(); _enableClock(SYS_PLL_CLOCK); diff --git a/src/DW1000Ng.hpp b/src/DW1000Ng.hpp index 33a629d0..cbe41092 100644 --- a/src/DW1000Ng.hpp +++ b/src/DW1000Ng.hpp @@ -47,7 +47,6 @@ #include #include #include -#include #include "DW1000NgConstants.hpp" #include "DW1000NgConfiguration.hpp" #include "DW1000NgCompileOptions.hpp" @@ -63,18 +62,14 @@ namespace DW1000Ng { */ void initialize(uint8_t ss, uint8_t irq, uint8_t rst = 0xff); - void initializeNoInterrupt(uint8_t ss, uint8_t rst = 0xff); - - /** - (Re-)selects a specific DW1000 chip for communication. Used in case you switched SPI to another device. - */ - void select(); - /** - Tells the driver library that no communication to a DW1000 will be required anymore. - This basically just frees SPI and the previously used pins. + Initiates and starts a sessions with a DW1000 without interrupt. If rst is not set or value 0xff, a soft resets (i.e. command + triggered) are used and it is assumed that no reset line is wired. + + @param[in] ss The SPI Selection pin used to identify the specific connection + @param[in] rst The reset line/pin for hard resets of ICs that connect to the Arduino. Value 0xff means soft reset. */ - void end(); + void initializeNoInterrupt(uint8_t ss, uint8_t rst = 0xff); /** Enable debounce Clock, used to clock the LED blinking diff --git a/src/DW1000NgConstants.hpp b/src/DW1000NgConstants.hpp index f5274257..097a3d78 100644 --- a/src/DW1000NgConstants.hpp +++ b/src/DW1000NgConstants.hpp @@ -155,6 +155,7 @@ constexpr byte SYS_AUTO_CLOCK = 0x00; constexpr byte SYS_XTI_CLOCK = 0x01; constexpr byte SYS_PLL_CLOCK = 0x02; constexpr byte TX_PLL_CLOCK = 0x20; +constexpr byte LDE_CLOCK = 0x03; /* range bias tables - APS011*/ @@ -229,4 +230,6 @@ enum class SFDMode {STANDARD_SFD, DECAWAVE_SFD}; enum class TransmitMode {IMMEDIATE, DELAYED}; -enum class ReceiveMode {IMMEDIATE, DELAYED}; \ No newline at end of file +enum class ReceiveMode {IMMEDIATE, DELAYED}; + +enum class SPIClock {SLOW, FAST}; \ No newline at end of file diff --git a/src/DW1000NgRegisters.hpp b/src/DW1000NgRegisters.hpp index 07da0d77..52d4e146 100644 --- a/src/DW1000NgRegisters.hpp +++ b/src/DW1000NgRegisters.hpp @@ -203,7 +203,7 @@ constexpr uint16_t LEN_OTP_ADDR = 2; constexpr uint16_t LEN_OTP_CTRL = 2; constexpr uint16_t LEN_OTP_RDAT = 4; -// AGC_TUNE1/2 (for re-tuning only) +// AGC_TUNE1/2/3 (for re-tuning only) constexpr uint16_t AGC_TUNE = 0x23; constexpr uint16_t AGC_TUNE1_SUB = 0x04; constexpr uint16_t AGC_TUNE2_SUB = 0x0C; @@ -212,6 +212,16 @@ constexpr uint16_t LEN_AGC_TUNE1 = 2; constexpr uint16_t LEN_AGC_TUNE2 = 4; constexpr uint16_t LEN_AGC_TUNE3 = 2; +// EXT_SYNC (External Synchronization Control) +constexpr uint16_t EXT_SYNC = 0x24; +constexpr uint16_t EC_CTRL_SUB = 0x00; +constexpr uint16_t PLLLDT_BIT = 2; +constexpr uint16_t EC_RXTC_SUB = 0x04; +constexpr uint16_t EC_GOLP_SUB = 0x08; +constexpr uint16_t LEN_EC_CTRL = 4; +constexpr uint16_t LEN_EC_RXTC = 4; +constexpr uint16_t LEN_EC_GOLP = 4; + // DRX_TUNE2 (for re-tuning only) constexpr uint16_t DRX_TUNE = 0x27; constexpr uint16_t DRX_TUNE0b_SUB = 0x02; diff --git a/src/DW1000NgUtils.cpp b/src/DW1000NgUtils.cpp index 9dc3ddd5..ac7b6dd0 100644 --- a/src/DW1000NgUtils.cpp +++ b/src/DW1000NgUtils.cpp @@ -48,6 +48,7 @@ #include "DW1000NgRegisters.hpp" namespace DW1000NgUtils { + /* * Set the value of a bit in an array of bytes that are considered * consecutive and stored from MSB to LSB. @@ -136,4 +137,5 @@ namespace DW1000NgUtils { } memcpy(bytes, eui_byte, LEN_EUI); } + } diff --git a/src/DW1000NgUtils.hpp b/src/DW1000NgUtils.hpp index 88dcd5e0..87fcccce 100644 --- a/src/DW1000NgUtils.hpp +++ b/src/DW1000NgUtils.hpp @@ -45,8 +45,10 @@ #pragma once #include +#include "DW1000NgConstants.hpp" namespace DW1000NgUtils { + /** Returns target bit value inside a byte array diff --git a/src/SPIporting.cpp b/src/SPIporting.cpp new file mode 100644 index 00000000..ff987592 --- /dev/null +++ b/src/SPIporting.cpp @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (c) 2018 Michele Biondi, Andrea Salvatori + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @file SPIporting.hpp + * Arduino porting for the SPI interface. +*/ + +#include +#include +#include "SPIporting.hpp" +#include "DW1000NgConstants.hpp" +#include "DW1000NgRegisters.hpp" + +namespace SPIporting { + + namespace { + + constexpr uint32_t EspSPImaximumSpeed = 20000000; //20MHz + constexpr uint32_t ArduinoSPImaximumSpeed = 16000000; //16MHz + constexpr uint32_t SPIminimumSpeed = 2000000; //2MHz + + /* SPI relative variables */ + #if defined(ESP32) || defined(ESP8266) + const SPISettings _fastSPI = SPISettings(EspSPImaximumSpeed, MSBFIRST, SPI_MODE0); + #else + const SPISettings _fastSPI = SPISettings(ArduinoSPImaximumSpeed, MSBFIRST, SPI_MODE0); + #endif + const SPISettings _slowSPI = SPISettings(SPIminimumSpeed, MSBFIRST, SPI_MODE0); + const SPISettings* _currentSPI = &_fastSPI; + + void _openSPI(uint8_t slaveSelectPIN) { + SPI.beginTransaction(*_currentSPI); + digitalWrite(slaveSelectPIN, LOW); + } + + void _closeSPI(uint8_t slaveSelectPIN) { + digitalWrite(slaveSelectPIN, HIGH); + SPI.endTransaction(); + } + } + + void SPIinit() { + SPI.begin(); + } + + void SPIend() { + SPI.end(); + } + + void SPIselect(uint8_t slaveSelectPIN, uint8_t irq) { + #if !defined(ESP32) && !defined(ESP8266) + if(irq != 0xff) + SPI.usingInterrupt(digitalPinToInterrupt(irq)); + #endif + pinMode(slaveSelectPIN, OUTPUT); + digitalWrite(slaveSelectPIN, HIGH); + } + + void writeToSPI(uint8_t slaveSelectPIN, uint8_t headerLen, byte header[], uint16_t dataLen, byte data[]) { + _openSPI(slaveSelectPIN); + for(auto i = 0; i < headerLen; i++) { + SPI.transfer(header[i]); // send header + } + for(auto i = 0; i < dataLen; i++) { + SPI.transfer(data[i]); // write values + } + delayMicroseconds(5); + _closeSPI(slaveSelectPIN); + } + + void readFromSPI(uint8_t slaveSelectPIN, uint8_t headerLen, byte header[], uint16_t dataLen, byte data[]){ + _openSPI(slaveSelectPIN); + for(auto i = 0; i < headerLen; i++) { + SPI.transfer(header[i]); // send header + } + for(auto i = 0; i < dataLen; i++) { + data[i] = SPI.transfer(0x00); // read values + } + delayMicroseconds(5); + _closeSPI(slaveSelectPIN); + } + + void setSPIspeed(SPIClock speed) { + if(speed == SPIClock::FAST) { + _currentSPI = &_fastSPI; + } else if(speed == SPIClock::SLOW) { + _currentSPI = &_slowSPI; + } + } + +} \ No newline at end of file diff --git a/src/SPIporting.hpp b/src/SPIporting.hpp new file mode 100644 index 00000000..4c4be220 --- /dev/null +++ b/src/SPIporting.hpp @@ -0,0 +1,79 @@ +/* + * MIT License + * + * Copyright (c) 2018 Michele Biondi, Andrea Salvatori + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @file SPIporting.hpp + * Arduino porting for the SPI interface. +*/ + +#pragma once + +#include +#include "DW1000NgConstants.hpp" + +namespace SPIporting{ + + /** + Initializes the SPI bus. + */ + void SPIinit(); + + /** + Tells the driver library that no communication to a DW1000 will be required anymore. + This basically just frees SPI and the previously used pins. + */ + void SPIend(); + + /** + (Re-)selects a specific DW1000 chip for communication. Used in case you switched SPI to another device. + */ + void SPIselect(uint8_t slaveSelectPIN, uint8_t irq = 0xff); + + /** + Arduino function to write to the SPI. + Takes two separate byte buffers for write header and write data + + @param [in] Header lenght + @param [in] Header array built before + @param [in] Data lenght + @param [in] Data array + */ + void writeToSPI(uint8_t slaveSelectPIN, uint8_t headerLen, byte header[], uint16_t dataLen, byte data[]); + + /** + Arduino function to read from the SPI. + Takes two separate byte buffers for write header and write data + + @param [in] Header lenght + @param [in] Header array built before + @param [in] Data lenght + @param [in] Data array + */ + void readFromSPI(uint8_t slaveSelectPIN, uint8_t headerLen, byte header[], uint16_t dataLen, byte data[]); + + /** + Sets speed of SPI clock, fast or slow(20MHz or 2MHz) + + @param [in] SPIClock FAST or SLOW + */ + void setSPIspeed(SPIClock speed); +} \ No newline at end of file