diff --git a/src/SparkFun_Toolkit.h b/src/SparkFun_Toolkit.h index 6e6670a..d69f7ca 100644 --- a/src/SparkFun_Toolkit.h +++ b/src/SparkFun_Toolkit.h @@ -1 +1,23 @@ -// Code! \ No newline at end of file +/* +The MIT License (MIT) + +Copyright (c) 2022 SparkFun Electronics +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. +*/ + +#pragma once + +#include "sfe_bus.h" diff --git a/src/sfe_bus.h b/src/sfe_bus.h index 415218f..01bb2bb 100644 --- a/src/sfe_bus.h +++ b/src/sfe_bus.h @@ -1,6 +1,4 @@ /* -sfe_bus.h - The MIT License (MIT) Copyright (c) 2022 SparkFun Electronics @@ -18,46 +16,35 @@ 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. - -The following virtual class provides an abstract communication interface. - */ #pragma once -#include - -// To repeatedly use this toolkit, you may need to wrap this class in a namespace. -// namespace sfe_XXX { - -// The following abstract class is used an interface for upstream implementation. -class SfeBus +// Error and warning codes +#define SFE_BUS_OK 0 +#define SFE_BUS_E_UNKNOWN -1 +#define SFE_BUS_E_NULL_PTR -2 +#define SFE_BUS_E_TIMEOUT -3 +#define SFE_BUS_E_NO_RESPONSE -4 +#define SFE_BUS_E_DATA_TOO_LONG -5 +#define SFE_BUS_W_UNKNOWN 1 +#define SFE_BUS_W_UNDER_READ 1 + +/// @brief An abstract interface for a communication bus +class SFE_Bus { public: - /// @brief A simple ping of the device at the given address. - /// @param devAddr Address of the device - virtual bool ping(uint8_t devAddr) = 0; - - /// @brief Write a single byte to the given register - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - virtual bool writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t data) = 0; - - /// @brief Writes a number of bytes starting at the given register's address. - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - virtual int writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length) = 0; - - /// @brief Reads a block of data from the given register. - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - virtual int readRegisterRegion(uint8_t addr, uint8_t reg, uint8_t *data, uint16_t numBytes) = 0; + /// @brief Writes a number of bytes starting at the given register address. + /// @param regAddr The first register address to write to. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t writeRegisters(uint8_t regAddr, const uint8_t *data, uint8_t numBytes) = 0; + + /// @brief Reads a number of bytes starting at the given register address. + /// @param regAddr The first register address to read from. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t readRegisters(uint8_t regAddr, uint8_t *data, uint8_t numBytes) = 0; }; - -//}; diff --git a/src/sfe_i2c arduino.cpp b/src/sfe_i2c arduino.cpp new file mode 100644 index 0000000..fd971d2 --- /dev/null +++ b/src/sfe_i2c arduino.cpp @@ -0,0 +1,136 @@ +/* +The MIT License (MIT) + +Copyright (c) 2022 SparkFun Electronics +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. +*/ + +#include "sfe_i2c_arduino.h" + +int8_t SFE_I2C_Arduino::init(uint8_t devAddr) +{ + // Set default I2C port to Wire + return init(devAddr, Wire); +} + +int8_t SFE_I2C_Arduino::init(uint8_t devAddr, TwoWire& i2cPort, bool beginBus) +{ + // Store provided values + _devAddr = devAddr; + _i2cPort = &i2cPort; + + // Null pointer check + if (!_i2cPort) + return SFE_BUS_E_NULL_PTR; + + // Begin bus if requested + if(beginBus) + _i2cPort->begin(); + + // Success + return SFE_BUS_OK; +} + +int8_t SFE_I2C_Arduino::ping() +{ + // Null pointer check + if (!_i2cPort) + return SFE_BUS_E_NULL_PTR; + + // Begin and end transmission to check for ACK response + _i2cPort->beginTransmission(_devAddr); + uint8_t result = _i2cPort->endTransmission(); + + // Check result + if(result == 0) + return SFE_BUS_OK; + else if(result == 1) + return SFE_BUS_E_DATA_TOO_LONG; + else if((result == 2) || (result == 3)) + return SFE_BUS_E_NO_RESPONSE; + else if(result == 5) + return SFE_BUS_E_TIMEOUT; + else + return SFE_BUS_E_UNKNOWN; +} + +int8_t SFE_I2C_Arduino::writeRegisters(uint8_t regAddr, const uint8_t *data, uint8_t numBytes) +{ + // Null pointer check + if (!_i2cPort) + return SFE_BUS_E_NULL_PTR; + + // Start I2C message + _i2cPort->beginTransmission(_devAddr); + + // Write register address + _i2cPort->write(regAddr); + + // Write data + _i2cPort->write(data, numBytes); + + // End I2C message + uint8_t result = _i2cPort->endTransmission(); + + // Check result + if(result == 0) + return SFE_BUS_OK; + else if(result == 1) + return SFE_BUS_E_DATA_TOO_LONG; + else if((result == 2) || (result == 3)) + return SFE_BUS_E_NO_RESPONSE; + else if(result == 5) + return SFE_BUS_E_TIMEOUT; + else + return SFE_BUS_E_UNKNOWN; +} + +int8_t SFE_I2C_Arduino::readRegisters(uint8_t regAddr, uint8_t *data, uint8_t numBytes) +{ + // Null pointer check + if (!_i2cPort) + return SFE_BUS_E_NULL_PTR; + + // Write desired register address + _i2cPort->beginTransmission(_devAddr); + _i2cPort->write(regAddr); + uint8_t result = _i2cPort->endTransmission(); + + // Check result + if(result == 1) + return SFE_BUS_E_DATA_TOO_LONG; + else if((result == 2) || (result == 3)) + return SFE_BUS_E_NO_RESPONSE; + else if(result == 5) + return SFE_BUS_E_TIMEOUT; + else if(result != 0) + return SFE_BUS_E_UNKNOWN; + + // Read data from device + uint8_t numRead = _i2cPort->requestFrom(_devAddr, numBytes); + + // Read data into buffer one byte at a time + for(uint16_t i = 0; i < numRead; i++) + { + data[i] = _i2cPort->read(); + } + + // Check number of bytes read + if(numRead < numBytes) + return SFE_BUS_W_UNDER_READ; + else + return SFE_BUS_OK; +} diff --git a/src/sfe_i2c.cpp b/src/sfe_i2c.cpp deleted file mode 100644 index 8a8c725..0000000 --- a/src/sfe_i2c.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* -sfe_i2c.cpp - -The MIT License (MIT) - -Copyright (c) 2022 SparkFun Electronics -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. -*/ - -#include "sfe_i2c.h" - -#define maxI2CBufferLength 32 -// What we use for transfer chunk size -#define buffSize maxI2CBufferLength; - -bool SfeI2C::init(TwoWire &wirePort, bool bInit) -{ - - // if we don't have a wire port already - if (!_i2cPort) - { - _i2cPort = &wirePort; - - if (bInit) - _i2cPort->begin(); - } - - return true; -} - -bool SfeI2C::init() -{ - if (!_i2cPort) - return init(Wire); - else - return false; -} - -bool SfeI2C::ping(uint8_t devAddr) -{ - - if (!_i2cPort) - return false; - - _i2cPort->beginTransmission(devAddr); - return _i2cPort->endTransmission() == 0; -} - -bool SfeI2C::writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t dataToWrite) -{ - - if (!_i2cPort) - return false; - - _i2cPort->beginTransmission(devAddr); - _i2cPort->write(devReg); - _i2cPort->write(dataToWrite); - return _i2cPort->endTransmission() == 0; -} - -int SfeI2C::writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length) -{ - - _i2cPort->beginTransmission(devAddr); - _i2cPort->write(devReg); - _i2cPort->write(data, (int)length); - - return _i2cPort->endTransmission() ? -1 : 0; // -1 = error, 0 = success -} - -int SfeI2C::readRegisterRegion(uint8_t devAddr, uint8_t devReg, uint8_t *data, uint16_t numBytes) -{ - uint8_t nChunk; - uint16_t nReturned; - - if (!_i2cPort) - return -1; - - int i; // counter in loop - bool bFirstInter = true; // Flag for first iteration - used to send devRegister - - while (numBytes > 0) - { - _i2cPort->beginTransmission(devAddr); - - if (bFirstInter) - { - _i2cPort->write(devReg); - bFirstInter = false; - } - - if (_i2cPort->endTransmission() != 0) - return -1; // error with the end transmission - - // We're chunking in data - keeping the max chunk to kMaxI2CBufferLength - nChunk = numBytes > buffSize ? buffSize : numBytes; - - nReturned = _i2cPort->requestFrom((int)devAddr, (int)nChunk, (int)true); - - // No data returned, no dice - if (nReturned == 0) - return -1; // error - - // Copy the retrieved data chunk to the current index in the data segment - for (i = 0; i < nReturned; i++) - { - *data++ = _i2cPort->read(); - } - - // Decrement the amount of data recieved from the overall data request amount - numBytes = numBytes - nReturned; - - } // end while - - return 0; // Success -} diff --git a/src/sfe_i2c.h b/src/sfe_i2c.h index 80369db..2cb273b 100644 --- a/src/sfe_i2c.h +++ b/src/sfe_i2c.h @@ -1,6 +1,4 @@ /* -sfe_i2c.h - The MIT License (MIT) Copyright (c) 2022 SparkFun Electronics @@ -13,67 +11,30 @@ 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. - -The following classes specify the behavior for communicating -over Inter-Integrated Circuit (I2C) - +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. */ #pragma once #include "sfe_bus.h" -#include -/* -The SfeI2C device defines behavior for I2C implementation based around the TwoWire class (Wire). -This is Arduino specific. -*/ -class SfeI2C : public SfeBus +/// @brief An abstract interface for an I2C communication bus +class SFE_I2C : public SFE_Bus { public: - /// @brief Constructor - SfeI2C(void) : _i2cPort(nullptr) {}; - - /// @brief Method sets up the required I2C settings. This function - /// provides a default I2C Port. - /// @return True on successful execution. - bool init(); - - /// @brief Method sets up the required I2C settings. - /// @param wirePort Port for I2C communcation. - /// @param bInit This flag tracks whether the bus has been initialized. - /// @return True on successful execution. - bool init(TwoWire &wirePort, bool bInit = false); - - /// @brief A simple ping of the device at the given address. - /// @param devAddr Address of the device - bool ping(uint8_t devAddr); - - /// @brief Write a single byte to the given register - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - bool writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t data); - - /// @brief Writes a number of bytes starting at the given register's address. - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - int writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length); - - /// @brief Reads a block of data from the given register. - /// @param devAddr The device's I2C address. - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - int readRegisterRegion(uint8_t addr, uint8_t reg, uint8_t *data, uint16_t numBytes); - - private: - TwoWire *_i2cPort; + /// @brief Initialize I2C parameters. + /// @param devAddr I2C address of device. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t init(uint8_t devAddr) = 0; + + /// @brief Pings I2C device and looks for an ACK response. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t ping() = 0; + + protected: + uint8_t _devAddr; }; diff --git a/src/sfe_i2c_arduino.h b/src/sfe_i2c_arduino.h new file mode 100644 index 0000000..7cc30e4 --- /dev/null +++ b/src/sfe_i2c_arduino.h @@ -0,0 +1,62 @@ +/* +The MIT License (MIT) + +Copyright (c) 2022 SparkFun Electronics +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. +*/ + +#pragma once + +#include "sfe_i2c.h" +#include + +/// @brief An I2C communication bus implementation for Arduino +class SFE_I2C_Arduino : public SFE_I2C +{ + public: + /// @brief Initialize I2C parameters. + /// @param devAddr I2C address of device. + /// @return 0 for success, negative for failure, positive for warning. + int8_t init(uint8_t devAddr); + + /// @brief Initialize I2C parameters. + /// @param devAddr I2C address of device. + /// @param i2cPort Arduino Wire port to use. + /// @param beginBus Whether to initialize the I2C bus. + /// @return 0 for success, negative for failure, positive for warning. + int8_t init(uint8_t devAddr, TwoWire& i2cPort, bool beginBus = false); + + /// @brief Pings I2C device and looks for an ACK response. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t ping(); + + /// @brief Writes a number of bytes starting at the given register address. + /// @param regAddr The first register address to write to. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t writeRegisters(uint8_t regAddr, const uint8_t *data, uint8_t numBytes); + + /// @brief Reads a number of bytes starting at the given register address. + /// @param regAddr The first register address to read from. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t readRegisters(uint8_t regAddr, uint8_t *data, uint8_t numBytes); + + protected: + TwoWire* _i2cPort; +}; diff --git a/src/sfe_spi.cpp b/src/sfe_spi.cpp deleted file mode 100644 index f617ca9..0000000 --- a/src/sfe_spi.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* -sfe_spi.h - -The MIT License (MIT) - -Copyright (c) 2022 SparkFun Electronics -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. - -*/ - -#pragma once - -#include "sfe_spi.h" -#define SPI_READ 0x80 - -bool SfeSPI::init(SPIClass &spiPort, SPISettings &busSPISettings, uint8_t cs, bool bInit) -{ - - // if we don't have a SPI port already - if (!_spiPort) - { - _spiPort = &spiPort; - - if (bInit) - _spiPort->begin(); - } - - // SPI settings are needed for every transaction - _sfeSPISettings = busSPISettings; - - // The chip select pin can vary from platform to platform and project to project - // and so it must be given by the user. - if (!cs) - return false; - - _cs = cs; - - return true; -} - -bool SfeSPI::init(uint8_t cs, bool bInit) -{ - - // If the transaction settings are not provided by the user they are built here. - SPISettings spiSettings = SPISettings(3000000, MSB_FIRST, SPI_MODE3); - - // In addition of the port is not provided by the user, it defaults to SPI here. - return init(SPI, spiSettings, cs, bInit); -} - -bool SfeSPI::ping(uint8_t devAddr) -{ - return true; -} - -bool SfeSPI::writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t dataToWrite) -{ - - if (!_spiPort) - return false; - - // Apply settings - _spiPort->beginTransaction(_sfeSPISettings); - // Signal communication start - digitalWrite(_cs, LOW); - - _spiPort->transfer(devReg); - _spiPort->transfer(dataToWrite); - - // End communcation - digitalWrite(_cs, HIGH); - _spiPort->endTransaction(); - - return true; -} - -int SfeSPI::writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length) -{ - - int i; - - // Apply settings - _spiPort->beginTransaction(_sfeSPISettings); - // Signal communication start - digitalWrite(_cs, LOW); - _spiPort->transfer(devReg); - - for (i = 0; i < length; i++) - { - _spiPort->transfer(*data++); - } - - // End communication - digitalWrite(_cs, HIGH); - _spiPort->endTransaction(); - return 0; -} - -int SfeSPI::readRegisterRegion(uint8_t devAddr, uint8_t devReg, uint8_t *data, uint16_t numBytes) -{ - if (!_spiPort) - return -1; - - int i; - - // Apply settings - _spiPort->beginTransaction(_sfeSPISettings); - // Signal communication start - digitalWrite(_cs, LOW); - // A leading "1" must be added to transfer with devRegister to indicate a "read" - devReg = (devReg | SPI_READ); - _spiPort->transfer(devReg); - - for (i = 0; i < numBytes; i++) - { - *data++ = _spiPort->transfer(0x00); - } - - // End transaction - digitalWrite(_cs, HIGH); - _spiPort->endTransaction(); - return 0; -} diff --git a/src/sfe_spi.h b/src/sfe_spi.h index 91c51ed..a9aeea4 100644 --- a/src/sfe_spi.h +++ b/src/sfe_spi.h @@ -1,6 +1,4 @@ /* -sfe_spi.h - The MIT License (MIT) Copyright (c) 2022 SparkFun Electronics @@ -18,73 +16,21 @@ 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. - -The following class specifies the behavior for communicating -over Serial Peripheral Interface (SPI). - */ #pragma once #include "sfe_bus.h" -#include -/* -The SfeSPI class defines behavior for SPI implementation based around the SPIClass class (SPI). -This is Arduino specific. -Paramaters like "devAddr" are kept although irrelevant to SPI due to the use of the abstract class -as interface, SfeBus. -*/ -class SfeSPI : public SfeBus +/// @brief An abstract interface for an SPI communication bus +class SFE_SPI : public SFE_Bus { public: - /// @brief Constructor - SfeSPI(void) : _spiPort(nullptr) {}; - - /// @brief Method sets up the required SPI settings. This function - /// provides a default SPI Port. - /// @param cs The chip select pin. - /// @param bInit This flag tracks whether the bus has been initialized. - /// @return True on successful execution. - bool init(uint8_t cs, bool bInit = false); - - /// @brief Method sets up the required SPI settings. - /// @param spiPort Port for SPI communcation. - /// @param busSPISettings Settings for speed, endianness, and spi mode of the SPI bus. - /// @param cs The chip select pin. - /// @param bInit This flag tracks whether the bus has been initialized. - /// @return True on successful execution. - bool init(SPIClass &spiPort, SPISettings &busSPISettings, uint8_t cs, bool bInit = false); - - /// @brief Is a device connected? The SPI ping is not relevant but is defined here to - /// keep consistency with I2C class i.e. provided for the abstract interface. - /// @param devAddr Address of the device (unused) - bool ping(uint8_t devAddr = 0); - - /// @brief Write a single byte to the given register - /// @param devAddr The device's I2C address (ignored). - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - bool writeRegisterByte(uint8_t devAddr, uint8_t devReg, uint8_t data); - - /// @brief Writes a number of bytes starting at the given register's address. - /// @param devAddr The device's I2C address (ignored). - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - int writeRegisterRegion(uint8_t devAddr, uint8_t devReg, const uint8_t *data, uint16_t length); - - /// @brief Reads a block of data from the given register. - /// @param devAddr The device's I2C address (ignored). - /// @param devReg The device's register's address. - /// @param data Data to write. - /// @brief returns true on successful execution. - int readRegisterRegion(uint8_t addr, uint8_t reg, uint8_t *data, uint16_t numBytes); + /// @brief Initialize SPI parameters. + /// @param csPin Chip select pin. + /// @return 0 for success, negative for failure, positive for warning. + virtual int8_t init(uint8_t csPin) = 0; - private: - SPIClass *_spiPort; - // Settings are used for every transaction. - SPISettings _sfeSPISettings; - uint8_t _cs; + protected: + uint8_t _csPin; }; diff --git a/src/sfe_spi_arduino.cpp b/src/sfe_spi_arduino.cpp new file mode 100644 index 0000000..11bdf2d --- /dev/null +++ b/src/sfe_spi_arduino.cpp @@ -0,0 +1,108 @@ +/* +The MIT License (MIT) + +Copyright (c) 2022 SparkFun Electronics +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. +*/ + +#include "sfe_spi_arduino.h" + +int8_t SFE_SPI_Arduino::init(uint8_t csPin) +{ + // Set default SPI port to SPI + return init(csPin, SPI); +} + +int8_t SFE_SPI_Arduino::init(uint8_t csPin, SPIClass &spiPort, bool beginBus) +{ + // Set default SPI settings to 1MHz, MSB first, and Mode 0 + return init(csPin, SPISettings(1000000, MSBFIRST, SPI_MODE0), spiPort); +} + +int8_t SFE_SPI_Arduino::init(uint8_t csPin, SPISettings spiSettings, SPIClass &spiPort, bool beginBus) +{ + // Store provided values + _csPin = csPin; + _spiSettings = spiSettings; + _spiPort = &spiPort; + + // Null pointer check + if (!_spiPort) + return SFE_BUS_E_NULL_PTR; + + // Begin bus if requested + if(beginBus) + _spiPort->begin(); + + // Ensure CS pin is set to output mode and driving high for now + // Writing high before setting mode helps ensure signal stays high + digitalWrite(_csPin, HIGH); + pinMode(_csPin, OUTPUT); + + // Success + return SFE_BUS_OK; +} + +int8_t SFE_SPI_Arduino::writeRegisters(uint8_t regAddr, const uint8_t *data, uint8_t numBytes) +{ + // Null pointer check + if (!_spiPort) + return SFE_BUS_E_NULL_PTR; + + // Start SPI message + _spiPort->beginTransaction(_spiSettings); + digitalWrite(_csPin, LOW); + + // Write register address with write bit + _spiPort->transfer(regAddr & 0x7F); + + // Write data + _spiPort->transfer((void*) data, numBytes); + + // End SPI message + digitalWrite(_csPin, HIGH); + _spiPort->endTransaction(); + + // Success + return SFE_BUS_OK; +} + +int8_t SFE_SPI_Arduino::readRegisters(uint8_t regAddr, uint8_t *data, uint8_t numBytes) +{ + // Null pointer check + if (!_spiPort) + return SFE_BUS_E_NULL_PTR; + + // Start SPI message + _spiPort->beginTransaction(_spiSettings); + digitalWrite(_csPin, LOW); + + // Write register address with read bit + _spiPort->transfer(regAddr | 0x80); + + // Read data into buffer one byte at a time + for(uint16_t i = 0; i < numBytes; i++) + { + data[i] = _spiPort->transfer(0x00); + } + + // End SPI message + digitalWrite(_csPin, HIGH); + _spiPort->endTransaction(); + + // Success + return SFE_BUS_OK; +} diff --git a/src/sfe_spi_arduino.h b/src/sfe_spi_arduino.h new file mode 100644 index 0000000..e127129 --- /dev/null +++ b/src/sfe_spi_arduino.h @@ -0,0 +1,67 @@ +/* +The MIT License (MIT) + +Copyright (c) 2022 SparkFun Electronics +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. +*/ + +#pragma once + +#include "sfe_spi.h" +#include + +/// @brief An SPI communication bus implementation for Arduino +class SFE_SPI_Arduino : public SFE_SPI +{ + public: + /// @brief Initialize SPI parameters. + /// @param csPin Chip select pin. + /// @return 0 for success, negative for failure, positive for warning. + int8_t init(uint8_t csPin); + + /// @brief Initialize SPI parameters. + /// @param csPin Chip select pin. + /// @param spiPort Arduino SPI port to use. + /// @param beginBus Whether to initialize the I2C bus. + /// @return 0 for success, negative for failure, positive for warning. + int8_t init(uint8_t csPin, SPIClass &spiPort, bool beginBus = false); + + /// @brief Initialize SPI parameters. + /// @param csPin Chip select pin. + /// @param spiSettings Arduino SPISettings to use for transactions. + /// @param spiPort Arduino SPI port to use. + /// @param beginBus Whether to initialize the I2C bus. + /// @return 0 for success, negative for failure, positive for warning. + int8_t init(uint8_t csPin, SPISettings spiSettings, SPIClass &spiPort, bool beginBus = false); + + /// @brief Writes a number of bytes starting at the given register address. + /// @param regAddr The first register address to write to. + /// @param data Data buffer to write to registers. + /// @param numBytes Number of bytes to write. + /// @return 0 for success, negative for failure, positive for warning. + int8_t writeRegisters(uint8_t regAddr, const uint8_t *data, uint8_t numBytes); + + /// @brief Reads a number of bytes starting at the given register address. + /// @param regAddr The first register address to read from. + /// @param data Data buffer to read from registers. + /// @param numBytes Number of bytes to read. + /// @return 0 for success, negative for failure, positive for warning. + int8_t readRegisters(uint8_t regAddr, uint8_t *data, uint8_t numBytes); + + protected: + SPIClass* _spiPort; + SPISettings _spiSettings; +};