Files
MySensors/libraries/DigitalIO/SoftI2cMaster.cpp
T
Henrik Ekblad 2fd948fc66 Add new scene controller with touch display example.
Upgraded RF24 to latest version which supports SoftSPI. 
Added DigitalIO and UTFT libraries to support the new example.
Fix problem reported in RelayWithButtonActuator example.
2014-10-17 00:01:55 +02:00

241 lines
7.3 KiB
C++

/* Arduino DigitalIO Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino DigitalIO 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 DigitalIO Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Software I2C library
*
* @defgroup softI2C Software I2C
* @details Software Two Wire Interface library.
* @{
*/
#include <SoftI2cMaster.h>
//------------------------------------------------------------------------------
/**
* Start an I2C transfer with possible continuation.
*
* @param[in] addrRW I2C slave address plus R/W bit.
* The I2C slave address is in the high seven bits
* and is ORed with on of the following:
* - I2C_READ for a read transfer.
* - I2C_WRITE for a write transfer.
* .
* @param[in,out] buf Source or destination for transfer.
* @param[in] nbytes Number of bytes to transfer (may be zero).
* @param[in] option Option for ending the transfer, one of:
* - I2C_STOP end the transfer with an I2C stop
* condition.
* - I2C_REP_START end the transfer with an I2C
* repeated start condition.
* - I2C_CONTINUE allow additional transferContinue()
* calls.
* .
* @return true for success else false.
*/
bool I2cMasterBase::transfer(uint8_t addrRW,
void *buf, size_t nbytes, uint8_t option) {
uint8_t* p = reinterpret_cast<uint8_t*>(buf);
if (_state != STATE_REP_START) {
start();
}
if (!write(addrRW)) {
_state = addrRW & I2C_READ ? STATE_RX_ADDR_NACK : STATE_TX_ADDR_NACK;
return false;
}
_state = addrRW & I2C_READ ? STATE_RX_DATA : STATE_TX_DATA;
return transferContinue(buf, nbytes, option);
}
//------------------------------------------------------------------------------
/**
* Continue an I2C transfer.
*
* @param[in,out] buf Source or destination for transfer.
* @param[in] nbytes Number of bytes to transfer (may be zero).
* @param[in] option Option for ending the transfer, one of:
* - I2C_STOP end the transfer with an I2C stop
* condition.
* - I2C_REP_START end the transfer with an I2C
* repeated start condition.
* - I2C_CONTINUE allow additional transferContinue()
* calls.
* .
* @return true for success else false.
*/
bool I2cMasterBase::transferContinue(void *buf, size_t nbytes, uint8_t option) {
uint8_t* p = reinterpret_cast<uint8_t*>(buf);
if (_state == STATE_RX_DATA) {
for (size_t i = 0; i < nbytes; i++) {
p[i] = read(i == (nbytes - 1) && option != I2C_CONTINUE);
}
} else if (_state == STATE_TX_DATA) {
for (size_t i = 0; i < nbytes; i++) {
if (!write(p[i])) {
_state = STATE_TX_DATA_NACK;
return false;
}
}
} else {
return false;
}
if (option == I2C_STOP) {
stop();
_state = STATE_STOP;
} else if (option == I2C_REP_START) {
start();
_state = STATE_STOP;
}
return true;
}
//==============================================================================
// WARNING don't change SoftI2cMaster unless you verify the change with a scope
//------------------------------------------------------------------------------
/**
* Constructor, initialize SCL/SDA pins and set the bus high.
*
* @param[in] sdaPin The software SDA pin number.
*
* @param[in] sclPin The software SCL pin number.
*/
SoftI2cMaster::SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin) {
begin(sclPin, sdaPin);
}
//------------------------------------------------------------------------------
/**
* Initialize SCL/SDA pins and set the bus high.
*
* @param[in] sdaPin The software SDA pin number.
*
* @param[in] sclPin The software SCL pin number.
*/
void SoftI2cMaster::begin(uint8_t sclPin, uint8_t sdaPin) {
uint8_t port;
// Get bit mask and address of scl registers.
_sclBit = digitalPinToBitMask(sclPin);
port = digitalPinToPort(sclPin);
_sclDDR = portModeRegister(port);
volatile uint8_t* sclOutReg = portOutputRegister(port);
// Get bit mask and address of sda registers.
_sdaBit = digitalPinToBitMask(sdaPin);
port = digitalPinToPort(sdaPin);
_sdaDDR = portModeRegister(port);
_sdaInReg = portInputRegister(port);
volatile uint8_t* sdaOutReg = portOutputRegister(port);
// Clear PORT bit for scl and sda.
uint8_t s = SREG;
noInterrupts();
*sclOutReg &= ~_sclBit;
*sdaOutReg &= ~_sdaBit;
SREG = s;
// Set scl and sda high.
writeScl(HIGH);
writeSda(HIGH);
}
//------------------------------------------------------------------------------
/* Read a byte and send ACK if more reads follow else NACK to terminate read.
*
* @param[in] last Set true to terminate the read else false.
*
* @return The byte read from the I2C bus.
*/
uint8_t SoftI2cMaster::read(uint8_t last) {
uint8_t b = 0;
// Set sda to high Z mode for read.
writeSda(HIGH);
// Read a byte.
for (uint8_t i = 0; i < 8; i++) {
// Don't change this loop unless you verify the change with a scope.
b <<= 1;
sclDelay(16);
writeScl(HIGH);
sclDelay(12);
if (readSda()) b |= 1;
writeScl(LOW);
}
// send ACK or NACK
writeSda(last);
sclDelay(12);
writeScl(HIGH);
sclDelay(18);
writeScl(LOW);
writeSda(LOW);
return b;
}
//------------------------------------------------------------------------------
/* Issue a start condition. */
void SoftI2cMaster::start() {
if (!readSda()) {
writeSda(HIGH);
writeScl(HIGH);
sclDelay(20);
}
writeSda(LOW);
sclDelay(20);
writeScl(LOW);
}
//------------------------------------------------------------------------------
/* Issue a stop condition. */
void SoftI2cMaster::stop(void) {
writeSda(LOW);
sclDelay(20);
writeScl(HIGH);
sclDelay(20);
writeSda(HIGH);
sclDelay(20);
}
//------------------------------------------------------------------------------
/*
* Write a byte.
*
* @param[in] data The byte to send.
*
* @return The value true, 1, if the slave returned an ACK or false for NACK.
*/
bool SoftI2cMaster::write(uint8_t data) {
// write byte
for (uint8_t m = 0X80; m != 0; m >>= 1) {
// don't change this loop unless you verify the change with a scope
writeSda(m & data);
sclDelay(8);
writeScl(HIGH);
sclDelay(18);
writeScl(LOW);
}
sclDelay(8);
// Go to sda high Z mode for input.
writeSda(HIGH);
writeScl(HIGH);
sclDelay(16);
// Get ACK or NACK.
uint8_t rtn = readSda();
// pull scl low.
writeScl(LOW);
// Pull sda low.
writeSda(LOW);
return rtn == 0;
}
/** @} */