Extended MaximInterface

Dependents:   mbed_DS28EC20_GPIO

Devices/DS28EC20.cpp

Committer:
reARMnimator
Date:
2020-01-06
Revision:
10:de4b8812877d
Parent:
9:aeda90624ad0

File content as of revision 10:de4b8812877d:

/*******************************************************************************
* Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
*
* 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 MAXIM INTEGRATED 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.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************/

#include "DS28EC20.hpp"
#include <MaximInterface/Links/OneWireMaster.hpp>
#include <MaximInterface/Utilities/Error.hpp>
#include <MaximInterface/Utilities/crc.hpp>

#include <algorithm>
#include <string.h>

namespace MaximInterface {

typedef enum _Command_t
{
    WriteScratchpad = 0x0F,
    ReadScratchpad = 0xAA,
    CopyScratchpad = 0x55,
    ReadMemory = 0xF0,
    ReadMemoryExt = 0xA5
} Command;



error_code writeMemory(DS28EC20 & device, DS28EC20::Address targetAddress,
                       const DS28EC20::Scratchpad & data) {
  error_code result = device.writeScratchpad(targetAddress, data);
  if (result) {
    return result;
  }
  DS28EC20::Scratchpad readData;
  uint_least8_t esByte;
  result = device.readScratchpad(readData, esByte);
  if (result) {
    return result;
  }
  result = device.copyScratchpad(targetAddress, esByte);
  return result;
}


error_code writeMemory(DS28EC20 & device, DS28EC20::Address inAddress,
                       const uint_least8_t * dataIn, size_t dataLen)
{
    DS28EC20::Scratchpad scratchpad;

    DS28EC20::Address offset = inAddress - (inAddress & ~(DS28EC20::pageSizeBytes - 1));
    DS28EC20::Address targetAddress = inAddress - offset;

    uint_least8_t *p_Data = scratchpad.data();
    p_Data += offset;

    MaximInterface::error_code error;
    size_t readLen = (dataLen + offset);

    /* ... Align length to page boundary */
    readLen += readLen + (DS28EC20::pageSizeBytes - 1u);
    readLen &= ~(DS28EC20::pageSizeBytes - 1u);

    for (size_t writeLen = 0u; writeLen < readLen; writeLen += DS28EC20::pageSizeBytes) {
        if (offset && dataLen){
            error = device.readMemoryExt(targetAddress, scratchpad.data(), DS28EC20::pageSizeBytes);
        }

        if (error) {
            return error;
        }

        if (dataLen) {
            if (dataLen >= DS28EC20::pageSizeBytes) {
                (void)memcpy(p_Data, dataIn, DS28EC20::pageSizeBytes);
                dataIn += DS28EC20::pageSizeBytes;
                dataLen -= DS28EC20::pageSizeBytes;
            }
            else {
                (void)memcpy(p_Data, dataIn, dataLen);
                dataLen = 0;
            }
            p_Data = scratchpad.data();
        }

        error = writeMemory(device, targetAddress, scratchpad);
        targetAddress += DS28EC20::pageSizeBytes;

        if (error) {
            return error;
        }

        offset = 0;
    }

    return error;
}


/* TODO: Test readMemory */
error_code DS28EC20::readMemory(Address beginAddress, uint_least8_t * data,
                              size_t dataLen) const {
  error_code owmResult = selectRom(*master);
  if (owmResult) {
    return owmResult;
  }
  const uint_least8_t sendBlock[] = {ReadMemory, static_cast<uint_least8_t>(beginAddress), static_cast<uint_least8_t>(beginAddress >> 8)};
  owmResult =
      master->writeBlock(make_span(sendBlock, sizeof(sendBlock) / sizeof(sendBlock[0])));
  if (owmResult) {
    return owmResult;
  }
  owmResult = master->readBlock(make_span(data, dataLen));
  return owmResult;
}

#if 0
static unsigned short crc16(const unsigned char* pin, int sz, unsigned short crc_in)
{
    unsigned short crc = crc_in;
    unsigned short tmp, in;
    int i, index, bit;
    for (index = 0, bit = 0, i = 0; i < 8 * sz; i++) {
        in = pin[index];
        in >>= (bit);
        tmp = (crc & 0x8000);
        tmp >>= 15;
        in ^= tmp;
        in &= 1;
        bit++;
        if (bit >= 8) {
            bit = 0;
            index++;
        }

        crc <<= 1;
        tmp = crc;
        tmp &= 0x8004;
        if (in) {
            tmp ^= 0x8004;
            tmp &= 0x8004;
        }
        crc &= (~0x8004);
        crc |= tmp;
        if (in)
            crc |= 1;
        else
            crc &= (~1);
    }

    tmp = (crc & 1);
    for (i = 0; i < 15; i++) {
        tmp <<= 1;
        crc >>= 1;
        tmp |= (crc & 1);
    }
    return ~tmp;
}

#   define CRC16(...)\
    crc16(__VA_ARGS__)
#else
#   define CRC16(data, len, ...)\
    calculateCrc16(make_span(data, len), __VA_ARGS__)
#endif

#define PAGE_SIZE_BYTES 32
#define CRC_LEN_BYTES   2


static error_code processCrc16(OneWireMaster *master, uint_least16_t & crcLocal)
{
    /* ... Read CRC */
    uint_least8_t crcRemote[CRC_LEN_BYTES];
    error_code owmResult = master->readBlock(make_span(&crcRemote[0], sizeof(crcRemote) / sizeof(crcRemote[0])));

    if (owmResult) {
        return owmResult;
    }

    crcLocal = ~crcLocal;
    crcLocal ^= crcRemote[0] | (static_cast<uint_least16_t>(crcRemote[1]) << 8);

    if (crcLocal) {
        owmResult = make_error_code(DS28EC20::CrcError);
        return owmResult;
    }

    crcLocal = 0;

    return owmResult;
}


error_code DS28EC20::readMemoryExt(Address beginAddress, uint_least8_t * data,
                                   size_t dataLen) const
{
    error_code owmResult = selectRom(*master);
    if (owmResult) {
        return owmResult;
    }

    uint_least8_t addressMsb = static_cast<uint_least8_t>(beginAddress >> 8);
    uint_least8_t addressLsb = static_cast<uint_least8_t>(beginAddress >> 0);

    uint_least8_t offset = addressLsb & ~(PAGE_SIZE_BYTES - 1);
    offset = addressLsb - offset;

    const uint_least8_t sendBlock[] = { ReadMemoryExt, static_cast<uint_least8_t>(addressLsb - offset), addressMsb };
    owmResult = master->writeBlock(make_span(sendBlock, sizeof(sendBlock) / sizeof(sendBlock[0])));

    if (owmResult) {
        return owmResult;
    }

    uint_least16_t crcLocal = 0;

    /* ... Calculate CRC of control fields */
    crcLocal = CRC16(&sendBlock[0],  sizeof(sendBlock) / sizeof(sendBlock[0]), crcLocal);

    uint_least8_t byte = 0;
    /* ... Calculate CRC of head bytes */
    {
        uint32_t n = offset;
        while (n--) {
            owmResult = master->readByte(byte);
            crcLocal = CRC16(&byte, sizeof(byte), crcLocal);
        }
    }

    /* ... Read data blocks */
    const size_t blockCnt = dataLen / PAGE_SIZE_BYTES;
    size_t blockLen = PAGE_SIZE_BYTES - offset;
    for (size_t block = 0; block < blockCnt; ++block)
    {
        /* ... Read data */
        owmResult = master->readBlock(make_span(data, blockLen));

        if (owmResult) {
            return owmResult;
        }

        /* ... Calculate CRC of data */
        crcLocal = CRC16(data, blockLen, crcLocal);
        data += blockLen;

        /* ... Read CRC */
        owmResult = processCrc16(master, crcLocal);

        if (owmResult) {
            return owmResult;
        }

        blockLen = PAGE_SIZE_BYTES;
    }


    /* ... Read data reminder */
    size_t dataBlockLen = 0;
    size_t remLen       = 0;
    if (PAGE_SIZE_BYTES > blockLen)
    {

        /* ... Total amount of user data is less than one block */
        if (PAGE_SIZE_BYTES > (offset + dataLen)) {
            /* ... Total length does not cross block boundary.
             *     Read till the end of block. */
            dataBlockLen = dataLen;
            remLen = blockLen - dataBlockLen;

        }
        else {
            /* ... Total length is longer than one block.
             *     Read till the end of block and then the rest. */
            dataBlockLen = blockLen;

            /* ... Read data */
            owmResult = master->readBlock(make_span(data, dataBlockLen));

            if (owmResult) {
                return owmResult;
            }

            /* ... Calculate CRC of data */
            crcLocal = CRC16(data, dataBlockLen, crcLocal);
            data += dataBlockLen;

            /* ... Read CRC */
            owmResult = processCrc16(master, crcLocal);

            if (owmResult) {
                return owmResult;
            }

            dataBlockLen = dataLen - dataBlockLen;
            remLen = PAGE_SIZE_BYTES - dataBlockLen;
        }

    }
    else
    {
        /* ... Total amount of data is more than one block */
        dataBlockLen = dataLen % PAGE_SIZE_BYTES;
        remLen = PAGE_SIZE_BYTES - dataBlockLen;
    }

    /* ... Read data */
    owmResult = master->readBlock(make_span(data, dataBlockLen));

    if (owmResult) {
        return owmResult;
    }

    /* ... Calculate CRC of data */
    crcLocal = CRC16(data, dataBlockLen, crcLocal);
    data += dataBlockLen;

    /* ... Read tail */
    while (remLen--) {
        owmResult = master->readByte(byte);
        crcLocal = CRC16(&byte, sizeof(byte), crcLocal);

        if (owmResult) {
            return owmResult;
        }
    }

    /* ... Read CRC */
    owmResult = processCrc16(master, crcLocal);

    if (owmResult) {
        return owmResult;
    }

    return owmResult;
}


error_code DS28EC20::writeScratchpad(Address targetAddress,
                                   const Scratchpad & data) {
  error_code owmResult = selectRom(*master);
  if (owmResult) {
    return owmResult;
  }

  uint_least8_t addressMsb = static_cast<uint_least8_t>(targetAddress >> 8);
  uint_least8_t addressLsb = static_cast<uint_least8_t>(targetAddress >> 0);
  addressLsb &= ~(pageSizeBytes - 1);

  array<uint_least8_t, 3 + Scratchpad::csize> block;
  block[0] = WriteScratchpad;
  block[1] = addressLsb;
  block[2] = addressMsb;

  std::copy(data.begin(), data.end(), block.begin() + 3);
  owmResult = master->writeBlock(make_span(block.data(), block.size()));
  if (owmResult) {
    return owmResult;
  }
  const uint_fast16_t calculatedCrc = calculateCrc16(make_span(block.data(), block.size())) ^ 0xFFFFU;
  owmResult = master->readBlock(make_span(block.data(), 2));
  if (owmResult) {
    return owmResult;
  }
  if (calculatedCrc !=
      ((static_cast<uint_fast16_t>(block[1]) << 8) | block[0])) {
    owmResult = make_error_code(CrcError);
  }
  return owmResult;
}

error_code DS28EC20::readScratchpad(Scratchpad & data, uint_least8_t & esByte) {
  typedef array<uint_least8_t, 6 + Scratchpad::csize> Block;

  error_code owmResult = selectRom(*master);
  if (owmResult) {
    return owmResult;
  }
  Block block; block[0] = ReadScratchpad;
  owmResult = master->writeByte(block.front());
  if (owmResult) {
    return owmResult;
  }
  owmResult = master->readBlock(make_span(block.data() + 1, block.size() - 1));
  if (owmResult) {
    return owmResult;
  }
  Block::const_iterator blockIt = block.end();
  uint_fast16_t receivedCrc = static_cast<uint_fast16_t>(*(--blockIt)) << 8;
  receivedCrc |= *(--blockIt);
  const uint_fast16_t expectedCrc = calculateCrc16(make_span(block.data(), block.size() - 2)) ^ 0xFFFFU;
  if (expectedCrc == receivedCrc) {
    Block::const_iterator blockItEnd = blockIt;
    blockIt -= data.size();
    std::copy(blockIt, blockItEnd, data.begin());
    esByte = *(--blockIt);
  } else {
    owmResult = make_error_code(CrcError);
  }
  return owmResult;
}

/* TODO: Test copyScratchpad */
error_code DS28EC20::copyScratchpad(Address targetAddress, uint_least8_t esByte) {
  error_code owmResult = selectRom(*master);
  if (owmResult) {
    return owmResult;
  }

  uint_least8_t addressMsb = static_cast<uint_least8_t>(targetAddress >> 8);
  uint_least8_t addressLsb = static_cast<uint_least8_t>(targetAddress >> 0);
  addressLsb &= ~(pageSizeBytes - 1);

  uint_least8_t block[] = {CopyScratchpad, addressLsb, addressMsb};

  owmResult = master->writeBlock(make_span(block, sizeof(block) / sizeof(block[0])));
  if (owmResult) {
    return owmResult;
  }
  owmResult = master->writeByteSetLevel(esByte, OneWireMaster::StrongLevel);
  if (owmResult) {
    return owmResult;
  }
  (*sleep)(10);
  owmResult = master->setLevel(OneWireMaster::NormalLevel);
  if (owmResult) {
    return owmResult;
  }
  owmResult = master->readByte(block[0]);
  if (owmResult) {
    return owmResult;
  }
  if (block[0] != 0xAA) {
    owmResult = make_error_code(OperationFailure);
  }
  return owmResult;
}

const error_category & DS28EC20::errorCategory() {
  static class : public error_category {
  public:
    virtual const char * name() const { return "DS28EC20"; }

    virtual std::string message(int condition) const {
      switch (condition) {
      case CrcError:
        return "CRC Error";

      case OperationFailure:
        return "Operation Failure";

      default:
        return defaultErrorMessage(condition);
      }
    }
  } instance;
  return instance;
}

} // namespace MaximInterface