diff --git a/doc/Doxyfile b/doc/Doxyfile index 6ecba011..b41daaaf 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -610,7 +610,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../libraries/FreetronicsLCD ../libraries/BlinkLED . +INPUT = ../libraries/FreetronicsLCD ../libraries/BlinkLED ../libraries/BitBangI2C . # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/libraries/BitBangI2C/BitBangI2C.cpp b/libraries/BitBangI2C/BitBangI2C.cpp new file mode 100644 index 00000000..214af934 --- /dev/null +++ b/libraries/BitBangI2C/BitBangI2C.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * 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 "BitBangI2C.h" +#include + +/** + * \class BitBangI2C BitBangI2C.h + * \brief Bit-banged implementation of an I2C master. + * + * This class implements the I2C master protocol on any arbitrary pair + * of data and clock pins. It is not restricted to pre-defined pins as + * is the case for the standard \c Wire library. + * + * This implementation only implements the master side of the protocol. + * It assumes that there is a single bus master, no arbitration, and + * no clock stretching. + */ + +#define i2cDelay() delayMicroseconds(5) + +/** + * \brief Constructs a new bit-banged I2C master on \a dataPin and \a clockPin. + */ +BitBangI2C::BitBangI2C(uint8_t dataPin, uint8_t clockPin) + : _dataPin(dataPin) + , _clockPin(clockPin) + , started(false) +{ + // Initially set the CLOCK and DATA lines to be outputs in the high state. + pinMode(_clockPin, OUTPUT); + pinMode(_dataPin, OUTPUT); + digitalWrite(_clockPin, HIGH); + digitalWrite(_dataPin, HIGH); +} + +/** + * \typedef BitBangI2C::ack_t + * \brief Type that represents an I2C ACK or NACK indication. + */ + +/** + * \var BitBangI2C::ACK + * \brief Indicates that an I2C operation was acknowledged. + */ + +/** + * \var BitBangI2C::NACK + * \brief Indicates that an I2C operation was not acknowledged. + */ + +/** + * \brief Transmits an I2C start condition on the bus. + * + * \sa startRead(), startWrite() + */ +void BitBangI2C::start() +{ + pinMode(_dataPin, OUTPUT); + if (started) { + // Already started, so send a restart condition. + digitalWrite(_dataPin, HIGH); + digitalWrite(_clockPin, HIGH); + i2cDelay(); + } + digitalWrite(_dataPin, LOW); + i2cDelay(); + digitalWrite(_clockPin, LOW); + i2cDelay(); + started = true; +} + +/** + * \brief Transmits an I2C stop condition on the bus. + */ +void BitBangI2C::stop() +{ + pinMode(_dataPin, OUTPUT); + digitalWrite(_dataPin, LOW); + digitalWrite(_clockPin, HIGH); + i2cDelay(); + digitalWrite(_dataPin, HIGH); + i2cDelay(); + started = false; +} + +#define I2C_WRITE 0x00 +#define I2C_WRITE10 0xF0 +#define I2C_READ 0x01 +#define I2C_READ10 0xF1 + +/** + * \brief Starts a write operation by sending a start condition and the I2C control byte. + * + * The \a address must be the 7-bit or 10-bit address of the I2C slave + * on the bus. + * + * Returns BitBangI2C::ACK or BitBangI2C::NACK to indicate whether the + * read operation was acknowledged by the slave or not. + * + * \sa startWrite() + */ +BitBangI2C::ack_t BitBangI2C::startWrite(unsigned int address) +{ + start(); + if (address < 0x80) { + // 7-bit address. + return write((uint8_t)((address << 1) | I2C_WRITE)); + } else { + // 10-bit address. + if (write((uint8_t)(((address >> 7) & 0x06)) | I2C_WRITE10) == NACK) + return NACK; + return write((uint8_t)address); + } +} + +/** + * \brief Starts a read operation by sending the start condition and the I2C control byte. + * + * The \a address must be the 7-bit or 10-bit address of the I2C slave + * on the bus. + * + * Returns BitBangI2C::ACK or BitBangI2C::NACK to indicate whether the + * read operation was acknowledged by the slave or not. + * + * \sa startWrite() + */ +BitBangI2C::ack_t BitBangI2C::startRead(unsigned int address) +{ + start(); + if (address < 0x80) { + // 7-bit address. + return write((uint8_t)((address << 1) | I2C_READ)); + } else { + // 10-bit address. + if (write((uint8_t)(((address >> 7) & 0x06)) | I2C_READ10) == NACK) + return NACK; + return write((uint8_t)address); + } +} + +/** + * \brief Writes a single byte \a value on the I2C bus. + * + * Returns BitBangI2C::ACK or BitBangI2C::NACK to indicate whether the + * slave acknowledged the byte or not. + */ +BitBangI2C::ack_t BitBangI2C::write(uint8_t value) +{ + uint8_t mask = 0x80; + while (mask != 0) { + writeBit((value & mask) != 0); + mask >>= 1; + } + return readBit(); +} + +/** + * \brief Reads a single byte from the I2C bus. + * + * If \a ack is BitBangI2C::ACK, then the byte is acknowledged. Otherwise + * the byte will not be acknowledged. + */ +uint8_t BitBangI2C::read(ack_t ack) +{ + uint8_t value = 0; + for (uint8_t bit = 0; bit < 8; ++bit) + value = (value << 1) | readBit(); + writeBit(ack); + return value; +} + +void BitBangI2C::writeBit(bool bit) +{ + pinMode(_dataPin, OUTPUT); + if (bit) + digitalWrite(_dataPin, HIGH); + else + digitalWrite(_dataPin, LOW); + i2cDelay(); + digitalWrite(_clockPin, HIGH); + i2cDelay(); + digitalWrite(_clockPin, LOW); + i2cDelay(); +} + +bool BitBangI2C::readBit() +{ + pinMode(_dataPin, INPUT); + digitalWrite(_dataPin, HIGH); + digitalWrite(_clockPin, HIGH); + bool bit = digitalRead(_dataPin); + i2cDelay(); + digitalWrite(_clockPin, LOW); + i2cDelay(); + return bit; +} diff --git a/libraries/BitBangI2C/BitBangI2C.h b/libraries/BitBangI2C/BitBangI2C.h new file mode 100644 index 00000000..da966332 --- /dev/null +++ b/libraries/BitBangI2C/BitBangI2C.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * 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. + */ + +#ifndef BitBangI2C_h +#define BitBangI2C_h + +#include + +class BitBangI2C { +public: + BitBangI2C(uint8_t dataPin, uint8_t clockPin); + + typedef bool ack_t; + + static const ack_t ACK = false; + static const ack_t NACK = true; + + void start(); + void stop(); + + ack_t startWrite(unsigned int address); + ack_t startRead(unsigned int address); + + ack_t write(uint8_t value); + uint8_t read(ack_t ack = ACK); + +private: + uint8_t _dataPin; + uint8_t _clockPin; + bool started; + + void writeBit(bool bit); + bool readBit(); +}; + +#endif