diff --git a/app/os/main.cpp b/app/os/main.cpp index 2b0d48dc1e..2569581b27 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -2,6 +2,7 @@ // Copyright 2020-2022 APF France handicap // SPDX-License-Identifier: Apache-2.0 +#include "mbed_retarget.h" #include "mbed_stats.h" #include "drivers/Watchdog.h" @@ -71,6 +72,15 @@ using namespace leka; using namespace std::chrono; +// +// MARK: - Mbed retarget +// + +auto mbed::mbed_override_console([[maybe_unused]] int fd) -> mbed::FileHandle * +{ + return leka::logger::internal::filehandle; +} + // // MARK: - Global definitions // @@ -321,14 +331,14 @@ namespace firmware { } // namespace firmware -namespace rfid { +namespace rrfid { auto serial = CoreBufferedSerial(RFID_UART_TX, RFID_UART_RX, 57600); auto reader = CoreRFIDReaderCR95HF(serial); -} // namespace rfid +} // namespace rrfid -auto rfidkit = RFIDKit(rfid::reader); +auto rfidkit = RFIDKit(rrfid::reader); namespace mcuboot { @@ -522,6 +532,8 @@ auto main() -> int rtos::ThisThread::sleep_for(1s); + rrfid::serial.disable_input(); + auto version = firmware::version(); log_info("\n\n"); diff --git a/config/mbed_app.json b/config/mbed_app.json index b7cdb4aec5..f9bb933cae 100644 --- a/config/mbed_app.json +++ b/config/mbed_app.json @@ -29,7 +29,8 @@ "target.printf_lib": "std", "target.features_add": [ "EXPERIMENTAL_API" - ] + ], + "platform.stdio-baud-rate": 115200 }, "LEKA_DISCO": { "target_name": "\"LEKA_DISCO\"" diff --git a/drivers/CoreBufferedSerial/include/CoreBufferedSerial.h b/drivers/CoreBufferedSerial/include/CoreBufferedSerial.h index 633fca02b0..30253bfcd0 100644 --- a/drivers/CoreBufferedSerial/include/CoreBufferedSerial.h +++ b/drivers/CoreBufferedSerial/include/CoreBufferedSerial.h @@ -23,6 +23,9 @@ class CoreBufferedSerial : public interface::BufferedSerial auto readable() -> bool final; + void enable_input() final; + void disable_input() final; + void sigio(mbed::Callback func) final; private: diff --git a/drivers/CoreBufferedSerial/source/CoreBufferedSerial.cpp b/drivers/CoreBufferedSerial/source/CoreBufferedSerial.cpp index 50f4214e15..d0a2838656 100644 --- a/drivers/CoreBufferedSerial/source/CoreBufferedSerial.cpp +++ b/drivers/CoreBufferedSerial/source/CoreBufferedSerial.cpp @@ -23,6 +23,16 @@ auto CoreBufferedSerial::readable() -> bool return _serial.readable(); } +void CoreBufferedSerial::enable_input() +{ + _serial.enable_input(true); +} + +void CoreBufferedSerial::disable_input() +{ + _serial.enable_input(false); +} + void CoreBufferedSerial::sigio(mbed::Callback func) { _serial.sigio(func); diff --git a/drivers/CoreMotor/source/CoreMotor.cpp b/drivers/CoreMotor/source/CoreMotor.cpp index 396aa63cda..7fb1f02e6f 100644 --- a/drivers/CoreMotor/source/CoreMotor.cpp +++ b/drivers/CoreMotor/source/CoreMotor.cpp @@ -23,6 +23,7 @@ void CoreMotor::stop() { setDirections(0, 0); setSpeed(0); + _speed.suspend(); } void CoreMotor::setDirections(int dir_1, int dir_2) @@ -33,13 +34,17 @@ void CoreMotor::setDirections(int dir_1, int dir_2) void CoreMotor::setSpeed(float speed) { + // TODO (@ladislas) - add tests if (speed < 0.0F) { _speed.write(0); + _speed.suspend(); } else if (speed > 1.0F) { + _speed.resume(); _speed.write(1.0F); } else { + _speed.resume(); _speed.write(speed); } } diff --git a/drivers/CoreMotor/tests/CoreMotor_test.cpp b/drivers/CoreMotor/tests/CoreMotor_test.cpp index e30edc97ed..2e141b8f36 100644 --- a/drivers/CoreMotor/tests/CoreMotor_test.cpp +++ b/drivers/CoreMotor/tests/CoreMotor_test.cpp @@ -82,6 +82,7 @@ TEST_F(CoreMotorTest, stop) EXPECT_CALL(dir_1, write(0)); EXPECT_CALL(dir_2, write(0)); EXPECT_CALL(speed, write(0)); + EXPECT_CALL(speed, suspend()); motor.stop(); } diff --git a/drivers/CorePwm/include/CorePwm.h b/drivers/CorePwm/include/CorePwm.h index 4546098c95..f1a28e1e87 100644 --- a/drivers/CorePwm/include/CorePwm.h +++ b/drivers/CorePwm/include/CorePwm.h @@ -18,6 +18,9 @@ class CorePwm : public interface::PwmOut auto read() -> float final; void write(float value) final; + void suspend() final; + void resume() final; + private: mbed::PwmOut _pwm; }; diff --git a/drivers/CorePwm/source/CorePwm.cpp b/drivers/CorePwm/source/CorePwm.cpp index b4c0d68027..4f394332d4 100644 --- a/drivers/CorePwm/source/CorePwm.cpp +++ b/drivers/CorePwm/source/CorePwm.cpp @@ -15,3 +15,13 @@ void CorePwm::write(float value) { _pwm.write(value); } + +void CorePwm::suspend() +{ + _pwm.suspend(); +} + +void CorePwm::resume() +{ + _pwm.resume(); +} diff --git a/drivers/CorePwm/tests/CorePwm_test.cpp b/drivers/CorePwm/tests/CorePwm_test.cpp index cc16371707..f358a0f09c 100644 --- a/drivers/CorePwm/tests/CorePwm_test.cpp +++ b/drivers/CorePwm/tests/CorePwm_test.cpp @@ -14,6 +14,7 @@ TEST(CorePwmTest, initialisation) auto corepwm = CorePwm {NC}; ASSERT_NE(&corepwm, nullptr); + ASSERT_FALSE(spy_PwmOut_isSuspended()); } TEST(CorePwmTest, write) @@ -22,11 +23,11 @@ TEST(CorePwmTest, write) corepwm.write(0.5f); - ASSERT_EQ(spy_PwmOut_value, 0.5f); + ASSERT_EQ(spy_PwmOut_getValue(), 0.5f); corepwm.write(1.0f); - ASSERT_EQ(spy_PwmOut_value, 1.0f); + ASSERT_EQ(spy_PwmOut_getValue(), 1.0f); } TEST(CorePwmTest, read) @@ -35,9 +36,31 @@ TEST(CorePwmTest, read) corepwm.write(0.5f); - ASSERT_EQ(spy_PwmOut_value, 0.5f); + ASSERT_EQ(spy_PwmOut_getValue(), 0.5f); auto val = corepwm.read(); ASSERT_EQ(val, 0.5f); } + +TEST(CorePwmTest, suspend) +{ + auto corepwm = CorePwm {NC}; + + corepwm.suspend(); + + ASSERT_TRUE(spy_PwmOut_isSuspended()); +} + +TEST(CorePwmTest, suspendThenResume) +{ + auto corepwm = CorePwm {NC}; + + corepwm.suspend(); + + ASSERT_TRUE(spy_PwmOut_isSuspended()); + + corepwm.resume(); + + ASSERT_FALSE(spy_PwmOut_isSuspended()); +} diff --git a/drivers/CoreVideo/source/CoreLCDDriverOTM8009A.cpp b/drivers/CoreVideo/source/CoreLCDDriverOTM8009A.cpp index 441d0c5cbd..a9d2def1a5 100644 --- a/drivers/CoreVideo/source/CoreLCDDriverOTM8009A.cpp +++ b/drivers/CoreVideo/source/CoreLCDDriverOTM8009A.cpp @@ -16,12 +16,14 @@ void CoreLCDDriverOTM8009A::turnOn() { _dsi.write(display::turn_on::array, std::size(display::turn_on::array)); setBrightness(_previous_brightness_value); + _backlight.resume(); } void CoreLCDDriverOTM8009A::turnOff() { _dsi.write(display::turn_off::array, std::size(display::turn_off::array)); setBrightness(0.F); + _backlight.suspend(); } void CoreLCDDriverOTM8009A::setBrightness(float value) diff --git a/include/interface/drivers/BufferedSerial.h b/include/interface/drivers/BufferedSerial.h index 4c3f840208..88e5e317f5 100644 --- a/include/interface/drivers/BufferedSerial.h +++ b/include/interface/drivers/BufferedSerial.h @@ -21,6 +21,9 @@ class BufferedSerial virtual auto readable() -> bool = 0; + virtual void disable_input() = 0; + virtual void enable_input() = 0; + virtual void sigio(mbed::Callback func) = 0; // TODO (@HPezz) replace mbed callback by std function }; diff --git a/include/interface/drivers/PwmOut.h b/include/interface/drivers/PwmOut.h index d124463a09..c008659ead 100644 --- a/include/interface/drivers/PwmOut.h +++ b/include/interface/drivers/PwmOut.h @@ -13,6 +13,9 @@ class PwmOut virtual auto read() -> float = 0; virtual void write(float value) = 0; + + virtual void suspend() = 0; + virtual void resume() = 0; }; } // namespace leka::interface diff --git a/libs/LogKit/include/LogKit.h b/libs/LogKit/include/LogKit.h index 30a201f89b..af4d914938 100644 --- a/libs/LogKit/include/LogKit.h +++ b/libs/LogKit/include/LogKit.h @@ -216,6 +216,7 @@ inline void init(filehandle_ptr fh = &internal::default_serial, const sink_function_t &sink = internal::default_sink_function) { set_filehandle_pointer(fh); + fh->enable_input(false); set_sink_function(sink); internal::start_event_queue(); } diff --git a/spikes/CMakeLists.txt b/spikes/CMakeLists.txt index d626c11ee0..e88f40cf38 100644 --- a/spikes/CMakeLists.txt +++ b/spikes/CMakeLists.txt @@ -37,6 +37,7 @@ add_subdirectory(${SPIKES_DIR}/lk_update_process_app_base) add_subdirectory(${SPIKES_DIR}/lk_update_process_app_update) add_subdirectory(${SPIKES_DIR}/mbed_blinky) +add_subdirectory(${SPIKES_DIR}/mbed_deep_sleep) add_subdirectory(${SPIKES_DIR}/mbed_watchdog_ticker_vs_thread) add_subdirectory(${SPIKES_DIR}/stl_cxxsupport) @@ -76,6 +77,7 @@ add_dependencies(spikes_leka add_custom_target(spikes_mbed) add_dependencies(spikes_mbed spike_mbed_blinky + spike_mbed_deep_sleep spike_mbed_watchdog_ticker_vs_thread ) diff --git a/spikes/lk_rfid/main.cpp b/spikes/lk_rfid/main.cpp index 70cea240d0..5042c9c9fe 100644 --- a/spikes/lk_rfid/main.cpp +++ b/spikes/lk_rfid/main.cpp @@ -5,6 +5,9 @@ #include #include +#include "drivers/Watchdog.h" +#include "platform/mbed_power_mgmt.h" +#include "platform/mbed_stats.h" #include "rtos/ThisThread.h" #include "CoreBufferedSerial.h" @@ -16,9 +19,95 @@ using namespace leka; using namespace std::chrono; +namespace watchdog { + +namespace internal { + + auto &instance = mbed::Watchdog::get_instance(); + constexpr auto timeout = 30000ms; + auto thread = rtos::Thread {osPriorityLow}; + + namespace stats { + + auto cpu = mbed_stats_cpu_t {}; + auto stack = mbed_stats_stack_t {}; + auto heap = mbed_stats_heap_t {}; + + } // namespace stats + + __attribute__((noreturn)) void watchdog_kick() + { + static auto kick_count = uint32_t {0}; + + static auto start = rtos::Kernel::Clock::now(); + static auto stop = rtos::Kernel::Clock::now(); + static auto delta = static_cast((stop - start).count()); + + static auto sleep_ratio = uint8_t {}; + static auto deep_sleep_ratio = uint8_t {}; + + static auto stack_used_delta = int32_t {}; + static auto stack_used_size = uint32_t {}; + static auto stack_reserved_size = uint32_t {}; + static auto stack_used_ratio = uint8_t {}; + + static auto heap_used_delta = int32_t {}; + static auto heap_used_size = uint32_t {}; + static auto heap_reserved_size = uint32_t {}; + static auto heap_used_ratio = uint8_t {}; + + while (true) { + internal::instance.kick(); + ++kick_count; + + stop = rtos::Kernel::Clock::now(); + delta = static_cast((stop - start).count()); + + mbed_stats_cpu_get(&stats::cpu); + + sleep_ratio = static_cast(((stats::cpu.sleep_time / 1000 * 100) / (stats::cpu.uptime / 1000))); + deep_sleep_ratio = + static_cast(((stats::cpu.deep_sleep_time / 1000 * 100) / (stats::cpu.uptime / 1000))); + + mbed_stats_stack_get(&stats::stack); + + stack_used_delta = static_cast(stats::stack.max_size - stack_used_size); + stack_used_size = stats::stack.max_size; + stack_reserved_size = stats::stack.reserved_size; + stack_used_ratio = static_cast((stack_used_size * 100) / stack_reserved_size); + + mbed_stats_heap_get(&stats::heap); + + heap_used_delta = static_cast(stats::heap.current_size - heap_used_size); + heap_used_size = stats::heap.current_size; + heap_reserved_size = stats::heap.reserved_size; + heap_used_ratio = static_cast((heap_used_size * 100) / heap_reserved_size); + + log_info( + "dt: %i, kck: %u, slp: %u%%, dsl: %u%%, sur: %u%% (%+i)[%u/" + "%u], hur: %u%% (%+i)[%u/%u]", + delta, kick_count, sleep_ratio, deep_sleep_ratio, stack_used_ratio, stack_used_delta, stack_used_size, + stack_reserved_size, heap_used_ratio, heap_used_delta, heap_used_size, heap_reserved_size); + + start = rtos::Kernel::Clock::now(); + rtos::ThisThread::sleep_for(5s); + } + } + +} // namespace internal + +void start() +{ + internal::instance.start(internal::timeout.count()); + internal::thread.start(watchdog::internal::watchdog_kick); +} + +} // namespace watchdog + auto main() -> int { logger::init(); + watchdog::start(); log_info("Hello, World!\n\n"); @@ -34,6 +123,9 @@ auto main() -> int HelloWorld hello; hello.start(); + // ? Uncomment to see the impact on deep sleep ratio + // rfidserial.disable_input(); + while (true) { rtos::ThisThread::sleep_for(10s); } diff --git a/spikes/mbed_deep_sleep/CMakeLists.txt b/spikes/mbed_deep_sleep/CMakeLists.txt new file mode 100644 index 0000000000..6fd1a3d4a1 --- /dev/null +++ b/spikes/mbed_deep_sleep/CMakeLists.txt @@ -0,0 +1,17 @@ +# Leka - LekaOS +# Copyright 2020 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_mbed_executable(spike_mbed_deep_sleep) + +target_include_directories(spike_mbed_deep_sleep + PRIVATE + . +) + +target_sources(spike_mbed_deep_sleep + PRIVATE + main.cpp +) + +target_link_custom_leka_targets(spike_mbed_deep_sleep) diff --git a/spikes/mbed_deep_sleep/main.cpp b/spikes/mbed_deep_sleep/main.cpp new file mode 100644 index 0000000000..a7b6c64128 --- /dev/null +++ b/spikes/mbed_deep_sleep/main.cpp @@ -0,0 +1,138 @@ +// mbed Microcontroller Library +// Copyright (c) 2019 ARM Limited +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "drivers/Watchdog.h" +#include "platform/mbed_power_mgmt.h" +#include "platform/mbed_stats.h" +#include "rtos/ThisThread.h" + +#include "LogKit.h" + +using namespace std::chrono; + +namespace watchdog { + +namespace internal { + + auto &instance = mbed::Watchdog::get_instance(); + constexpr auto timeout = 30000ms; + auto thread = rtos::Thread {osPriorityLow}; + + namespace stats { + + auto cpu = mbed_stats_cpu_t {}; + auto stack = mbed_stats_stack_t {}; + auto heap = mbed_stats_heap_t {}; + + } // namespace stats + + __attribute__((noreturn)) void watchdog_kick() + { + static auto kick_count = uint32_t {0}; + + static auto start = rtos::Kernel::Clock::now(); + static auto stop = rtos::Kernel::Clock::now(); + static auto delta = static_cast((stop - start).count()); + + static auto sleep_ratio = uint8_t {}; + static auto deep_sleep_ratio = uint8_t {}; + + static auto stack_used_delta = int32_t {}; + static auto stack_used_size = uint32_t {}; + static auto stack_reserved_size = uint32_t {}; + static auto stack_used_ratio = uint8_t {}; + + static auto heap_used_delta = int32_t {}; + static auto heap_used_size = uint32_t {}; + static auto heap_reserved_size = uint32_t {}; + static auto heap_used_ratio = uint8_t {}; + + while (true) { + internal::instance.kick(); + ++kick_count; + + stop = rtos::Kernel::Clock::now(); + delta = static_cast((stop - start).count()); + + mbed_stats_cpu_get(&stats::cpu); + + sleep_ratio = static_cast(((stats::cpu.sleep_time / 1000 * 100) / (stats::cpu.uptime / 1000))); + deep_sleep_ratio = + static_cast(((stats::cpu.deep_sleep_time / 1000 * 100) / (stats::cpu.uptime / 1000))); + + mbed_stats_stack_get(&stats::stack); + + stack_used_delta = static_cast(stats::stack.max_size - stack_used_size); + stack_used_size = stats::stack.max_size; + stack_reserved_size = stats::stack.reserved_size; + stack_used_ratio = static_cast((stack_used_size * 100) / stack_reserved_size); + + mbed_stats_heap_get(&stats::heap); + + heap_used_delta = static_cast(stats::heap.current_size - heap_used_size); + heap_used_size = stats::heap.current_size; + heap_reserved_size = stats::heap.reserved_size; + heap_used_ratio = static_cast((heap_used_size * 100) / heap_reserved_size); + + log_info( + "dt: %i, kck: %u, slp: %u%%, dsl: %u%%, sur: %u%% (%+i)[%u/" + "%u], hur: %u%% (%+i)[%u/%u]", + delta, kick_count, sleep_ratio, deep_sleep_ratio, stack_used_ratio, stack_used_delta, stack_used_size, + stack_reserved_size, heap_used_ratio, heap_used_delta, heap_used_size, heap_reserved_size); + + start = rtos::Kernel::Clock::now(); + rtos::ThisThread::sleep_for(5s); + } + } + +} // namespace internal + +void start() +{ + internal::instance.start(internal::timeout.count()); + internal::thread.start(watchdog::internal::watchdog_kick); +} + +} // namespace watchdog + +auto main() -> int +{ + leka::logger::init(); + watchdog::start(); + + auto allowed = sleep_manager_can_deep_sleep() ? 1 : 0; + log_info("Deep sleep allowed: %d", allowed); + + while (true) { + // ? This part is taken from mbed's documentation + // ? sleep_manager_can_deep_sleep doesn't seem to work as expected + // ? as it always return the same value (not allowed) + // ? The statistics are good though as we see that sleep/deep sleep ratio + // ? reach a 50/50 ratio as time passes by... + + // Deep sleep for 1 second + allowed = sleep_manager_can_deep_sleep() ? 1 : 0; + log_info("Deep sleep allowed: %d", allowed); + rtos::ThisThread::sleep_for(1000ms); + + // Lock deep sleep + log_info("Locking deep sleep"); + sleep_manager_lock_deep_sleep(); + + // Sleep for 1 second + allowed = sleep_manager_can_deep_sleep() ? 1 : 0; + log_info("Deep sleep allowed: %d", allowed); + rtos::ThisThread::sleep_for(1000ms); + + // Unlock deep sleep + log_info("Unlocking deep sleep"); + sleep_manager_unlock_deep_sleep(); + + // ? As an alternative, only keeping the following line + // ? will make the deep sleep ratio reach 99% + // rtos::ThisThread::sleep_for(1000ms); + } +} diff --git a/tests/unit/mocks/mocks/leka/CoreBufferedSerial.h b/tests/unit/mocks/mocks/leka/CoreBufferedSerial.h index cd26950e36..8fd155380b 100644 --- a/tests/unit/mocks/mocks/leka/CoreBufferedSerial.h +++ b/tests/unit/mocks/mocks/leka/CoreBufferedSerial.h @@ -15,6 +15,8 @@ class CoreBufferedSerial : public interface::BufferedSerial MOCK_METHOD(std::size_t, read, (uint8_t *, std::size_t), (override)); MOCK_METHOD(std::size_t, write, (const uint8_t *, std::size_t), (override)); MOCK_METHOD(bool, readable, (), (override)); + MOCK_METHOD(void, disable_input, (), (override)); + MOCK_METHOD(void, enable_input, (), (override)); MOCK_METHOD(void, sigio, (mbed::Callback), (override)); }; diff --git a/tests/unit/mocks/mocks/leka/PwmOut.h b/tests/unit/mocks/mocks/leka/PwmOut.h index 463e9f430a..89480d274b 100644 --- a/tests/unit/mocks/mocks/leka/PwmOut.h +++ b/tests/unit/mocks/mocks/leka/PwmOut.h @@ -14,6 +14,8 @@ class PwmOut : public interface::PwmOut public: MOCK_METHOD(float, read, (), (override)); MOCK_METHOD(void, write, (float), (override)); + MOCK_METHOD(void, suspend, (), (override)); + MOCK_METHOD(void, resume, (), (override)); }; } // namespace leka::mock diff --git a/tests/unit/stubs/stubs/mbed/PwmOut.h b/tests/unit/stubs/stubs/mbed/PwmOut.h index 6a46d70049..8a23722fae 100644 --- a/tests/unit/stubs/stubs/mbed/PwmOut.h +++ b/tests/unit/stubs/stubs/mbed/PwmOut.h @@ -6,10 +6,10 @@ namespace leka { -extern float spy_PwmOut_value; float spy_PwmOut_getValue(); -extern float spy_PwmOut_period; float spy_PwmOut_getPeriod(); +bool spy_PwmOut_isSuspended(); + } // namespace leka diff --git a/tests/unit/stubs/stubs/mbed/source/PwmOut.cpp b/tests/unit/stubs/stubs/mbed/source/PwmOut.cpp index c5eaf24a5f..2ab89cbc31 100644 --- a/tests/unit/stubs/stubs/mbed/source/PwmOut.cpp +++ b/tests/unit/stubs/stubs/mbed/source/PwmOut.cpp @@ -4,49 +4,66 @@ #include "../PwmOut.h" +namespace { +float spy_PwmOut_value = -42.0f; +float spy_PwmOut_period = -42.0f; +bool spy_PwmOut_is_suspended = true; +} // namespace + namespace mbed { PwmOut::PwmOut(PinName) { - return; + spy_PwmOut_is_suspended = false; } PwmOut::~PwmOut() { - return; + spy_PwmOut_is_suspended = true; } void PwmOut::write(float value) { - leka::spy_PwmOut_value = value; + spy_PwmOut_value = value; } auto PwmOut::read() -> float { - return leka::spy_PwmOut_value; + return spy_PwmOut_value; } void PwmOut::period(float value) { - leka::spy_PwmOut_period = value; + spy_PwmOut_period = value; +} + +void PwmOut::suspend() +{ + spy_PwmOut_is_suspended = true; +} + +void PwmOut::resume() +{ + spy_PwmOut_is_suspended = false; } } // namespace mbed namespace leka { -float spy_PwmOut_value = -42.0f; - float spy_PwmOut_getValue() { return spy_PwmOut_value; } -float spy_PwmOut_period = -42.0f; - float spy_PwmOut_getPeriod() { return spy_PwmOut_period; } +bool spy_PwmOut_isSuspended() +{ + return spy_PwmOut_is_suspended; +} + } // namespace leka