diff --git a/src/Module.cpp b/src/Module.cpp index 522d6ae2..88d12d20 100644 --- a/src/Module.cpp +++ b/src/Module.cpp @@ -253,6 +253,132 @@ void Module::SPItransfer(uint8_t cmd, uint8_t reg, uint8_t* dataOut, uint8_t* da this->SPIendTransaction(); } +int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForGpio, uint32_t timeout) { + #if defined(RADIOLIB_VERBOSE) + uint8_t debugBuff[RADIOLIB_STATIC_ARRAY_SIZE]; + #endif + + // pull NSS low + this->digitalWrite(this->getCs(), LOW); + + // ensure GPIO is low + uint32_t start = this->millis(); + while(this->digitalRead(this->getGpio())) { + this->yield(); + if(this->millis() - start >= timeout) { + this->digitalWrite(this->getCs(), HIGH); + return(RADIOLIB_ERR_SPI_CMD_TIMEOUT); + } + } + + // start transfer + this->SPIbeginTransaction(); + + // send command byte(s) + for(uint8_t n = 0; n < cmdLen; n++) { + this->SPItransfer(cmd[n]); + } + + // variable to save error during SPI transfer + int16_t state = RADIOLIB_ERR_NONE; + + // send/receive all bytes + if(write) { + for(uint8_t n = 0; n < numBytes; n++) { + // send byte + uint8_t in = this->SPItransfer(dataOut[n]); + #if defined(RADIOLIB_VERBOSE) + debugBuff[n] = in; + #endif + + // check status + if(this->SPIparseStatusCb != nullptr) { + state = this->SPIparseStatusCb(in); + } + } + + } else { + // skip the first byte for read-type commands (status-only) + uint8_t in = this->SPItransfer(this->SPIreadCommand); + #if defined(RADIOLIB_VERBOSE) + debugBuff[0] = in; + #endif + + // check status + if(this->SPIparseStatusCb != nullptr) { + state = this->SPIparseStatusCb(in); + } else { + state = RADIOLIB_ERR_NONE; + } + + // read the data + if(state == RADIOLIB_ERR_NONE) { + for(uint8_t n = 0; n < numBytes; n++) { + dataIn[n] = this->SPItransfer(this->SPIreadCommand); + } + } + } + + // stop transfer + this->SPIendTransaction(); + this->digitalWrite(this->getCs(), HIGH); + + // wait for GPIO to go high and then low + if(waitForGpio) { + this->delayMicroseconds(1); + uint32_t start = this->millis(); + while(this->digitalRead(this->getGpio())) { + this->yield(); + if(this->millis() - start >= timeout) { + state = RADIOLIB_ERR_SPI_CMD_TIMEOUT; + break; + } + } + } + + // print debug output + #if defined(RADIOLIB_VERBOSE) + // print command byte(s) + RADIOLIB_VERBOSE_PRINT("CMD\t"); + for(uint8_t n = 0; n < cmdLen; n++) { + RADIOLIB_VERBOSE_PRINT(cmd[n], HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + } + RADIOLIB_VERBOSE_PRINTLN(); + + // print data bytes + RADIOLIB_VERBOSE_PRINT("DAT"); + if(write) { + RADIOLIB_VERBOSE_PRINT("W\t"); + for(uint8_t n = 0; n < numBytes; n++) { + RADIOLIB_VERBOSE_PRINT(dataOut[n], HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + RADIOLIB_VERBOSE_PRINT(debugBuff[n], HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + } + RADIOLIB_VERBOSE_PRINTLN(); + } else { + RADIOLIB_VERBOSE_PRINT("R\t"); + // skip the first byte for read-type commands (status-only) + RADIOLIB_VERBOSE_PRINT(this->SPIreadCommand, HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + RADIOLIB_VERBOSE_PRINT(debugBuff[0], HEX); + RADIOLIB_VERBOSE_PRINT('\t') + + for(uint8_t n = 0; n < numBytes; n++) { + RADIOLIB_VERBOSE_PRINT(this->SPIreadCommand, HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + RADIOLIB_VERBOSE_PRINT(dataIn[n], HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + } + RADIOLIB_VERBOSE_PRINTLN(); + } + RADIOLIB_VERBOSE_PRINTLN(); + #endif + + return(state); +} + void Module::waitForMicroseconds(uint32_t start, uint32_t len) { #if defined(RADIOLIB_INTERRUPT_TIMING) (void)start; diff --git a/src/Module.h b/src/Module.h index 526f24fa..5597303f 100644 --- a/src/Module.h +++ b/src/Module.h @@ -139,6 +139,23 @@ class Module { */ uint8_t SPIwriteCommand = 0b10000000; + /*! + \brief Whether the SPI interface is stream-type (e.g. SX126x) or register-type (e.g. SX127x). + Defaults to register-type SPI interfaces. + */ + bool SPIstreamType = false; + + /*! + \brief SPI status parsing callback typedef. + */ + typedef int16_t (*SPIparseStatusCb_t)(uint8_t in); + + /*! + \brief Callback to function that will parse the module-specific status codes to RadioLib status codes. + Typically used for modules with SPI stream-type interface (e.g. SX126x/SX128x). + */ + SPIparseStatusCb_t SPIparseStatusCb = nullptr; + #if defined(RADIOLIB_INTERRUPT_TIMING) /*! @@ -258,6 +275,29 @@ class Module { \param numBytes Number of bytes to transfer. */ void SPItransfer(uint8_t cmd, uint8_t reg, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes); + + /*! + \brief SPI single transfer method for modules with stream-type SPI interface (SX126x, SX128x etc.). + + \param cmd SPI operation command. + + \param cmd SPI command length in bytes. + + \param write Set to true for write commands, false for read commands. + + \param dataOut Data that will be transfered from master to slave. + + \param dataIn Data that was transfered from slave to master. + + \param numBytes Number of bytes to transfer. + + \param waitForGpio Whether to wait for some GPIO at the end of transfer (e.g. BUSY line on SX126x/SX128x). + + \param timeout GPIO wait period timeout in milliseconds. + + \returns \ref status_codes + */ + int16_t SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForGpio, uint32_t timeout); // pin number access methods