|
| 1 | +# Overview - Device Bus interface - sfeTkIBus |
| 2 | + |
| 3 | +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. |
| 4 | + |
| 5 | +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. |
| 6 | + |
| 7 | +The key goals set for the Bus implementation in the Toolkit include: |
| 8 | + |
| 9 | +* Separate device setup from device communication |
| 10 | +* Define a common bus interface for use across a variety of common device bus types |
| 11 | +* Deliver support for both SPI and I2C bus types initially, focusing on Arduino |
| 12 | +* Structure the bus/toolkit implementation such that it's platform independent |
| 13 | + |
| 14 | +## Architecture Overview |
| 15 | + |
| 16 | +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. |
| 17 | + |
| 18 | +The key class to support this pattern are: |
| 19 | + |
| 20 | +| | | |
| 21 | +|------|-------| |
| 22 | +**sfeTkIBus** | A virtual C++ class that device the bus ```sfeTkIBus``` interface | |
| 23 | +**sfeTkII2C** | Sub-class of the ```sfeTkIIBus``` interface, it provides an interface for I2C devices| |
| 24 | +**sfeTkISPI** | Sub-class of the ```sfeTkIIBus``` interface, it provides an interface for SPI devices | |
| 25 | + |
| 26 | +### The sfeTkIBus Interface |
| 27 | + |
| 28 | +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. |
| 29 | + |
| 30 | +The interface methods: |
| 31 | + |
| 32 | +| Method| Definition | |
| 33 | +|------|-------| |
| 34 | +**writeRegisterByte** | Write a byte of data to a particular register of a device | |
| 35 | +**writeRegisterWord** | Write a word of data to a particular register of a device | |
| 36 | +**writeRegisterRegion** | Write an array of data to a particular register of a device| |
| 37 | +**readRegisterByte** | Read a byte of data from a particular register of a device | |
| 38 | +**readRegisterWord** | Read a word of data from a particular register of a device | |
| 39 | +**readRegisterRegion** | Read an array of data from a particular register of a device | |
| 40 | + |
| 41 | +> [!NOTE] |
| 42 | +> 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. |
| 43 | +
|
| 44 | +### The sfeTkII2C Implementation |
| 45 | + |
| 46 | +This class sub-classes from the ```sfeTkIBus``` interface adding additional functionally focused on supporting an I2C implementation. This interface provides the additional functionality. |
| 47 | + |
| 48 | +| Method| Definition | |
| 49 | +|------|-------| |
| 50 | +**ping** | Determine if a devices is connected to the I2C device at the address set on this bus object. This is an interface method | |
| 51 | +**setAddress** | Set the I2C address to use for this I2C object | |
| 52 | +**address** | Returns the address used by this I2C object | |
| 53 | + |
| 54 | +> [!NOTE] |
| 55 | +> The ```sfeTkII2C``` class manages the device address for the I2C bus. As such, each I2C device instantiates/uses an instance of the ```sfeTkII2C``` class. |
| 56 | +
|
| 57 | +### The sfeTkISPI Implementation |
| 58 | + |
| 59 | +This class sub-classes from the ```sfeTkIBus``` interface adding additional functionally focused on supporting an SPI implementation. This interface provides the additional functionality. |
| 60 | + |
| 61 | +| Method| Definition | |
| 62 | +|------|-------| |
| 63 | +**setCS** | Set the CS Pin to use for this SPI object | |
| 64 | +**cs** | Returns the CS Pin used by this SPI object | |
| 65 | + |
| 66 | +> [!NOTE] |
| 67 | +> The ```sfeTkISPI``` class manages CS Pin for the SPI bus. As such, each SPI device instantiates/uses an instance of the ```sfeTkISPI``` class. |
| 68 | +
|
| 69 | +The class diagram of these base class interfaces/implementation: |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +## sfeTkIIBus - Arduino Implementation |
| 74 | + |
| 75 | +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. |
| 76 | + |
| 77 | +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. |
| 78 | + |
| 79 | +> [!IMPORTANT] |
| 80 | +> The intent is that each user of an particular bus - a device in most cases - contains an instance of the specific bus object. |
| 81 | +
|
| 82 | +The class diagram for the Arduino implementation is as follows: |
| 83 | + |
| 84 | + |
| 85 | + |
| 86 | +### The sfeTkArdI2C Class |
| 87 | + |
| 88 | +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. |
| 89 | + |
| 90 | +### The sfeTkArdSPI Class |
| 91 | + |
| 92 | +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. |
| 93 | + |
| 94 | +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. |
| 95 | + |
| 96 | +## sfeTkIBus Use |
| 97 | + |
| 98 | +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. |
| 99 | + |
| 100 | +The general pattern for a device driver implementation that uses the SparkFun Toolkit is the following: |
| 101 | + |
| 102 | +### Implement a Platform Independent Driver |
| 103 | + |
| 104 | +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. |
| 105 | + |
| 106 | +>[!IMPORTANT] |
| 107 | +> At this level, the driver is only using a ```sfeTkIBus``` interface, not any specific bus implementation. |
| 108 | +
|
| 109 | +This driver has the following unique functionality: |
| 110 | + |
| 111 | +1) A method to set the object that implements the ```sfeTkIBus``` interface object should use. Since |
| 112 | +1) If the device supports identification capabilities, the driver provides this functionality. |
| 113 | + |
| 114 | +#### SImple Example of an Independent Driver Implementation |
| 115 | + |
| 116 | +This implementation would take the following form: |
| 117 | + |
| 118 | +```c++ |
| 119 | + |
| 120 | +class myDriverClass |
| 121 | +{ |
| 122 | +public: |
| 123 | + |
| 124 | + myDriverClass(uint8_t address) : _addr{address}{} |
| 125 | + |
| 126 | + bool begin() |
| 127 | + { |
| 128 | + // initialize things ... |
| 129 | + |
| 130 | + return true; |
| 131 | + } |
| 132 | + void setCommunicationBus(sfeTkIBus *theBus) |
| 133 | + { |
| 134 | + _theBus = theBus; |
| 135 | + } |
| 136 | + |
| 137 | + bool updateDeviceData(uint8_t *data, size_t len) |
| 138 | + { |
| 139 | + if (!_theBus || !data || len == 0) |
| 140 | + return false; |
| 141 | + |
| 142 | + int status = _theBus->writeRegisterRegion(THE_REG, data, len); |
| 143 | + |
| 144 | + return (status == 0); |
| 145 | + } |
| 146 | + |
| 147 | + bool checkDeviceID() |
| 148 | + { |
| 149 | + // do some device ID checks in registers ...etc |
| 150 | + return true; |
| 151 | + } |
| 152 | +private: |
| 153 | + sfeTkIBus *_theBus; |
| 154 | +}; |
| 155 | +``` |
| 156 | +
|
| 157 | +### Write a Platform Specific Driver, based on the core driver |
| 158 | +
|
| 159 | +This driver sub-classes from the general/core driver class, builds and configures the desired bus object and passes this into the core driver. |
| 160 | +
|
| 161 | +The key concepts for these Platform specific drivers include: |
| 162 | +
|
| 163 | +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. |
| 164 | +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. |
| 165 | +
|
| 166 | +#### Basic concept - creating an I2C class in Arduino |
| 167 | +
|
| 168 | +The following is an example of an I2C class in Arduino based on the previous platform independent driver. |
| 169 | +
|
| 170 | +> [!NOTE] |
| 171 | +> 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. |
| 172 | +
|
| 173 | +```c++ |
| 174 | +
|
| 175 | +class myArduinoDriverI2C : public myDriverClass |
| 176 | +{ |
| 177 | + public: |
| 178 | + myArduinoDriverI2C() |
| 179 | + {} |
| 180 | + |
| 181 | + bool begin() |
| 182 | + { |
| 183 | + if (!_theI2CBus.init(MY_DEVICE_ADDRESS)) |
| 184 | + return false; |
| 185 | + setCommunicationBus(&_theI2CBus); |
| 186 | +
|
| 187 | + return myDriverClass::begin(); |
| 188 | + } |
| 189 | +
|
| 190 | + bool isConnected() |
| 191 | + { |
| 192 | + if (!_theI2CBus.ping()) |
| 193 | + return false; |
| 194 | +
|
| 195 | + return checkDeviceID(); |
| 196 | + } |
| 197 | +
|
| 198 | +private: |
| 199 | + sfeTkArdI2C _theI2CBus; |
| 200 | +}; |
| 201 | +``` |
| 202 | + |
| 203 | +#### Basic concept - creating an SPI class in Arduino |
| 204 | + |
| 205 | +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. |
| 206 | + |
| 207 | +> [!NOTE] |
| 208 | +> This class implements a ```isConnected()``` method that just calls the superclasses ```checkDeviceID()``` method to determine if the device is available on the bus. |
| 209 | +
|
| 210 | +```c++ |
| 211 | + |
| 212 | +class myArduinoDriveSPI : public myDriverClass |
| 213 | +{ |
| 214 | + public: |
| 215 | + myArduinoDriverSPI() |
| 216 | + {} |
| 217 | + |
| 218 | + bool begin() |
| 219 | + { |
| 220 | + SPISettings spiSettings = SPISettings(4000000, MSBFIRST, SPI_MODE3); |
| 221 | + |
| 222 | + if (!_theSPIBus.init(SPI, spiSettings, MY_DEFAULT_CS, true)) |
| 223 | + return false; |
| 224 | + setCommunicationBus(&_theSPIBus); |
| 225 | + |
| 226 | + return myDriverClass::begin(); |
| 227 | + } |
| 228 | + |
| 229 | + bool isConnected() |
| 230 | + { |
| 231 | + return checkDeviceID(); |
| 232 | + } |
| 233 | + |
| 234 | +private: |
| 235 | + sfeTkArdSPI _theSPIBus; |
| 236 | +}; |
| 237 | +``` |
0 commit comments