-
Notifications
You must be signed in to change notification settings - Fork 2
Add I2C and SPI interfaces #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,23 @@ | ||
| // Code! | ||
| /* | ||
| 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" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <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 | ||
| // 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Class name does not follow code style conventions. I would rename to:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, my mistake! Do we want to use the word "Base" in the name? I know some people have strong opinions about that.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be following how I've seen Kirk do things in the past. Base is usually used for purely virtual classes anyway. |
||
| { | ||
| 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; | ||
| }; | ||
|
|
||
| //}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, thought I'd fixed that, guess not! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
|
Comment on lines
+58
to
+67
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thoughts on making this a switch for readability? It wouldn't make too much of a difference speed wise, the compiler will generate an appropriate jump table either way.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could also see breaking this out into a virtual function at the SFE_Bus level.
virtual int8_t interpretError(int8_t error);this would then become if(result)
return interpretError(result);
else
return SFE_BUS_OK;The
int8_t SFE_I2C_Arduino::interpretError(int8_t error)
{
switch(error) {
case 1:
return SFE_BUS_E_DATA_TOO_LONG;
case 2:
case 3:
return SFE_BUS_E_NO_RESPONSE;
case 5:
return SFE_BUS_E_TIMEOUT;
case 4:
default:
return SFE_BUS_E_UNKNOWN;
}
}
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about exactly this as well, just didn't get around to it. Let's discuss it tomorrow to make sure we're all on board with the error handling in general before updating this. |
||
| } | ||
|
|
||
| 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; | ||
| } | ||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a 2 or are we ok with multiple warnings with the same code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, typo! Should be 2. IMO every return code should have a unique value so a user can properly debug.