395 lines
11 KiB
C++
395 lines
11 KiB
C++
/***************************************************************************
|
|
|
|
BionxControl
|
|
© 2025 -2026 christoph holzheuer
|
|
christoph.holzheuer@gmail.com
|
|
|
|
Using:
|
|
|
|
mhs_can_drv.c
|
|
© 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
|
|
Klaus Demlehner, klaus@mhs-elektronik.de
|
|
@see www.mhs-elektronik.de
|
|
|
|
Based on Bionx data type descriptions from:
|
|
|
|
BigXionFlasher USB V 0.2.4 rev. 97
|
|
© 2011-2013 by Thomas Koenig <info@bigxionflasher.org>
|
|
@see www.bigxionflasher.org
|
|
|
|
Bionx Bike Info
|
|
© 2018 Thorsten Schmidt (tschmidt@ts-soft.de)
|
|
@see www.ts-soft.de
|
|
|
|
This program 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.
|
|
@see https://github.com/bikemike/bionx-bikeinfo
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
#include <//qDebug>
|
|
|
|
#include <bcdrivertinycan.h>
|
|
#include <can_drv.h>
|
|
|
|
|
|
#if defined(Q_OS_WIN)
|
|
// Unter Windows steht der Treibername in der registry
|
|
static const char* cMHS_DRIVERNAME = NULL;
|
|
#elif defined(Q_OS_LINUX)
|
|
// Unter linux(artigen) muss der Treibername explizit mit übergeben werden
|
|
static const char* cMHS_DRIVERNAME = "libmhstcan.so";
|
|
#endif
|
|
|
|
/*
|
|
|
|
// TinyCan C-Api
|
|
// -------------
|
|
|
|
int32_t CanInitDriver(char *options);
|
|
void CanDownDriver(void);
|
|
int32_t CanSetOptions(char *options);
|
|
int32_t CanDeviceOpen(uint32_t index, char *parameter);
|
|
int32_t CanDeviceClose(uint32_t index);
|
|
|
|
int32_t CanSetMode(uint32_t index, unsigned char can_op_mode, uint16_t can_command);
|
|
|
|
int32_t CanTransmit(uint32_t index, struct TCanMsg *msg, int32_t count);
|
|
void CanTransmitClear(uint32_t index);
|
|
uint32_t CanTransmitGetCount(uint32_t index);
|
|
int32_t CanTransmitSet(uint32_t index, uint16_t cmd, uint32_t time);
|
|
int32_t CanReceive(uint32_t index, struct TCanMsg *msg, int32_t count);
|
|
void CanReceiveClear(uint32_t index);
|
|
uint32_t CanReceiveGetCount(uint32_t index);
|
|
|
|
int32_t CanSetSpeed(uint32_t index, uint16_t speed);
|
|
int32_t CanSetSpeedUser(uint32_t index, uint32_t value);
|
|
char* CanDrvInfo(void);
|
|
char* CanDrvHwInfo(uint32_t index);
|
|
int32_t CanSetFilter(uint32_t index, struct TMsgFilter *msg_filter);
|
|
int32_t CanGetDeviceStatus(uint32_t index, struct TDeviceStatus *status);
|
|
void CanSetPnPEventCallback(void (DRV_CALLBACK_TYPE *event)(uint32_t index, int32_t status));
|
|
void CanSetStatusEventCallback(void (DRV_CALLBACK_TYPE *event) (uint32_t index, struct TDeviceStatus *device_status) );
|
|
void CanSetRxEventCallback(void (DRV_CALLBACK_TYPE *event)(uint32_t index, struct TCanMsg *msg, int32_t count) );
|
|
|
|
void CanSetEvents( uint16_t events );
|
|
uint32_t CanEventStatus(void);
|
|
*/
|
|
|
|
/**
|
|
* @brief Destruktor. Entlädt den CAN-Bus Treiber wieder.
|
|
*/
|
|
|
|
BCDriverTinyCan::~BCDriverTinyCan()
|
|
{
|
|
resetDriver();
|
|
}
|
|
|
|
/**
|
|
* @brief BCDriverTinyCan::loadAndStartDriver
|
|
* @return
|
|
*/
|
|
|
|
BCDriver::DriverStateResult BCDriverTinyCan::loadAndStartDriver()
|
|
{
|
|
DriverStateResult result;
|
|
if( _driverState < DriverState::Opened)
|
|
result = loadDriver();
|
|
if( _driverState == DriverState::Opened)
|
|
result = setConsoleSlaveMode();
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief BCDriverTinyCan::loadDriver
|
|
* @return
|
|
*/
|
|
|
|
BCDriver::DriverStateResult BCDriverTinyCan::loadDriver()
|
|
{
|
|
auto callLoadDriver = [&]() -> DriverStateResult
|
|
{
|
|
if( ::LoadDriver( cMHS_DRIVERNAME ) < 0 )
|
|
return std::unexpected(QString("Driver Error: 'LoadDriver'"));
|
|
_driverState = DriverState::Loaded;
|
|
return _driverState;
|
|
};
|
|
|
|
auto callInitDriver = [&](DriverState state) -> DriverStateResult
|
|
{
|
|
Q_UNUSED(state)
|
|
if( ::CanInitDriver( NULL ) < 0 )
|
|
return std::unexpected(QString("Driver Error: 'InitDriver'"));
|
|
_driverState = DriverState::Initialized;
|
|
return _driverState;
|
|
};
|
|
|
|
auto callOpenDevice = [&](DriverState state) -> DriverStateResult
|
|
{
|
|
Q_UNUSED(state)
|
|
if( ::CanDeviceOpen( 0, NULL ) < 0 )
|
|
return std::unexpected(QString("Driver Error: 'DeviceOpen'"));
|
|
|
|
::TDeviceStatus deviceStatus;
|
|
|
|
::CanSetSpeed( 0, CAN_125K_BIT );
|
|
::CanSetMode( 0, OP_CAN_START, CAN_CMD_ALL_CLEAR );
|
|
::CanGetDeviceStatus( 0, &deviceStatus );
|
|
|
|
if( deviceStatus.DrvStatus < DRV_STATUS_CAN_OPEN )
|
|
return std::unexpected(QString("Driver Error: could not open device." ));
|
|
|
|
if( deviceStatus.CanStatus == CAN_STATUS_BUS_OFF )
|
|
{
|
|
::CanSetMode( 0, OP_CAN_RESET, CAN_CMD_NONE );
|
|
return std::unexpected(QString("Driver Error: CAN Status 'BusOff'" ));
|
|
}
|
|
_driverState = DriverState::Opened;
|
|
return _driverState;
|
|
};
|
|
|
|
|
|
// #1. erstmal komplett zurücksetzen
|
|
resetDriver();
|
|
// #2. Treiber laden, initialisieren und
|
|
// mit dem tinyCan Interface verbinden.
|
|
auto newDriverState = callLoadDriver()
|
|
.and_then( callInitDriver )
|
|
.and_then( callOpenDevice );
|
|
|
|
// in Fehlerfall ist der Errorstring gesetzt,
|
|
// der interne _driverstate ist
|
|
// irgendwo unter DriverState::Opened
|
|
return newDriverState;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Um mit dem Bionx eBike reden zu können, müssen wir
|
|
* die Console in den Slave-Mode setzen.
|
|
* @return Fehlerstring oder DriverState::DeviceReady
|
|
*/
|
|
|
|
BCDriver::DriverStateResult BCDriverTinyCan::setConsoleSlaveMode()
|
|
{
|
|
|
|
uint32_t console = static_cast<uint32_t>(BCDevice::ID::Console);
|
|
uint8_t slaveFlag = static_cast<uint8_t> (BC::ID::Cons_Status_Slave);
|
|
|
|
//qDebug() << "XXX BCDriverTinyCan::Driver Init: putting Console in slave mode ... ";
|
|
|
|
TransmitResult isSlave = 0;
|
|
// Already slave?
|
|
isSlave = readRawByte( console, slaveFlag );
|
|
if( isSlave.has_value() )
|
|
{
|
|
//qDebug() << "Console responded: " << isSlave.value();
|
|
if( isSlave.value() == 1 )
|
|
{
|
|
//qDebug() << "Console already in slave mode. good!";
|
|
return DriverState::DeviceReady;
|
|
}
|
|
}
|
|
|
|
//qDebug() << "BCDriverTinyCan::BCDriverTinyCan::XXX Driver Init: putting Console in slave mode ... ";
|
|
|
|
unsigned int retry = cTimeOuts;
|
|
|
|
do
|
|
{
|
|
writeRawByte( console, slaveFlag, 1 );
|
|
isSlave = readRawByte( console, slaveFlag );
|
|
bc::delay_millis( 200 );
|
|
|
|
} while( retry-- && !(*isSlave) );
|
|
|
|
bc::delay_millis( 500 ); // give the Console some time to settle
|
|
//if( !isSlave )
|
|
//emit statusHint( QString("putting Console in slave mode ") + (isSlave ? "done" : "failed") );
|
|
|
|
// ist das jetzt irgendwie schlimm, wenn wir keine slave Console haben
|
|
return isSlave ? DriverState::DeviceReady : DriverState::Opened;
|
|
|
|
}
|
|
|
|
void BCDriverTinyCan::resetDriver()
|
|
{
|
|
if( _driverState > DriverState::NotPresent )
|
|
{
|
|
::CanDownDriver();
|
|
::UnloadDriver();
|
|
_driverState = DriverState::NotPresent;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief BCDriverTinyCan::readRawByte
|
|
* Kapselt den Treiberzugiff über die legacy C-Api. Liest ein byte, gibt dieses im std::expected aber
|
|
* als uint32_t zurück, um mit der ReadFunctions der ValueTypes kompatible zu sein.
|
|
* Im Fehlerfall wird ein Fehlerstring zurückgegeben.
|
|
*/
|
|
|
|
TransmitResult BCDriverTinyCan::readRawByte( uint32_t deviceID, uint8_t registerID ) const
|
|
{
|
|
|
|
//qDebug() << " --- CAN Read Byte: Device: "<< deviceID << " register: " << registerID << " TRY! ";
|
|
|
|
struct TCanMsg msg;
|
|
int err, retry = 20;
|
|
int timeout = 80;
|
|
|
|
unsigned char receipient = (unsigned char) deviceID;
|
|
unsigned char reg = (unsigned char) registerID;
|
|
|
|
msg.MsgFlags = 0L;
|
|
msg.Id = receipient;
|
|
msg.MsgLen = 2;
|
|
msg.MsgData[0] = 0x00;
|
|
msg.MsgData[1] = reg;
|
|
|
|
CanTransmit(0, &msg, 1);
|
|
|
|
while(timeout-- && CanTransmitGetCount(0))
|
|
bc::delay_millis( cTIMEOUT_MS );
|
|
|
|
if (timeout == -1)
|
|
//qDebug() << "error: could not send value to node ";
|
|
|
|
retry:
|
|
|
|
timeout = 80;
|
|
while(timeout-- && !CanReceiveGetCount(0))
|
|
bc::delay_millis( cTIMEOUT_MS );
|
|
|
|
if (timeout == -1)
|
|
{
|
|
//qDebug() << "error: no response from node";
|
|
return 0;
|
|
}
|
|
|
|
if ((err = CanReceive(0, &msg, 1)) > 0)
|
|
{
|
|
//qDebug() << " retry: " << retry << " BIB:" << BC::ID::ID_Bib << " msg.Id: " << msg.Id << " msg.MsgLen: " << msg.MsgLen << " msg.MsgData[1]: " << msg.MsgData[1] << " reg: " << reg;
|
|
if (--retry && (msg.Id != (uint32_t)BC::ID::ID_Bib|| msg.MsgLen != 4 || msg.MsgData[1] != reg))
|
|
goto retry;
|
|
|
|
if (!retry)
|
|
{
|
|
//qDebug() << "XXX error: no response from node: " << err;
|
|
return 0;
|
|
}
|
|
//qDebug() << " --- CAN Read Byte: Device: "<< deviceID << " register: " << registerID << " BYTE: " << (uint32_t) msg.MsgData[3];
|
|
return (unsigned int) msg.MsgData[3];
|
|
}
|
|
else
|
|
{
|
|
//qDebug() << "Error:" <<err;
|
|
}
|
|
|
|
return 0;
|
|
|
|
/*
|
|
//TransmitResult
|
|
//qDebug() << " --- BCDriverTinyCan::readRawByte DriverState: " << getDriverState();
|
|
|
|
if( _driverState <DriverState::Opened )
|
|
return std::unexpected(QString("readRawValue error: driver not loaded." ) );
|
|
|
|
unsigned char receipient = (unsigned char ) deviceID;
|
|
::TCanMsg msg;
|
|
|
|
// msg verpacken
|
|
msg.MsgFlags = 0L;
|
|
msg.Id = receipient;//deviceID;
|
|
msg.MsgLen = 2;
|
|
msg.MsgData[0] = 0x00;
|
|
msg.MsgData[1] = registerID;
|
|
|
|
// msg verschicken
|
|
::CanTransmit( 0, &msg, 1 );
|
|
|
|
int retries = cRetries; // 5?
|
|
// cTimeOuts (== 20) mal cTIMEOUT_MS (== 10 ms ) Versuche ...
|
|
int timeOuts = cTimeOuts; // 20 ?
|
|
|
|
// ... warten bis der Sendepuffer leer ist
|
|
while( timeOuts-- && ::CanTransmitGetCount( 0 ) )
|
|
bc::delay_millis( cTIMEOUT_MS );
|
|
|
|
if( timeOuts == -1 )
|
|
return std::unexpected(QString("readRawValue error: could not send value" ));
|
|
|
|
retry:
|
|
|
|
// cTimeOuts (== 20) mal cTIMEOUT_MS (== 10 ms ) Versuche ...
|
|
timeOuts = cTimeOuts;
|
|
// ... warten, bis der Empfangspuffer nicht mehr leer ist
|
|
while( timeOuts-- && !::CanReceiveGetCount( 0 ) )
|
|
bc::delay_millis( cTIMEOUT_MS );
|
|
|
|
if( timeOuts == -1 )
|
|
return std::unexpected(QString("getValue error: no response from node" ));
|
|
|
|
// message empfangen
|
|
int err = ::CanReceive( 0, &msg, 1 );
|
|
////qDebug() << "HÄÄ ?" << err << "reg: "<< registerID <<" timeOuts: " << timeOuts;
|
|
|
|
if( err < 0 )
|
|
//throw BCException( "getValue error: could not receive value" );
|
|
//qDebug( "getValue error: could not receive value" );
|
|
|
|
////qDebug() << "HÄÄ 2" <<msg.Id;
|
|
////qDebug() << "HÄÄ 2" <<msg.MsgLen;
|
|
////qDebug() << "HÄÄ 2" <<msg.MsgData[1];
|
|
|
|
//if( err > 0 )
|
|
if( --retries && ( msg.Id != BIB || msg.MsgLen != 4 || msg.MsgData[1] != registerID ) )
|
|
goto retry;
|
|
|
|
if( !timeOuts )
|
|
return std::unexpected(QString("CAN response errror: timeout" ));
|
|
|
|
//qDebug() << " --- CAN Read Byte: " << (uint32_t) msg.MsgData[3] << " Device:: "<< deviceID << " register: " << registerID;
|
|
return (uint32_t) msg.MsgData[3];
|
|
*/
|
|
}
|
|
|
|
|
|
// void BCDriverTinyCan::setValue( unsigned char receipient, unsigned char reg, unsigned char value )
|
|
TransmitResult BCDriverTinyCan::writeRawByte( uint32_t deviceID, uint8_t registerID, uint8_t value ) const
|
|
{
|
|
|
|
if( _driverState <DriverState::Opened )
|
|
return std::unexpected(QString("readRawValue error: driver not loaded." ) );
|
|
|
|
//qDebug() << " --- BCDriverTinyCan writeRawValue: " << value;
|
|
|
|
::TCanMsg msg;
|
|
int timeout_count = cTIMEOUT_COUNT;
|
|
|
|
|
|
msg.MsgFlags = 0L;
|
|
msg.Id = deviceID;
|
|
msg.MsgLen = 4;
|
|
msg.MsgData[0] = 0x00;
|
|
msg.MsgData[1] = registerID;
|
|
msg.MsgData[2] = 0x00;
|
|
msg.MsgData[3] = value;
|
|
|
|
::CanTransmit( 0, &msg, 1 );
|
|
|
|
while( timeout_count-- && ::CanTransmitGetCount( 0 ) )
|
|
bc::delay_millis( cTIMEOUT_MS );
|
|
|
|
if( timeout_count == -1 )
|
|
return std::unexpected(QString("error: could not send value to %1" ).arg( deviceID ) );
|
|
|
|
return uint32_t(1); // als 'true'
|
|
}
|
|
|