/* * © 2023, Neil McKechnie. All rights reserved. * * This file is part of CommandStation-EX * * This 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. * * It 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 CommandStation. If not, see . */ #ifndef I2CMANAGER_WIRE_H #define I2CMANAGER_WIRE_H #include #include #include "I2CManager.h" // This module is only compiled if I2C_USE_WIRE is defined, so define it here // to get intellisense to work correctly. #if !defined(I2C_USE_WIRE) #define I2C_USE_WIRE #endif // Older versions of Wire don't have setWireTimeout function. AVR does. #ifdef ARDUINO_ARCH_AVR #define WIRE_HAS_TIMEOUT #endif /*************************************************************************** * Initialise I2C interface software ***************************************************************************/ void I2CManagerClass::_initialise() { Wire.begin(); #if defined(WIRE_HAS_TIMEOUT) Wire.setWireTimeout(timeout, true); #endif } /*************************************************************************** * Set I2C clock speed. Normally 100000 (Standard) or 400000 (Fast) * on Arduino. Mega4809 supports 1000000 (Fast+) too. ***************************************************************************/ void I2CManagerClass::_setClock(unsigned long i2cClockSpeed) { Wire.setClock(i2cClockSpeed); } /*************************************************************************** * Set I2C timeout value in microseconds. The timeout applies to each * Wire call separately, i.e. in a write+read, the timer is reset before the * read is started. ***************************************************************************/ void I2CManagerClass::setTimeout(unsigned long value) { timeout = value; #if defined(WIRE_HAS_TIMEOUT) Wire.setWireTimeout(value, true); #endif } /*************************************************************************** * Initiate a write to an I2C device (blocking operation on Wire) ***************************************************************************/ uint8_t I2CManagerClass::write(uint8_t address, const uint8_t buffer[], uint8_t size, I2CRB *rb) { uint8_t status = I2C_STATUS_OK; uint8_t retryCount = 0; // If request fails, retry up to the defined limit, unless the NORETRY flag is set // in the request block. do { Wire.beginTransmission(address); if (size > 0) Wire.write(buffer, size); status = Wire.endTransmission(); } while (!(status == I2C_STATUS_OK || ++retryCount > MAX_I2C_RETRIES || rb->operation & OPERATION_NORETRY)); rb->status = status; return I2C_STATUS_OK; } /*************************************************************************** * Initiate a write from PROGMEM (flash) to an I2C device (blocking operation on Wire) ***************************************************************************/ uint8_t I2CManagerClass::write_P(uint8_t address, const uint8_t buffer[], uint8_t size, I2CRB *rb) { uint8_t ramBuffer[size]; const uint8_t *p1 = buffer; for (uint8_t i=0; i 0) { Wire.beginTransmission(address); Wire.write(writeBuffer, writeSize); status = Wire.endTransmission(false); // Don't free bus yet } if (status == I2C_STATUS_OK) { #ifdef WIRE_HAS_TIMEOUT Wire.clearWireTimeoutFlag(); #endif Wire.requestFrom(address, (size_t)readSize); #ifdef WIRE_HAS_TIMEOUT if (!Wire.getWireTimeoutFlag()) { #endif while (Wire.available() && nBytes < readSize) readBuffer[nBytes++] = Wire.read(); if (nBytes < readSize) status = I2C_STATUS_TRUNCATED; #ifdef WIRE_HAS_TIMEOUT } else { status = I2C_STATUS_TIMEOUT; } #endif } } while (!(status == I2C_STATUS_OK || ++retryCount > MAX_I2C_RETRIES || rb->operation & OPERATION_NORETRY)); rb->nBytes = nBytes; rb->status = status; return I2C_STATUS_OK; } /*************************************************************************** * Function to queue a request block and initiate operations. * * For the Wire version, this executes synchronously. * The read/write/write_P functions return I2C_STATUS_OK always, and the * completion status of the operation is in the request block, as for * the non-blocking version. ***************************************************************************/ void I2CManagerClass::queueRequest(I2CRB *req) { switch (req->operation & OPERATION_MASK) { case OPERATION_READ: read(req->i2cAddress, req->readBuffer, req->readLen, NULL, 0, req); break; case OPERATION_SEND: write(req->i2cAddress, req->writeBuffer, req->writeLen, req); break; case OPERATION_SEND_P: write_P(req->i2cAddress, req->writeBuffer, req->writeLen, req); break; case OPERATION_REQUEST: read(req->i2cAddress, req->readBuffer, req->readLen, req->writeBuffer, req->writeLen, req); break; } } /*************************************************************************** * Loop function, for general background work ***************************************************************************/ void I2CManagerClass::loop() {} #endif