Introduce Timebase module, refactor timing code + unit tests

pull/26/head
D.R.racer 2021-06-18 09:15:36 +02:00 committed by DRracer
parent 398181e26c
commit ede475c5a6
22 changed files with 199 additions and 68 deletions

View File

@ -203,6 +203,7 @@ target_sources(
src/modules/motion.cpp src/modules/motion.cpp
src/modules/permanent_storage.cpp src/modules/permanent_storage.cpp
src/modules/selector.cpp src/modules/selector.cpp
src/modules/timebase.cpp
src/logic/command_base.cpp src/logic/command_base.cpp
src/logic/cut_filament.cpp src/logic/cut_filament.cpp
src/logic/eject_filament.cpp src/logic/eject_filament.cpp

View File

@ -267,15 +267,14 @@ void loop() {
if (CheckMsgs()) { if (CheckMsgs()) {
ProcessRequestMsg(protocol.GetRequestMsg()); ProcessRequestMsg(protocol.GetRequestMsg());
} }
mb::buttons.Step(hal::adc::ReadADC(0)); mb::buttons.Step();
ml::leds.Step(10); ml::leds.Step();
mf::finda.Step(0); mf::finda.Step();
mfs::fsensor.Step(0); mfs::fsensor.Step();
mi::idler.Step(); mi::idler.Step();
ms::selector.Step(); ms::selector.Step();
currentCommand->Step(); currentCommand->Step();
// add a watchdog reset // add a watchdog reset
_delay_ms(10);
} }
int main() { int main() {

View File

@ -1,5 +1,6 @@
#include "buttons.h" #include "buttons.h"
#include "../hal/adc.h" #include "../hal/adc.h"
#include "timebase.h"
namespace modules { namespace modules {
namespace buttons { namespace buttons {
@ -22,7 +23,8 @@ int8_t Buttons::DecodeADC(uint16_t rawADC) {
return -1; return -1;
} }
void Buttons::Step(uint16_t millis) { void Buttons::Step() {
uint16_t millis = modules::time::timebase.Millis();
int8_t currentState = DecodeADC(hal::adc::ReadADC(0)); int8_t currentState = DecodeADC(hal::adc::ReadADC(0));
for (uint_fast8_t b = 0; b < N; ++b) { for (uint_fast8_t b = 0; b < N; ++b) {
// this button was pressed if b == currentState, released otherwise // this button was pressed if b == currentState, released otherwise

View File

@ -32,7 +32,7 @@ public:
inline constexpr Buttons() = default; inline constexpr Buttons() = default;
/// State machine step - reads the ADC, processes debouncing, updates states of individual buttons /// State machine step - reads the ADC, processes debouncing, updates states of individual buttons
void Step(uint16_t millis); void Step();
/// @returns true if button at index is pressed /// @returns true if button at index is pressed
/// @@TODO add range checking if necessary /// @@TODO add range checking if necessary

View File

@ -1,13 +1,14 @@
#include "finda.h" #include "finda.h"
#include "../hal/adc.h" #include "../hal/adc.h"
#include "timebase.h"
namespace modules { namespace modules {
namespace finda { namespace finda {
FINDA finda; FINDA finda;
void FINDA::Step(uint16_t time) { void FINDA::Step() {
debounce::Debouncer::Step(time, hal::adc::ReadADC(1) > adcDecisionLevel); debounce::Debouncer::Step(modules::time::timebase.Millis(), hal::adc::ReadADC(1) > adcDecisionLevel);
} }
} // namespace finda } // namespace finda

View File

@ -9,7 +9,7 @@ class FINDA : protected debounce::Debouncer {
public: public:
inline constexpr FINDA() inline constexpr FINDA()
: debounce::Debouncer(debounce) {}; : debounce::Debouncer(debounce) {};
void Step(uint16_t time); void Step();
using debounce::Debouncer::Pressed; using debounce::Debouncer::Pressed;
private: private:

View File

@ -1,17 +1,18 @@
#include "fsensor.h" #include "fsensor.h"
#include "timebase.h"
namespace modules { namespace modules {
namespace fsensor { namespace fsensor {
FSensor fsensor; FSensor fsensor;
void FSensor::Step(uint16_t time) { void FSensor::Step() {
debounce::Debouncer::Step(time, reportedFSensorState); debounce::Debouncer::Step(modules::time::timebase.Millis(), reportedFSensorState);
} }
void FSensor::ProcessMessage(bool on) { void FSensor::ProcessMessage(bool on) {
reportedFSensorState = on; reportedFSensorState = on;
} }
} // namespace finda } // namespace fsensor
} // namespace modules } // namespace modules

View File

@ -14,7 +14,7 @@ public:
inline constexpr FSensor() inline constexpr FSensor()
: debounce::Debouncer(debounce) : debounce::Debouncer(debounce)
, reportedFSensorState(false) {}; , reportedFSensorState(false) {};
void Step(uint16_t time); void Step();
using debounce::Debouncer::Pressed; using debounce::Debouncer::Pressed;
void ProcessMessage(bool on); void ProcessMessage(bool on);

View File

@ -1,5 +1,6 @@
#include "leds.h" #include "leds.h"
#include "../hal/shr16.h" #include "../hal/shr16.h"
#include "timebase.h"
namespace modules { namespace modules {
namespace leds { namespace leds {
@ -39,9 +40,9 @@ bool LED::Step(bool oddPeriod) {
return state.on; return state.on;
} }
void LEDs::Step(uint16_t delta_ms) { void LEDs::Step() {
ms += delta_ms; uint16_t millis = modules::time::timebase.Millis();
bool oddPeriod = ((ms / 1000U) & 0x01U) != 0; bool oddPeriod = ((millis / 1000U) & 0x01U) != 0;
uint16_t result = 0; uint16_t result = 0;
for (int8_t i = ledPairs * 2 - 1; i >= 0; --i) { for (int8_t i = ledPairs * 2 - 1; i >= 0; --i) {
result <<= 1; result <<= 1;

View File

@ -53,11 +53,10 @@ private:
/// main LED API /// main LED API
class LEDs { class LEDs {
public: public:
constexpr inline LEDs() constexpr inline LEDs() = default;
: ms(0) {};
/// step LED automaton /// step LED automaton
void Step(uint16_t delta_ms); void Step();
inline constexpr uint8_t LedPairsCount() const { return ledPairs; } inline constexpr uint8_t LedPairsCount() const { return ledPairs; }
@ -96,7 +95,6 @@ private:
/// [8] - green LED slot 4 /// [8] - green LED slot 4
/// [9] - red LED slot 4 /// [9] - red LED slot 4
LED leds[ledPairs * 2]; LED leds[ledPairs * 2];
uint16_t ms;
}; };
extern LEDs leds; extern LEDs leds;

18
src/modules/timebase.cpp Normal file
View File

@ -0,0 +1,18 @@
#include "timebase.h"
#include "../hal/timers.h"
namespace modules {
namespace time {
void Timebase::Init() {
}
void Timebase::ISR() {
}
uint16_t Timebase::Millis() const {
return ms;
}
} // namespace time
} // namespace modules

28
src/modules/timebase.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <stdint.h>
namespace modules {
namespace time {
/// A basic time tracking class
/// Works on top of processor timers and provides real-time steady clock
/// (at least what the CPU thinks ;) )
class Timebase {
public:
constexpr inline Timebase()
: ms(0) {}
void Init();
/// @returns current milliseconds elapsed from the initialization of this class
/// (usually the start of the firmware)
uint16_t Millis() const;
private:
uint16_t ms;
static void ISR();
};
extern Timebase timebase;
} // namespace time
} // namespace modules

View File

@ -17,6 +17,7 @@ add_executable(
../../modules/stubs/stub_adc.cpp ../../modules/stubs/stub_adc.cpp
../../modules/stubs/stub_eeprom.cpp ../../modules/stubs/stub_eeprom.cpp
../../modules/stubs/stub_shr16.cpp ../../modules/stubs/stub_shr16.cpp
../../modules/stubs/stub_timebase.cpp
../stubs/main_loop_stub.cpp ../stubs/main_loop_stub.cpp
../stubs/stub_motion.cpp ../stubs/stub_motion.cpp
test_cut_filament.cpp test_cut_filament.cpp

View File

@ -29,16 +29,16 @@ bool WhileCondition(COND cond, uint32_t maxLoops = 5000) {
TEST_CASE("cut_filament::cut0", "[cut_filament]") { TEST_CASE("cut_filament::cut0", "[cut_filament]") {
using namespace logic; using namespace logic;
ForceReinitAllAutomata();
CutFilament cf; CutFilament cf;
main_loop();
// let's assume we have the filament NOT loaded
modules::globals::globals.SetFilamentLoaded(false);
// restart the automaton // restart the automaton
currentCommand = &cf; currentCommand = &cf;
cf.Reset(0); cf.Reset(0);
main_loop();
// it should have instructed the selector and idler to move to slot 1 // it should have instructed the selector and idler to move to slot 1
// check if the idler and selector have the right command // check if the idler and selector have the right command
CHECK(modules::motion::axes[modules::motion::Idler].targetPos == 0); // @@TODO constants CHECK(modules::motion::axes[modules::motion::Idler].targetPos == 0); // @@TODO constants
@ -49,7 +49,7 @@ TEST_CASE("cut_filament::cut0", "[cut_filament]") {
// idler and selector reached their target positions and the CF automaton will start feeding to FINDA as the next step // idler and selector reached their target positions and the CF automaton will start feeding to FINDA as the next step
REQUIRE(cf.State() == ProgressCode::FeedingToFinda); REQUIRE(cf.State() == ProgressCode::FeedingToFinda);
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::FeedingToFinda; }, 5000)); REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::FeedingToFinda; }, 50000));
// filament fed into FINDA, cutting... // filament fed into FINDA, cutting...
REQUIRE(cf.State() == ProgressCode::PreparingBlade); REQUIRE(cf.State() == ProgressCode::PreparingBlade);

View File

@ -14,6 +14,7 @@ add_executable(
../../modules/stubs/stub_adc.cpp ../../modules/stubs/stub_adc.cpp
../../modules/stubs/stub_eeprom.cpp ../../modules/stubs/stub_eeprom.cpp
../../modules/stubs/stub_shr16.cpp ../../modules/stubs/stub_shr16.cpp
../../modules/stubs/stub_timebase.cpp
../stubs/main_loop_stub.cpp ../stubs/main_loop_stub.cpp
../stubs/stub_motion.cpp ../stubs/stub_motion.cpp
test_feed_to_finda.cpp test_feed_to_finda.cpp

View File

@ -1,6 +1,7 @@
#include "main_loop_stub.h" #include "main_loop_stub.h"
#include "../../modules/stubs/stub_adc.h" #include "../../modules/stubs/stub_adc.h"
#include "../../modules/stubs/stub_timebase.h"
#include "../../../../src/modules/buttons.h" #include "../../../../src/modules/buttons.h"
#include "../../../../src/modules/finda.h" #include "../../../../src/modules/finda.h"
@ -16,20 +17,18 @@
logic::CommandBase *currentCommand = nullptr; logic::CommandBase *currentCommand = nullptr;
// just like in the real FW, step all the known automata
uint16_t millis = 0;
void main_loop() { void main_loop() {
modules::buttons::buttons.Step(millis); modules::buttons::buttons.Step();
modules::leds::leds.Step(millis); modules::leds::leds.Step();
modules::finda::finda.Step(millis); modules::finda::finda.Step();
modules::fsensor::fsensor.Step(millis); modules::fsensor::fsensor.Step();
modules::idler::idler.Step(); modules::idler::idler.Step();
modules::selector::selector.Step(); modules::selector::selector.Step();
modules::motion::motion.Step(); modules::motion::motion.Step();
if (currentCommand) if (currentCommand)
currentCommand->Step(); currentCommand->Step();
++millis;
modules::time::IncMillis();
} }
void ForceReinitAllAutomata() { void ForceReinitAllAutomata() {
@ -58,6 +57,8 @@ void ForceReinitAllAutomata() {
// finda OFF // finda OFF
hal::adc::ReinitADC(1, hal::adc::TADCData({ 0 }), 1); hal::adc::ReinitADC(1, hal::adc::TADCData({ 0 }), 1);
modules::time::ReinitTimebase();
// let's assume we have the filament NOT loaded and active slot 0 // let's assume we have the filament NOT loaded and active slot 0
modules::globals::globals.SetFilamentLoaded(false); modules::globals::globals.SetFilamentLoaded(false);
modules::globals::globals.SetActiveSlot(0); modules::globals::globals.SetActiveSlot(0);

View File

@ -1,7 +1,7 @@
# define the test executable # define the test executable
add_executable( add_executable(
buttons_tests ../../../../src/modules/buttons.cpp ../../../../src/modules/debouncer.cpp buttons_tests ../../../../src/modules/buttons.cpp ../../../../src/modules/debouncer.cpp
../stubs/stub_adc.cpp test_buttons.cpp ../stubs/stub_adc.cpp ../stubs/stub_timebase.cpp test_buttons.cpp
) )
# define required search paths # define required search paths

View File

@ -1,26 +1,33 @@
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
#include "buttons.h" #include "buttons.h"
#include "../stubs/stub_adc.h" #include "../stubs/stub_adc.h"
#include "../stubs/stub_timebase.h"
using Catch::Matchers::Equals; using Catch::Matchers::Equals;
uint16_t millis = 0; uint16_t millis = 0;
bool Step_Basic_One_Button_Test(modules::buttons::Buttons &b, uint8_t oversampleFactor, uint8_t testedButtonIndex, uint8_t otherButton1, uint8_t otherButton2) { bool Step_Basic_One_Button_Test(modules::buttons::Buttons &b, uint8_t oversampleFactor, uint8_t testedButtonIndex, uint8_t otherButton1, uint8_t otherButton2) {
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // should detect the press but remain in detected state - wait for debounce b.Step(); // should detect the press but remain in detected state - wait for debounce
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(testedButtonIndex)); CHECK(!b.ButtonPressed(testedButtonIndex));
CHECK(!b.ButtonPressed(otherButton1)); CHECK(!b.ButtonPressed(otherButton1));
CHECK(!b.ButtonPressed(otherButton2)); CHECK(!b.ButtonPressed(otherButton2));
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // reset to waiting b.Step(); // reset to waiting
modules::time::IncMillis();
}
CHECK(b.ButtonPressed(testedButtonIndex)); CHECK(b.ButtonPressed(testedButtonIndex));
CHECK(!b.ButtonPressed(otherButton1)); CHECK(!b.ButtonPressed(otherButton1));
CHECK(!b.ButtonPressed(otherButton2)); CHECK(!b.ButtonPressed(otherButton2));
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // pressed again, still in debouncing state b.Step(); // pressed again, still in debouncing state
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(testedButtonIndex)); CHECK(!b.ButtonPressed(testedButtonIndex));
CHECK(!b.ButtonPressed(otherButton1)); CHECK(!b.ButtonPressed(otherButton1));
CHECK(!b.ButtonPressed(otherButton2)); CHECK(!b.ButtonPressed(otherButton2));
@ -31,7 +38,7 @@ bool Step_Basic_One_Button_Test(modules::buttons::Buttons &b, uint8_t oversample
/// This test verifies the behaviour of a single button. The other buttons must remain intact. /// This test verifies the behaviour of a single button. The other buttons must remain intact.
bool Step_Basic_One_Button(hal::adc::TADCData &&d, uint8_t testedButtonIndex) { bool Step_Basic_One_Button(hal::adc::TADCData &&d, uint8_t testedButtonIndex) {
using namespace modules::buttons; using namespace modules::buttons;
millis = 0; modules::time::ReinitTimebase();
Buttons b; Buttons b;
// need to oversample the data as debouncing takes 100 cycles to accept a pressed button // need to oversample the data as debouncing takes 100 cycles to accept a pressed button
@ -95,68 +102,87 @@ TEST_CASE("buttons::Step-debounce-one-button", "[buttons]") {
// need to oversample the data as debouncing takes 100 cycles to accept a pressed button // need to oversample the data as debouncing takes 100 cycles to accept a pressed button
constexpr uint8_t oversampleFactor = 25; constexpr uint8_t oversampleFactor = 25;
hal::adc::ReinitADC(0, std::move(d), oversampleFactor); hal::adc::ReinitADC(0, std::move(d), oversampleFactor);
modules::time::ReinitTimebase();
Buttons b; Buttons b;
// 5 // 5
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // should detect the press but remain in detected state - wait for debounce b.Step(); // should detect the press but remain in detected state - wait for debounce
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(0)); CHECK(!b.ButtonPressed(0));
CHECK(!b.ButtonPressed(1)); CHECK(!b.ButtonPressed(1));
CHECK(!b.ButtonPressed(2)); CHECK(!b.ButtonPressed(2));
// 1023 // 1023
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // reset to waiting b.Step(); // reset to waiting
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(0)); CHECK(!b.ButtonPressed(0));
CHECK(!b.ButtonPressed(1)); CHECK(!b.ButtonPressed(1));
CHECK(!b.ButtonPressed(2)); CHECK(!b.ButtonPressed(2));
// 5 // 5
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // pressed again, still in debouncing state b.Step(); // pressed again, still in debouncing state
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(0)); CHECK(!b.ButtonPressed(0));
CHECK(!b.ButtonPressed(1)); CHECK(!b.ButtonPressed(1));
CHECK(!b.ButtonPressed(2)); CHECK(!b.ButtonPressed(2));
// 9 // 9
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // no change b.Step(); // no change
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(0)); CHECK(!b.ButtonPressed(0));
CHECK(!b.ButtonPressed(1)); CHECK(!b.ButtonPressed(1));
CHECK(!b.ButtonPressed(2)); CHECK(!b.ButtonPressed(2));
// 6 // 6
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // no change b.Step(); // no change
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(0)); CHECK(!b.ButtonPressed(0));
CHECK(!b.ButtonPressed(1)); CHECK(!b.ButtonPressed(1));
CHECK(!b.ButtonPressed(2)); CHECK(!b.ButtonPressed(2));
// 7 // 7
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // one step from "pressed" b.Step(); // one step from "pressed"
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(0)); CHECK(!b.ButtonPressed(0));
CHECK(!b.ButtonPressed(1)); CHECK(!b.ButtonPressed(1));
CHECK(!b.ButtonPressed(2)); CHECK(!b.ButtonPressed(2));
// 8 // 8
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // fifth set of samples - should report "pressed" finally b.Step(); // fifth set of samples - should report "pressed" finally
modules::time::IncMillis();
}
CHECK(b.ButtonPressed(0)); CHECK(b.ButtonPressed(0));
CHECK(!b.ButtonPressed(1)); CHECK(!b.ButtonPressed(1));
CHECK(!b.ButtonPressed(2)); CHECK(!b.ButtonPressed(2));
// 1023 // 1023
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // sixth set of samples - button released (no debouncing on release) b.Step(); // sixth set of samples - button released (no debouncing on release)
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(0)); CHECK(!b.ButtonPressed(0));
CHECK(!b.ButtonPressed(1)); CHECK(!b.ButtonPressed(1));
CHECK(!b.ButtonPressed(2)); CHECK(!b.ButtonPressed(2));
// 1023 // 1023
for (uint8_t i = 0; i < oversampleFactor; ++i) for (uint8_t i = 0; i < oversampleFactor; ++i) {
b.Step(++millis); // seventh set of samples - still released b.Step(); // seventh set of samples - still released
modules::time::IncMillis();
}
CHECK(!b.ButtonPressed(0)); CHECK(!b.ButtonPressed(0));
CHECK(!b.ButtonPressed(1)); CHECK(!b.ButtonPressed(1));
CHECK(!b.ButtonPressed(2)); CHECK(!b.ButtonPressed(2));

View File

@ -1,5 +1,8 @@
# define the test executable # define the test executable
add_executable(leds_tests ../../../../src/modules/leds.cpp ../stubs/stub_shr16.cpp test_leds.cpp) add_executable(
leds_tests ../../../../src/modules/leds.cpp ../stubs/stub_shr16.cpp ../stubs/stub_timebase.cpp
test_leds.cpp
)
# define required search paths # define required search paths
target_include_directories( target_include_directories(

View File

@ -1,9 +1,12 @@
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
#include "leds.h" #include "leds.h"
#include "shr16.h" #include "shr16.h"
#include "../stubs/stub_timebase.h"
using Catch::Matchers::Equals; using Catch::Matchers::Equals;
uint16_t millis = 0;
namespace hal { namespace hal {
namespace shr16 { namespace shr16 {
extern uint16_t shr16_v_copy; extern uint16_t shr16_v_copy;
@ -25,6 +28,7 @@ extern uint16_t shr16_v_copy;
TEST_CASE("leds::single", "[leds]") { TEST_CASE("leds::single", "[leds]") {
using namespace modules::leds; using namespace modules::leds;
using namespace hal::shr16; using namespace hal::shr16;
modules::time::ReinitTimebase(); // reset timing
LEDs leds; LEDs leds;
@ -47,13 +51,15 @@ TEST_CASE("leds::single", "[leds]") {
// turn LED on // turn LED on
leds.SetMode(index, color, on); leds.SetMode(index, color, on);
leds.Step(0); leds.Step();
modules::time::IncMillis();
CHECK(leds.LedOn(index, color) == true); CHECK(leds.LedOn(index, color) == true);
CHECK(shr16_v_copy == shr16_register); CHECK(shr16_v_copy == shr16_register);
// turn LED off // turn LED off
leds.SetMode(index, color, off); leds.SetMode(index, color, off);
leds.Step(0); leds.Step();
modules::time::IncMillis();
CHECK(leds.LedOn(index, color) == false); CHECK(leds.LedOn(index, color) == false);
CHECK(shr16_v_copy == 0); CHECK(shr16_v_copy == 0);
} }
@ -61,10 +67,12 @@ TEST_CASE("leds::single", "[leds]") {
void TestBlink(uint8_t index, modules::leds::Color color, uint16_t shr16_register, bool shouldBeOn, modules::leds::Mode blinkMode) { void TestBlink(uint8_t index, modules::leds::Color color, uint16_t shr16_register, bool shouldBeOn, modules::leds::Mode blinkMode) {
using namespace modules::leds; using namespace modules::leds;
using namespace hal::shr16; using namespace hal::shr16;
modules::time::ReinitTimebase(); // reset timing
LEDs leds; LEDs leds;
leds.SetMode(index, color, blinkMode); leds.SetMode(index, color, blinkMode);
leds.Step(1); leds.Step();
modules::time::IncMillis();
REQUIRE(leds.LedOn(index, color) == shouldBeOn); REQUIRE(leds.LedOn(index, color) == shouldBeOn);
CHECK(shr16_v_copy == (shouldBeOn ? shr16_register : 0)); CHECK(shr16_v_copy == (shouldBeOn ? shr16_register : 0));
@ -72,7 +80,8 @@ void TestBlink(uint8_t index, modules::leds::Color color, uint16_t shr16_registe
// test 4 seconds of blinking // test 4 seconds of blinking
for (uint8_t s = 1; s < 4; ++s) { for (uint8_t s = 1; s < 4; ++s) {
// one second elapsed ;) // one second elapsed ;)
leds.Step(1000); modules::time::IncMillis(1000);
leds.Step();
shouldBeOn = !shouldBeOn; shouldBeOn = !shouldBeOn;
CHECK(leds.LedOn(index, color) == shouldBeOn); CHECK(leds.LedOn(index, color) == shouldBeOn);
CHECK(shr16_v_copy == (shouldBeOn ? shr16_register : 0)); CHECK(shr16_v_copy == (shouldBeOn ? shr16_register : 0));
@ -80,7 +89,8 @@ void TestBlink(uint8_t index, modules::leds::Color color, uint16_t shr16_registe
// turn LED off // turn LED off
leds.SetMode(index, color, off); leds.SetMode(index, color, off);
leds.Step(0); leds.Step();
modules::time::IncMillis();
CHECK(leds.LedOn(index, color) == false); CHECK(leds.LedOn(index, color) == false);
CHECK(shr16_v_copy == 0); CHECK(shr16_v_copy == 0);
} }
@ -88,7 +98,6 @@ void TestBlink(uint8_t index, modules::leds::Color color, uint16_t shr16_registe
TEST_CASE("leds::blink0-single", "[leds]") { TEST_CASE("leds::blink0-single", "[leds]") {
using namespace modules::leds; using namespace modules::leds;
using namespace hal::shr16; using namespace hal::shr16;
uint8_t index; uint8_t index;
Color color; Color color;
uint16_t shr16_register; uint16_t shr16_register;

View File

@ -0,0 +1,28 @@
#include "stub_timebase.h"
#include "../../../../src/modules/timebase.h"
namespace modules {
namespace time {
Timebase timebase;
uint16_t millis = 0;
void Timebase::Init() {}
void Timebase::ISR() {}
uint16_t Timebase::Millis() const {
return millis;
}
void ReinitTimebase(uint16_t ms /* = 0 */) {
millis = ms;
}
void IncMillis(uint16_t inc /* = 1*/) {
millis += inc;
}
} // namespace time
} // namespace modules

View File

@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
#include <vector>
namespace modules {
namespace time {
extern void ReinitTimebase(uint16_t ms = 0);
extern void IncMillis(uint16_t inc = 1);
} // namespace time
} // namespace modules