/* Arduino I2cMaster Library * Copyright (C) 2012 by William Greiman * * This file is part of the Arduino I2cMaster Library * * This Library is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the Arduino I2cMaster Library. If not, see * . */ #ifndef I2C_MASTER_H #define I2C_MASTER_H /** * \file * \brief Software I2C and hardware TWI library */ #if ARDUINO < 100 #error Requires Arduino 1.0 or greater. #else // ARDUINO #include #endif // ARDUINO //------------------------------------------------------------------------------ /** I2cMaster version YYYYMMDD */ #define I2C_MASTER_VERSION 20120716 //------------------------------------------------------------------------------ /** Default hardware I2C clock in Hz */ uint32_t const F_TWI = 400000L; /** Delay used for software I2C */ uint8_t const I2C_DELAY_USEC = 4; /** Bit to or with address for read start and read restart */ uint8_t const I2C_READ = 1; /** Bit to or with address for write start and write restart */ uint8_t const I2C_WRITE = 0; //------------------------------------------------------------------------------ // Status codes in TWSR - names are from Atmel TWSR.h with TWSR_ added /** start condition transmitted */ uint8_t const TWSR_START = 0x08; /** repeated start condition transmitted */ uint8_t const TWSR_REP_START = 0x10; /** slave address plus write bit transmitted, ACK received */ uint8_t const TWSR_MTX_ADR_ACK = 0x18; /** data transmitted, ACK received */ uint8_t const TWSR_MTX_DATA_ACK = 0x28; /** slave address plus read bit transmitted, ACK received */ uint8_t const TWSR_MRX_ADR_ACK = 0x40; //============================================================================== /** * \class I2cMasterBase * \brief Base class for FastI2cMaster, SoftI2cMaster, and TwiMaster */ class I2cMasterBase { public: /** Read a byte * \param[in] last send Ack if last is false else Nak to terminate read * \return byte read from I2C bus */ virtual uint8_t read(uint8_t last) = 0; /** Send new address and read/write bit without sending a stop. * \param[in] addressRW i2c address with read/write bit * \return true for success false for failure */ virtual bool restart(uint8_t addressRW) = 0; /** Issue a start condition * \param[in] addressRW i2c address with read/write bit * \return true for success false for failure */ virtual bool start(uint8_t addressRW) = 0; /** Issue a stop condition. */ virtual void stop(void) = 0; /** Write a byte * \param[in] data byte to write * \return true for Ack or false for Nak */ virtual bool write(uint8_t data) = 0; }; //============================================================================== /** * \class SoftI2cMaster * \brief Software I2C master class */ class SoftI2cMaster : public I2cMasterBase { public: SoftI2cMaster(uint8_t sdaPin, uint8_t sclPin, bool enablePullup = true); uint8_t read(uint8_t last); bool restart(uint8_t addressRW); bool start(uint8_t addressRW); void stop(void); bool write(uint8_t b); private: SoftI2cMaster() {} bool enablePullup_; uint8_t sdaPin_; uint8_t sclPin_; }; //============================================================================== /** * \class TwiMaster * \brief Hardware I2C master class * * Uses ATmega TWI hardware port */ class TwiMaster : public I2cMasterBase { public: explicit TwiMaster(bool enablePullup); uint8_t read(uint8_t last); bool restart(uint8_t addressRW); void speed(bool fast); bool start(uint8_t addressRW); /** \return status from last TWI command - useful for library debug */ uint8_t status(void) {return status_;} void stop(void); bool write(uint8_t data); private: TwiMaster() {} uint8_t status_; void execCmd(uint8_t cmdReg); }; //============================================================================== // experimental template based fast software I2C #ifndef USE_FAST_I2C_MASTER /** disable experimental fast software I2C */ #define USE_FAST_I2C_MASTER 0 #endif // USE_FAST_I2C_MASTER #if USE_FAST_I2C_MASTER || DOXYGEN #include #include //------------------------------------------------------------------------------ /** * \class FastI2cMaster * \brief Fast software I2C master class */ template class FastI2cMaster : public I2cMasterBase { public: FastI2cMaster() { fastPinMode(sdaPin, OUTPUT); fastDigitalWrite(sdaPin, HIGH); fastPinMode(sclPin, OUTPUT); fastDigitalWrite(sclPin, HIGH); } //---------------------------------------------------------------------------- uint8_t read(uint8_t last) { uint8_t data = 0; // make sure pull-up enabled fastPinMode(sdaPin, INPUT); fastDigitalWrite(sdaPin, enablePullups); readBit(7, &data); readBit(6, &data); readBit(5, &data); readBit(4, &data); readBit(3, &data); readBit(2, &data); readBit(1, &data); readBit(0, &data); // send Ack or Nak fastPinMode(sdaPin, OUTPUT); fastDigitalWrite(sdaPin, last); fastDigitalWrite(sclPin, HIGH); sclDelay(3); fastDigitalWrite(sclPin, LOW); fastDigitalWrite(sdaPin, LOW); return data; } //---------------------------------------------------------------------------- bool restart(uint8_t addressRW) { fastDigitalWrite(sdaPin, HIGH); sclDelay(8); fastDigitalWrite(sclPin, HIGH); sclDelay(8); return start(addressRW); } //---------------------------------------------------------------------------- bool start(uint8_t addressRW) { fastDigitalWrite(sdaPin, LOW); sclDelay(8); fastDigitalWrite(sclPin, LOW); sclDelay(8); return write(addressRW); } //---------------------------------------------------------------------------- void stop(void) { fastDigitalWrite(sdaPin, LOW); sclDelay(8); fastDigitalWrite(sclPin, HIGH); sclDelay(8); fastDigitalWrite(sdaPin, HIGH); sclDelay(8); } //---------------------------------------------------------------------------- bool write(uint8_t data) { // write byte writeBit(7, data); writeBit(6, data); writeBit(5, data); writeBit(4, data); writeBit(3, data); writeBit(2, data); writeBit(1, data); writeBit(0, data); // get Ack or Nak fastPinMode(sdaPin, INPUT); // enable pullup fastDigitalWrite(sdaPin, HIGH); fastDigitalWrite(sclPin, HIGH); sclDelay(3); uint8_t rtn = fastDigitalRead(sdaPin); fastDigitalWrite(sclPin, LOW); fastPinMode(sdaPin, OUTPUT); fastDigitalWrite(sdaPin, LOW); return rtn == 0; } //---------------------------------------------------------------------------- private: inline __attribute__((always_inline)) void readBit(uint8_t bit, uint8_t* data) { fastDigitalWrite(sclPin, HIGH); sclDelay(3); if (fastDigitalRead(sdaPin)) *data |= 1 << bit; fastDigitalWrite(sclPin, LOW); sclDelay(7); } //---------------------------------------------------------------------------- void sclDelay(uint8_t n) { _delay_loop_1(n); } //---------------------------------------------------------------------------- inline __attribute__((always_inline)) void writeBit(uint8_t bit, uint8_t data) { fastDigitalWrite(sdaPin, data & (1 << bit)); fastDigitalWrite(sclPin, HIGH); sclDelay(4); fastDigitalWrite(sclPin, LOW); sclDelay(6); } }; #endif // USE_FAST_I2C_MASTER #endif // I2C_MASTER_H