Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions docs/ar_ibus.md
Original file line number Diff line number Diff line change
@@ -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;
};
```
1 change: 0 additions & 1 deletion docs/docs.md

This file was deleted.

Binary file added docs/images/tk_IBUS.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/tk_ibus_ard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 33 additions & 1 deletion src/SparkFun_Toolkit.h
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
// Code!

/*
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"
69 changes: 48 additions & 21 deletions src/sfe_bus.h → src/sfeTk/sfeTkIBus.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@

// sfeTkIBus.h
//
// Defines the communication bus interface for the SparkFun Electronics Toolkit -> sfeTk
/*
sfe_bus.h

The MIT License (MIT)

Expand All @@ -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 <cstdint>

// 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;
};

//};
Loading