diff --git a/docs/ar_ibus.md b/docs/ar_ibus.md new file mode 100644 index 0000000..6bb0bad --- /dev/null +++ b/docs/ar_ibus.md @@ -0,0 +1,237 @@ +# Overview - Device Bus interface - sfeTkIBus + +One of the foundational capabilities of the SparkFun Toolkit is bus communication with devices. This is a common task almost all libraries implement using their own implementation for I2C, SPI or UART bus communication. + +For bus communication, the SparkFun Toolkit is designed to provide a common implementation for use across all SparkFun libraries. Additionally, the bus architecture is modeled on a *driver* pattern, separating the individual bus setup/configuration from data communication, enabling a single device implementation to easily support a variety of device bus types. + +The key goals set for the Bus implementation in the Toolkit include: + +* Separate device setup from device communication +* Define a common bus interface for use across a variety of common device bus types +* Deliver support for both SPI and I2C bus types initially, focusing on Arduino +* Structure the bus/toolkit implementation such that it's platform independent + +## Architecture Overview + +To meet the goals for this subsystem, the Flux framework follows a ***Driver Pattern***, defining a common interface for bus communication. Device drivers are designed around this interface, leaving bus configuration and implementation to platform specific implementation. + +The key class to support this pattern are: + +| | | +|------|-------| +**sfeTkIBus** | A virtual C++ class that device the bus ```sfeTkIBus``` interface | +**sfeTkII2C** | Sub-class of the ```sfeTkIIBus``` interface, it provides an interface for I2C devices| +**sfeTkISPI** | Sub-class of the ```sfeTkIIBus``` interface, it provides an interface for SPI devices | + +### The sfeTkIBus Interface + +The key to meeting the goals of the Toolkit is the IBus interface. This interface defines the methods used to communicate with a device. The setup, configuration and implementation of this interface is performed by platform specific implementations of the interface. + +The interface methods: + +| Method| Definition | +|------|-------| +**writeRegisterByte** | Write a byte of data to a particular register of a device | +**writeRegisterWord** | Write a word of data to a particular register of a device | +**writeRegisterRegion** | Write an array of data to a particular register of a device| +**readRegisterByte** | Read a byte of data from a particular register of a device | +**readRegisterWord** | Read a word of data from a particular register of a device | +**readRegisterRegion** | Read an array of data from a particular register of a device | + +> [!NOTE] +> This interface only defines the methods to read and write data on the given bus. Any address, or bus specific settings is provided/implemented by the implementation/specialization of this interface. + +### The sfeTkII2C Implementation + +This class sub-classes from the ```sfeTkIBus``` interface adding additional functionally focused on supporting an I2C implementation. This interface provides the additional functionality. + +| Method| Definition | +|------|-------| +**ping** | Determine if a devices is connected to the I2C device at the address set on this bus object. This is an interface method | +**setAddress** | Set the I2C address to use for this I2C object | +**address** | Returns the address used by this I2C object | + +> [!NOTE] +> The ```sfeTkII2C``` class manages the device address for the I2C bus. As such, each I2C device instantiates/uses an instance of the ```sfeTkII2C``` class. + +### The sfeTkISPI Implementation + +This class sub-classes from the ```sfeTkIBus``` interface adding additional functionally focused on supporting an SPI implementation. This interface provides the additional functionality. + +| Method| Definition | +|------|-------| +**setCS** | Set the CS Pin to use for this SPI object | +**cs** | Returns the CS Pin used by this SPI object | + +> [!NOTE] +> The ```sfeTkISPI``` class manages CS Pin for the SPI bus. As such, each SPI device instantiates/uses an instance of the ```sfeTkISPI``` class. + +The class diagram of these base class interfaces/implementation: + +![IBus diagram](images/tk_IBUS.png) + +## sfeTkIIBus - Arduino Implementation + +The initial implementation of the toolkit IBus interface is for the Arduino environment. This implementation consists of two classes, ```sfeTkArdI2C``` and ```sfeTkArdSPI```, each of which sub-class from their respective bus type interfaces within the core toolkit. + +These driver implementations provide the platform specific implementation for the toolkit bus interfaces, supporting the methods defined by the interfaces, as well as contain and manage the platform specific settings and attributes for each bus type. + +> [!IMPORTANT] +> The intent is that each user of an particular bus - a device in most cases - contains an instance of the specific bus object. + +The class diagram for the Arduino implementation is as follows: + +![Arduino IBus Implementation](images/tk_ibus_ard.png) + +### The sfeTkArdI2C Class + +This class provides the Arduino implementation of I2C in the SparkFun Toolkit. It implements the methods of the ```sfeTkIIBus``` and ```sfeTkII2C``` interfaces, as well as manages any Arduino specific state. + +### The sfeTkArdSPI Class + +This class provides the Arduino implementation of SPI in the SparkFun Toolkit. It implements the methods of the ```sfeTkIIBus``` and ```sfeTkISPI``` interfaces, as well as manages any Arduino specific state for the SPI bus - namely the SPISettings class. + +Before each use of the SPI bus, the methods of the ```sfeTkArdSPI``` uses an internal SPISettings class to ensure the SPI bus is operating in the desired mode for the device. + +## sfeTkIBus Use + +The general steps when using the sfeTkIBus in device development are outlined in the following steps. This example uses the Arduino implementation of the bus. + +The general pattern for a device driver implementation that uses the SparkFun Toolkit is the following: + +### Implement a Platform Independent Driver + +The first step is to implement a core, platform independent version of the driver that communicates to the target device using the methods of a ```sfeTkIIBus``` interface. + +>[!IMPORTANT] +> At this level, the driver is only using a ```sfeTkIBus``` interface, not any specific bus implementation. + +This driver has the following unique functionality: + +1) A method to set the object that implements the ```sfeTkIBus``` interface object should use. Since +1) If the device supports identification capabilities, the driver provides this functionality. + +#### SImple Example of an Independent Driver Implementation + +This implementation would take the following form: + +```c++ + +class myDriverClass +{ +public: + + myDriverClass(uint8_t address) : _addr{address}{} + + bool begin() + { + // initialize things ... + + return true; + } + void setCommunicationBus(sfeTkIBus *theBus) + { + _theBus = theBus; + } + + bool updateDeviceData(uint8_t *data, size_t len) + { + if (!_theBus || !data || len == 0) + return false; + + int status = _theBus->writeRegisterRegion(THE_REG, data, len); + + return (status == 0); + } + + bool checkDeviceID() + { + // do some device ID checks in registers ...etc + return true; + } +private: + sfeTkIBus *_theBus; +}; +``` + +### Write a Platform Specific Driver, based on the core driver + +This driver sub-classes from the general/core driver class, builds and configures the desired bus object and passes this into the core driver. + +The key concepts for these Platform specific drivers include: + +1) Perform any platform specific bus setup during the instantiation of the device. This might just be setting the target (pin, address) for the device on the bus. +1) Implement any bus specific device identification use at this level. For example, on I2C, a ping call might be made on the bus before a more detailed identification method is used. + +#### Basic concept - creating an I2C class in Arduino + +The following is an example of an I2C class in Arduino based on the previous platform independent driver. + +> [!NOTE] +> This class implements a ```isConnected()``` method that calls the ```ping()``` method of the I2C bus class being used, and if this passes, then calls the ```checkDeviceID()``` method of the superclass. + +```c++ + +class myArduinoDriverI2C : public myDriverClass +{ + public: + myArduinoDriverI2C() + {} + + bool begin() + { + if (!_theI2CBus.init(MY_DEVICE_ADDRESS)) + return false; + setCommunicationBus(&_theI2CBus); + + return myDriverClass::begin(); + } + + bool isConnected() + { + if (!_theI2CBus.ping()) + return false; + + return checkDeviceID(); + } + +private: + sfeTkArdI2C _theI2CBus; +}; +``` + +#### Basic concept - creating an SPI class in Arduino + +The following is a SPI version of the driver implemented in Arduino. While similar to the I2C implementation, it focuses on the specific needs of the SPI bus, specifically the ```SPISettings``` this particular device requires when using the SPI bus. + +> [!NOTE] +> This class implements a ```isConnected()``` method that just calls the superclasses ```checkDeviceID()``` method to determine if the device is available on the bus. + +```c++ + +class myArduinoDriveSPI : public myDriverClass +{ + public: + myArduinoDriverSPI() + {} + + bool begin() + { + SPISettings spiSettings = SPISettings(4000000, MSBFIRST, SPI_MODE3); + + if (!_theSPIBus.init(SPI, spiSettings, MY_DEFAULT_CS, true)) + return false; + setCommunicationBus(&_theSPIBus); + + return myDriverClass::begin(); + } + + bool isConnected() + { + return checkDeviceID(); + } + +private: + sfeTkArdSPI _theSPIBus; +}; +``` diff --git a/docs/docs.md b/docs/docs.md deleted file mode 100644 index 30e20bc..0000000 --- a/docs/docs.md +++ /dev/null @@ -1 +0,0 @@ -Docs! \ No newline at end of file diff --git a/docs/images/tk_IBUS.png b/docs/images/tk_IBUS.png new file mode 100644 index 0000000..6cf0355 Binary files /dev/null and b/docs/images/tk_IBUS.png differ diff --git a/docs/images/tk_ibus_ard.png b/docs/images/tk_ibus_ard.png new file mode 100644 index 0000000..320518c Binary files /dev/null and b/docs/images/tk_ibus_ard.png differ diff --git a/src/SparkFun_Toolkit.h b/src/SparkFun_Toolkit.h index 6e6670a..c236f72 100644 --- a/src/SparkFun_Toolkit.h +++ b/src/SparkFun_Toolkit.h @@ -1 +1,33 @@ -// Code! \ No newline at end of file + +/* +SparkFun_Toolkit.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 + +// Purpose: +// +// The SparkFun Toolkit provides a set of common implementations used throughout our (and others) +// Arduino Libraries. + +// Just include the toolkit headers + +#include "sfeTkArdI2C.h" +#include "sfeTkArdSPI.h" \ No newline at end of file diff --git a/src/sfe_bus.h b/src/sfeTk/sfeTkIBus.h similarity index 51% rename from src/sfe_bus.h rename to src/sfeTk/sfeTkIBus.h index 415218f..7e06eea 100644 --- a/src/sfe_bus.h +++ b/src/sfeTk/sfeTkIBus.h @@ -1,5 +1,8 @@ + +// sfeTkIBus.h +// +// Defines the communication bus interface for the SparkFun Electronics Toolkit -> sfeTk /* -sfe_bus.h The MIT License (MIT) @@ -18,46 +21,70 @@ 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 +class sfeTkIBus { 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. + /// + /// @retval bool - true on successful execution. + /// + virtual bool writeRegisterByte(uint8_t devReg, uint8_t data) = 0; + + /// @brief Write a single word (16 bit) to the given register + /// /// @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; + /// + /// @retval bool - true on successful execution. + /// + virtual bool writeRegisterWord(uint8_t devReg, uint16_t data) = 0; /// @brief Writes a number of bytes starting at the given register's address. - /// @param devAddr The device's I2C address. + /// + /// @param devAddr The device's address/pin /// @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; + /// + /// @retval int returns the number of bytes written, < 0 on error + /// + virtual int writeRegisterRegion(uint8_t devReg, const uint8_t *data, size_t length) = 0; + + /// @brief Read a single byte from the given register + /// + /// @param devReg The device's register's address. + /// @param data Data to read. + /// + /// @retval bool - true on successful execution. + /// + virtual bool readRegisterByte(uint8_t devReg, uint8_t &data) = 0; + + /// @brief Read a single word (16 bit) from the given register + /// + /// @param devReg The device's register's address. + /// @param data Data to read. + /// + /// @retval bool - true on successful execution. + /// + virtual bool readRegisterWord(uint8_t devReg, uint16_t &data) = 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; + /// + /// @retval int returns 0 on success, or error code + /// + virtual int readRegisterRegion(uint8_t reg, uint8_t *data, size_t numBytes) = 0; }; //}; diff --git a/src/sfeTk/sfeTkII2C.h b/src/sfeTk/sfeTkII2C.h new file mode 100644 index 0000000..c81a5d8 --- /dev/null +++ b/src/sfeTk/sfeTkII2C.h @@ -0,0 +1,71 @@ + +// sfeTkII2C.h +// +// Defines the I2C communication bus interface for the SparkFun Electronics Toolkit +/* + +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 "sfeTkIBus.h" + +class sfeTkII2C : public sfeTkIBus +{ + public: + sfeTkII2C() : _address{kNoAddress} + { + } + sfeTkII2C(uint8_t addr) : _address{addr} + { + } + + /// + /// @brief A simple ping of the device at the set address + /// + /// @retval bool - true on success, false on failure + /// + virtual bool ping() = 0; + + /// @brief setter for the I2C address + /// + /// @param devAddr The device's address + /// + virtual void setAddress(uint8_t devAddr) + { + _address = devAddr; + } + + /// @brief getter for the I2C address + /// + /// @retval uint8_t returns the address for the device + /// + virtual uint8_t address(void) + { + return _address; + } + + static constexpr uint8_t kNoAddress = 0; + + private: + uint8_t _address; +}; + +//}; diff --git a/src/sfeTk/sfeTkISPI.h b/src/sfeTk/sfeTkISPI.h new file mode 100644 index 0000000..e5b34a1 --- /dev/null +++ b/src/sfeTk/sfeTkISPI.h @@ -0,0 +1,64 @@ + +// sfeTkISPI.h +// +// Defines the SPI communication bus interface for the SparkFun Electronics Toolkit +/* + +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 "sfeTkIBus.h" + +class sfeTkISPI : public sfeTkIBus +{ + public: + sfeTkISPI() : _cs{kNoCSPin} + { + } + + sfeTkISPI(uint8_t csPin) : _cs{csPin} + { + } + /// @brief setter for the CS Pin + /// + /// @param devCS The device's CS Pin + /// + virtual void setCS(uint8_t devCS) + { + _cs = devCS; + } + + /// @brief getter for the cs pin + /// + /// @retval uint8_t returns the CS pin for the device + /// + virtual uint8_t cs(void) + { + return _cs; + } + + static constexpr uint8_t kNoCSPin = 0; + + private: + uint8_t _cs; +}; + +//}; diff --git a/src/sfeTkArdI2C.cpp b/src/sfeTkArdI2C.cpp new file mode 100644 index 0000000..dafca5e --- /dev/null +++ b/src/sfeTkArdI2C.cpp @@ -0,0 +1,239 @@ +/* +sfeTkArdI2C.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 "sfeTkArdI2C.h" + +#define kMaxI2CBufferLength 32 + +//--------------------------------------------------------------------------------- +// init() +// +// Arduino version of init - pass in already setup wire port ... +// +bool sfeTkArdI2C::init(TwoWire &wirePort, uint8_t addr, bool bInit) +{ + // if we don't have a wire port already + if (!_i2cPort) + { + // use the pass in port + _i2cPort = &wirePort; + + if (bInit) + _i2cPort->begin(); + } + + setAddress(addr); + return true; +} + +//--------------------------------------------------------------------------------- +// init() +// +// no parameters version of init. Setups a a wire port if needed. +// +bool sfeTkArdI2C::init(uint8_t addr) +{ + // no port yet, do the default version of it + if (!_i2cPort) + return init(Wire, addr); + + // We have a port, so arcady init'd - right? + return true; +} + +//--------------------------------------------------------------------------------- +// init() +// +// no parameters version of init. Setups a a wire port if needed. +// +bool sfeTkArdI2C::init(void) +{ + // call with our currently set address ... + return init(address()); +} +//--------------------------------------------------------------------------------- +// ping() +// +// Ping an I2C address to see if something is there. +// +bool sfeTkArdI2C::ping() +{ + // no port, no + if (!_i2cPort) + return false; + + _i2cPort->beginTransmission(address()); + return _i2cPort->endTransmission() == 0; +} + +//--------------------------------------------------------------------------------- +// writeRegisterByte() +// +// Writes a byte to a given register. +// +// Returns true on success, false on failure +// +bool sfeTkArdI2C::writeRegisterByte(uint8_t devReg, uint8_t dataToWrite) +{ + if (!_i2cPort) + return false; + + // do the Arduino I2C work + _i2cPort->beginTransmission(address()); + _i2cPort->write(devReg); + _i2cPort->write(dataToWrite); + return _i2cPort->endTransmission() == 0; +} +//--------------------------------------------------------------------------------- +// writeRegisterWord() +// +// Writes a world to a given register. +// +// Returns true on success, false on failure +// +bool sfeTkArdI2C::writeRegisterWord(uint8_t devReg, uint16_t dataToWrite) +{ + if (!_i2cPort) + return false; + + return writeRegisterRegion(devReg, (uint8_t *)&dataToWrite, sizeof(u_int16_t)) == 0; +} + +//--------------------------------------------------------------------------------- +// writeRegisterRegion() +// +// Writes an array of bytes to a given register on the target address +// +// Returns the number of bytes written, < 0 is an error +// +int sfeTkArdI2C::writeRegisterRegion(uint8_t devReg, const uint8_t *data, size_t length) +{ + if (!_i2cPort) + return -1; + + _i2cPort->beginTransmission(address()); + _i2cPort->write(devReg); + _i2cPort->write(data, (int)length); + + return _i2cPort->endTransmission() ? -1 : 0; // -1 = error, 0 = success +} + +//--------------------------------------------------------------------------------- +// readRegisterByte() +// +// Reads a byte to a given register. +// +// Returns true on success, false on failure +// +bool sfeTkArdI2C::readRegisterByte(uint8_t devReg, uint8_t &dataToRead) +{ + if (!_i2cPort) + return false; + + // Return value + uint8_t result = 0; + + int nData = 0; + + _i2cPort->beginTransmission(address()); + _i2cPort->write(devReg); + _i2cPort->endTransmission(); + _i2cPort->requestFrom(address(), (uint8_t)1); + + while (_i2cPort->available()) // slave may send less than requested + { + result = _i2cPort->read(); // receive a byte as a proper uint8_t + nData++; + } + + if (nData == 1) // Only update outputPointer if a single byte was returned + dataToRead = result; + + return (nData == 1); +} +//--------------------------------------------------------------------------------- +// readRegisterWord() +// +// Reads a world to a given register. +// +// Returns true on success, false on failure +// +bool sfeTkArdI2C::readRegisterWord(uint8_t devReg, uint16_t &dataToRead) +{ + if (!_i2cPort) + return false; + + return readRegisterRegion(devReg, (uint8_t *)&dataToRead, sizeof(uint16_t)) == 2; +} + +//--------------------------------------------------------------------------------- +// readRegisterRegion() +// +// Reads an array of bytes to a given register on the target address +// +// Returns the number of bytes read, < 0 is an error +// +int sfeTkArdI2C::readRegisterRegion(uint8_t devReg, uint8_t *data, size_t numBytes) +{ + // got port + if (!_i2cPort) + return -1; + + uint16_t nOrig = numBytes; // original number of bytes. + uint8_t nChunk; + uint16_t nReturned; + int i; // counter in loop + bool bFirstInter = true; // Flag for first iteration - used to send devRegister + + while (numBytes > 0) + { + _i2cPort->beginTransmission(address()); + + 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 > kMaxI2CBufferLength ? kMaxI2CBufferLength : numBytes; + + nReturned = _i2cPort->requestFrom((int)address(), (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 received from the overall data request amount + numBytes = numBytes - nReturned; + + } // end while + + return nOrig - numBytes; // Success +} diff --git a/src/sfeTkArdI2C.h b/src/sfeTkArdI2C.h new file mode 100644 index 0000000..39496de --- /dev/null +++ b/src/sfeTkArdI2C.h @@ -0,0 +1,147 @@ +/* +sfeTkArdI2c.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. + +The following classes specify the behavior for communicating +over Inter-Integrated Circuit (I2C) in Arduino + +*/ + +#pragma once + +#include + +// Include our platform I2C interface definition. +#include + +/// @brief +/// The sfeTkArdI2C implements an sfeTkII2C interface, defining the Arduino implementation for I2C in the Toolkit +/// +class sfeTkArdI2C : public sfeTkII2C +{ + public: + /// + /// @brief Constructor + /// + sfeTkArdI2C(void) : _i2cPort(nullptr) + { + } + + sfeTkArdI2C(uint8_t addr) : sfeTkII2C(addr) + { + } + + // copy constructor + sfeTkArdI2C(sfeTkArdI2C const &rhs) : sfeTkII2C(), _i2cPort{rhs._i2cPort} + { + } + + // Copy assignment + sfeTkArdI2C &operator=(const sfeTkArdI2C &rhs) + { + _i2cPort = rhs._i2cPort; + return *this; + } + + /// @brief Method sets up the required I2C settings. + /// @note This function provides a default I2C Port. + /// + /// @retval True on successful execution. + /// + bool init(); + + // @brief - address version of the init method + bool init(uint8_t addr); + + /// @brief Method sets up the required I2C settings. + /// + /// @param wirePort Port for I2C communication. + /// @param bInit This flag tracks whether the bus has been initialized. + /// + /// @retval True on successful execution. + /// + bool init(TwoWire &wirePort, uint8_t addr, bool bInit = false); + + /// @brief A simple ping of the device at the given address. + /// @note sfeTkIBus interface method + /// + /// @retval bool - true on success, false on error + /// + bool ping(); + + /// @brief Write a single byte to the given register + /// @note sfeTkIBus interface method + /// + /// @param devReg The device's register's address. + /// @param data Data to write. + /// + /// @retval returns true on successful false on failure. + bool writeRegisterByte(uint8_t devReg, uint8_t data); + + bool writeRegisterWord(uint8_t devReg, uint16_t data); + + /// @brief Writes a number of bytes starting at the given register's address. + /// @note sfeTkIBus interface method + /// @note This method is virtual to allow it to be overridden to support a device that requires a unique impl + /// + /// @param devReg The device's register's address. + /// @param data Data to write. + /// + /// @retval returns number of bytes written, < 0 is an error code + /// + virtual int writeRegisterRegion(uint8_t devReg, const uint8_t *data, size_t length); + + /// @brief Reads a byte of data from the given register. + /// @note sfeTkIBus interface method + /// + /// @param devReg The device's register's address. + /// @param data Data to read. + /// + /// @retval true on success, false on error + /// + bool readRegisterByte(uint8_t devReg, uint8_t &data); + + /// @brief Reads a word of data from the given register. + /// @note sfeTkIBus interface method + /// + /// @param devReg The device's register's address. + /// @param data Data to read. + /// + /// @retval true on success, false on error + /// + bool readRegisterWord(uint8_t devReg, uint16_t &data); + + /// @brief Reads a block of data from the given register. + /// @note sfeTkIBus interface method + /// @note This method is virtual to allow it to be overridden to support a device that requires a unique impl + /// + /// @param devReg The device's register's address. + /// @param data Data to write. + /// + /// @retval 0 on success, < 0 on error - see error code + /// + virtual int readRegisterRegion(uint8_t devReg, uint8_t *data, size_t numBytes); + + protected: + // note: The wire port is protected, allowing access if a sub-class is + // created to implement a special read/write routine + // + // The actual Arduino i2c port + TwoWire *_i2cPort; +}; diff --git a/src/sfeTkArdSPI.cpp b/src/sfeTkArdSPI.cpp new file mode 100644 index 0000000..a70ee71 --- /dev/null +++ b/src/sfeTkArdSPI.cpp @@ -0,0 +1,188 @@ + +// sfeTkArdSPI.cpp - Arduino SPI implementation for the toolkit + +/* +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 "sfeTkArdSPI.h" +#include + +// Note: A leading "1" must be added to transfer with register to indicate a "read" +// Note to our future selves: +// This works / is required on both the ISM330 and MMC5983, +// but will cause badness with other SPI devices. +// We may need to add an alternate method if we ever add another SPI device. +#define kSPIReadBit 0x80 + +//--------------------------------------------------------------------------------- +// init() +// +// Arduino version of init. Will take in a defined SPI port/settings +// +bool sfeTkArdSPI::init(SPIClass &spiPort, SPISettings &busSPISettings, uint8_t csPin, bool bInit) +{ + // if we don't have a SPI port already + if (!_spiPort) + { + _spiPort = &spiPort; + + if (bInit) + _spiPort->begin(); + } + + setCS(csPin); + + // SPI settings are needed for every transaction + _sfeSPISettings = busSPISettings; + + return true; +} + +//--------------------------------------------------------------------------------- +// init() +// +// Arduino version of init. +// +bool sfeTkArdSPI::init(uint8_t csPin, bool bInit) +{ + // If the transaction settings are not provided by the user they are built here. + SPISettings spiSettings = SPISettings(3000000, SPI_MSBFIRST, SPI_MODE3); + + // In addition of the port is not provided by the user, it defaults to SPI here. + return init(SPI, spiSettings, csPin, bInit); +} + +//--------------------------------------------------------------------------------- +// init() +// +// Arduino version of init. +// +bool sfeTkArdSPI::init(bool bInit) +{ + return init(cs(), bInit); +} + +//--------------------------------------------------------------------------------- +// writeRegisterByte() +// +// Writes a byte to a given register. +// +// Returns true on success, false on failure +// +bool sfeTkArdSPI::writeRegisterByte(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 communication + digitalWrite(cs(), HIGH); + _spiPort->endTransaction(); + + return true; +} +//--------------------------------------------------------------------------------- +// writeRegisterWord() +// +// Writes a world to a given register. +// +// Returns true on success, false on failure +// +bool sfeTkArdSPI::writeRegisterWord(uint8_t devReg, uint16_t dataToWrite) +{ + return writeRegisterRegion(devReg, (uint8_t *)&dataToWrite, sizeof(uint8_t)) > 0; +} +//--------------------------------------------------------------------------------- +// writeRegisterRegion() +// +// Writes an array of bytes to a given register on the target address +// +// Returns the number of bytes written, < 0 is an error +// +int sfeTkArdSPI::writeRegisterRegion(uint8_t devReg, const uint8_t *data, size_t length) +{ + if (!_spiPort) + return -1; + + // Apply settings + _spiPort->beginTransaction(_sfeSPISettings); + + // Signal communication start + digitalWrite(cs(), LOW); + _spiPort->transfer(devReg); + + for (int i = 0; i < length; i++) + _spiPort->transfer(*data++); + + // End communication + digitalWrite(cs(), HIGH); + _spiPort->endTransaction(); + + return length; +} + +bool sfeTkArdSPI::readRegisterByte(uint8_t devReg, uint8_t &data) +{ + return readRegisterRegion(devReg, &data, sizeof(data)) == 1; +} + +bool sfeTkArdSPI::readRegisterWord(uint8_t devReg, uint16_t &data) +{ + return readRegisterRegion(devReg, (uint8_t *)&data, sizeof(data)) == 2; +} +//--------------------------------------------------------------------------------- +// readRegisterRegion() +// +// Reads an array of bytes to a given register on the target address +// +// Returns the number of bytes read, < 0 is an error +// +int sfeTkArdSPI::readRegisterRegion(uint8_t devReg, uint8_t *data, size_t numBytes) +{ + if (!_spiPort) + return -1; + + // 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" + _spiPort->transfer(devReg | kSPIReadBit); + + for (int i = 0; i < numBytes; i++) + *data++ = _spiPort->transfer(0x00); + + // End transaction + digitalWrite(cs(), HIGH); + _spiPort->endTransaction(); + + return numBytes; +} diff --git a/src/sfeTkArdSPI.h b/src/sfeTkArdSPI.h new file mode 100644 index 0000000..40e273e --- /dev/null +++ b/src/sfeTkArdSPI.h @@ -0,0 +1,140 @@ + + +// sfeTkBusSPI.h - Defines the Arduino SPI interface for the SparkFun Toolkit SDK + +/* + +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 +#include + +/// @brief This class implements the IBus interface for an SPI Implementation on Arduino +/// +class sfeTkArdSPI : public sfeTkISPI +{ + public: + /// + /// @brief Constructor + /// + sfeTkArdSPI(void) : _spiPort(nullptr){}; + + // copy constructor + sfeTkArdSPI(sfeTkArdSPI const &rhs) : sfeTkISPI(), _spiPort{rhs._spiPort}, _sfeSPISettings{rhs._sfeSPISettings} + { + } + + // Copy assignment + sfeTkArdSPI &operator=(const sfeTkArdSPI &rhs) + { + _spiPort = rhs._spiPort; + _sfeSPISettings = rhs._sfeSPISettings; + return *this; + } + /// @brief Method sets up the required SPI settings. + /// @note This function provides a default SPI Port. + /// + /// @param bInit This flag tracks whether the bus has been initialized. + /// + /// @retval bool - True on successful, false on error + /// + bool init(bool bInit = false); + + bool init(uint8_t csPin, bool bInit = false); + + /// @brief Method sets up the required SPI settings. + /// + /// @param spiPort Port for SPI communication. + /// @param busSPISettings Settings for speed, endianness, and spi mode of the SPI bus. + /// @param bInit This flag tracks whether the bus has been initialized. + /// + /// @retval bool - True on successful, false on error + /// + bool init(SPIClass &spiPort, SPISettings &busSPISettings, uint8_t csPin, bool bInit = false); + + /// @brief Write a single byte to the given register + /// + /// @param devReg The device's register's address. + /// @param data Data to write. + /// + /// @retval bool - true on success, false on error + /// + bool writeRegisterByte(uint8_t devReg, uint8_t data); + + /// @brief Write a single word to the given register + /// + /// @param devReg The device's register's address. + /// @param data Data to write. + /// + /// @retval bool - true on success, false on error + /// + bool writeRegisterWord(uint8_t devReg, uint16_t data); + + /// @brief Writes a number of bytes starting at the given register's address. + /// @note This method is virtual to allow it to be overridden to support a device that requires a unique impl + // + /// @param devReg The device's register's address. + /// @param data Data to write. + /// + /// @retval int - number of bytes written, < 0 = error value + /// + virtual int writeRegisterRegion(uint8_t devReg, const uint8_t *data, size_t length); + + /// @brief Read a single byte from the given register + /// + /// @param devReg The device's register's address. + /// @param data Data to read. + /// + /// @retval bool - true on success, false on error + /// + bool readRegisterByte(uint8_t devReg, uint8_t &data); + + /// @brief read a single word to the given register + /// + /// @param devReg The device's register's address. + /// @param data Data to write. + /// + /// @retval bool - true on success, false on error + /// + bool readRegisterWord(uint8_t devReg, uint16_t &data); + + /// @brief Reads a block of data from the given register. + /// @note This method is virtual to allow it to be overridden to support a device that requires a unique impl + /// + /// @param devReg The device's register's address. + /// @param data Data to write. + /// + /// @retval int returns the number of bytes read, < 0 on error + /// + virtual int readRegisterRegion(uint8_t reg, uint8_t *data, size_t numBytes); + + protected: + // note: The instance data is protected, allowing access if a sub-class is + // created to implement a special read/write routine + // + + SPIClass *_spiPort; + + // Settings are used for every transaction. + SPISettings _sfeSPISettings; +}; 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 deleted file mode 100644 index 80369db..0000000 --- a/src/sfe_i2c.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -sfe_i2c.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. - -The following classes specify the behavior for communicating -over Inter-Integrated Circuit (I2C) - -*/ - -#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 -{ - 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; -}; 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 deleted file mode 100644 index 91c51ed..0000000 --- a/src/sfe_spi.h +++ /dev/null @@ -1,90 +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. - -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 -{ - 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); - - private: - SPIClass *_spiPort; - // Settings are used for every transaction. - SPISettings _sfeSPISettings; - uint8_t _cs; -};