/*************************************************************************** BionxControl Copyright © 2025 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 { 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() { /* consoleInSlaveMode = getValue(CONSOLE, CONSOLE_STATUS_SLAVE); if (consoleInSlaveMode) { printf("console already in salve mode. good!" _NL _NL); } else { if (gConsoleSetSlaveMode) { int retry = 20; printf("putting console in salve mode ... "); do { setValue(CONSOLE, CONSOLE_STATUS_SLAVE, 1); consoleInSlaveMode = getValue(CONSOLE, CONSOLE_STATUS_SLAVE); usleep(200000); } while(retry-- && !consoleInSlaveMode); doSleep(500); // give the console some time to settle printf("%s" _NL _NL, consoleInSlaveMode ? "done" : "failed"); } else { printf("console not in slave mode" _NL _NL); } } */ 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 ... "; // Console already in slave mode. good! if( readRawByte( console, slaveFlag ) ) return DriverState::DeviceReady; qDebug() << "BCDriverTinyCan::BCDriverTinyCan::XXX Driver Init: putting Console in slave mode ... "; unsigned int retry = cTimeOuts; TransmitResult isSlave = 0; 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 { //TransmitResult qDebug() << " --- BCDriverTinyCan::readRawByte DriverState: " << getDriverState(); if( _driverState != DriverState::DeviceReady) return std::unexpected(QString("readRawValue error: driver not loaded." ) ); ::TCanMsg msg; // msg verpacken msg.MsgFlags = 0L; msg.Id = 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" < 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" )); 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::DeviceReady) return std::unexpected(QString("writeRawValue 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' }