blob: f4d994122b424124896028319d2b3eb77807ef51 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 - 2018 Johannes Messmer (admin@jomess.com), fortiss GmbH
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Johannes Messmer - initial API and implementation and/or initial documentation
* Jose Cabral - Cleaning of namespaces
*******************************************************************************/
#include "bus.h"
#include <algorithm>
#include <slave/slave.h>
#include <slave/packages.h>
#include <slave/handles/bit.h>
#include <slave/handles/analog.h>
#include <slave/handles/analog10.h>
#include "criticalregion.h"
#include <utils/timespec_utils.h>
const char * const EmbrickBusHandler::scmSlaveUpdateFailed = "Update of slave failed.";
const char * const EmbrickBusHandler::scmNoSlavesFound = "No slave modules found.";
EmbrickBusHandler::EmbrickBusHandler(CDeviceExecution& paDeviceExecution) : forte::core::io::IODeviceMultiController(paDeviceExecution),
mSpi(0), mSlaveSelect(0), mSlaves(0), mSlaveCount(0), mLoopActive(false), mSList(0) {
// Set init time
struct timespec ts;
// TODO Check compile error. Had to to add rt libary to c++ make flags
clock_gettime(CLOCK_MONOTONIC, &ts);
mInitTime = ts.tv_sec;
mLastTransfer = micros();
// Default config
mConfig.mBusInterface = 1;
mConfig.mBusSelectPin = 49;
mConfig.mBusInitSpeed = EmbrickSPIHandler::scmDefaultSpiSpeed;
mConfig.mBusLoopSpeed = EmbrickSPIHandler::scmMaxSpiSpeed;
}
void EmbrickBusHandler::setConfig(struct forte::core::io::IODeviceController::Config* paConfig) {
// Check if BusHandler is active -> configuration changes are not allowed
if (isAlive()) {
DEVLOG_ERROR(
"emBrick[BusHandler]: Cannot change configuration while running.\n");
return;
}
this->mConfig = *static_cast<Config*>(paConfig);
}
const char* EmbrickBusHandler::init() {
// Start handlers
mSpi = new EmbrickSPIHandler(mConfig.mBusInterface);
mSlaveSelect = new EmbrickPinHandler(mConfig.mBusSelectPin);
mSlaves = new TSlaveList();
// Check handlers
if (checkHandlerError()) {
return mError;
}
// Set SPI sped for initialization
mSpi->setSpeed(mConfig.mBusInitSpeed);
// Disable slave select -> reset all slaves
mSlaveSelect->disable();
// Wait for reset
sleep(1);
// Enable slave select -> the first slave waits for initialization
mSlaveSelect->enable();
// Wait for init
microsleep(SyncGapDuration * 2);
// Init the slaves sequentially. Abort if the init package is ignored -> no further slaves found.
int slaveCounter = 1;
int attempts = 0;
do {
EmbrickSlaveHandler *slave = EmbrickSlaveHandler::sendInit(this, slaveCounter);
if (slave != 0) {
mSlaves->pushBack(slave);
// Activate next slave by sending the 'SelectNextSlave' command to the current slave
// It enables the slave select pin for the next slave on the bus
transfer(slave->mAddress, SelectNextSlave);
slaveCounter++;
attempts = 0;
}
microsleep(SyncGapDuration * 2);
} while (++attempts < 3);
mSlaveCount = slaveCounter - 1;
if (mSlaveCount == 0) {
return scmNoSlavesFound;
}
// Increase the speed of the bus for data transfers
mSpi->setSpeed(mConfig.mBusLoopSpeed);
return 0;
}
void EmbrickBusHandler::deInit() {
// Free memory -> delete all slave instances and hardware handlers
TSlaveList::Iterator itEnd(mSlaves->end());
for(TSlaveList::Iterator it = mSlaves->begin(); it != itEnd; ++it) {
delete *it;
}
delete mSlaves;
mSlaves = 0;
delete mSpi;
delete mSlaveSelect;
}
forte::core::io::IOHandle* EmbrickBusHandler::initHandle(
forte::core::io::IODeviceController::HandleDescriptor *paHandleDescriptor) {
HandleDescriptor &desc = static_cast<HandleDescriptor&>(*paHandleDescriptor);
EmbrickSlaveHandler *slave = getSlave(desc.mSlaveIndex);
if(slave == 0) {
return 0;
}
switch (desc.mType) {
case Bit:
return new EmbrickBitSlaveHandle(this, desc.mDirection, desc.mOffset, desc.mPosition,
slave);
case Analog:
return new EmbrickAnalogSlaveHandle(this, desc.mDirection, desc.mOffset, slave);
case Analog10:
return new EmbrickAnalog10SlaveHandle(this, desc.mDirection, desc.mOffset, slave);
}
return 0;
}
void EmbrickBusHandler::prepareLoop() {
// Get current time
clock_gettime(CLOCK_MONOTONIC, &mNextLoop);
// Scheduling
// TODO Combine slaves list and scheduling information
// DISCUSS Speed of array and forte_list?
TSlaveList::Iterator it = mSlaves->begin();
mSList = (struct SEntry**) forte_malloc(sizeof(struct SEntry*) * mSlaveCount);
for (int i = 0; i < mSlaveCount; i++) {
mSList[i] = (struct SEntry*) forte_malloc(sizeof(struct SEntry));
mSList[i]->mSlave = *it;
mSList[i]->mNextDeadline = mNextLoop;
mSList[i]->mLastDuration = 0;
mSList[i]->mForced = false;
mSList[i]->mDelayed = false;
++it;
}
mSNext = mSList[0];
mLoopActive = false;
}
void EmbrickBusHandler::runLoop() {
prepareLoop();
// Init loop variables
SEntry *sCur = 0;
while (isAlive()) {
// Sleep till next deadline is reached or loop is waked up
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if(timespecLessThan(&now, &mSNext->mNextDeadline)){ //only wait if the deadline is in the future
struct timespec timeToSleep;
timespecSub(&mSNext->mNextDeadline, &now, &timeToSleep);
mForceLoop.timedWait(timeToSleep.tv_sec * 1E9 + timeToSleep.tv_nsec);
}
// Set current slave
mLoopActive = true;
sCur = mSNext;
// Set next deadline of current slave
clock_gettime(CLOCK_MONOTONIC, &sCur->mNextDeadline);
addTime(sCur->mNextDeadline, 1000000 / sCur->mSlave->mConfig.mUpdateInterval);
// Remove delayed and forced flag
sCur->mForced = false;
sCur->mDelayed = false;
// Remove lock during blocking operation -> allows forced update interrupts
mSyncObject.unlock();
uint64_t ms = micros();
// Perform update on current slave
if (-1 == sCur->mSlave->update()) {
mError = scmSlaveUpdateFailed;
// Check for critical bus errors
if(checkHandlerError() || hasError()) {
break;
}
}
// Search for next deadline -> set lock to avoid changes of list
mSyncObject.lock();
mLoopActive = false;
// Store update duration
sCur->mLastDuration = (uint16_t) (micros() - ms);
// If current slave is forced again -> add update duration to deadline
if(sCur->mForced) {
addTime(sCur->mNextDeadline, sCur->mLastDuration);
}
for (unsigned int i = 0; i < mSlaveCount; i++) {
if (timespecLessThan(&mSList[i]->mNextDeadline, &mSNext->mNextDeadline)) {
mSNext = mSList[i];
}
}
}
// Clean loop
cleanLoop();
}
void EmbrickBusHandler::cleanLoop() {
// Free memory of list
for(int i = 0; i < mSlaveCount; i++) {
forte_free(mSList[i]);
}
forte_free(mSList);
mSList = 0;
}
bool EmbrickBusHandler::checkHandlerError() {
if (mSpi->hasError()) {
mError = mSpi->mError;
return true;
}
if (mSlaveSelect->hasError()) {
mError = mSlaveSelect->paError;
return true;
}
return false;
}
EmbrickSlaveHandler* EmbrickBusHandler::getSlave(int paIndex) {
if (mSlaves == 0) {
return 0;
}
TSlaveList::Iterator itEnd = mSlaves->end();
int i = 0;
for(TSlaveList::Iterator it = mSlaves->begin(); it != itEnd; ++it, i++) {
if (paIndex == i) {
return *it;
}
}
return 0;
}
void EmbrickBusHandler::forceUpdate(int paIndex) {
mSyncObject.lock();
if (mSList == 0 || mSlaveCount <= paIndex || mSList[paIndex]->mForced) {
mSyncObject.unlock();
return;
}
SEntry *e = mSList[paIndex];
e->mForced = true;
clock_gettime(CLOCK_MONOTONIC, &e->mNextDeadline);
if (!mLoopActive) {
if(!mSNext->mDelayed && !mSNext->mForced) {
struct timespec ts = e->mNextDeadline;
addTime(ts, e->mLastDuration * 2);
if(!timespecLessThan(&ts, &mSNext->mNextDeadline)) {
mSNext->mDelayed = true;
}
mSNext = e;
}
// Force next loop
mForceLoop.inc();
}
mSyncObject.unlock();
}
void EmbrickBusHandler::addSlaveHandle(int paIndex, forte::core::io::IOHandle* paHandle) {
EmbrickSlaveHandler* slave = getSlave(paIndex);
if(slave == 0) {
return;
}
slave->addHandle((EmbrickSlaveHandle*) paHandle);
}
void EmbrickBusHandler::dropSlaveHandles(int paIndex) {
EmbrickSlaveHandler* slave = getSlave(paIndex);
if(slave == 0) {
return;
}
slave->dropHandles();
}
bool EmbrickBusHandler::isSlaveAvailable(int paIndex) {
return getSlave(paIndex) != 0;
}
bool EmbrickBusHandler::checkSlaveType(int paIndex, int paType) {
EmbrickSlaveHandler* slave = getSlave(paIndex);
if(slave == 0) {
return false;
}
return slave->mType == paType;
}
bool EmbrickBusHandler::transfer(unsigned int paTarget, Command paCmd,
unsigned char* paDataSend, int paDataSendLength, unsigned char* paDataReceive,
int paDataReceiveLength, EmbrickSlaveHandler::SlaveStatus* paStatus, CSyncObject *paSyncMutex) {
unsigned int dataLength = std::max(paDataSendLength, paDataReceiveLength + 1); // + 1 status byte
unsigned int bufferLength = sizeof(EmbrickHeaderPackage) + dataLength + 1; // + 1 checksum byte
if (bufferLength > TransferBufferLength) {
DEVLOG_ERROR("emBrick[BusHandler]: Transfer buffer size exceeded.\n");
return false;
}
memset(mSendBuffer, 0, bufferLength);
memset(mRecvBuffer, 0, bufferLength);
// Prepare header
EmbrickHeaderPackage* header = (EmbrickHeaderPackage*) mSendBuffer;
header->mAddress = (char) paTarget;
header->mCommand = paCmd;
header->mChecksum = calcChecksum((unsigned char*) header, 2);
if(paSyncMutex) {
paSyncMutex->lock();
}
memcpy(mSendBuffer + sizeof(EmbrickHeaderPackage), paDataSend, paDataSendLength);
if(paSyncMutex) {
paSyncMutex->unlock();
}
mSendBuffer[sizeof(EmbrickHeaderPackage) + paDataSendLength] = calcChecksum(
mSendBuffer + sizeof(EmbrickHeaderPackage), paDataSendLength);
// Invert data of master
for(unsigned int i = 0; i < bufferLength; i++) {
mSendBuffer[i] = (unsigned char) ~mSendBuffer[i];
}
// Send and receive buffer via SPI
int attempts = 3;
int fails = 0;
bool ok;
do {
// Wait required microseconds between messages
uint64_t microTime = micros();
if(mLastTransfer + SyncGapDuration > microTime) {
microsleep(mLastTransfer + (uint64_t) SyncGapDuration - microTime);
}
ok = mSpi->transfer(mSendBuffer, mRecvBuffer, bufferLength);
mLastTransfer = micros();
// Critical error of bus -> break immediately
if(!ok) {
break;
}
// Validate checksum
ok = calcChecksum(mRecvBuffer + sizeof(EmbrickHeaderPackage),
bufferLength - sizeof(EmbrickHeaderPackage)) == 0;
if (!ok) {
DEVLOG_DEBUG("emBrick[BusHandler]: Transfer - Invalid checksum\n");
}
} while (!ok && ++fails < attempts);
// Check if command was transmitted successfully
if (!ok) {
if (paTarget != 0 && paCmd != Data) {
DEVLOG_DEBUG(
"emBrick[BusHandler]: Failed to send command %d to slave %d.\n", paCmd,
paTarget);
}
return false;
}
// Copy result
if(paSyncMutex) {
paSyncMutex->lock();
}
if(paStatus) {
*paStatus = (EmbrickSlaveHandler::SlaveStatus) mRecvBuffer[sizeof(EmbrickHeaderPackage)];
}
memcpy(paDataReceive, mRecvBuffer + sizeof(EmbrickHeaderPackage) + 1,
paDataReceiveLength);
if(paSyncMutex) {
paSyncMutex->unlock();
}
return true;
}
unsigned char EmbrickBusHandler::calcChecksum(unsigned char * paData, int paDataLen) {
unsigned char c = 0;
for(int i = 0; i < paDataLen; i++) {
c = static_cast<unsigned char>(c + paData[i]);
}
return static_cast<unsigned char>(scmChecksumConstant - c);
}
uint64_t EmbrickBusHandler::micros() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return round(ts.tv_nsec / 1.0e3) + (ts.tv_sec - mInitTime) * 1E6;
}
unsigned long EmbrickBusHandler::millis() {
// TODO Improve timing func. Maybe replace with existing forte implementation.
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return static_cast<unsigned long>(round(ts.tv_nsec / 1.0e6)) + (ts.tv_sec - mInitTime) * 1000;
}
void EmbrickBusHandler::microsleep(uint64_t paMicroseconds) {
struct timespec ts;
ts.tv_sec = (long) (paMicroseconds / (uint64_t) 1E6);
ts.tv_nsec = (long) paMicroseconds * (long) 1E3 - ts.tv_sec * (long) 1E9;
nanosleep(&ts, 0);
}
void EmbrickBusHandler::addTime(struct timespec& paTs, unsigned long paMicroseconds) {
paTs.tv_sec += paMicroseconds / (unsigned long) 1E6;
unsigned long t = paTs.tv_nsec + paMicroseconds * (unsigned long) 1E3
- (paMicroseconds / (unsigned long) 1E6) * (unsigned long) 1E9;
if (t >= (unsigned long) 1E9) {
t -= (unsigned long) 1E9;
paTs.tv_sec++;
}
paTs.tv_nsec = t;
}