// Copyright 2022 Haute école d'ingénierie et d'architecture de Fribourg // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /**************************************************************************** * @file bike_system.cpp * @author Serge Ayer * @author Rémi Heredero * @author Yann Sierro * * @brief Bike System implementation (static scheduling) * * @date 2023-11-15 * @version 1.1.0 ***************************************************************************/ #include "bike_system.hpp" #include #include "cmsis_os2.h" #include "constants.hpp" #include "mbed_trace.h" #if MBED_CONF_MBED_TRACE_ENABLE #define TRACE_GROUP "BikeSystem" #endif // MBED_CONF_MBED_TRACE_ENABLE namespace multi_tasking { static constexpr std::chrono::milliseconds kGearTaskPeriod = 800ms; static constexpr std::chrono::milliseconds kGearTaskDelay = 0ms; static constexpr std::chrono::milliseconds kGearTaskComputationTime = 100ms; static constexpr std::chrono::milliseconds kSpeedDistanceTaskPeriod = 400ms; static constexpr std::chrono::milliseconds kSpeedDistanceTaskDelay = 0ms; // 0 or 100ms static constexpr std::chrono::milliseconds kSpeedDistanceTaskComputationTime = 200ms; static constexpr std::chrono::milliseconds kDisplayTask1Period = 1600ms; static constexpr std::chrono::milliseconds kDisplayTask1Delay = 300ms; static constexpr std::chrono::milliseconds kDisplayTask1ComputationTime = 200ms; static constexpr std::chrono::milliseconds kResetTaskPeriod = 800ms; static constexpr std::chrono::milliseconds kResetTaskDelay = 700ms; static constexpr std::chrono::milliseconds kResetTaskComputationTime = 100ms; static constexpr std::chrono::milliseconds kTemperatureTaskPeriod = 1600ms; static constexpr std::chrono::milliseconds kTemperatureTaskDelay = 1100ms; static constexpr std::chrono::milliseconds kTemperatureTaskComputationTime = 100ms; static constexpr std::chrono::milliseconds kDisplayTask2Period = 1600ms; static constexpr std::chrono::milliseconds kDisplayTask2Delay = 1200ms; static constexpr std::chrono::milliseconds kDisplayTask2ComputationTime = 100ms; static constexpr std::chrono::milliseconds kCPUTaskPeriod = 1600ms; static constexpr std::chrono::milliseconds kCPUTaskDelay = 0ms; static constexpr std::chrono::milliseconds kCPUTaskComputationTime = 0ms; BikeSystem::BikeSystem() : _gearDevice(callback(this, &BikeSystem::onGearUp), callback(this, &BikeSystem::onGearDown)), _pedalDevice(), _resetDevice(callback(this, &BikeSystem::onReset)), _speedometer(_timer), _cpuLogger(_timer), _isrEventThread(osPriorityAboveNormal, OS_STACK_SIZE, nullptr, "ISR_Event") {} void BikeSystem::start() { tr_info("Starting Super-Loop with event handling"); init(); Event speedDistanceEvent(&_eventQueue, callback(this, &BikeSystem::speedDistanceTask)); speedDistanceEvent.delay(kSpeedDistanceTaskDelay); speedDistanceEvent.period(kSpeedDistanceTaskPeriod); speedDistanceEvent.post(); Event display1Event(&_eventQueue, callback(this, &BikeSystem::displayTask1)); display1Event.delay(kDisplayTask1Delay); display1Event.period(kDisplayTask1Period); display1Event.post(); Event temperatureEvent(&_eventQueue, callback(this, &BikeSystem::temperatureTask)); temperatureEvent.delay(kTemperatureTaskDelay); temperatureEvent.period(kTemperatureTaskPeriod); temperatureEvent.post(); Event display2Event(&_eventQueue, callback(this, &BikeSystem::displayTask2)); display2Event.delay(kDisplayTask2Delay); display2Event.period(kDisplayTask2Period); display2Event.post(); osStatus status = _isrEventThread.start(callback(this, &BikeSystem::dispatch_isr_events)); if (status != osOK) { tr_error("Thread %s started with status %d", _isrEventThread.get_name(), status); } dispatch_events(); } void BikeSystem::onReset() { _resetTime = _timer.elapsed_time(); Event resetEvent(&_isrEventQueue, callback(this, &BikeSystem::resetTask)); resetEvent.post(); } void BikeSystem::onGearUp() { _onGearUpTime = _timer.elapsed_time(); Event gearUpEvent(&_isrEventQueue, callback(this, &BikeSystem::gearUpTask)); gearUpEvent.post(); } void BikeSystem::onGearDown() { _onGearDownTime = _timer.elapsed_time(); Event gearDownEvent(&_isrEventQueue, callback(this, &BikeSystem::gearDownTask)); gearDownEvent.post(); } #if defined(MBED_TEST_MODE) const advembsof::TaskLogger& BikeSystem::getTaskLogger() { return _taskLogger; } #endif // defined(MBED_TEST_MODE) void BikeSystem::init() { // start the timer _timer.start(); // initialize the lcd display disco::ReturnCode rc = _displayDevice.init(); if (rc != disco::ReturnCode::Ok) { tr_error("Ffalseailed to initialized the lcd display: %ld", static_cast(rc)); } // initialize the sensor device bool present = _sensorDevice.init(); if (!present) { tr_error("Sensor not present or initialization failed"); } // enable/disable task logging _taskLogger.enable(false); } void BikeSystem::gearUpTask() { auto taskStartTime = _timer.elapsed_time(); std::chrono::microseconds responseTime = _timer.elapsed_time() - _onGearUpTime; tr_info("Gear up task: response time is %" PRIu64 " usecs", responseTime.count()); // CRITICAL SECTION mutexGear.lock(); if (_currentGear < bike_computer::kMaxGear) { _currentGear++; mutexGearSize.lock(); _currentGearSize = bike_computer::kMaxGearSize - _currentGear; mutexGearSize.unlock(); } mutexGear.unlock(); // END CRITICAL SECTION _taskLogger.logPeriodAndExecutionTime( _timer, advembsof::TaskLogger::kGearTaskIndex, taskStartTime); } void BikeSystem::gearDownTask() { auto taskStartTime = _timer.elapsed_time(); std::chrono::microseconds responseTime = _timer.elapsed_time() - _onGearDownTime; tr_info("Gear down task: response time is %" PRIu64 " usecs", responseTime.count()); // CRITICAL SECTION mutexGear.lock(); if (_currentGear > bike_computer::kMinGear) { _currentGear--; mutexGearSize.lock(); _currentGearSize = bike_computer::kMaxGearSize - _currentGear; mutexGearSize.unlock(); } mutexGear.unlock(); // END CRITICAL SECTION _taskLogger.logPeriodAndExecutionTime( _timer, advembsof::TaskLogger::kGearTaskIndex, taskStartTime); } void BikeSystem::speedDistanceTask() { auto taskStartTime = _timer.elapsed_time(); const auto pedalRotationTime = _pedalDevice.getCurrentRotationTime(); _speedometer.setCurrentRotationTime(pedalRotationTime); _speedometer.setGearSize(getCurrentGearSize()); _currentSpeed = _speedometer.getCurrentSpeed(); _traveledDistance = _speedometer.getDistance(); _taskLogger.logPeriodAndExecutionTime( _timer, advembsof::TaskLogger::kSpeedTaskIndex, taskStartTime); } void BikeSystem::temperatureTask() { auto taskStartTime = _timer.elapsed_time(); // tr_warn("Tick1 %" PRIu64, _timer.elapsed_time().count()); // no need to protect access to data members (single threaded) _currentTemperature = _sensorDevice.readTemperature(); // tr_warn("Tick2 %" PRIu64, _timer.elapsed_time().count()); ThisThread::sleep_for(std::chrono::duration_cast( kTemperatureTaskComputationTime - (_timer.elapsed_time() - taskStartTime))); _taskLogger.logPeriodAndExecutionTime( _timer, advembsof::TaskLogger::kTemperatureTaskIndex, taskStartTime); } void BikeSystem::resetTask() { auto taskStartTime = _timer.elapsed_time(); std::chrono::microseconds responseTime = _timer.elapsed_time() - _resetTime; tr_info("Reset task: response time is %" PRIu64 " usecs", responseTime.count()); _speedometer.reset(); _taskLogger.logPeriodAndExecutionTime( _timer, advembsof::TaskLogger::kResetTaskIndex, taskStartTime); } void BikeSystem::displayTask1() { auto taskStartTime = _timer.elapsed_time(); _displayDevice.displayGear(getCurrentGear()); _displayDevice.displaySpeed(_currentSpeed); _displayDevice.displayDistance(_traveledDistance); ThisThread::sleep_for(std::chrono::duration_cast( kDisplayTask1ComputationTime - (_timer.elapsed_time() - taskStartTime))); _taskLogger.logPeriodAndExecutionTime( _timer, advembsof::TaskLogger::kDisplayTask1Index, taskStartTime); } void BikeSystem::displayTask2() { auto taskStartTime = _timer.elapsed_time(); _displayDevice.displayTemperature(_currentTemperature); ThisThread::sleep_for(std::chrono::duration_cast( kDisplayTask2ComputationTime - (_timer.elapsed_time() - taskStartTime))); _taskLogger.logPeriodAndExecutionTime( _timer, advembsof::TaskLogger::kDisplayTask2Index, taskStartTime); } void BikeSystem::cpuTask() { _cpuLogger.printStats(); } void BikeSystem::dispatch_isr_events() { tr_info("Start dispatching isr events"); _isrEventQueue.dispatch_forever(); tr_info("Stop dispatching isr events"); } void BikeSystem::dispatch_events() { tr_info("Start dispatching main events"); _eventQueue.dispatch_forever(); tr_info("Stop dispatching main events"); } uint8_t BikeSystem::getCurrentGear() { uint8_t currentGear; // CRITICAL SECTION mutexGear.lock(); currentGear = _currentGear; mutexGear.unlock(); // END CRITICAL SECTION return currentGear; } uint8_t BikeSystem::getCurrentGearSize() { uint8_t currentGearSize; // CRITICAL SECTION mutexGearSize.lock(); currentGearSize = _currentGearSize; mutexGearSize.unlock(); // END CRITICAL SECTION return currentGearSize; } } // namespace multi_tasking