Skip to content

Commit

Permalink
[delay] Make modm::delay() std::chrono compatible
Browse files Browse the repository at this point in the history
  • Loading branch information
salkinium committed Apr 6, 2020
1 parent ef386b2 commit 8f49d35
Show file tree
Hide file tree
Showing 19 changed files with 416 additions and 308 deletions.
2 changes: 1 addition & 1 deletion examples/nucleo_f429zi/cmsis_dsp/runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ int main()
const uint32_t diff{DWT->CYCCNT - start};

if (status != ARM_MATH_TEST_FAILURE) {
MODM_LOG_INFO << "Example '" << example_name << "' passed in ~" << (diff / modm::clock::fcpu_MHz) << "us!" << modm::endl;
MODM_LOG_INFO << "Example '" << example_name << "' passed in ~" << (diff / modm::platform::delay_fcpu_MHz) << "us!" << modm::endl;
} else {
MODM_LOG_ERROR << "Example '" << example_name << "' failed!" << modm::endl;
}
Expand Down
89 changes: 71 additions & 18 deletions src/modm/architecture/interface/delay.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017-2018, Niklas Hauser
* Copyright (c) 2020, Niklas Hauser
*
* This file is part of the modm project.
*
Expand All @@ -9,33 +9,67 @@
*/
// ----------------------------------------------------------------------------

#ifndef MODM_INTERFACE_DELAY_HPP
#define MODM_INTERFACE_DELAY_HPP

#include <stdint.h>

#pragma once
#include <chrono>

#ifdef __DOXYGEN__

namespace modm
{

/// Spin for nanoseconds.
/// This function is implemented as best-effort and its resolution will be very
/// coarse especially on platforms with very slow clocks.
/**
* Spin for nanoseconds.
*
* @warning This function is implemented as best-effort and its resolution will
* be very coarse especially on platforms with very slow clocks.
*
* @warning The maximum delay is less than 1 millisecond.
*
* @note In debug mode this function may raise "delay.us" if input validation
* fails.
*
* @ingroup modm_architecture_delay
*/
void delay(std::chrono::nanoseconds ns);
/// @ingroup modm_architecture_delay
void
delayNanoseconds(uint16_t ns);
void delay_ns(uint32_t ns);

/// Spin for microseconds.
/**
* Is true if nanosecond delay is reasonably accurate.
* On devices with low clock speed it may not be possible to provide this
* function with <100ns or even <1000ns accuracy.
*
* @ingroup modm_architecture_delay
*/
#define MODM_DELAY_NS_IS_ACCURATE bool(true or false)

/**
* Spin for microseconds.
*
* @warning The maximum delay is less than 10 seconds.
*
* @note In debug mode this function may raise "delay.us" if input validation
* fails.
*
* @ingroup modm_architecture_delay
*/
void delay(std::chrono::microseconds us);
/// @ingroup modm_architecture_delay
void
delayMicroseconds(uint16_t us);
void delay_us(uint32_t us);

/// Spin for milliseconds.
/**
* Spin for milliseconds.
*
* @warning The maximum delay is less than 10 seconds.
*
* @note In debug mode this function may raise "delay.us" if input validation
* fails.
*
* @ingroup modm_architecture_delay
*/
void delay(std::chrono::milliseconds ms);
/// @ingroup modm_architecture_delay
void
delayMilliseconds(uint16_t ms);
void delay_ms(uint32_t ms);

}

Expand All @@ -45,6 +79,25 @@ delayMilliseconds(uint16_t ms);
// there being a link-able function and delegate this choice to the platform.
#include <modm/platform/core/delay_impl.hpp>

namespace modm
{

// Everything else is cast to microseconds
template<class Rep, class Period>
inline void delay(std::chrono::duration<Rep, Period> time)
{ delay(std::chrono::duration_cast<std::chrono::microseconds>(time)); }

// The old methods are deprecated
[[deprecated("Use `modm::delay_ns(uint32_t ns)` instead!")]]
inline void delayNanoseconds(uint32_t ns) { delay_ns(ns); }

[[deprecated("Use `modm::delay_us(uint32_t us)` instead!")]]
inline void delayMicroseconds(uint32_t us) { delay_us(us); }

[[deprecated("Use `modm::delay_ms(uint32_t ms)` instead!")]]
inline void delayMilliseconds(uint32_t ms) { delay_ms(ms); }

} // namespace modm

#endif

#endif // MODM_INTERFACE_DELAY_HPP
59 changes: 47 additions & 12 deletions src/modm/architecture/interface/delay.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,54 @@

These functions allow you to spin for a short time using only the CPU.

You should always prefer Software Timers (see `modm:processing:timer`) over these
*blocking* delay functions. However, when `modm::Clock` is not set up yet, or
when you need very small delays (for example to bit-bang a protocol), you need
to use these delay functions.
In general it is recommended to use the `std::chrono` duration types to allow
the compiler to choose the optimal implementation for you:

