/*************************************************************************** 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 @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 #include #include /** * @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 { qDebug() << " -- Hier!"; if( ::LoadDriver( NULL ) < 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 ); // success: if(newDriverState) { // return 'DriverState::Opened' return _driverState; } // return driver error message, // _driverState ist irgendwo unter DriverState::Opened return newDriverState; } // __fix BCDriver::DriverStateResult BCDriverTinyCan::setConsoleSlaveMode() { uint32_t console = static_cast(BCDevice::ID::Console); uint8_t slaveFlag = static_cast (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:" < 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