92 Commits
Phase1 ... main

Author SHA1 Message Date
6a37259a63 Merge multi-tasking and bootloader from /develop
Some checks failed
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-bike-system) (push) Failing after 2s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-sensor-device) (push) Failing after 2s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-speedometer) (push) Failing after 2s
ADD multi-tasking and bootloader
2025-01-07 11:18:06 +01:00
fdab57142a ADD multi-tasking and own test
Some checks failed
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-bike-system) (push) Failing after 11s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-sensor-device) (push) Failing after 3s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-speedometer) (push) Failing after 5s
2025-01-07 10:31:02 +01:00
787fa94e91 FIX speedometer include and mbed_app.json
Some checks failed
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-bike-system) (push) Failing after 2s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-sensor-device) (push) Failing after 2s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-speedometer) (push) Failing after 2s
2025-01-07 10:22:55 +01:00
e460a4f695 FIX kMaxGearSize and reset callback 2025-01-07 00:35:20 +01:00
a183ad4858 FIX kMaxGearSize value static scheduling with event 2025-01-06 23:07:46 +01:00
dfd94b647c FIX kMaxGearSize value 2025-01-06 23:00:53 +01:00
d0ff3defa1 FIX mbed_app.json 2025-01-06 18:27:12 +01:00
7d228d67cc FIX mbed_app.json 2025-01-06 18:18:49 +01:00
7838ed0448 Merge branch 'develop' into FEAT/multi-tasking 2025-01-06 11:32:17 +01:00
6d42dd2ba1 ADD real timing test for reset task 2025-01-06 11:18:14 +01:00
0ec735adde FIX unused variables 2025-01-06 11:05:23 +01:00
618ddaaa1b ADD test for multi-tasking 2025-01-06 00:14:43 +01:00
aed65401f5 ADD pre-commit config 2025-01-05 11:44:03 +01:00
686eff6269 ADD minimal bootloader (main.cpp) 2025-01-05 11:44:03 +01:00
0d61cd598c ADD minimal bootloader 2025-01-05 11:44:03 +01:00
55a6594646 ADD preparation for tests 2025-01-04 16:41:02 +01:00
3a3d27f93e ADD common/ in pre-commit action file 2025-01-04 15:54:43 +01:00
afa1fb8aef FIX clang format 2025-01-04 15:53:13 +01:00
b9c129e872 ADD measure time call 2025-01-04 15:51:51 +01:00
4fb09c690c ADD mail box for gear device 2025-01-03 20:22:23 +01:00
62fd302d84 FIX kMinGearSize value 2025-01-03 20:20:44 +01:00
95e92e2564 ADD mail box for pedal device 2025-01-03 17:10:42 +01:00
980eb2b376 Merge pull request #33 from Fastium/FIX/init-dec_order
FIX initialisation and declaration order
2024-12-31 16:08:03 +01:00
75b0c31092 Merge pull request #34 from Fastium/FIX/init-dec_order
Some checks failed
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-bike-system) (push) Failing after 3s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-sensor-device) (push) Failing after 3s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-speedometer) (push) Failing after 2s
FIX initialisation and declaration order
2024-12-31 16:07:59 +01:00
946e86025c ADD part of mail for pedal device 2024-12-31 16:02:42 +01:00
36e4a07102 ADD run multi-tasking in README 2024-12-31 14:47:45 +01:00
957860b2ff ADD answer question 1 2024-12-31 14:41:16 +01:00
f64b8bdd9a ADD gear event driven 2024-12-31 13:47:36 +01:00
34a57162ba ADD reset button with its own dispatch queue 2024-12-31 10:31:00 +01:00
5a85e1da8d Merge branch 'develop' into FIX/init-dec_order
Some checks failed
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-bike-system) (push) Failing after 2s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-sensor-device) (push) Failing after 3s
Build test application / build-cli-v1 (debug, DISCO_H747I, tests-bike-computer-speedometer) (push) Failing after 3s
2024-12-31 09:32:08 +01:00
9eb4f64b73 FIX initialisation and declaration order 2024-12-31 09:15:50 +01:00
031b98a0f7 FIX lib disco in gitignore 2024-12-31 08:37:19 +01:00
ca33d26769 FIX cpplint errors 2024-12-30 17:52:08 +01:00
4c9a0e25a7 ADD modification from issue 2024-12-30 17:38:02 +01:00
544df31a1b FIX cast with chrono duration 2024-12-30 17:02:20 +01:00
1f9e4ebd3c FIX cast with chrono duration 2024-12-30 17:02:16 +01:00
69bc8785a8 ADD threads dispatcher and drive reset with event 2024-12-30 16:56:04 +01:00
31e1f22c59 FIX tests namespaces 2024-12-30 13:58:25 +01:00
c0335b38a4 ADD cpplint args in action file 2024-12-30 13:58:25 +01:00
f8077db299 FIX delete flag unused function 2024-12-30 13:58:25 +01:00
367c7178c0 ADD suppress args for cppcheck 2024-12-30 13:58:25 +01:00
394f4febff ADD files to pre-commit 2024-12-30 13:58:25 +01:00
a887c91775 ADD test pre-commit 2024-12-30 13:58:25 +01:00
d176856326 ADD project folders to pre-commit 2024-12-30 13:58:25 +01:00
7d7fb400ff FIX tests namespaces 2024-12-30 13:57:42 +01:00
db834de1fd ADD cpplint args in action file 2024-12-30 13:57:42 +01:00
84bd49be80 FIX delete flag unused function 2024-12-30 13:57:42 +01:00
ebfcef2c57 ADD suppress args for cppcheck 2024-12-30 13:57:42 +01:00
e7a2a7da59 ADD files to pre-commit 2024-12-30 13:57:42 +01:00
9aa656c7ac ADD test pre-commit 2024-12-30 13:57:42 +01:00
9c104fc3a2 ADD project folders to pre-commit 2024-12-30 13:57:42 +01:00
da79a18cd9 ADD correct disco lib in gitignore 2024-12-17 16:20:09 +01:00
c4ff5361e9 ADD isr queue 2024-12-17 15:37:16 +01:00
7b5bcfc35e ADD typo pre-commit 2024-11-26 16:43:08 +01:00
c92de22c09 ADD create files for multi tasking and modify the main 2024-11-26 16:26:41 +01:00
1ba466569e FIX delta to use GCC rather than ARMC6 2024-11-19 14:38:42 +01:00
98f3a32a68 FIX delta to use GCC rather than ARMC6 2024-11-19 14:38:28 +01:00
171c43b653 ADD Phase 2 2024-11-19 12:01:17 +01:00
60baebd59b Merge branch 'main' into develop 2024-11-19 11:53:56 +01:00
eeb88edba3 UPD CI workflow 2024-11-18 10:13:01 +01:00
ac4067e5a4 RM folder ignore for tests 2024-11-18 10:13:01 +01:00
86e033fe2f ADD answer to questions 2024-11-18 10:13:01 +01:00
f6f3d59ef3 ADD run configurations 2024-11-18 10:13:01 +01:00
20406664c3 FIX reset task 2024-11-18 10:13:01 +01:00
62d7d7b376 FIX Static scheduling with event 2024-11-18 10:13:01 +01:00
35d2d76bca ADD [WIP] Static scheduling with event 2024-11-18 10:13:01 +01:00
0bd561b947 ADD EventQueue for static_scheduling 2024-11-18 10:13:01 +01:00
b52f476124 ADD Thread sleep 2024-11-18 10:13:01 +01:00
c66c8a0963 ADD cpu logger 2024-11-18 10:13:01 +01:00
4c174ce444 ADD bike-system super-loop with while 2024-11-18 10:13:01 +01:00
ed9ab169bb CHG switch CI trigger from push to pull_request 2024-11-12 16:00:13 +01:00
6d0452d9ca FIX ResetDevice
- Initialisation
- Callback
- return of checkReset
2024-11-12 16:00:13 +01:00
874a968f5c ADD ResetDevice 2024-11-12 15:35:40 +01:00
7b62a3634b ADD PedalDevice 2024-11-12 15:35:40 +01:00
a7c28c0e52 FIX distance calculation in speedometer. 2024-11-12 13:57:04 +01:00
69047369f0 FIX GitHub action 2024-11-06 14:40:17 +01:00
af0b6180da FIX raw + unique ptr tests (#12)
FIX issues on raw and unique pointer tests
+ Merge individual test files for unique and raw pointers and merged their test cases into a single test file for simplicity.

Co-authored-by: Fastium <79766552+Fastium@users.noreply.github.com>
2024-11-06 12:35:04 +01:00
43cacadfb7 FIX conflict when merge main to develop 2024-11-05 16:53:08 +01:00
15d1183bac FIX raw + unique ptr tests
FIX issues on raw and unique pointer tests
+ Merge individual test files for unique and raw pointers and merged their test cases into a single test file for simplicity.
2024-11-05 16:36:29 +01:00
7ef2519c9c Merge pull request #9 from Fastium/architecture-bike-computer
ADD architecture (speedometer and sensor device) for the bike computer
2024-11-05 16:32:07 +01:00
dc99ac08b5 ADD speedometer tests in CI for github 2024-11-05 16:21:51 +01:00
ac8e030089 ADD speedometer implementation 2024-11-05 15:30:12 +01:00
0d18073562 ADD (WIP) speedometer implementation 2024-10-29 17:10:58 +01:00
db2d5308bc ADD tests for speedometer and sensor-device 2024-10-29 16:16:30 +01:00
0d4e603a64 ADD speedometer and gear class 2024-10-29 16:15:32 +01:00
b9fda1aefe ADD Tests for the sensor 2024-10-29 16:13:20 +01:00
b1d04badfb ADD sensor device classes 2024-10-23 10:59:38 +02:00
a2d3ca3096 Merge pull request #6 from Fastium/library-disco-sensor
Library for sensor and disco board with tests
2024-10-23 09:24:39 +02:00
0634d16ba3 UPD test for github action (sensor) 2024-10-23 08:54:27 +02:00
5ef0203a27 UPD README.md for wrong symbol to illustrate commands 2024-10-22 15:48:08 +02:00
f8ff28aef6 UDP README.md for additional libraries and test 2024-10-22 15:44:41 +02:00
86ccb3ca17 ADD libraries for disco board and extarnal sensor 2024-10-22 15:37:47 +02:00
53 changed files with 4168 additions and 368 deletions

View File

@ -1,6 +1,5 @@
name: Build test application
on:
pull_request:
push:
jobs:
@ -13,12 +12,11 @@ jobs:
strategy:
matrix:
target: [DISCO_H747I]
profile: [develop, debug, release]
profile: [debug]
tests: [
tests-simple-test-always-succeed,
tests-simple-test-ptr-test,
tests-simple-unique-ptr,
tests-simple-test-raw-ptr
tests-bike-computer-sensor-device,
tests-bike-computer-speedometer,
tests-bike-computer-bike-system,
]

2
.gitignore vendored
View File

@ -4,3 +4,5 @@ projectfiles
*.py*
BUILD
mbed-os
DISCO_H747I
advdembsof_library

View File

@ -14,4 +14,5 @@ mbed-os/features/FEATURE_BOOTLOADER/*
mbed-os/features/frameworks/mbed-client-cli/*
mbed-os/features/frameworks/COMPONENT_FPGA_CI_TEST_SHIELD/*
mbed-os/platform/randlib/*
mbed-os/storage/kvstore/*
mbed-os/storage/kvstore/*
mbed-os-bootloader/*

View File

@ -1,24 +1,26 @@
files: ^main.cpp
files: ^main.cpp|^static_scheduling|^static_scheduling_with_event|^TESTS|^multi_tasking|^common
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-yaml
args: [--allow-multiple-documents]
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 'v14.0.6'
hooks:
- id: clang-format
- repo: https://github.com/cpplint/cpplint
rev: '1.6.1'
hooks:
- id: cpplint
- repo: local
hooks:
- id: cppcheck
name: cppcheck
require_serial: true
entry: cppcheck --enable=all --suppress=missingInclude:* --inline-suppr -i mbed-os --std=c++14 --error-exitcode=1
language: system
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-yaml
args: [--allow-multiple-documents]
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v14.0.6"
hooks:
- id: clang-format
- repo: https://github.com/cpplint/cpplint
rev: "1.6.1"
hooks:
- id: cpplint
name: cpplint
entry: cpplint --linelength=90 --filter=-build/include_subdir,-whitespace/indent,-build/namespaces,-build/c++11
- repo: local
hooks:
- id: cppcheck
name: cppcheck
require_serial: true
entry: cppcheck --enable=all --suppress=missingInclude --suppress=missingIncludeSystem --inline-suppr -i mbed-os --std=c++14 --error-exitcode=1
language: system

1
DISCO_H747I.lib Normal file
View File

@ -0,0 +1 @@
https://github.com/SergeAyer/DISCO_H747I/#2b367efbbf0d66205403647c0d0227808d0529ed

113
README.md
View File

@ -5,4 +5,117 @@ AdvEmbSoft/HESSO-Master/2024
# BikeComputer
This is a project done in the course of AdvEmbSoft during the master's degree. It contains a program for spinning bikes.
# Configuration
## Libraries
Add disco libraries :
```terminal
mbed add https://github.com/SergeAyer/DISCO_H747I.git
```
Add sensor libraries :
```terminal
mbed add https://github.com/SergeAyer/advdembsof_library.git
```
Test sensor libraries :
```terminal
mbed test -m DISCO_H747I -t GCC_ARM -n advdembsof_library-tests-sensors-hdc1000 --compile --run
```
## Run static scheduling
On `.mbedignore` put at the end of the file
```
static_scheduling_with_event/*
multi_tasking/*
```
On main.cpp include `"static_scheduling/bike_system.hpp"` and use :
```cpp
static_scheduling::BikeSystem bikeSystem;
bikeSystem.start();
```
## Run static scheduling with event queue
On `.mbedignore` put at the end of the file :
```
static_scheduling_with_event/*
multi_tasking/*
```
On main.cpp include `"static_scheduling/bike_system.hpp"` and use :
```cpp
static_scheduling::BikeSystem bikeSystem;
bikeSystem.startWithEventQueue();
```
## Run static scheduling with event scheduling
On `.mbedignore` put at the end of the file
```
static_scheduling/*
static_scheduling_with_event/*
```
On main.cpp include `"multi_tasking/bike_system.hpp"` and use :
```cpp
multi_tasking::BikeSystem bikeSystem;
bikeSystem.start();
```
## Run multi-tasking
On `.mbedignore` put at the end of the file
```
static_scheduling/*
multi_tasking/*
```
On main.cpp include `"static_scheduling_with_event/bike_system.hpp"` and use :
```cpp
static_scheduling_with_event::BikeSystem bikeSystem;
bikeSystem.start();
```
# Some questions
## Question 1
`If you print CPU statistics at the end of every major cycle (in the super-loop), what CPU usage do you observe? How can you explain the observed CPU uptime?`
We observe a 100% usage because on each CPU cycle it compare if time is done.
## Question 2
`If you run the program after the change from busy wait to sleep calls, what CPU usage do you observe? How can you explain the observed CPU uptime?`
We can observe only a usage of 75% because the CPU is more on Idle with Thread sleep.
## Question 3
`If you run the static_scheduling_with_event program, what CPU usage do you observe? How can you explain the observed CPU uptime?`
We observe a light usage of 1% of CPU. The CPU is now sleeping all the time and doing small task only on event.
## Question 4
`When you run multiple tests for computing the response time of the reset event, what do you observe? Is there an improvement as compared to the static_scheduling::BikeSystem implementation?`
` - If you do not press long enough on the push button, the event may be missed and no reset happens.`
`Based on the program itself and on the task scheduling, explain these two behaviors. Explain also why such behaviors may be problematic.`
We notice, that we miss such less event when is event driven (or not at all). But with a static scheduling the response time is still long because the reset task is call with a certain period.
# Multi-tasking
## Question 1
We have computed the elapsed time between the event is sent by the interruption until it is executed :
|Rtos priority |time |
|---|---|
|osPriorityBelowNormal |13 usecs |
|osPriorityNormal |13 usecs |
|osPriorityAboveNormal |13 usecs |
To abtain these values, we have to consider that there is a isr thread. He dispatchs all the events from the gear device and the reset device. The rest is processed by the main thread.
Main of the time, it meares these values. But sometimes, higher values can appear. These values doesn't change because there isn't often more than one event in the queue.
# Issues
When compile with GCC, the full loop of static scheduling is 2 to 3 ms faster than expected.
This problem doesn't occur if we compile with ARMC6.
As the acceptable delta is 2ms and the teacher test is done with GCC, we modify the delta on the test to be 3ms

View File

@ -0,0 +1,408 @@
// 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 main.cpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Bike computer test suite: scheduling
*
* @date 2023-08-26
* @version 0.1.0
***************************************************************************/
#include <chrono>
#include <cstdint>
#include "gear_device.hpp"
#include "greentea-client/test_env.h"
#include "mbed.h"
#include "mbed_trace.h" // NOLINT
#include "multi_tasking/bike_system.hpp"
#include "static_scheduling/bike_system.hpp"
#include "static_scheduling_with_event/bike_system.hpp"
#include "task_logger.hpp"
#include "unity/unity.h"
#include "utest/utest.h"
#if defined(MBED_CONF_MBED_TRACE_ENABLE)
#define TRACE_GROUP "TEST_BIKE_SYSTEM"
#endif // MBED_CONF_MBED_TRACE_ENAB
namespace utest {
namespace v1 {
// test_bike_system handler function
static void test_bike_system() {
// create the BikeSystem instance
static_scheduling::BikeSystem bikeSystem;
// run the bike system in a separate thread
Thread thread;
thread.start(callback(&bikeSystem, &static_scheduling::BikeSystem::start));
// let the bike system run for 20 secs
ThisThread::sleep_for(20s);
// stop the bike system
bikeSystem.stop();
// check whether scheduling was correct
// Order is kGearTaskIndex, kSpeedTaskIndex, kTemperatureTaskIndex,
// kResetTaskIndex, kDisplayTask1Index, kDisplayTask2Index
constexpr std::chrono::microseconds taskComputationTimes[] = {
100000us, 200000us, 100000us, 100000us, 200000us, 100000us};
constexpr std::chrono::microseconds taskPeriods[] = {
800000us, 400000us, 1600000us, 800000us, 1600000us, 1600000us};
// allow for 2 msecs offset
uint64_t deltaUs = 3000;
for (uint8_t taskIndex = 0; taskIndex < advembsof::TaskLogger::kNbrOfTasks;
taskIndex++) {
TEST_ASSERT_UINT64_WITHIN(
deltaUs,
taskPeriods[taskIndex].count(),
bikeSystem.getTaskLogger().getPeriod(taskIndex).count());
TEST_ASSERT_UINT64_WITHIN(
deltaUs,
taskComputationTimes[taskIndex].count(),
bikeSystem.getTaskLogger().getComputationTime(taskIndex).count());
}
}
// test_bike_system_event_queue handler function
static void test_bike_system_event_queue() {
// create the BikeSystem instance
static_scheduling::BikeSystem bikeSystem;
// run the bike system in a separate thread
Thread thread;
thread.start(
callback(&bikeSystem, &static_scheduling::BikeSystem::startWithEventQueue));
// let the bike system run for 20 secs
ThisThread::sleep_for(20s);
// stop the bike system
bikeSystem.stop();
// check whether scheduling was correct
// Order is kGearTaskIndex, kSpeedTaskIndex, kTemperatureTaskIndex,
// kResetTaskIndex, kDisplayTask1Index, kDisplayTask2Index
// When we use the event queue, we do not check the computation time
constexpr std::chrono::microseconds taskPeriods[] = {
800000us, 400000us, 1600000us, 800000us, 1600000us, 1600000us};
// allow for 2 msecs offset (with EventQueue)
uint64_t deltaUs = 3000;
for (uint8_t taskIndex = 0; taskIndex < advembsof::TaskLogger::kNbrOfTasks;
taskIndex++) {
TEST_ASSERT_UINT64_WITHIN(
deltaUs,
taskPeriods[taskIndex].count(),
bikeSystem.getTaskLogger().getPeriod(taskIndex).count());
}
}
// test_bike_system_with_event handler function
static void test_bike_system_with_event() {
// create the BikeSystem instance
static_scheduling_with_event::BikeSystem bikeSystem;
// run the bike system in a separate thread
Thread thread;
thread.start(callback(&bikeSystem, &static_scheduling_with_event::BikeSystem::start));
// let the bike system run for 20 secs
ThisThread::sleep_for(20s);
// stop the bike system
bikeSystem.stop();
// check whether scheduling was correct
// Order is kGearTaskIndex, kSpeedTaskIndex, kTemperatureTaskIndex,
// kResetTaskIndex, kDisplayTask1Index, kDisplayTask2Index
// When we use event handling, we do not check the computation time
constexpr std::chrono::microseconds taskPeriods[] = {
800000us, 400000us, 1600000us, 800000us, 1600000us, 1600000us};
// allow for 2 msecs offset (with EventQueue)
uint64_t deltaUs = 3000;
for (uint8_t taskIndex = 0; taskIndex < advembsof::TaskLogger::kNbrOfTasks;
taskIndex++) {
TEST_ASSERT_UINT64_WITHIN(
deltaUs,
taskPeriods[taskIndex].count(),
bikeSystem.getTaskLogger().getPeriod(taskIndex).count());
}
}
// test_multi_tasking_bike_system handler function
static void test_multi_tasking_bike_system() {
tr_info("test multi tasking bike system");
// create the BikeSystem instance
multi_tasking::BikeSystem bikeSystem;
// run the bike system in a separate thread
Thread thread;
osStatus status =
thread.start(callback(&bikeSystem, &multi_tasking::BikeSystem::start));
if (status != osOK) {
tr_error("Thread bike system is not OK !");
}
tr_info("bike system has started");
// let the bike system run for 20 secs
ThisThread::sleep_for(20s);
// check whether scheduling was correct
// Order is kGearTaskIndex, kSpeedTaskIndex, kTemperatureTaskIndex,
// kResetTaskIndex, kDisplayTask1Index, kDisplayTask2Index
// When we use event handling, we do not check the computation time
constexpr std::chrono::microseconds taskPeriods[] = {
800000us, 400000us, 1600000us, 800000us, 1600000us, 1600000us};
// allow for 2 msecs offset (with EventQueue)
constexpr uint64_t kDeltaUs = 2000;
// stop the bike system
bikeSystem.stop();
thread.terminate();
tr_info("Threads have stopped");
TEST_ASSERT_UINT64_WITHIN(
kDeltaUs,
taskPeriods[advembsof::TaskLogger::kTemperatureTaskIndex].count(),
bikeSystem.getTaskLogger()
.getPeriod(advembsof::TaskLogger::kTemperatureTaskIndex)
.count());
TEST_ASSERT_UINT64_WITHIN(
kDeltaUs,
taskPeriods[advembsof::TaskLogger::kDisplayTask1Index].count(),
bikeSystem.getTaskLogger()
.getPeriod(advembsof::TaskLogger::kDisplayTask1Index)
.count());
}
// test_reset_multi_tasking_bike_system handler function
Timer timer;
static std::chrono::microseconds resetTime = std::chrono::microseconds::zero();
static EventFlags eventFlags;
static constexpr uint32_t kResetEventFlag = (1UL << 0);
static void resetCallback() {
resetTime = timer.elapsed_time();
eventFlags.set(kResetEventFlag);
}
static void test_reset_multi_tasking_bike_system() {
tr_info("test reset multi tasking bike system");
// create the BikeSystem instance
multi_tasking::BikeSystem bikeSystem;
// run the bike system in a separate thread
Thread thread;
osStatus status =
thread.start(callback(&bikeSystem, &multi_tasking::BikeSystem::start));
if (status != osOK) {
tr_error("Thread bike system is not OK !");
}
tr_info("Bike system has started");
// let the bike system run for 2 secs
ThisThread::sleep_for(2s);
// test reset on BikeSystem
bikeSystem.getSpeedometer().setOnResetCallback(resetCallback);
// start the timer instance
timer.start();
// check for reset response time
constexpr uint8_t kNbrOfResets = 10;
std::chrono::microseconds lastResponseTime = std::chrono::microseconds::zero();
for (uint8_t i = 0; i < kNbrOfResets; i++) {
tr_info("Reset test N°%d", i);
// take time before reset
auto startTime = timer.elapsed_time();
// reset the BikeSystem
bikeSystem.onReset();
// wait for resetCallback to be called
eventFlags.wait_all(kResetEventFlag);
// get the response time and check it
auto responseTime = resetTime - startTime;
// cppcheck generates an internal error with 20us
constexpr std::chrono::microseconds kMaxExpectedResponseTime(20);
tr_info("Reset task: response time is %lld usecs\n, expected : %lld",
responseTime.count(),
kMaxExpectedResponseTime.count());
TEST_ASSERT_TRUE(responseTime.count() <= kMaxExpectedResponseTime.count());
// jitter of 20us is accepted
constexpr uint64_t kDeltaUs = 4;
constexpr std::chrono::microseconds kMaxExpectedJitter(3);
if (i > 0) {
auto jitter = responseTime - lastResponseTime;
tr_info("Reset task: jitter is %lld usecs\n", std::abs(jitter.count()));
TEST_ASSERT_UINT64_WITHIN(
kDeltaUs, kMaxExpectedJitter.count(), std::abs(jitter.count()));
}
lastResponseTime = responseTime;
// let the bike system run for 2 secs
ThisThread::sleep_for(2s);
}
// stop the bike system
bikeSystem.stop();
thread.terminate();
tr_info("Threads have stopped");
timer.stop();
}
Timer timerGear;
static std::chrono::microseconds gearTime = std::chrono::microseconds::zero();
static EventFlags eventGearFlags;
static constexpr uint32_t kGearEventFlag = (1UL << 0);
constexpr uint8_t kNbrOfGear = 9;
static void onGearChange() {
gearTime = timerGear.elapsed_time();
eventGearFlags.set(kGearEventFlag);
tr_info("Gear changed");
}
static void gearTest(Callback<void()> gearChange) {
// std::chrono::microseconds lastResponseTime = std::chrono::microseconds::zero();
for (uint8_t i = 1; i < kNbrOfGear; i++) {
tr_info("Gear test N°%d", i);
// take time before increase the gear
auto startTime = timerGear.elapsed_time();
// change the gear with the callback
tr_info("Change gear");
gearChange();
// wait flag
eventGearFlags.wait_all(kGearEventFlag);
// get the response time and check it
auto responseTime = gearTime - startTime;
constexpr std::chrono::microseconds kMaxExpectedResponseTime(100000);
tr_info("Change gear task: response time is %lld usecs\n, expected : %lld",
responseTime.count(),
kMaxExpectedResponseTime.count());
TEST_ASSERT_TRUE(responseTime.count() <= kMaxExpectedResponseTime.count());
// jitter of 20us is accepted
// constexpr uint64_t kDeltaUs = 2000;
// constexpr std::chrono::microseconds kMaxExpectedJitter(4000);
// if (i > 1) {
// auto jitter = responseTime - lastResponseTime;
// tr_info("Gear task: jitter is %lld usecs\n", std::abs(jitter.count()));
// TEST_ASSERT_UINT64_WITHIN(
// kDeltaUs, kMaxExpectedJitter.count(), std::abs(jitter.count()));
// }
// lastResponseTime = responseTime;
// let the bike system run for 2 secs
ThisThread::sleep_for(2s);
}
}
static void test_gear_multi_tasking_bike_system() {
tr_info("test reset multi tasking bike system");
// create the BikeSystem instance
multi_tasking::BikeSystem bikeSystem;
// run the bike system in a separate thread
Thread thread;
osStatus status =
thread.start(callback(&bikeSystem, &multi_tasking::BikeSystem::start));
if (status != osOK) {
tr_error("Thread bike system is not OK !");
}
tr_info("Bike system has started");
// let the bike system run for 2 secs
ThisThread::sleep_for(2s);
// get the gear device to call onUp and onDown functions
multi_tasking::GearDevice& gearDevice = bikeSystem.getGearDevice();
// set callback for response time measuring
bikeSystem.setCallbackGearChage(onGearChange);
// start the timer instance
timerGear.start();
// test timing incresing the gear
tr_info("Test incresing gear");
gearTest(callback(&gearDevice, &multi_tasking::GearDevice::onUp));
// test timing decreasing the gear
tr_info("Test decreasing gear");
gearTest(callback(&gearDevice, &multi_tasking::GearDevice::onDown));
// stop the bike system
bikeSystem.stop();
thread.terminate();
tr_info("Threads have stopped");
timerGear.stop();
}
static status_t greentea_setup(const size_t number_of_cases) {
// Here, we specify the timeout (60s) and the host test (a built-in host test
// or the name of our Python file)
GREENTEA_SETUP(180, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}
// List of test cases in this file
static Case cases[] = {
Case("test bike system", test_bike_system),
Case("test bike system with event queue", test_bike_system_event_queue),
Case("test bike system with event", test_bike_system_with_event),
Case("test multi-tasking bike system", test_multi_tasking_bike_system),
Case("test reset multi-tasking bike system", test_reset_multi_tasking_bike_system),
Case("test gear multi-tasking bike system", test_gear_multi_tasking_bike_system),
};
static Specification specification(greentea_setup, cases);
}; // namespace v1
}; // namespace utest
int main() {
#if defined(MBED_CONF_MBED_TRACE_ENABLE)
mbed_trace_init();
#endif
return !utest::v1::Harness::run(utest::v1::specification);
}

View File

@ -0,0 +1,72 @@
// 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 main.cpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Bike computer test suite: sensor device
*
* @date 2023-08-26
* @version 0.1.0
***************************************************************************/
#include "greentea-client/test_env.h"
#include "hdc1000.hpp"
#include "mbed.h"
#include "sensor_device.hpp"
#include "unity/unity.h"
#include "utest/utest.h"
namespace utest {
namespace v1 {
// test_hdc1000 test handler function
static control_t test_sensor_device(const size_t call_count) {
// create the SensorDevice instance
bike_computer::SensorDevice sensorDevice;
bool rc = sensorDevice.init();
TEST_ASSERT_TRUE(rc);
float temperature = sensorDevice.readTemperature();
static constexpr float kTemperatureRange = 20.0f;
static constexpr float kMeanTemperature = 15.0f;
TEST_ASSERT_FLOAT_WITHIN(kTemperatureRange, kMeanTemperature, temperature);
float humidity = sensorDevice.readHumidity();
static constexpr float kHumidityRange = 40.0f;
static constexpr float kMeanHumidity = 50.0f;
TEST_ASSERT_FLOAT_WITHIN(kHumidityRange, kMeanHumidity, humidity);
// execute the test only once and move to the next one, without waiting
return CaseNext;
}
static status_t greentea_setup(const size_t number_of_cases) {
// Here, we specify the timeout (60s) and the host test (a built-in host test
// or the name of our Python file)
GREENTEA_SETUP(60, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}
// List of test cases in this file
static Case cases[] = {Case("test sensor device", test_sensor_device)};
static Specification specification(greentea_setup, cases);
}; // namespace v1
}; // namespace utest
int main() { return !utest::v1::Harness::run(utest::v1::specification); }

View File

@ -0,0 +1,359 @@
// 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 main.cpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Bike computer test suite: speedometer device
*
* @date 2023-08-26
* @version 0.1.0
***************************************************************************/
#include <chrono>
#include "common/constants.hpp"
#include "common/speedometer.hpp"
#include "greentea-client/test_env.h"
#include "mbed.h"
#include "static_scheduling/gear_device.hpp"
#include "unity/unity.h"
#include "utest/utest.h"
// allow for 0.1 km/h difference
static constexpr float kAllowedSpeedDelta = 0.1f;
// allow for 1m difference
static constexpr float kAllowedDistanceDelta = 1.0f / 1000.0;
namespace utest {
namespace v1 {
// function called by test handler functions for verifying the current speed
void check_current_speed(const std::chrono::milliseconds& pedalRotationTime,
uint8_t traySize,
uint8_t gearSize,
float wheelCircumference,
float currentSpeed) {
// compute the number of pedal rotation per hour
uint32_t milliSecondsPerHour = 1000 * 3600;
float pedalRotationsPerHour = static_cast<float>(milliSecondsPerHour) /
static_cast<float>(pedalRotationTime.count());
// compute the expected speed in km / h
// first compute the distance in meter for each pedal turn
float trayGearRatio = static_cast<float>(traySize) / static_cast<float>(gearSize);
float distancePerPedalTurn = trayGearRatio * wheelCircumference;
float expectedSpeed = (distancePerPedalTurn / 1000.0f) * pedalRotationsPerHour;
printf(" Expected speed is %f, current speed is %f\n", expectedSpeed, currentSpeed);
TEST_ASSERT_FLOAT_WITHIN(kAllowedSpeedDelta, expectedSpeed, currentSpeed);
}
// compute the traveled distance for a time interval
float compute_distance(const std::chrono::milliseconds& pedalRotationTime,
uint8_t traySize,
uint8_t gearSize,
float wheelCircumference,
const std::chrono::milliseconds& travelTime) {
// compute the number of pedal rotation during travel time
// both times are expressed in ms
float pedalRotations = static_cast<float>(travelTime.count()) /
static_cast<float>(pedalRotationTime.count());
// compute the distance in meter for each pedal turn
float trayGearRatio = static_cast<float>(traySize) / static_cast<float>(gearSize);
float distancePerPedalTurn = trayGearRatio * wheelCircumference;
// distancePerPedalTurn is expressed in m, divide per 1000 for a distance in
// km
return (distancePerPedalTurn * pedalRotations) / 1000.0;
}
// function called by test handler functions for verifying the distance traveled
void check_distance(const std::chrono::milliseconds& pedalRotationTime,
uint8_t traySize,
uint8_t gearSize,
float wheelCircumference,
const std::chrono::milliseconds& travelTime,
float distance) {
// distancePerPedalTurn is expressed in m, divide per 1000 for a distance in
// km
float expectedDistance = compute_distance(
pedalRotationTime, traySize, gearSize, wheelCircumference, travelTime);
printf(" Expected distance is %f, current distance is %f\n",
expectedDistance,
distance);
TEST_ASSERT_FLOAT_WITHIN(kAllowedDistanceDelta, expectedDistance, distance);
}
// test the speedometer by modifying the gear
static control_t test_gear_size(const size_t call_count) {
// create a timer
Timer timer;
// start the timer
timer.start();
// create a speedometer instance
bike_computer::Speedometer speedometer(timer);
// get speedometer constant values (for this test)
const auto traySize = speedometer.getTraySize();
const auto wheelCircumference = speedometer.getWheelCircumference();
const auto pedalRotationTime = speedometer.getCurrentPedalRotationTime();
for (uint8_t gearSize = bike_computer::kMinGearSize;
gearSize <= bike_computer::kMaxGearSize;
gearSize++) {
// set the gear
printf("Testing gear size %d\n", gearSize);
speedometer.setGearSize(gearSize);
// get the current speed
auto currentSpeed = speedometer.getCurrentSpeed();
// check the speed against the expected one
check_current_speed(
pedalRotationTime, traySize, gearSize, wheelCircumference, currentSpeed);
}
// execute the test only once and move to the next one, without waiting
return CaseNext;
}
// test the speedometer by modifying the pedal rotation speed
static control_t test_rotation_speed(const size_t call_count) {
// create a timer
Timer timer;
// start the timer
timer.start();
// create a speedometer instance
bike_computer::Speedometer speedometer(timer);
// set the gear size
speedometer.setGearSize(bike_computer::kMaxGearSize);
// get speedometer constant values
const auto traySize = speedometer.getTraySize();
const auto wheelCircumference = speedometer.getWheelCircumference();
const auto gearSize = speedometer.getGearSize();
// first test increasing rotation speed (decreasing rotation time)
auto pedalRotationTime = speedometer.getCurrentPedalRotationTime();
while (pedalRotationTime > bike_computer::kMinPedalRotationTime) {
// decrease the pedal rotation time
pedalRotationTime -= bike_computer::kDeltaPedalRotationTime;
speedometer.setCurrentRotationTime(pedalRotationTime);
// get the current speed
const auto currentSpeed = speedometer.getCurrentSpeed();
// check the speed against the expected one
check_current_speed(
pedalRotationTime, traySize, gearSize, wheelCircumference, currentSpeed);
}
// second test decreasing rotation speed (increasing rotation time)
pedalRotationTime = speedometer.getCurrentPedalRotationTime();
while (pedalRotationTime < bike_computer::kMaxPedalRotationTime) {
// increase the pedal rotation time
pedalRotationTime += bike_computer::kDeltaPedalRotationTime;
speedometer.setCurrentRotationTime(pedalRotationTime);
// get the current speed
const auto currentSpeed = speedometer.getCurrentSpeed();
// check the speed against the expected one
check_current_speed(
pedalRotationTime, traySize, gearSize, wheelCircumference, currentSpeed);
}
// execute the test only once and move to the next one, without waiting
return CaseNext;
}
// test the speedometer by modifying the pedal rotation speed
static control_t test_distance(const size_t call_count) {
// create a timer
Timer timer;
// create a speedometer instance
bike_computer::Speedometer speedometer(timer);
// set the gear size
speedometer.setGearSize(bike_computer::kMaxGearSize);
// get speedometer constant values
const auto traySize = speedometer.getTraySize();
const auto wheelCircumference = speedometer.getWheelCircumference();
auto gearSize = speedometer.getGearSize();
auto pedalRotationTime = speedometer.getCurrentPedalRotationTime();
// test different travel times
const std::chrono::milliseconds travelTimes[] = {500ms, 1000ms, 5s, 10s};
const uint8_t nbrOfTravelTimes = sizeof(travelTimes) / sizeof(travelTimes[0]);
// start the timer (for simulating bike start)
timer.start();
// first check travel distance without changing gear and rotation speed
std::chrono::milliseconds totalTravelTime = std::chrono::milliseconds::zero();
for (uint8_t index = 0; index < nbrOfTravelTimes; index++) {
// run for the travel time and get the distance
ThisThread::sleep_for(travelTimes[index]);
// get the distance traveled
const auto distance = speedometer.getDistance();
// accumulate travel time
totalTravelTime += travelTimes[index];
// check the distance vs the expected one
check_distance(pedalRotationTime,
traySize,
gearSize,
wheelCircumference,
totalTravelTime,
distance);
}
// now change gear at each time interval
auto expectedDistance = speedometer.getDistance();
for (uint8_t index = 0; index < nbrOfTravelTimes; index++) {
// update the gear size
gearSize++;
speedometer.setGearSize(gearSize);
// run for the travel time and get the distance
ThisThread::sleep_for(travelTimes[index]);
// compute the expected distance for this time segment
float distance = compute_distance(pedalRotationTime,
traySize,
gearSize,
wheelCircumference,
travelTimes[index]);
expectedDistance += distance;
// get the distance traveled
const auto traveledDistance = speedometer.getDistance();
printf(" Expected distance is %f, current distance is %f\n",
expectedDistance,
traveledDistance);
TEST_ASSERT_FLOAT_WITHIN(
kAllowedDistanceDelta, expectedDistance, traveledDistance);
}
// now change rotation speed at each time interval
expectedDistance = speedometer.getDistance();
for (uint8_t index = 0; index < nbrOfTravelTimes; index++) {
// update the rotation speed
pedalRotationTime += bike_computer::kDeltaPedalRotationTime;
speedometer.setCurrentRotationTime(pedalRotationTime);
// run for the travel time and get the distance
ThisThread::sleep_for(travelTimes[index]);
// compute the expected distance for this time segment
float distance = compute_distance(pedalRotationTime,
traySize,
gearSize,
wheelCircumference,
travelTimes[index]);
expectedDistance += distance;
// get the distance traveled
const auto traveledDistance = speedometer.getDistance();
printf(" Expected distance is %f, current distance is %f\n",
expectedDistance,
traveledDistance);
TEST_ASSERT_FLOAT_WITHIN(
kAllowedDistanceDelta, expectedDistance, traveledDistance);
}
// execute the test only once and move to the next one, without waiting
return CaseNext;
}
// test the speedometer by modifying the pedal rotation speed
static control_t test_reset(const size_t call_count) {
// create a timer instance
Timer timer;
// create a speedometer instance
bike_computer::Speedometer speedometer(timer);
// set the gear size
speedometer.setGearSize(bike_computer::kMinGearSize);
// get speedometer constant values
const auto traySize = speedometer.getTraySize();
const auto wheelCircumference = speedometer.getWheelCircumference();
const auto gearSize = speedometer.getGearSize();
const auto pedalRotationTime = speedometer.getCurrentPedalRotationTime();
// start the timer (for simulating bike start)
timer.start();
// travel for 1 second
const auto travelTime = 1000ms;
ThisThread::sleep_for(travelTime);
// check the expected distaance traveled
const auto expectedDistance = compute_distance(
pedalRotationTime, traySize, gearSize, wheelCircumference, travelTime);
// get the distance traveled
auto traveledDistance = speedometer.getDistance();
printf(" Expected distance is %f, current distance is %f\n",
expectedDistance,
traveledDistance);
TEST_ASSERT_FLOAT_WITHIN(kAllowedDistanceDelta, expectedDistance, traveledDistance);
// reset the speedometer
speedometer.reset();
// traveled distance should now be zero
traveledDistance = speedometer.getDistance();
printf(" Expected distance is %f, current distance is %f\n", 0.0f, traveledDistance);
TEST_ASSERT_FLOAT_WITHIN(kAllowedDistanceDelta, 0.0f, traveledDistance);
// execute the test only once and move to the next one, without waiting
return CaseNext;
}
static status_t greentea_setup(const size_t number_of_cases) {
// Here, we specify the timeout (60s) and the host test (a built-in host test
// or the name of our Python file)
GREENTEA_SETUP(180, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}
// List of test cases in this file
static Case cases[] = {
Case("test speedometer gear size change", test_gear_size),
Case("test speedometer rotation speed change", test_rotation_speed),
Case("test speedometer distance", test_distance),
Case("test speedometer reset", test_reset)};
static Specification specification(greentea_setup, cases);
}; // namespace v1
}; // namespace utest
int main() { return !utest::v1::Harness::run(utest::v1::specification); }

View File

@ -27,7 +27,8 @@
#include "unity/unity.h"
#include "utest/utest.h"
using namespace utest::v1;
namespace utest {
namespace v1 {
// test handler function
static control_t always_succeed(const size_t call_count) {
@ -38,9 +39,9 @@ static control_t always_succeed(const size_t call_count) {
return CaseNext;
}
static utest::v1::status_t greentea_setup(const size_t number_of_cases) {
// Here, we specify the timeout (60s) and the host test (a built-in host test or the
// name of our Python file)
static status_t greentea_setup(const size_t number_of_cases) {
// Here, we specify the timeout (60s) and the host test (a built-in host test
// or the name of our Python file)
GREENTEA_SETUP(60, "default_auto");
return greentea_test_setup_handler(number_of_cases);
@ -51,4 +52,7 @@ static Case cases[] = {Case("always succeed test", always_succeed)};
static Specification specification(greentea_setup, cases);
int main() { return !Harness::run(specification); }
}; // namespace v1
}; // namespace utest
int main() { return !utest::v1::Harness::run(utest::v1::specification); }

View File

@ -15,19 +15,23 @@
/****************************************************************************
* @file main.cpp
* @author Serge Ayer <serge.ayer@hefr.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
* @author Rémi Heredero <remi@heredero.ch>
*
* @brief Simple example of test program for raw and shared pointers
*
* @date 2022-09-01
* @version 0.1.0
* @date 2024-11-02
* @version 0.2.0
***************************************************************************/
#include "greentea-client/test_env.h"
#include "mbed.h"
#include "unity/unity.h"
#include "utest/utest.h"
#include "greentea-client/test_env.h" // NOLINT
#include "mbed.h" // NOLINT
#include "unity/unity.h" // NOLINT
#include "utest/utest.h" // NOLINT
namespace utest {
namespace v1 {
using namespace utest::v1;
struct Test {
Test() {
_instanceCount++;
@ -46,7 +50,8 @@ struct Test {
uint32_t Test::_instanceCount = 0;
/**
* Test that a shared pointer correctly manages the lifetime of the underlying raw pointer
* Test that a shared pointer correctly manages the lifetime of the underlying
* raw pointer
*/
void test_single_sharedptr_lifetime() {
// Sanity-check value of counter
@ -64,8 +69,8 @@ void test_single_sharedptr_lifetime() {
}
/**
* Test that multiple instances of shared pointers correctly manage the reference count
* to release the object at the correct point
* Test that multiple instances of shared pointers correctly manage the
* reference count to release the object at the correct point
*/
void test_instance_sharing() {
std::shared_ptr<Test> shared_ptr1(nullptr);
@ -94,18 +99,195 @@ void test_instance_sharing() {
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
static utest::v1::status_t greentea_setup(const size_t number_of_cases) {
// Here, we specify the timeout (60s) and the host test (a built-in host test or the
// name of our Python file)
/**********************
* UNIQUE PTR EXERCISE *
**********************/
/*
* Check normal lifetime on a unique_ptr
*/
void test_single_unique_ptr_lifetime() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
// create and destroy a unique_ptr
{
std::unique_ptr<Test> p1 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
const uint32_t number = 42;
p1->_value = number;
TEST_ASSERT_EQUAL(number, p1->_value);
p1.reset();
TEST_ASSERT(!p1);
}
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
/*
* Check transfer on a unique_ptr
*/
void test_unique_ptr_transfer() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
{
// create p1
std::unique_ptr<Test> p1 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
// transfer p1 to p2
std::unique_ptr<Test> p2 = std::move(p1);
TEST_ASSERT_EQUAL(Test::kMagicNumber, p2->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
p2.reset();
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
TEST_ASSERT(!p1); // cppcheck-suppress accessMoved
TEST_ASSERT(!p2);
}
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
/*
* Check the release of a unique ptr
*/
void test_unique_ptr_release() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
{
// create p1
std::unique_ptr<Test> p1 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
// transfer and release p1 to p2
Test* p2 = p1.release();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p2->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
delete p2;
p2 = nullptr;
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
TEST_ASSERT(!p1);
TEST_ASSERT(!p2);
}
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
/*
* Check the swap of 2 unique ptr
*/
void test_unique_ptr_swap() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
{
const uint32_t number1 = 65;
const uint32_t number2 = 42;
// create p1
std::unique_ptr<Test> p1 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
p1->_value = number1;
TEST_ASSERT_EQUAL(number1, p1->_value);
// create p2
std::unique_ptr<Test> p2 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p2->_value);
TEST_ASSERT_EQUAL(2, Test::_instanceCount);
p2->_value = number2;
TEST_ASSERT_EQUAL(number2, p2->_value);
// swap
p1.swap(p2);
TEST_ASSERT_EQUAL(number1, p2->_value);
TEST_ASSERT_EQUAL(number2, p1->_value);
p1.reset();
p2.reset();
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
TEST_ASSERT(!p1);
TEST_ASSERT(!p2);
}
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
/*******************
* RAW PTR EXERCISE *
*******************/
/**
* Test that a shared pointer correctly manages the lifetime of the underlying
* raw pointer
*/
void test_single_raw_ptr_lifetime() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
// Create and destroy raw pointer in given scope
{
Test t1;
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
TEST_ASSERT_EQUAL(Test::kMagicNumber, t1._value);
Test* p1 = &t1;
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
const uint32_t number1 = 42;
p1->_value = number1;
TEST_ASSERT_EQUAL(number1, p1->_value);
TEST_ASSERT_EQUAL(number1, t1._value);
p1 = nullptr;
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
TEST_ASSERT(!p1);
}
// Destroy shared pointer
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
static status_t greentea_setup(const size_t number_of_cases) {
// Here, we specify the timeout (60s) and the host test (a built-in host test
// or the name of our Python file)
GREENTEA_SETUP(60, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}
// List of test cases in this file
static Case cases[] = {
// Shared test pointer
Case("Test single shared pointer instance", test_single_sharedptr_lifetime),
Case("Test instance sharing across multiple shared pointers", test_instance_sharing)};
Case("Test instance sharing across multiple shared pointers", test_instance_sharing),
// Unique test pointer
Case("Test single unique pointer instance", test_single_unique_ptr_lifetime),
Case("Test transfer of unique pointer instance", test_unique_ptr_transfer),
Case("Test release of unique pointer instance", test_unique_ptr_release),
Case("Test swap of 2 unique ptr instance", test_unique_ptr_swap),
// Raw test pointer
Case("Test single raw pointer instance", test_single_raw_ptr_lifetime),
};
static Specification specification(greentea_setup, cases);
int main() { return !Harness::run(specification); }
}; // namespace v1
}; // namespace utest
int main() { return !utest::v1::Harness::run(utest::v1::specification); }

View File

@ -1,96 +0,0 @@
// 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 main.cpp
* @author Sierro Yann
*
* @brief Test of raw pointer
*
* @date 2022-09-01
* @version 0.1.0
***************************************************************************/
#include "greentea-client/test_env.h"
#include "mbed.h"
#include "unity/unity.h"
#include "utest/utest.h"
using namespace utest::v1;
struct Test {
Test() {
_instanceCount++;
_value = kMagicNumber;
}
~Test() {
_instanceCount--;
_value = 0;
}
int _value;
static constexpr uint32_t kMagicNumber = 33;
static uint32_t _instanceCount;
};
uint32_t Test::_instanceCount = 0;
/**
* Test that a shared pointer correctly manages the lifetime of the underlying raw pointer
*/
void test_single_raw_ptr_lifetime() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
// Create and destroy raw pointer in given scope
{
Test t1;
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
TEST_ASSERT_EQUAL(Test::kMagicNumber, t1._value);
Test * p1 = &t1;
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
const uint32_t number1 = 42;
p1->_value = number1;
TEST_ASSERT_EQUAL(number1, p1->_value);
TEST_ASSERT_EQUAL(number1, t1._value);
p1 = nullptr;
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
TEST_ASSERT(!p1);
t1.~Test();
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
// Destroy shared pointer
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
static utest::v1::status_t greentea_setup(const size_t number_of_cases) {
// Here, we specify the timeout (60s) and the host test (a built-in host test or the
// name of our Python file)
GREENTEA_SETUP(60, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}
// List of test cases in this file
static Case cases[] = {
Case("Test single raw pointer instance", test_single_raw_ptr_lifetime)};
static Specification specification(greentea_setup, cases);
int main() { return !Harness::run(specification); }

View File

@ -1,189 +0,0 @@
// 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 main.cpp
* @author Sierro Yann
*
* @brief Test for std::unique_ptr template class
*
* @date 2024-10-20
* @version 0.1.0
***************************************************************************/
#include "greentea-client/test_env.h"
#include "mbed.h"
#include "unity/unity.h"
#include "utest/utest.h"
#include <memory>
using namespace utest::v1;
struct Test {
Test() {
_instanceCount++;
_value = kMagicNumber;
}
~Test() {
_instanceCount--;
_value = 0;
}
uint32_t _value;
static constexpr uint32_t kMagicNumber = 33;
static uint32_t _instanceCount;
};
uint32_t Test::_instanceCount = 0;
/*
* Check normal lifetime on a unique_ptr
*/
void test_single_unique_ptr_lifetime() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
// create and destroy a unique_ptr
{
std::unique_ptr<Test> p1 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
const uint32_t number = 42;
p1->_value = number;
TEST_ASSERT_EQUAL(number, p1->_value);
p1.reset();
TEST_ASSERT(!p1);
}
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
/*
* Check transfer on a unique_ptr
*/
void test_unique_ptr_transfer() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
{
//create p1
std::unique_ptr<Test> p1 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
//transfer p1 to p2
std::unique_ptr<Test> p2 = std::move(p1);
TEST_ASSERT_EQUAL(Test::kMagicNumber, p2->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
p2.reset();
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
TEST_ASSERT(!p1);
TEST_ASSERT(!p2);
}
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
/*
* Check the release of a unique ptr
*/
void test_unique_ptr_release() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
{
//create p1
std::unique_ptr<Test> p1 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
//transfer and release p1 to p2
Test * p2 = p1.release();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p2->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
delete p2;
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
TEST_ASSERT(!p1);
TEST_ASSERT(!p2);
}
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
/*
* Check the swap of 2 unique ptr
*/
void test_unique_ptr_swap() {
// Sanity-check value of counter
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
{
const uint32_t number1 = 65;
const uint32_t number2 = 42;
//create p1
std::unique_ptr<Test> p1 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p1->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
p1->_value = number1;
TEST_ASSERT_EQUAL(number1, p1->_value);
//create p2
std::unique_ptr<Test> p2 = std::make_unique<Test>();
TEST_ASSERT_EQUAL(Test::kMagicNumber, p2->_value);
TEST_ASSERT_EQUAL(1, Test::_instanceCount);
p2->_value = number2;
TEST_ASSERT_EQUAL(number2, p2->_value);
//swap
p1.swap(p2);
TEST_ASSERT_EQUAL(number1, p2->_value);
TEST_ASSERT_EQUAL(number2, p1->_value);
p1.reset();
p2.reset();
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
TEST_ASSERT(!p1);
TEST_ASSERT(!p2);
}
TEST_ASSERT_EQUAL(0, Test::_instanceCount);
}
static utest::v1::status_t greentea_setup(const size_t number_of_cases) {
// Here, we specify the timeout (60s) and the host test (a built-in host test or the
// name of our Python file)
GREENTEA_SETUP(60, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}
// List of test cases in this file
static Case cases[] = {
Case("Test single unique pointer instance", test_single_unique_ptr_lifetime),
Case("Test transfer of unique pointer instance", test_unique_ptr_transfer),
Case("Test release of unique pointer instance", test_unique_ptr_release),
Case("Test swap of 2 unique ptr instance", test_unique_ptr_swap)
};
static Specification specification(greentea_setup, cases);
int main() { return !Harness::run(specification); }

1
advdembsof_library.lib Normal file
View File

@ -0,0 +1 @@
https://github.com/SergeAyer/advdembsof_library/#c0687fc795de83780fa9e7e935c9f465d697cc2a

59
common/constants.hpp Normal file
View File

@ -0,0 +1,59 @@
// 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 constants.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Constants definition used for implementing the bike system
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#pragma once
#include <stdint.h>
#include "mbed.h"
namespace bike_computer {
// gear related constants
static constexpr uint8_t kMinGear = 1;
static constexpr uint8_t kMaxGear = 9;
// smallest gear (= 1) corresponds to a gear size of 20
// when the gear increases, the gear size descreases
static constexpr uint8_t kMaxGearSize = 20;
static constexpr uint8_t kMinGearSize = kMaxGearSize - kMaxGear;
// pedal related constants
// When compiling and linking with gcc, we get a link error when using static
// constexpr. The error is related to template instantiation.
// definition of pedal rotation initial time (corresponds to 80 turn / min)
static constexpr std::chrono::milliseconds kInitialPedalRotationTime = 750ms;
// definition of pedal minimal rotation time (corresponds to 160 turn / min)
static constexpr std::chrono::milliseconds kMinPedalRotationTime = 375ms;
// definition of pedal maximal rotation time (corresponds to 10 turn / min)
static constexpr std::chrono::milliseconds kMaxPedalRotationTime = 1500ms;
// definition of pedal rotation time change upon acceleration/deceleration
static constexpr std::chrono::milliseconds kDeltaPedalRotationTime = 25ms;
static constexpr uint32_t kNbrOfSteps = static_cast<uint32_t>(
(bike_computer::kMaxPedalRotationTime - bike_computer::kMinPedalRotationTime)
.count() /
bike_computer::kDeltaPedalRotationTime.count());
} // namespace bike_computer

37
common/sensor_device.cpp Normal file
View File

@ -0,0 +1,37 @@
// 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 speedometer_device.cpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief WheelCounterDevice implementation (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#include "sensor_device.hpp"
namespace bike_computer {
SensorDevice::SensorDevice() : _hdc1000(I2C_SDA, I2C_SCL, STMOD_11) {}
bool SensorDevice::init() { return this->_hdc1000.probe(); }
float SensorDevice::readTemperature(void) { return this->_hdc1000.getTemperature(); }
float SensorDevice::readHumidity(void) { return this->_hdc1000.getHumidity(); }
} // namespace bike_computer

49
common/sensor_device.hpp Normal file
View File

@ -0,0 +1,49 @@
// 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 sensor_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief SensorDevice header file (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#pragma once
#include "hdc1000.hpp"
#include "mbed.h"
namespace bike_computer {
class SensorDevice {
public:
// constructor
SensorDevice();
// method for initializing the device
bool init();
// methods used for
float readTemperature(void);
float readHumidity(void);
private:
// data members
advembsof::HDC1000 _hdc1000;
};
} // namespace bike_computer

151
common/speedometer.cpp Normal file
View File

@ -0,0 +1,151 @@
// 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 speedometer_device.cpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief WheelCounterDevice implementation (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#include "speedometer.hpp"
#include <chrono>
#include <cstddef>
#include <ratio>
// from disco_h747i/wrappers
#include "joystick.hpp"
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "Speedometer"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace bike_computer {
Speedometer::Speedometer(Timer& timer) : _timer(timer) {
// update _lastTime
_lastTime = _timer.elapsed_time();
}
void Speedometer::setCurrentRotationTime(
const std::chrono::milliseconds& currentRotationTime) {
if (_pedalRotationTime != currentRotationTime) {
// compute distance before changing the rotation time
computeDistance();
// change pedal rotation time
_pedalRotationTime = currentRotationTime;
// compute speed with the new pedal rotation time
computeSpeed();
}
}
void Speedometer::setGearSize(uint8_t gearSize) {
if (_gearSize != gearSize) {
// compute distance before chaning the gear size
computeDistance();
// change gear size
_gearSize = gearSize;
// compute speed with the new gear size
computeSpeed();
}
}
float Speedometer::getCurrentSpeed() const { return _currentSpeed; }
float Speedometer::getDistance() {
// make sure to update the distance traveled
computeDistance();
return _totalDistance;
}
void Speedometer::reset() {
#if defined(MBED_TEST_MODE)
if (_cbOnReset != NULL) {
_cbOnReset();
}
#endif
this->_totalDistanceMutex.lock();
this->_totalDistance = 0.0f;
this->_totalDistanceMutex.unlock();
}
#if defined(MBED_TEST_MODE)
uint8_t Speedometer::getGearSize() const { return _gearSize; }
float Speedometer::getWheelCircumference() const { return kWheelCircumference; }
float Speedometer::getTraySize() const { return kTraySize; }
std::chrono::milliseconds Speedometer::getCurrentPedalRotationTime() const {
return _pedalRotationTime;
}
void Speedometer::setOnResetCallback(mbed::Callback<void()> cb) { _cbOnReset = cb; }
#endif // defined(MBED_TEST_MODE)
void Speedometer::computeSpeed() {
// For computing the speed given a rear gear (braquet), one must divide the size of
// the tray (plateau) by the size of the rear gear (pignon arrière), and then multiply
// the result by the circumference of the wheel. Example: tray = 50, rear gear = 15.
// Distance run with one pedal turn (wheel circumference = 2.10 m) = 50/15 * 2.1 m
// = 6.99m If you ride at 80 pedal turns / min, you run a distance of 6.99 * 80 / min
// ~= 560 m / min = 33.6 km/h
// Distance run with one pedal turn = tray size / rear gear size * circumference of
// the wheel
constexpr float ms_in_hour = static_cast<float>(3600 * 1000);
float pedal_rotation_per_hour =
ms_in_hour /
std::chrono::duration_cast<std::chrono::milliseconds>(_pedalRotationTime).count();
float gear_ratio =
static_cast<float>(kTraySize) / static_cast<float>(this->_gearSize);
float wheel_dist_km = static_cast<float>(this->kWheelCircumference) / 1000.0;
this->_currentSpeed = gear_ratio * wheel_dist_km * pedal_rotation_per_hour;
}
void Speedometer::computeDistance() {
// For computing the speed given a rear gear (braquet), one must divide the size of
// the tray (plateau) by the size of the rear gear (pignon arrière), and then multiply
// the result by the circumference of the wheel. Example: tray = 50, rear gear = 15.
// Distance run with one pedal turn (wheel circumference = 2.10 m) = 50/15 * 2.1 m
// = 6.99m If you ride at 80 pedal turns / min, you run a distance of 6.99 * 80 / min
// ~= 560 m / min = 33.6 km/h. We then multiply the speed by the time for getting the
// distance traveled.
Speedometer::computeSpeed();
// compute distance
const std::chrono::microseconds timeNow = _timer.elapsed_time();
const std::chrono::microseconds timeDiff = timeNow - _lastTime;
constexpr float ms_in_hour = static_cast<float>(3600 * 1000);
float traveled_dist = _currentSpeed * timeDiff.count() / (ms_in_hour * 1000.0 /*μs*/);
this->_totalDistanceMutex.lock();
this->_totalDistance += traveled_dist;
this->_totalDistanceMutex.unlock();
_lastTime = _timer.elapsed_time();
}
} // namespace bike_computer

90
common/speedometer.hpp Normal file
View File

@ -0,0 +1,90 @@
// 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 speedometer_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief WheelCounterDevice header file (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#pragma once
#include "constants.hpp"
#include "mbed.h"
namespace bike_computer {
class Speedometer {
public:
explicit Speedometer(Timer& timer); // NOLINT(runtime/references)
// method used for setting the current pedal rotation time
void setCurrentRotationTime(const std::chrono::milliseconds& currentRotationTime);
// method used for setting/getting the current gear
void setGearSize(uint8_t gearSize);
// method called for getting the current speed (expressed in km / h)
float getCurrentSpeed() const;
// method called for getting the current traveled distance (expressed in km)
float getDistance();
// method called for resetting the traveled distance
void reset();
// methods used for tests only
#if defined(MBED_TEST_MODE)
uint8_t getGearSize() const;
float getWheelCircumference() const;
float getTraySize() const;
std::chrono::milliseconds getCurrentPedalRotationTime() const;
void setOnResetCallback(mbed::Callback<void()> cb);
mbed::Callback<void()> _cbOnReset = NULL;
#endif // defined(MBED_TEST_MODE)
private:
// private methods
void computeSpeed();
void computeDistance();
// definition of task period time
static constexpr std::chrono::milliseconds kTaskPeriod = 400ms;
// definition of task execution time
static constexpr std::chrono::microseconds kTaskRunTime = 200000us;
// constants related to speed computation
static constexpr float kWheelCircumference = 2.1f;
static constexpr uint8_t kTraySize = 50;
std::chrono::microseconds _lastTime = std::chrono::microseconds::zero();
std::chrono::milliseconds _pedalRotationTime = kInitialPedalRotationTime;
// data members
Timer& _timer;
LowPowerTicker _ticker;
float _currentSpeed = 0.0f;
Mutex _totalDistanceMutex;
float _totalDistance = 0.0f;
uint8_t _gearSize = bike_computer::kMaxGearSize;
#if defined(MBED_TEST_MODE)
mbed::Callback<void()> _cb;
#endif
};
} // namespace bike_computer

View File

@ -5,20 +5,29 @@
#if !MBED_TEST_MODE
#include "mbed.h" // NOLINT
#include "mbed.h" // NOLINT
#include "mbed_trace.h" // NOLINT
// #include "static_scheduling/bike_system.hpp"
#include "static_scheduling_with_event/bike_system.hpp"
// #include "multi_tasking/bike_system.hpp"
// Blinking rate in milliseconds
#define BLINKING_RATE 500ms
#if defined(MBED_CONF_MBED_TRACE_ENABLE)
#define TRACE_GROUP "MAIN"
#endif // MBED_CONF_MBED_TRACE_ENAB
int main() {
// Initialise the digital pin LED1 as an output
#if defined(MBED_CONF_MBED_TRACE_ENABLE)
mbed_trace_init();
#endif
DigitalOut led(LED1);
// static_scheduling::BikeSystem bikeSystem;
// bikeSystem.start();
while (true) {
led = !led;
ThisThread::sleep_for(BLINKING_RATE);
}
static_scheduling_with_event::BikeSystem bikeSystem;
bikeSystem.start();
// multi_tasking::BikeSystem bikeSystem;
// bikeSystem.start();
}
#endif // MBED_TEST_MODE

View File

@ -0,0 +1,12 @@
---
BasedOnStyle: Google
IndentWidth: 4
---
Language: Cpp
ColumnLimit: 90
AlignConsecutiveAssignments: true
DerivePointerAlignment: false
PointerAlignment: Left
BinPackArguments: false
BinPackParameters: false
IndentAccessModifiers: false

8
mbed-os-bootloader/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.build
.mbed
projectfiles
*.py*
mbed-os
BUILD
!BUILD/DISCO_H747I/GCC_ARM/mbed-os-bootloader.bin

View File

@ -0,0 +1,17 @@
mbed-os/drivers/device_key/*
mbed-os/drivers/source/usb/USBMSD.cpp
mbed-os/drivers/source/SFDP.cpp
mbed-os/connectivity/cellular/*
mbed-os/connectivity/drivers/*
mbed-os/connectivity/FEATURE_BLE/*
mbed-os/connectivity/libraries/*
mbed-os/connectivity/lorawan/*
mbed-os/connectivity/lwipstack/*
mbed-os/connectivity/nanostack/*
mbed-os/connectivity/netsocket/*
mbed-os/connectivity/nfc/*
mbed-os/features/FEATURE_BOOTLOADER/*
mbed-os/features/frameworks/mbed-client-cli/*
mbed-os/features/frameworks/COMPONENT_FPGA_CI_TEST_SHIELD/*
mbed-os/platform/randlib/*
mbed-os/storage/kvstore/*

View File

@ -0,0 +1,26 @@
files: ^main.cpp
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-yaml
args: [--allow-multiple-documents]
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v14.0.6"
hooks:
- id: clang-format
- repo: https://github.com/cpplint/cpplint
rev: "1.6.1"
hooks:
- id: cpplint
name: cpplint
entry: cpplint --linelength=90 --filter=-build/include_subdir,-whitespace/indent,-build/namespaces,-build/c++11
- repo: local
hooks:
- id: cppcheck
name: cppcheck
require_serial: true
entry: cppcheck --enable=all --suppress=missingInclude --inline-suppr -i mbed-os --std=c++14 --error-exitcode=1
language: system

View File

@ -0,0 +1,35 @@
#include "mbed.h"
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "bootloader"
#endif // MBED_CONF_MBED_TRACE_ENABLE
#if MBED_CONF_MBED_TRACE_ENABLE
static UnbufferedSerial g_uart(CONSOLE_TX, CONSOLE_RX);
// Function that directly outputs to an unbuffered serial port in blocking mode.
static void boot_debug(const char *s) {
size_t len = strlen(s);
g_uart.write(s, len);
g_uart.write("\r\n", 2);
}
#endif
int main() {
#if MBED_CONF_MBED_TRACE_ENABLE
mbed_trace_init();
mbed_trace_print_function_set(boot_debug);
#endif // MBED_CONF_MBED_TRACE_ENABLE
tr_debug("BikeComputer bootloader\r\n");
// at this stage we directly branch to the main application
void *sp = *((void **) POST_APPLICATION_ADDR + 0); // NOLINT(readability/casting)
void *pc = *((void **) POST_APPLICATION_ADDR + 1); // NOLINT(readability/casting)
tr_debug("Starting application at address 0x%08x (sp 0x%08x, pc 0x%08x)\r\n", POST_APPLICATION_ADDR, (uint32_t) sp, (uint32_t) pc);
mbed_start_application(POST_APPLICATION_ADDR);
return 0;
}

View File

@ -0,0 +1 @@
https://github.com/ARMmbed/mbed-os.git#17dc3dc2e6e2817a8bd3df62f38583319f0e4fed

View File

@ -0,0 +1,28 @@
{
"macros": [
"MBED_CONF_MBED_TRACE_FEA_IPV6=0"
],
"config": {
"main-stack-size": {
"value": 4096
}
},
"target_overrides": {
"*": {
"mbed-trace.enable": false,
"platform.stdio-convert-newlines": true,
"platform.stdio-baud-rate": 115200,
"platform.default-serial-baud-rate": 115200,
"platform.stdio-buffered-serial": true,
"platform.all-stats-enabled": true,
"target.printf_lib":"minimal-printf",
"platform.minimal-printf-enable-floating-point": true,
"platform.minimal-printf-set-floating-point-max-decimals": 2
},
"DISCO_H747I": {
"target.restrict_size": "0x20000",
"mbed-trace.enable": true,
"mbed-trace.max-level": "TRACE_LEVEL_DEBUG"
}
}
}

View File

@ -1,26 +1,27 @@
{
"macros": [
"MBED_CONF_MBED_TRACE_FEA_IPV6=0"
],
"config": {
"main-stack-size": {
"value": 4096
}
},
"target_overrides": {
"*": {
"mbed-trace.enable": false,
"platform.stdio-convert-newlines": true,
"platform.stdio-baud-rate": 115200,
"platform.default-serial-baud-rate": 115200,
"platform.stdio-buffered-serial": true,
"target.printf_lib":"minimal-printf",
"platform.minimal-printf-enable-floating-point": true,
"platform.minimal-printf-set-floating-point-max-decimals": 2
},
"DISCO_H747I": {
"mbed-trace.enable": true,
"mbed-trace.max-level": "TRACE_LEVEL_DEBUG"
}
"macros": ["MBED_CONF_MBED_TRACE_FEA_IPV6=0"],
"config": {
"main-stack-size": {
"value": 8192
}
}
},
"target_overrides": {
"*": {
"mbed-trace.enable": false,
"platform.stdio-convert-newlines": true,
"platform.stdio-baud-rate": 115200,
"platform.default-serial-baud-rate": 115200,
"platform.stdio-buffered-serial": true,
"platform.all-stats-enabled": true,
"target.printf_lib": "minimal-printf",
"platform.minimal-printf-enable-floating-point": true,
"platform.minimal-printf-set-floating-point-max-decimals": 2
},
"DISCO_H747I": {
"mbed-trace.enable": true,
"mbed-trace.max-level": "TRACE_LEVEL_DEBUG",
"target.bootloader_img": "./mbed-os-bootloader/BUILD/DISCO_H747I/GCC_ARM/mbed-os-bootloader.bin",
"target.app_offset": "0x20000"
}
}
}

View File

@ -0,0 +1,406 @@
// 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 <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Bike System implementation (static scheduling)
*
* @date 2023-11-15
* @version 1.1.0
***************************************************************************/
#include "bike_system.hpp"
#include <chrono>
#include <cstdint>
#include "cmsis_os.h"
#include "common/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 kGearTaskComputationTime = 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 kTemperatureTaskPeriod = 1600ms;
static constexpr std::chrono::milliseconds kTemperatureTaskDelay = 1100ms;
static constexpr std::chrono::milliseconds kTemperatureTaskComputationTime = 100ms;
static constexpr std::chrono::milliseconds kCPUTaskPeriod = 1600ms;
static constexpr std::chrono::milliseconds kCPUTaskDelay = 0ms;
BikeSystem::BikeSystem()
: _timer(),
_isrEventQueue(),
_eventQueue(),
_mailPedalDevice(),
_mailGearDevice(),
_mutexGearSize(),
_mutexGear(),
_mutexSpeed(),
_mutexDistance(),
_mutexSpeedometer(),
_isrEventThread(osPriorityAboveNormal, OS_STACK_SIZE, nullptr, "ISR_Event"),
_speedDistanceThread(
osPriorityNormal, OS_STACK_SIZE, nullptr, "Speed_distance_Task"),
_gearTaskThread(osPriorityAboveNormal, OS_STACK_SIZE, nullptr, "Gear_Task"),
_gearDevice(&_mailGearDevice, _timer),
_pedalDevice(&_mailPedalDevice, _timer),
_resetDevice(callback(this, &BikeSystem::onReset)),
_displayDevice(),
_speedometer(_timer),
_sensorDevice(),
_taskLogger(),
_cpuLogger(_timer) {}
#if defined(MBED_TEST_MODE)
const advembsof::TaskLogger& BikeSystem::getTaskLogger() { return _taskLogger; }
bike_computer::Speedometer& BikeSystem::getSpeedometer() {
// ENTER CRITICAL SECTION
_mutexSpeedometer.lock();
bike_computer::Speedometer& speedometer = _speedometer;
_mutexSpeedometer.unlock();
// END CRITICAL SECTION
return speedometer;
}
GearDevice& BikeSystem::getGearDevice() { return _gearDevice; }
void BikeSystem::setCallbackGearChage(Callback<void()> cbGearChange) {
_cbGearChange = cbGearChange;
}
#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: %d", static_cast<int>(rc));
}
// initialize the sensor device
bool present = _sensorDevice.init();
if (!present) {
tr_error("Sensor not present or initialization failed");
}
// enable/disable task logging
bool runTaskLogger = false;
#if defined(MBED_TEST_MODE)
runTaskLogger = true;
#endif
_taskLogger.enable(runTaskLogger);
}
void BikeSystem::start() {
init();
Event<void()> temperatureEvent(&_eventQueue,
callback(this, &BikeSystem::temperatureTask));
temperatureEvent.delay(kTemperatureTaskDelay);
temperatureEvent.period(kTemperatureTaskPeriod);
temperatureEvent.post();
Event<void()> displayEvent(&_eventQueue, callback(this, &BikeSystem::displayTask));
displayEvent.delay(kDisplayTask1Delay);
displayEvent.period(kDisplayTask1Period);
displayEvent.post();
osStatus status =
_isrEventThread.start(callback(this, &BikeSystem::dispatch_isr_events));
if (status != osOK) {
tr_error("Thread %s started with status %ld",
_isrEventThread.get_name(),
static_cast<int32_t>(status));
}
status =
_speedDistanceThread.start(callback(this, &BikeSystem::loop_speed_distance_task));
if (status != osOK) {
tr_error("Thread %s started with status %ld",
_isrEventThread.get_name(),
static_cast<int32_t>(status));
}
status = _gearTaskThread.start(callback(this, &BikeSystem::loop_gear_task));
if (status != osOK) {
tr_error("Thread %s started with status %ld",
_gearTaskThread.get_name(),
static_cast<int32_t>(status));
}
#if !defined(MBED_TEST_MODE)
Event<void()> cpuEvent(&_eventQueue, callback(this, &BikeSystem::cpuTask));
cpuEvent.delay(kCPUTaskDelay);
cpuEvent.period(kCPUTaskPeriod);
cpuEvent.post();
#endif
// dispatch the main queue in the main thread
dispatch_events();
}
void BikeSystem::stop() {
osStatus status = _isrEventThread.terminate();
status += _speedDistanceThread.terminate();
status += _gearTaskThread.terminate();
if (status != 0) {
tr_error("Stop thread error");
}
tr_info("Bike system has stopped !");
}
/* Callback from isr */
void BikeSystem::onReset() {
_resetTime = _timer.elapsed_time();
Event<void()> resetEvent(&_isrEventQueue, callback(this, &BikeSystem::resetTask));
resetEvent.post();
}
// ISR thread functions
void BikeSystem::resetTask() {
#if !defined(MBED_TEST_MODE)
auto taskStartTime = _timer.elapsed_time();
std::chrono::microseconds responseTime = _timer.elapsed_time() - _resetTime;
tr_info("Reset task: response time is %" PRIu64 " usecs", responseTime.count());
#endif
// ENTER CRITICAL SECTION
_mutexSpeedometer.lock();
_speedometer.reset();
_mutexSpeedometer.unlock();
// END CRITICAL SECTION
#if !defined(MBED_TEST_MODE)
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kResetTaskIndex, taskStartTime);
#endif
}
// Speed distance thread functions
void BikeSystem::speedDistanceTask() {
auto taskStartTime = _timer.elapsed_time();
pedalMail_t* currentStep = _mailPedalDevice.try_get();
if (currentStep != nullptr) {
const auto pedalRotationTime =
PedalDevice::getCurrentRotationTime(currentStep->step);
// ENTER CRITICAL SECTION
_mutexSpeedometer.lock();
_speedometer.setCurrentRotationTime(pedalRotationTime);
_mutexSpeedometer.unlock();
// END CRITICAL SECTION
std::chrono::microseconds responseTime =
_timer.elapsed_time() - currentStep->callTime;
tr_info("Speed distance task: response time is %" PRIu64 " usecs",
responseTime.count());
osStatus status = _mailPedalDevice.free(currentStep);
if (status != osOK) {
tr_error("free current step in the speed distance tasks doesn't work !");
}
}
// ENTER CRITICAL SECTION
_mutexSpeedometer.lock();
_speedometer.setGearSize(getCurrentGearSize());
_mutexSpeed.lock();
_currentSpeed = _speedometer.getCurrentSpeed();
_mutexSpeed.unlock();
_mutexDistance.lock();
_traveledDistance = _speedometer.getDistance();
_mutexDistance.unlock();
_mutexSpeedometer.unlock();
// END CRITICAL SECTION
ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(
kSpeedDistanceTaskComputationTime - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kSpeedTaskIndex, taskStartTime);
}
/* Gear thread functions */
void BikeSystem::gearTask() {
auto taskStartTime = _timer.elapsed_time();
gearMail_t* currentGear = _mailGearDevice.try_get();
if (currentGear != nullptr) {
#if !defined(MBED_TEST_MODE)
std::chrono::microseconds responseTime =
_timer.elapsed_time() - currentGear->callTime;
tr_info("Gear task: response time is %" PRIu64 " usecs", responseTime.count());
#endif
#if defined(MBED_TEST_MODE)
_cbGearChange();
#endif
// ENTER CRITICAL SECTION
_mutexGear.lock();
_currentGear = currentGear->gear;
_mutexGear.unlock();
_mutexGearSize.lock();
_currentGearSize = bike_computer::kMaxGearSize - currentGear->gear;
_mutexGearSize.unlock();
// END CRITICAL SECTION
osStatus status = _mailGearDevice.free(currentGear);
if (status != osOK) {
tr_error("free current gear in the gear tasks doesn't work !");
}
}
ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(
kGearTaskComputationTime - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kGearTaskIndex, taskStartTime);
}
/* Main thread functions */
void BikeSystem::temperatureTask() {
auto taskStartTime = _timer.elapsed_time();
// no need to protect access to data members (single threaded)
_currentTemperature = _sensorDevice.readTemperature();
ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(
kTemperatureTaskComputationTime - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kTemperatureTaskIndex, taskStartTime);
}
void BikeSystem::displayTask() {
auto taskStartTime = _timer.elapsed_time();
// ENTER CRITICAL SECTION
_displayDevice.displayGear(getCurrentGear());
_displayDevice.displaySpeed(getCurrentSpeed());
_displayDevice.displayDistance(getCurrentDistance());
// END CRITICAL SECTION
_displayDevice.displayTemperature(_currentTemperature);
// ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(kDisplayTask1ComputationTime
// - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kDisplayTask1Index, 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");
}
void BikeSystem::loop_speed_distance_task() {
tr_info("Start loop speed-distance calculation");
while (true) {
speedDistanceTask();
}
}
void BikeSystem::loop_gear_task() {
tr_info("Start loop gear calculation");
while (true) {
gearTask();
}
}
uint8_t BikeSystem::getCurrentGear() {
uint8_t currentGear;
// ENTER CRITICAL SECTION
_mutexGear.lock();
currentGear = _currentGear;
_mutexGear.unlock();
// END CRITICAL SECTION
return currentGear;
}
uint8_t BikeSystem::getCurrentGearSize() {
uint8_t currentGearSize;
// ENTER CRITICAL SECTION
_mutexGearSize.lock();
currentGearSize = _currentGearSize;
_mutexGearSize.unlock();
// END CRITICAL SECTION
return currentGearSize;
}
float BikeSystem::getCurrentSpeed() {
float currentSpeed;
// ENTER CRITICAL SECTION
_mutexSpeed.lock();
currentSpeed = _currentSpeed;
_mutexSpeed.unlock();
// END CRITICAL SECTION
return currentSpeed;
}
float BikeSystem::getCurrentDistance() {
float currentDistance;
// ENTER CRITICAL SECTION
_mutexDistance.lock();
currentDistance = _traveledDistance;
_mutexDistance.unlock();
// END CRITICAL SECTION
return currentDistance;
}
} // namespace multi_tasking

View File

@ -0,0 +1,171 @@
// 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.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Bike System header file (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#pragma once
// from advembsof
#include "cpu_logger.hpp"
#include "display_device.hpp"
#include "mbed.h"
#include "task_logger.hpp"
// from common
#include "sensor_device.hpp"
#include "speedometer.hpp"
// local
#include "gear_device.hpp"
#include "pedal_device.hpp"
#include "reset_device.hpp"
namespace multi_tasking {
class BikeSystem {
public:
// constructor
BikeSystem();
// make the class non copyable
BikeSystem(BikeSystem&) = delete;
BikeSystem& operator=(BikeSystem&) = delete;
// method called in main() for starting the system
void start();
// method called for stopping the system
void stop();
#if defined(MBED_TEST_MODE)
const advembsof::TaskLogger& getTaskLogger();
bike_computer::Speedometer& getSpeedometer();
GearDevice& getGearDevice();
void setCallbackGearChage(Callback<void()> cbGearChange);
Callback<void()> _cbGearChange;
#endif // defined(MBED_TEST_MODE)
private:
// private methods
void init();
// Main Thread
void temperatureTask();
void displayTask();
void cpuTask();
// ISR Thread
#if defined(MBED_TEST_MODE)
public:
#endif
void onReset();
#if defined(MBED_TEST_MODE)
private:
#endif
void resetTask();
// gear Thread
void gearTask();
// Speed / Distance Thread
void speedDistanceTask();
// GETTER - SETTER
uint8_t getCurrentGear();
uint8_t getCurrentGearSize();
float getCurrentSpeed();
float getCurrentDistance();
void setCurrentGear(uint8_t gear);
// Thread functions
void dispatch_isr_events();
void dispatch_events();
void loop_speed_distance_task();
void loop_gear_task();
// timer instance used for loggint task time and used by ResetDevice
std::chrono::microseconds _resetTime = std::chrono::microseconds::zero();
std::chrono::microseconds _onGearUpTime = std::chrono::microseconds::zero();
std::chrono::microseconds _onGearDownTime = std::chrono::microseconds::zero();
Timer _timer;
// Event queues
EventQueue _isrEventQueue;
EventQueue _eventQueue;
// Mail
Mail<pedalMail_t, 16> _mailPedalDevice;
Mail<gearMail_t, 16> _mailGearDevice;
// mutex for shared resource
Mutex _mutexGearSize;
Mutex _mutexGear;
Mutex _mutexSpeed;
Mutex _mutexDistance;
Mutex _mutexSpeedometer;
// Tread for isr events
Thread _isrEventThread;
Thread _speedDistanceThread;
Thread _gearTaskThread;
// data member that represents the device for manipulating the gear
GearDevice _gearDevice;
//////////////////////////////////////////////////////////////
// shared resources between the main thread and the isr thread
uint8_t _currentGear = bike_computer::kMinGear;
uint8_t _currentGearSize = bike_computer::kMaxGearSize;
//////////////////////////////////////////////////////////////
// data member that represents the device for manipulating the pedal rotation
// speed/time
PedalDevice _pedalDevice;
//////////////////////////////////////////////////////////////
// shared resources between the main thread and the isr thread
float _currentSpeed = 0.0f;
float _traveledDistance = 0.0f;
//////////////////////////////////////////////////////////////
// data member that represents the device used for resetting
ResetDevice _resetDevice;
// data member that represents the device display
advembsof::DisplayDevice _displayDevice;
// data member that represents the device for counting wheel rotations
bike_computer::Speedometer _speedometer;
// data member that represents the sensor device
bike_computer::SensorDevice _sensorDevice;
float _currentTemperature = 0.0f;
// used for logging task info
advembsof::TaskLogger _taskLogger;
// cpu logger to measure cpu usage
advembsof::CPULogger _cpuLogger;
};
} // namespace multi_tasking

View File

@ -0,0 +1,74 @@
// 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 gear_device.cpp
* @author Serge Ayer <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Gear Device implementation (static scheduling)
*
* @date 2023-11-17
* @version 1.1.0
***************************************************************************/
#include "gear_device.hpp"
#include "bike_system.hpp"
// from disco_h747i/wrappers
#include "joystick.hpp"
#include "mbed_atomic.h"
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "GearDevice"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace multi_tasking {
GearDevice::GearDevice(Mail<gearMail_t, 16>* mailBox, Timer& timer)
: _mailBox(mailBox), _timer(timer) {
disco::Joystick::getInstance().setUpCallback(callback(this, &GearDevice::onUp));
disco::Joystick::getInstance().setDownCallback(callback(this, &GearDevice::onDown));
}
void GearDevice::onUp() {
if (_currentGear < bike_computer::kMaxGear) {
core_util_atomic_incr_u8(&_currentGear, 1);
sendMail(core_util_atomic_load_u8(&_currentGear));
}
}
void GearDevice::onDown() {
if (_currentGear > bike_computer::kMinGear) {
core_util_atomic_decr_u8(&_currentGear, 1);
sendMail(core_util_atomic_load_u8(&_currentGear));
}
}
void GearDevice::sendMail(uint32_t data) {
if (_mailBox != nullptr) {
gearMail_t* currentGear = _mailBox->try_alloc();
if (currentGear != nullptr) {
currentGear->gear = data;
currentGear->callTime = _timer.elapsed_time();
_mailBox->put(currentGear);
}
}
}
} // namespace multi_tasking

View File

@ -0,0 +1,61 @@
// 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 gear_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Gear Device header file (static scheduling)
*
* @date 2023-11-17
* @version 1.1.0
***************************************************************************/
#pragma once
#include "common/constants.hpp"
#include "mbed.h"
namespace multi_tasking {
typedef struct gearMail {
uint8_t gear;
std::chrono::microseconds callTime;
} gearMail_t;
class GearDevice {
public:
explicit GearDevice(Mail<gearMail_t, 16>* mailBox,
Timer& timer); // NOLINT(runtime/references)
// make the class non copyable
GearDevice(GearDevice&) = delete;
GearDevice& operator=(GearDevice&) = delete;
// method called for updating the bike system
void onUp();
void onDown();
void sendMail(uint32_t data);
private:
// data members
volatile uint8_t _currentGear = bike_computer::kMinGear;
Mail<gearMail_t, 16>* _mailBox;
Timer& _timer;
};
} // namespace multi_tasking

View File

@ -0,0 +1,85 @@
// 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 pedal_device.cpp
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Pedal Device implementation (static scheduling)
* @date 2024-11-17
* @version 1.1.0
****************************************************************************/
#include "pedal_device.hpp"
// from disco_h747i/wrappers
#include <chrono>
#include "bike_system.hpp"
#include "joystick.hpp"
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "PedalDevice"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace multi_tasking {
PedalDevice::PedalDevice(Mail<pedalMail_t, 16>* mailBox, Timer& timer)
: _mailBox(mailBox), _timer(timer) {
disco::Joystick::getInstance().setLeftCallback(callback(this, &PedalDevice::onLeft));
disco::Joystick::getInstance().setRightCallback(
callback(this, &PedalDevice::onRight));
}
void PedalDevice::increaseRotationSpeed() {
uint32_t currentStep = core_util_atomic_load_u32(&_currentStep);
if (currentStep > 0) {
core_util_atomic_decr_u32(&_currentStep, 1);
sendMail(core_util_atomic_load_u32(&_currentStep));
}
}
void PedalDevice::decreaseRotationSpeed() {
uint32_t currentStep = core_util_atomic_load_u32(&_currentStep);
if (currentStep < bike_computer::kNbrOfSteps) {
core_util_atomic_incr_u32(&_currentStep, 1);
sendMail(core_util_atomic_load_u32(&_currentStep));
}
}
void PedalDevice::onLeft() { decreaseRotationSpeed(); }
void PedalDevice::onRight() { increaseRotationSpeed(); }
void PedalDevice::sendMail(uint32_t data) {
if (_mailBox != nullptr) {
pedalMail_t* currentStep = _mailBox->try_alloc();
if (currentStep != nullptr) {
currentStep->step = data;
currentStep->callTime = _timer.elapsed_time();
_mailBox->put(currentStep);
}
}
}
// static methods
std::chrono::milliseconds PedalDevice::getCurrentRotationTime(uint32_t step) {
return bike_computer::kMinPedalRotationTime +
step * bike_computer::kDeltaPedalRotationTime;
}
} // namespace multi_tasking

View File

@ -0,0 +1,69 @@
// 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 pedal_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Pedal System header file (static scheduling)
*
* @date 2023-11-17
* @version 1.1.0
***************************************************************************/
#pragma once
#include "constants.hpp"
#include "mbed.h"
namespace multi_tasking {
typedef struct pedalMail {
uint8_t step;
std::chrono::microseconds callTime;
} pedalMail_t;
class PedalDevice {
public:
explicit PedalDevice(Mail<pedalMail_t, 16>* mailBox, // NOLINT (runtime/references)
Timer& timer); // NOLINT (runtime/references)
// make the class non copyable
PedalDevice(PedalDevice&) = delete;
PedalDevice& operator=(PedalDevice&) = delete;
// method called for updating the bike system
static std::chrono::milliseconds getCurrentRotationTime(uint32_t step);
private:
// private methods
void onLeft();
void onRight();
void increaseRotationSpeed();
void decreaseRotationSpeed();
void sendMail(uint32_t data);
// data members
volatile uint32_t _currentStep = static_cast<uint32_t>(
(bike_computer::kInitialPedalRotationTime - bike_computer::kMinPedalRotationTime)
.count() /
bike_computer::kDeltaPedalRotationTime.count());
Mail<pedalMail_t, 16>* _mailBox;
Timer& _timer;
};
} // namespace multi_tasking

View File

@ -0,0 +1,48 @@
// 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 reset_device.cpp
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Reset Device implementation (static scheduling with event)
* @date 2024-11-17
* @version 1.1.0
****************************************************************************/
#include "reset_device.hpp"
// from disco_h747i/wrappers
#include <chrono>
#include "joystick.hpp"
#include "mbed_trace.h"
#if defined(TARGET_DISCO_H747I)
#define PUSH_BUTTON BUTTON1
static constexpr uint8_t kPolarityPressed = 1;
#endif
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "ResetDevice"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace multi_tasking {
ResetDevice::ResetDevice(Callback<void()> cb) : _resetButton(PUSH_BUTTON) {
_resetButton.fall(cb);
}
} // namespace multi_tasking

View File

@ -0,0 +1,47 @@
// 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 reset_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief ResetDevice header file (static scheduling with event)
*
* @date 2023-11-17
* @version 1.1.0
***************************************************************************/
#pragma once
#include "mbed.h"
namespace multi_tasking {
class ResetDevice {
public:
explicit ResetDevice(Callback<void()> cb); // NOLINT(runtime/references)
// make the class non copyable
ResetDevice(ResetDevice&) = delete;
ResetDevice& operator=(ResetDevice&) = delete;
private:
// data members
// instance representing the reset button
InterruptIn _resetButton;
};
} // namespace multi_tasking

View File

@ -0,0 +1,265 @@
// 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 <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Bike System implementation (static scheduling)
*
* @date 2023-11-15
* @version 1.1.0
***************************************************************************/
#include "bike_system.hpp"
#include <chrono>
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "BikeSystem"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace static_scheduling {
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(_timer),
_pedalDevice(_timer),
_resetDevice(_timer),
_displayDevice(),
_speedometer(_timer),
_sensorDevice(),
_taskLogger(),
_cpuLogger(_timer) {}
void BikeSystem::start() {
tr_info("Starting Super-Loop without event handling");
init();
while (true) {
auto startTime = _timer.elapsed_time();
gearTask(); // 100ms : 0ms -> 100ms
speedDistanceTask(); // 200ms : 100ms -> 300ms
displayTask1(); // 200ms : 300ms -> 500ms
speedDistanceTask(); // 200ms : 500ms -> 700ms
resetTask(); // 100ms : 700ms -> 800ms
gearTask(); // 100ms : 800ms -> 900ms
speedDistanceTask(); // 200ms : 900ms -> 1100ms
temperatureTask(); // 100ms : 1100ms -> 1200ms
displayTask2(); // 100ms : 1200ms -> 1300ms
speedDistanceTask(); // 200ms : 1300ms -> 1500ms
resetTask(); // 100ms : 1500ms -> 1600ms
// register the time at the end of the cyclic schedule period and print the
// elapsed time for the period
std::chrono::microseconds endTime = _timer.elapsed_time();
const auto cycle =
std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
tr_debug("Repeating cycle time is %" PRIu64 " milliseconds", cycle.count());
bool fStop = false;
core_util_atomic_load(&fStop);
if (fStop) {
break;
}
#if !defined(MBED_TEST_MODE)
_cpuLogger.printStats();
#endif
}
}
void BikeSystem::startWithEventQueue() {
tr_info("Starting Super-Loop with event handling");
init();
EventQueue eventQueue;
Event<void()> gearEvent(&eventQueue, callback(this, &BikeSystem::gearTask));
gearEvent.delay(kGearTaskDelay);
gearEvent.period(kGearTaskPeriod);
gearEvent.post();
Event<void()> speedDistanceEvent(&eventQueue,
callback(this, &BikeSystem::speedDistanceTask));
speedDistanceEvent.delay(kSpeedDistanceTaskDelay);
speedDistanceEvent.period(kSpeedDistanceTaskPeriod);
speedDistanceEvent.post();
Event<void()> display1Event(&eventQueue, callback(this, &BikeSystem::displayTask1));
display1Event.delay(kDisplayTask1Delay);
display1Event.period(kDisplayTask1Period);
display1Event.post();
Event<void()> resetEvent(&eventQueue, callback(this, &BikeSystem::resetTask));
resetEvent.delay(kResetTaskDelay);
resetEvent.period(kResetTaskPeriod);
resetEvent.post();
Event<void()> temperatureEvent(&eventQueue,
callback(this, &BikeSystem::temperatureTask));
temperatureEvent.delay(kTemperatureTaskDelay);
temperatureEvent.period(kTemperatureTaskPeriod);
temperatureEvent.post();
Event<void()> display2Event(&eventQueue, callback(this, &BikeSystem::displayTask2));
display2Event.delay(kDisplayTask2Delay);
display2Event.period(kDisplayTask2Period);
display2Event.post();
#if !defined(MBED_TEST_MODE)
Event<void()> cpuEvent(&eventQueue, callback(this, &BikeSystem::cpuTask));
cpuEvent.delay(kCPUTaskDelay);
cpuEvent.period(kCPUTaskPeriod);
cpuEvent.post();
#endif
eventQueue.dispatch_forever();
}
void BikeSystem::stop() { core_util_atomic_store_bool(&_stopFlag, true); }
#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("Failed to initialized the lcd display: %d", static_cast<int>(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(true);
}
void BikeSystem::gearTask() {
// gear task
auto taskStartTime = _timer.elapsed_time();
// no need to protect access to data members (single threaded)
_currentGear = _gearDevice.getCurrentGear();
_currentGearSize = _gearDevice.getCurrentGearSize();
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kGearTaskIndex, taskStartTime);
}
void BikeSystem::speedDistanceTask() {
// speed and distance task
auto taskStartTime = _timer.elapsed_time();
const auto pedalRotationTime = _pedalDevice.getCurrentRotationTime();
_speedometer.setCurrentRotationTime(pedalRotationTime);
_speedometer.setGearSize(_currentGearSize);
// no need to protect access to data members (single threaded)
_currentSpeed = _speedometer.getCurrentSpeed();
_traveledDistance = _speedometer.getDistance();
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kSpeedTaskIndex, taskStartTime);
}
void BikeSystem::displayTask1() {
auto taskStartTime = _timer.elapsed_time();
_displayDevice.displayGear(_currentGear);
_displayDevice.displaySpeed(_currentSpeed);
_displayDevice.displayDistance(_traveledDistance);
ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(
kDisplayTask1ComputationTime - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kDisplayTask1Index, taskStartTime);
}
void BikeSystem::resetTask() {
auto taskStartTime = _timer.elapsed_time();
if (_resetDevice.checkReset()) {
std::chrono::microseconds responseTime =
_timer.elapsed_time() - _resetDevice.getPressTime();
tr_info("Reset task: response time is %" PRIu64 " usecs", responseTime.count());
_speedometer.reset();
}
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kResetTaskIndex, taskStartTime);
}
void BikeSystem::temperatureTask() {
auto taskStartTime = _timer.elapsed_time();
// no need to protect access to data members (single threaded)
_currentTemperature = _sensorDevice.readTemperature();
ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(
kTemperatureTaskComputationTime - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kTemperatureTaskIndex, taskStartTime);
}
void BikeSystem::displayTask2() {
auto taskStartTime = _timer.elapsed_time();
_displayDevice.displayTemperature(_currentTemperature);
ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(
kDisplayTask2ComputationTime - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kDisplayTask2Index, taskStartTime);
}
void BikeSystem::cpuTask() { _cpuLogger.printStats(); }
} // namespace static_scheduling

View File

@ -0,0 +1,106 @@
// 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.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Bike System header file (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#pragma once
// from advembsof
#include "cpu_logger.hpp"
#include "display_device.hpp"
#include "task_logger.hpp"
// from common
#include "sensor_device.hpp"
#include "speedometer.hpp"
// local
#include "gear_device.hpp"
#include "pedal_device.hpp"
#include "reset_device.hpp"
namespace static_scheduling {
class BikeSystem {
public:
// constructor
BikeSystem();
// make the class non copyable
BikeSystem(BikeSystem&) = delete;
BikeSystem& operator=(BikeSystem&) = delete;
// method called in main() for starting the system
void start();
// method called in main() for starting the sysytem with the event queue
void startWithEventQueue();
// method called for stopping the system
void stop();
#if defined(MBED_TEST_MODE)
const advembsof::TaskLogger& getTaskLogger();
#endif // defined(MBED_TEST_MODE)
private:
// private methods
void init();
void gearTask();
void speedDistanceTask();
void temperatureTask();
void resetTask();
void displayTask1();
void displayTask2();
void cpuTask();
// stop flag, used for stopping the super-loop (set in stop())
bool _stopFlag = false;
// timer instance used for loggint task time and used by ResetDevice
Timer _timer;
// data member that represents the device for manipulating the gear
GearDevice _gearDevice;
uint8_t _currentGear = bike_computer::kMinGear;
uint8_t _currentGearSize = bike_computer::kMaxGearSize;
// data member that represents the device for manipulating the pedal rotation
// speed/time
PedalDevice _pedalDevice;
float _currentSpeed = 0.0f;
float _traveledDistance = 0.0f;
// data member that represents the device used for resetting
ResetDevice _resetDevice;
// data member that represents the device display
advembsof::DisplayDevice _displayDevice;
// data member that represents the device for counting wheel rotations
bike_computer::Speedometer _speedometer;
// data member that represents the sensor device
bike_computer::SensorDevice _sensorDevice;
float _currentTemperature = 0.0f;
// used for logging task info
advembsof::TaskLogger _taskLogger;
// cpu logger to measure cpu usage
advembsof::CPULogger _cpuLogger;
};
} // namespace static_scheduling

View File

@ -0,0 +1,83 @@
// 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 gear_device.cpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Gear Device implementation (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#include "gear_device.hpp"
// from disco_h747i/wrappers
#include <chrono>
#include "joystick.hpp"
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "GearDevice"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace static_scheduling {
// definition of task execution time
static constexpr std::chrono::microseconds kTaskRunTime = 100000us;
GearDevice::GearDevice(Timer& timer) : _timer(timer) {}
uint8_t GearDevice::getCurrentGear() {
std::chrono::microseconds initialTime = _timer.elapsed_time();
std::chrono::microseconds elapsedTime = std::chrono::microseconds::zero();
// we bound the change to one increment/decrement per call
bool hasChanged = false;
while (elapsedTime < kTaskRunTime) {
if (!hasChanged) {
disco::Joystick::State joystickState =
disco::Joystick::getInstance().getState();
switch (joystickState) {
case disco::Joystick::State::UpPressed:
if (_currentGear < bike_computer::kMaxGear) {
_currentGear++;
}
hasChanged = true;
break;
case disco::Joystick::State::DownPressed:
if (_currentGear > bike_computer::kMinGear) {
_currentGear--;
}
hasChanged = true;
break;
default:
break;
}
}
elapsedTime = _timer.elapsed_time() - initialTime;
}
return _currentGear;
}
uint8_t GearDevice::getCurrentGearSize() const {
// simulate task computation by waiting for the required task run time
// wait_us(kTaskRunTime.count());
return bike_computer::kMaxGearSize - _currentGear;
}
} // namespace static_scheduling

View File

@ -0,0 +1,50 @@
// 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 gear_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Gear Device header file (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#pragma once
#include "constants.hpp"
#include "mbed.h"
namespace static_scheduling {
class GearDevice {
public:
explicit GearDevice(Timer& timer); // NOLINT(runtime/references)
// make the class non copyable
GearDevice(GearDevice&) = delete;
GearDevice& operator=(GearDevice&) = delete;
// method called for updating the bike system
uint8_t getCurrentGear();
uint8_t getCurrentGearSize() const;
private:
// data members
uint8_t _currentGear = bike_computer::kMinGear;
Timer& _timer;
};
} // namespace static_scheduling

View File

@ -0,0 +1,84 @@
// 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 pedal_device.cpp
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Pedal Device implementation (static scheduling)
* @date 2024-11-09
* @version 0.1.0
****************************************************************************/
#include "pedal_device.hpp"
// from disco_h747i/wrappers
#include <chrono>
#include "joystick.hpp"
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "PedalDevice"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace static_scheduling {
static constexpr std::chrono::microseconds kTaskRunTime = 200000us;
PedalDevice::PedalDevice(Timer& timer) : _timer(timer), _pedalRotationTime(0) {}
std::chrono::milliseconds PedalDevice::getCurrentRotationTime() {
std::chrono::microseconds initialTime = _timer.elapsed_time();
std::chrono::microseconds elapsedTime = std::chrono::microseconds::zero();
// we bound the change to one increment/decrement per call
bool hasChanged = false;
while (elapsedTime < kTaskRunTime) {
if (!hasChanged) {
disco::Joystick::State joystickState =
disco::Joystick::getInstance().getState();
switch (joystickState) {
case disco::Joystick::State::LeftPressed:
if (_pedalRotationTime < bike_computer::kMaxPedalRotationTime) {
decreaseRotationSpeed();
hasChanged = true;
}
break;
case disco::Joystick::State::DownPressed:
if (_pedalRotationTime > bike_computer::kMinPedalRotationTime) {
decreaseRotationSpeed();
hasChanged = true;
}
break;
default:
break;
}
}
elapsedTime = _timer.elapsed_time() - initialTime;
}
return _pedalRotationTime;
}
void PedalDevice::increaseRotationSpeed() {
_pedalRotationTime -= bike_computer::kDeltaPedalRotationTime;
}
void PedalDevice::decreaseRotationSpeed() {
_pedalRotationTime += bike_computer::kDeltaPedalRotationTime;
}
} // namespace static_scheduling

View File

@ -0,0 +1,54 @@
// 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 pedal_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Pedal System header file (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#pragma once
#include "constants.hpp"
#include "mbed.h"
namespace static_scheduling {
class PedalDevice {
public:
explicit PedalDevice(Timer& timer); // NOLINT(runtime/references)
// make the class non copyable
PedalDevice(PedalDevice&) = delete;
PedalDevice& operator=(PedalDevice&) = delete;
// method called for updating the bike system
std::chrono::milliseconds getCurrentRotationTime();
private:
// private methods
void increaseRotationSpeed();
void decreaseRotationSpeed();
// data members
Timer& _timer;
std::chrono::milliseconds _pedalRotationTime =
bike_computer::kInitialPedalRotationTime;
};
} // namespace static_scheduling

View File

@ -0,0 +1,70 @@
// 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 reset_device.cpp
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Reset Device implementation (static scheduling)
* @date 2024-11-12
* @version 0.1.0
****************************************************************************/
#include "reset_device.hpp"
// from disco_h747i/wrappers
#include <chrono>
#include "joystick.hpp"
#include "mbed_trace.h"
#if defined(TARGET_DISCO_H747I)
#define PUSH_BUTTON BUTTON1
static constexpr uint8_t kPolarityPressed = 1;
#endif
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "ResetDevice"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace static_scheduling {
static constexpr std::chrono::microseconds kTaskRunTime = 100000us;
ResetDevice::ResetDevice(Timer& timer)
: _resetButton(PUSH_BUTTON), _timer(timer), _pressTime(0) {
_resetButton.rise(callback(this, &ResetDevice::onRise));
}
bool ResetDevice::checkReset() {
std::chrono::microseconds initialTime = _timer.elapsed_time();
std::chrono::microseconds elapsedTime = std::chrono::microseconds::zero();
// we bound the change to one increment/decrement per call
bool isPressed = false;
while (elapsedTime < kTaskRunTime) {
if (!isPressed) {
isPressed = _resetButton.read() == kPolarityPressed;
}
elapsedTime = _timer.elapsed_time() - initialTime;
}
return isPressed;
}
std::chrono::microseconds ResetDevice::getPressTime() { return _pressTime; }
void ResetDevice::onRise() { _pressTime = _timer.elapsed_time(); }
} // namespace static_scheduling

View File

@ -0,0 +1,56 @@
// 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 reset_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief ResetDevice header file (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#pragma once
#include "mbed.h"
namespace static_scheduling {
class ResetDevice {
public:
explicit ResetDevice(Timer& timer); // NOLINT(runtime/references)
// make the class non copyable
ResetDevice(ResetDevice&) = delete;
ResetDevice& operator=(ResetDevice&) = delete;
// method called for checking the reset status
bool checkReset();
// for computing the response time
std::chrono::microseconds getPressTime();
private:
// called when the button is pressed
void onRise();
// data members
// instance representing the reset button
InterruptIn _resetButton;
Timer& _timer;
std::chrono::microseconds _pressTime;
};
} // namespace static_scheduling

View File

@ -0,0 +1,238 @@
// 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 <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Bike System implementation (static scheduling)
*
* @date 2023-11-15
* @version 1.1.0
***************************************************************************/
#include "bike_system.hpp"
#include <chrono>
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "BikeSystem"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace static_scheduling_with_event {
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 = 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 = 1200ms;
static constexpr std::chrono::milliseconds kCPUTaskComputationTime = 100ms;
BikeSystem::BikeSystem()
: _gearDevice(),
_pedalDevice(),
_resetDevice(callback(this, &BikeSystem::onReset)),
_displayDevice(),
_speedometer(_timer),
_sensorDevice(),
_taskLogger(),
_cpuLogger(_timer) {}
void BikeSystem::start() {
tr_info("Starting Super-Loop with event handling");
init();
EventQueue eventQueue;
Event<void()> gearEvent(&eventQueue, callback(this, &BikeSystem::gearTask));
gearEvent.delay(kGearTaskDelay);
gearEvent.period(kGearTaskPeriod);
gearEvent.post();
Event<void()> speedDistanceEvent(&eventQueue,
callback(this, &BikeSystem::speedDistanceTask));
speedDistanceEvent.delay(kSpeedDistanceTaskDelay);
speedDistanceEvent.period(kSpeedDistanceTaskPeriod);
speedDistanceEvent.post();
Event<void()> display1Event(&eventQueue, callback(this, &BikeSystem::displayTask1));
display1Event.delay(kDisplayTask1Delay);
display1Event.period(kDisplayTask1Period);
display1Event.post();
Event<void()> resetEvent(&eventQueue, callback(this, &BikeSystem::resetTask));
resetEvent.delay(kResetTaskDelay);
resetEvent.period(kResetTaskPeriod);
resetEvent.post();
Event<void()> temperatureEvent(&eventQueue,
callback(this, &BikeSystem::temperatureTask));
temperatureEvent.delay(kTemperatureTaskDelay);
temperatureEvent.period(kTemperatureTaskPeriod);
temperatureEvent.post();
Event<void()> display2Event(&eventQueue, callback(this, &BikeSystem::displayTask2));
display2Event.delay(kDisplayTask2Delay);
display2Event.period(kDisplayTask2Period);
display2Event.post();
#if !defined(MBED_TEST_MODE)
Event<void()> cpuEvent(&eventQueue, callback(this, &BikeSystem::cpuTask));
cpuEvent.delay(kCPUTaskDelay);
cpuEvent.period(kCPUTaskPeriod);
cpuEvent.post();
#endif
eventQueue.dispatch_forever();
}
void BikeSystem::onReset() {
_resetTime = _timer.elapsed_time();
core_util_atomic_store_bool(&_resetFlag, true);
}
void BikeSystem::stop() { core_util_atomic_store_bool(&_stopFlag, true); }
#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("Failed to initialized the lcd display: %d", static_cast<int>(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(true);
}
void BikeSystem::gearTask() {
// gear task
auto taskStartTime = _timer.elapsed_time();
// no need to protect access to data members (single threaded)
_currentGear = _gearDevice.getCurrentGear();
_currentGearSize = _gearDevice.getCurrentGearSize();
_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(_currentGearSize);
_currentSpeed = _speedometer.getCurrentSpeed();
_traveledDistance = _speedometer.getDistance();
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kSpeedTaskIndex, taskStartTime);
}
void BikeSystem::displayTask1() {
auto taskStartTime = _timer.elapsed_time();
_displayDevice.displayGear(_currentGear);
_displayDevice.displaySpeed(_currentSpeed);
_displayDevice.displayDistance(_traveledDistance);
ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(
kDisplayTask1ComputationTime - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kDisplayTask1Index, taskStartTime);
}
void BikeSystem::resetTask() {
auto taskStartTime = _timer.elapsed_time();
if (core_util_atomic_load_bool(&_resetFlag)) {
std::chrono::microseconds responseTime = _timer.elapsed_time() - _resetTime;
tr_info("Reset task: response time is %" PRIu64 " usecs", responseTime.count());
_speedometer.reset();
core_util_atomic_store_bool(&_resetFlag, false);
}
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kResetTaskIndex, taskStartTime);
}
void BikeSystem::temperatureTask() {
auto taskStartTime = _timer.elapsed_time();
// no need to protect access to data members (single threaded)
_currentTemperature = _sensorDevice.readTemperature();
ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(
kTemperatureTaskComputationTime - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kTemperatureTaskIndex, taskStartTime);
}
void BikeSystem::displayTask2() {
auto taskStartTime = _timer.elapsed_time();
_displayDevice.displayTemperature(_currentTemperature);
ThisThread::sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(
kDisplayTask2ComputationTime - (_timer.elapsed_time() - taskStartTime)));
_taskLogger.logPeriodAndExecutionTime(
_timer, advembsof::TaskLogger::kDisplayTask2Index, taskStartTime);
}
void BikeSystem::cpuTask() { _cpuLogger.printStats(); }
} // namespace static_scheduling_with_event

View File

@ -0,0 +1,111 @@
// 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.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
*
* @brief Bike System header file (static scheduling)
*
* @date 2023-08-20
* @version 1.0.0
***************************************************************************/
#pragma once
// from advembsof
#include "cpu_logger.hpp"
#include "display_device.hpp"
#include "task_logger.hpp"
// from common
#include "sensor_device.hpp"
#include "speedometer.hpp"
// local
#include "gear_device.hpp"
#include "pedal_device.hpp"
#include "reset_device.hpp"
namespace static_scheduling_with_event {
class BikeSystem {
public:
// constructor
BikeSystem();
// make the class non copyable
BikeSystem(BikeSystem&) = delete;
BikeSystem& operator=(BikeSystem&) = delete;
// method called in main() for starting the system
void start();
// method called in main() for starting the sysytem with the event queue
void startWithEventQueue();
// method called for stopping the system
void stop();
#if defined(MBED_TEST_MODE)
const advembsof::TaskLogger& getTaskLogger();
#endif // defined(MBED_TEST_MODE)
private:
// private methods
void init();
void onReset();
void gearTask();
void speedDistanceTask();
void temperatureTask();
void resetTask();
void displayTask1();
void displayTask2();
void cpuTask();
// stop flag, used for stopping the super-loop (set in stop())
bool _stopFlag = false;
std::chrono::microseconds _resetTime = std::chrono::microseconds::zero();
volatile bool _resetFlag = false;
// timer instance used for loggint task time and used by ResetDevice
Timer _timer;
// data member that represents the device for manipulating the gear
GearDevice _gearDevice;
uint8_t _currentGear = bike_computer::kMinGear;
uint8_t _currentGearSize = bike_computer::kMaxGearSize;
// data member that represents the device for manipulating the pedal rotation
// speed/time
PedalDevice _pedalDevice;
float _currentSpeed = 0.0f;
float _traveledDistance = 0.0f;
// data member that represents the device used for resetting
ResetDevice _resetDevice;
// data member that represents the device display
advembsof::DisplayDevice _displayDevice;
// data member that represents the device for counting wheel rotations
bike_computer::Speedometer _speedometer;
// data member that represents the sensor device
bike_computer::SensorDevice _sensorDevice;
float _currentTemperature = 0.0f;
// used for logging task info
advembsof::TaskLogger _taskLogger;
// cpu logger to measure cpu usage
advembsof::CPULogger _cpuLogger;
};
} // namespace static_scheduling_with_event

View File

@ -0,0 +1,64 @@
// 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 gear_device.cpp
* @author Serge Ayer <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Gear Device implementation (static scheduling)
*
* @date 2023-11-17
* @version 1.1.0
***************************************************************************/
#include "gear_device.hpp"
// from disco_h747i/wrappers
#include <chrono>
#include "joystick.hpp"
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "GearDevice"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace static_scheduling_with_event {
GearDevice::GearDevice() {
disco::Joystick::getInstance().setUpCallback(callback(this, &GearDevice::onUp));
disco::Joystick::getInstance().setDownCallback(callback(this, &GearDevice::onDown));
}
uint8_t GearDevice::getCurrentGear() { return core_util_atomic_load_u8(&_currentGear); }
uint8_t GearDevice::getCurrentGearSize() const {
return bike_computer::kMaxGearSize - core_util_atomic_load_u8(&_currentGear);
}
void GearDevice::onUp() {
if (_currentGear < bike_computer::kMaxGear) {
core_util_atomic_incr_u8(&_currentGear, 1);
}
}
void GearDevice::onDown() {
if (_currentGear > bike_computer::kMinGear) {
core_util_atomic_decr_u8(&_currentGear, 1);
}
}
} // namespace static_scheduling_with_event

View File

@ -0,0 +1,54 @@
// 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 gear_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Gear Device header file (static scheduling)
*
* @date 2023-11-17
* @version 1.1.0
***************************************************************************/
#pragma once
#include "constants.hpp"
#include "mbed.h"
namespace static_scheduling_with_event {
class GearDevice {
public:
GearDevice(); // NOLINT(runtime/references)
// make the class non copyable
GearDevice(GearDevice&) = delete;
GearDevice& operator=(GearDevice&) = delete;
// method called for updating the bike system
uint8_t getCurrentGear();
uint8_t getCurrentGearSize() const;
void onUp();
void onDown();
private:
// data members
volatile uint8_t _currentGear = bike_computer::kMinGear;
};
} // namespace static_scheduling_with_event

View File

@ -0,0 +1,69 @@
// 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 pedal_device.cpp
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Pedal Device implementation (static scheduling)
* @date 2024-11-17
* @version 1.1.0
****************************************************************************/
#include "pedal_device.hpp"
// from disco_h747i/wrappers
#include <chrono>
#include "joystick.hpp"
#include "mbed_trace.h"
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "PedalDevice"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace static_scheduling_with_event {
PedalDevice::PedalDevice() {
disco::Joystick::getInstance().setLeftCallback(callback(this, &PedalDevice::onLeft));
disco::Joystick::getInstance().setRightCallback(
callback(this, &PedalDevice::onRight));
}
std::chrono::milliseconds PedalDevice::getCurrentRotationTime() {
uint32_t currentStep = core_util_atomic_load_u32(&_currentStep);
return bike_computer::kMinPedalRotationTime +
currentStep * bike_computer::kDeltaPedalRotationTime;
}
void PedalDevice::increaseRotationSpeed() {
uint32_t currentStep = core_util_atomic_load_u32(&_currentStep);
if (currentStep > 0) {
core_util_atomic_decr_u32(&_currentStep, 1);
}
}
void PedalDevice::decreaseRotationSpeed() {
uint32_t currentStep = core_util_atomic_load_u32(&_currentStep);
if (currentStep < bike_computer::kNbrOfSteps) {
core_util_atomic_incr_u32(&_currentStep, 1);
}
}
void PedalDevice::onLeft() { decreaseRotationSpeed(); }
void PedalDevice::onRight() { increaseRotationSpeed(); }
} // namespace static_scheduling_with_event

View File

@ -0,0 +1,59 @@
// 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 pedal_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Pedal System header file (static scheduling)
*
* @date 2023-11-17
* @version 1.1.0
***************************************************************************/
#pragma once
#include "constants.hpp"
#include "mbed.h"
namespace static_scheduling_with_event {
class PedalDevice {
public:
PedalDevice(); // NOLINT(runtime/references)
// make the class non copyable
PedalDevice(PedalDevice&) = delete;
PedalDevice& operator=(PedalDevice&) = delete;
// method called for updating the bike system
std::chrono::milliseconds getCurrentRotationTime();
private:
// private methods
void onLeft();
void onRight();
void increaseRotationSpeed();
void decreaseRotationSpeed();
// data members
volatile uint32_t _currentStep = static_cast<uint32_t>(
(bike_computer::kInitialPedalRotationTime - bike_computer::kMinPedalRotationTime)
.count() /
bike_computer::kDeltaPedalRotationTime.count());
};
} // namespace static_scheduling_with_event

View File

@ -0,0 +1,48 @@
// 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 reset_device.cpp
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief Reset Device implementation (static scheduling with event)
* @date 2024-11-17
* @version 1.1.0
****************************************************************************/
#include "reset_device.hpp"
// from disco_h747i/wrappers
#include <chrono>
#include "joystick.hpp"
#include "mbed_trace.h"
#if defined(TARGET_DISCO_H747I)
#define PUSH_BUTTON BUTTON1
static constexpr uint8_t kPolarityPressed = 1;
#endif
#if MBED_CONF_MBED_TRACE_ENABLE
#define TRACE_GROUP "ResetDevice"
#endif // MBED_CONF_MBED_TRACE_ENABLE
namespace static_scheduling_with_event {
ResetDevice::ResetDevice(Callback<void()> cb) : _resetButton(PUSH_BUTTON) {
_resetButton.fall(cb);
}
} // namespace static_scheduling_with_event

View File

@ -0,0 +1,47 @@
// 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 reset_device.hpp
* @author Serge Ayer <serge.ayer@hefr.ch>
* @author Rémi Heredero <remi@heredero.ch>
* @author Yann Sierro <yannsierro.pro@gmail.com>
*
* @brief ResetDevice header file (static scheduling with event)
*
* @date 2023-11-17
* @version 1.1.0
***************************************************************************/
#pragma once
#include "mbed.h"
namespace static_scheduling_with_event {
class ResetDevice {
public:
explicit ResetDevice(Callback<void()> cb); // NOLINT(runtime/references)
// make the class non copyable
ResetDevice(ResetDevice&) = delete;
ResetDevice& operator=(ResetDevice&) = delete;
private:
// data members
// instance representing the reset button
InterruptIn _resetButton;
};
} // namespace static_scheduling_with_event