The only guarantee given to you is to delay for _at least_ the specified time.
Note that invocation of interrupts during spinning may add delay too.
```cpp
#include <chrono>

!!! warning "No real-time guarantees!"
You must not rely on delay functions for ANY time-keeping!
constexpr std::chrono::nanoseconds DELAY_TIME{100};
modm::delay(DELAY_TIME);

constexpr std::chrono::microseconds DELAY_TIME2{200};
modm::delay(DELAY_TIME2); // same signature, different implementation
```
In your code you can also use the `std::chrono` literals:
```cpp
using namespace std::chrono_literals;
modm::delay(100ns);
modm::delay(200us);
modm::delay(300ms);
```

In order to not require wild casting around of values, there are also three
overloads for (unsigned) integer values. These are particularly useful for when
you do not want to or cannot use chrono literals.

```cpp
modm::delay_ns(100);
modm::delay_us(200);
modm::delay_ms(300);
```
Note that these delay functions work at any CPU clock speed, even if changed
dynamically at runtime and are available very early in the startup process at
hardware-init time.
## Limitations
The main limitations are accuracy and length of delay. The only guarantee given
to you is to delay for _at least_ the specified time. Note that invocation of
interrupts during spinning may add delay too. For additional limitations also
check the description of the `modm:platform:core` modules.
You should always prefer Software Timers (see `modm:processing:timer`) over
these *blocking* delay functions. However, when `modm::Clock` is not set up yet,
or when you need very small delays (for example to bit-bang a protocol), you
need to use these delay functions.
Delay functions work at any CPU clock speed, even if changed dynamically and
are available very early in the startup process at hardware-init time.
!!! warning "Use the largest time unit possible!"
Correct behavior is not guaranteed for delays over 1000ns, us or ms!
32 changes: 0 additions & 32 deletions src/modm/platform/clock/common/common.hpp

This file was deleted.

1 change: 0 additions & 1 deletion src/modm/platform/clock/common/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def prepare(module, options):

def build(env):
env.outbasepath = "modm/src/modm/platform/clock"
env.copy("common.hpp")

if env[":target"].has_driver("clock:avr"):
env.copy("static.hpp")
1 change: 0 additions & 1 deletion src/modm/platform/clock/common/static.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#define MODM_AVR_CLOCK_HPP

#include <stdint.h>
#include "common.hpp"


namespace modm
Expand Down
9 changes: 3 additions & 6 deletions src/modm/platform/clock/stm32/rcc.cpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@
// ----------------------------------------------------------------------------

#include "rcc.hpp"
#include "common.hpp"

namespace modm::clock
namespace modm::platform
{
uint32_t modm_fastdata fcpu({{ "{0:,}".format(hsi_frequency|int * 1000000).replace(',', "'") }});
uint32_t modm_fastdata fcpu_kHz({{ "{0:,}".format(hsi_frequency|int * 1000).replace(',', "'") }});
uint16_t modm_fastdata fcpu_MHz({{ hsi_frequency }});
uint16_t modm_fastdata ns_per_loop({{ "{0:,}".format((loops * 1000.0 / hsi_frequency)|int).replace(',', "'") }});
uint16_t modm_fastdata delay_fcpu_MHz({{ hsi_frequency }});
uint16_t modm_fastdata delay_ns_per_loop({{ "{0:,}".format((loops * 1000.0 / hsi_frequency)|int).replace(',', "'") }});
}

// ----------------------------------------------------------------------------
Expand Down
11 changes: 6 additions & 5 deletions src/modm/platform/clock/stm32/rcc_impl.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
*/
// ----------------------------------------------------------------------------

#include "common.hpp"
#include <cmath>

namespace modm::platform
{

extern uint16_t delay_fcpu_MHz;
extern uint16_t delay_ns_per_loop;

constexpr Rcc::flash_latency
Rcc::computeFlashLatency(uint32_t Core_Hz, uint16_t Core_mV)
{
Expand Down Expand Up @@ -91,10 +94,8 @@ template< uint32_t Core_Hz >
void
Rcc::updateCoreFrequency()
{
modm::clock::fcpu = Core_Hz;
modm::clock::fcpu_kHz = Core_Hz / 1'000;
modm::clock::fcpu_MHz = Core_Hz / 1'000'000;
modm::clock::ns_per_loop = ::round({{loops}}000.f / (Core_Hz / 1'000'000));
delay_fcpu_MHz = Core_Hz / 1'000'000;
delay_ns_per_loop = std::round({{loops}}000.f / (Core_Hz / 1'000'000));
}

constexpr bool
Expand Down
50 changes: 0 additions & 50 deletions src/modm/platform/core/avr/delay_impl.hpp

This file was deleted.

Loading

0 comments on commit 8f49d35

Please sign in to comment.