Skip to content

Commit

Permalink
Merge pull request #31 from bxparks/develop
Browse files Browse the repository at this point in the history
merge v1.3.1 into master
  • Loading branch information
bxparks authored Jun 2, 2021
2 parents 9c698d1 + 205469a commit 256dfc3
Show file tree
Hide file tree
Showing 98 changed files with 2,006 additions and 764 deletions.
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# Changelog

* Unreleased
* 1.3.1 (2021-06-02)
* Bring back `COROUTINE_DELAY_MICROS()` and `COROUTINE_DELAY_SECONDS(),
with an alternate implemenation that increases flash and static memory
*only* if they are used.
* The `Coroutine` itself knows whether it is delaying in units of
milliseconds, microseconds, or seconds, based on its continuation
point.
* Make `CoroutineScheduler::runCoroutine()` always call into
`Coroutine::runCoroutine()` when the `Coroutine::mStatus` is delaying,
instead of preemptively trying to figure out if the delay has expired.
* `Coroutine` does not need a runtime `mDelayType` descriminator.
* The result is that the code to support `COROUTINE_DELAY_MICROS()` and
`COROUTINE_DELAY_SECONDS()` is not pulled into the program if they are
not used.
* 1.3.0 (2021-06-02)
* Activate GitHub Discussions for the project.
* **Potentially Breaking**: Change `Coroutine` destructor from virtual to
Expand All @@ -12,7 +26,7 @@
* **Potentially Breaking**: Lift `Coroutine` into `CoroutineTemplate` class.
Lift `CoroutineScheduler` into `CoroutineSchedulerTemplate` class.
* Define `Coroutine` to be `CoroutineTemplate<ClockInterface>`, almost
* fully backwards compatible with previous implementation.
fully backwards compatible with previous implementation.
* Define `CoroutineScheduler` to be
`CoroutineSchedulerTemplate<Coroutine>`, almost fully backwards
compatible with previous implementation.
Expand Down
120 changes: 74 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ their life cycle:
* `COROUTINE_AWAIT(condition)`: yield until `condition` becomes `true`
* `COROUTINE_DELAY(millis)`: yields back execution for `millis`. The `millis`
parameter is defined as a `uint16_t`.
* `COROUTINE_DELAY_MICROS(micros)`: yields back execution for `micros`. The
`micros` parameter is defined as a `uint16_t`.
* `COROUTINE_DELAY_SECONDS(seconds)`: yields back execution for
`seconds`. The `seconds` parameter is defined as a `uint16_t`.
* `COROUTINE_LOOP()`: convenience macro that loops forever
* `COROUTINE_CHANNEL_WRITE(channel, value)`: writes a value to a `Channel`
* `COROUTINE_CHANNEL_READ(channel, value)`: reads a value from a `Channel`
Expand All @@ -64,16 +68,16 @@ others (in my opinion of course):
* ~1.2 microseconds on a 16 MHz ATmega328P
* ~0.4 microseconds on a 48 MHz SAMD21
* ~0.3 microseconds on a 72 MHz STM32
* ~0.4 microseconds on a 80 MHz ESP8266
* ~0.03 microseconds on a 240 MHz ESP32
* ~0.3 microseconds on a 80 MHz ESP8266
* ~0.1 microseconds on a 240 MHz ESP32
* ~0.17 microseconds on 96 MHz Teensy 3.2 (depending on compiler
settings)
* Coroutine Scheduling (use `CoroutineScheduler::loop()`):
* ~5.2 microseconds on a 16 MHz ATmega328P
* ~1.9 microseconds on a 48 MHz SAMD21
* ~1.4 microseconds on a 72 MHz STM32
* ~1.1 microseconds on a 80 MHz ESP8266
* ~0.3 microseconds on a 240 MHz ESP32
* ~5.5 microseconds on a 16 MHz ATmega328P
* ~1.3 microseconds on a 48 MHz SAMD21
* ~0.9 microseconds on a 72 MHz STM32
* ~0.6 microseconds on a 80 MHz ESP8266
* ~0.2 microseconds on a 240 MHz ESP32
* ~0.5 microseconds on 96 MHz Teensy 3.2 (depending on compiler
settings)
* uses the [computed goto](https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html)
Expand Down Expand Up @@ -106,7 +110,7 @@ AceRoutine is a self-contained library that works on any platform supporting the
Arduino API (AVR, Teensy, ESP8266, ESP32, etc), and it provides a handful of
additional macros that can reduce boilerplate code.

**Version**: 1.3 (2021-06-02)
**Version**: 1.3.1 (2021-06-02)

**Changelog**: [CHANGELOG.md](CHANGELOG.md)

Expand Down Expand Up @@ -501,45 +505,69 @@ etc) for a handful of AceRoutine features. Here are some highlights:
**Arduino Nano (8-bits)**
```
+--------------------------------------------------------------+
| functionality | flash/ ram | delta |
|---------------------------------+--------------+-------------|
| Baseline | 606/ 11 | 0/ 0 |
|---------------------------------+--------------+-------------|
| One Delay Function | 654/ 13 | 48/ 2 |
| Two Delay Functions | 714/ 15 | 108/ 4 |
|---------------------------------+--------------+-------------|
| One Coroutine | 840/ 30 | 234/ 19 |
| Two Coroutines | 1010/ 47 | 404/ 36 |
|---------------------------------+--------------+-------------|
| Scheduler, One Coroutine | 946/ 32 | 340/ 21 |
| Scheduler, Two Coroutines | 1058/ 43 | 452/ 32 |
|---------------------------------+--------------+-------------|
| Blink Function | 938/ 14 | 332/ 3 |
| Blink Coroutine | 1154/ 30 | 548/ 19 |
+--------------------------------------------------------------+
+------------------------------------------------------------------+
| functionality | flash/ ram | delta |
|-------------------------------------+--------------+-------------|
| Baseline | 400/ 11 | 0/ 0 |
|-------------------------------------+--------------+-------------|
| One Delay Function | 450/ 13 | 50/ 2 |
| Two Delay Functions | 508/ 15 | 108/ 4 |
|-------------------------------------+--------------+-------------|
| One Coroutine | 628/ 30 | 228/ 19 |
| Two Coroutines | 796/ 47 | 396/ 36 |
|-------------------------------------+--------------+-------------|
| One Coroutine (micros) | 596/ 30 | 196/ 19 |
| Two Coroutines (micros) | 732/ 47 | 332/ 36 |
|-------------------------------------+--------------+-------------|
| One Coroutine (seconds) | 724/ 30 | 324/ 19 |
| Two Coroutines (seconds) | 920/ 47 | 520/ 36 |
|-------------------------------------+--------------+-------------|
| Scheduler, One Coroutine | 742/ 32 | 342/ 21 |
| Scheduler, Two Coroutines | 904/ 49 | 504/ 38 |
|-------------------------------------+--------------+-------------|
| Scheduler, One Coroutine (micros) | 710/ 32 | 310/ 21 |
| Scheduler, Two Coroutines (micros) | 840/ 49 | 440/ 38 |
|-------------------------------------+--------------+-------------|
| Scheduler, One Coroutine (seconds) | 838/ 32 | 438/ 21 |
| Scheduler, Two Coroutines (seconds) | 1028/ 49 | 628/ 38 |
|-------------------------------------+--------------+-------------|
| Blink Function | 546/ 14 | 146/ 3 |
| Blink Coroutine | 752/ 30 | 352/ 19 |
+------------------------------------------------------------------+
```
**ESP8266 (32-bits)**
```
+--------------------------------------------------------------+
| functionality | flash/ ram | delta |
|---------------------------------+--------------+-------------|
| Baseline | 256924/26800 | 0/ 0 |
|---------------------------------+--------------+-------------|
| One Delay Function | 256988/26808 | 64/ 8 |
| Two Delay Functions | 257052/26808 | 128/ 8 |
|---------------------------------+--------------+-------------|
| One Coroutine | 257104/26820 | 180/ 20 |
| Two Coroutines | 257264/26844 | 340/ 44 |
|---------------------------------+--------------+-------------|
| Scheduler, One Coroutine | 257152/26828 | 228/ 28 |
| Scheduler, Two Coroutines | 257232/26844 | 308/ 44 |
|---------------------------------+--------------+-------------|
| Blink Function | 257424/26816 | 500/ 16 |
| Blink Coroutine | 257556/26836 | 632/ 36 |
+--------------------------------------------------------------+
+------------------------------------------------------------------+
| functionality | flash/ ram | delta |
|-------------------------------------+--------------+-------------|
| Baseline | 256924/26800 | 0/ 0 |
|-------------------------------------+--------------+-------------|
| One Delay Function | 256988/26808 | 64/ 8 |
| Two Delay Functions | 257052/26808 | 128/ 8 |
|-------------------------------------+--------------+-------------|
| One Coroutine | 257104/26820 | 180/ 20 |
| Two Coroutines | 257264/26844 | 340/ 44 |
|-------------------------------------+--------------+-------------|
| One Coroutine (micros) | 257136/26820 | 212/ 20 |
| Two Coroutines (micros) | 257296/26844 | 372/ 44 |
|-------------------------------------+--------------+-------------|
| One Coroutine (seconds) | 257136/26820 | 212/ 20 |
| Two Coroutines (seconds) | 257312/26844 | 388/ 44 |
|-------------------------------------+--------------+-------------|
| Scheduler, One Coroutine | 257152/26828 | 228/ 28 |
| Scheduler, Two Coroutines | 257280/26844 | 356/ 44 |
|-------------------------------------+--------------+-------------|
| Scheduler, One Coroutine (micros) | 257168/26828 | 244/ 28 |
| Scheduler, Two Coroutines (micros) | 257312/26844 | 388/ 44 |
|-------------------------------------+--------------+-------------|
| Scheduler, One Coroutine (seconds) | 257168/26828 | 244/ 28 |
| Scheduler, Two Coroutines (seconds) | 257328/26844 | 404/ 44 |
|-------------------------------------+--------------+-------------|
| Blink Function | 257424/26816 | 500/ 16 |
| Blink Coroutine | 257556/26836 | 632/ 36 |
+------------------------------------------------------------------+
```
Comparing `Blink Function` and `Blink Coroutine` is probably the most
Expand Down Expand Up @@ -567,7 +595,7 @@ Arduino Nano:
|---------------------+--------+-------------+--------|
| EmptyLoop | 10000 | 1.700 | 0.000 |
| DirectScheduling | 10000 | 2.900 | 1.200 |
| CoroutineScheduling | 10000 | 6.900 | 5.200 |
| CoroutineScheduling | 10000 | 7.200 | 5.500 |
+---------------------+--------+-------------+--------+
```
Expand All @@ -577,9 +605,9 @@ ESP8266:
+---------------------+--------+-------------+--------+
| Functionality | iters | micros/iter | diff |
|---------------------+--------+-------------+--------|
| EmptyLoop | 10000 | 0.100 | 0.000 |
| DirectScheduling | 10000 | 0.500 | 0.400 |
| CoroutineScheduling | 10000 | 1.200 | 1.100 |
| EmptyLoop | 10000 | 0.200 | 0.000 |
| DirectScheduling | 10000 | 0.500 | 0.300 |
| CoroutineScheduling | 10000 | 0.800 | 0.600 |
+---------------------+--------+-------------+--------+
```
Expand Down
93 changes: 77 additions & 16 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ See the [README.md](README.md) for installation instructions and other
background information. This document describes how to use the library once it
is installed.

**Version**: 1.3 (2021-06-02)
**Version**: 1.3.1 (2021-06-02)

## Table of Contents

Expand Down Expand Up @@ -87,6 +87,10 @@ The following macros are available to hide a lot of boilerplate code:
* `COROUTINE_AWAIT(condition)`: yields until `condition` become `true`
* `COROUTINE_DELAY(millis)`: yields back execution for `millis`. The maximum
allowable delay is 32767 milliseconds.
* `COROUTINE_DELAY_MICROS(micros)`: yields back execution for `micros`. The
maximum allowable delay is 32767 microseconds.
* `COROUTINE_DELAY_SECONDS(seconds)`: yields back execution for `seconds`. The
maximum allowable delay is 32767 seconds.
* `COROUTINE_LOOP()`: convenience macro that loops forever, replaces
`COROUTINE_BEGIN()` and `COROUTINE_END()`
* `COROUTINE_CHANNEL_WRITE()`: writes a message to a `Channel`
Expand Down Expand Up @@ -277,9 +281,12 @@ while (!condition) COROUTINE_YIELD();
<a name="Delay"></a>
### Delay

**Delay Milliseconds**

The `COROUTINE_DELAY(millis)` macro yields back control to other coroutines
until `millis` milliseconds have elapsed. The following waits for 100
milliseconds:
until `millis` milliseconds have elapsed. This is analogous to the built-in
Arduino `delay()` function, except that this is non-blocking. The following
waits for 100 milliseconds:

```C++
COROUTINE(waitMillis) {
Expand All @@ -291,26 +298,80 @@ COROUTINE(waitMillis) {
}
```
The `millis` argument is a `uint16_t`, a 16-bit unsigned integer, which reduces
The `millis` argument is a `uint16_t`, a 16-bit unsigned integer, which saves
the size of each coroutine instance by 4 bytes (8-bit processors) or 8 bytes
(32-bit processors). However, the actual maximum delay is limited to 32767
milliseconds to avoid overflow situations if the other coroutines in the system
take too much time for their work before returning control to the waiting
coroutine. With this limit, the other coroutines have as much as 32767
milliseconds before it must yield, which should be more than enough time for any
conceivable situation. In practice, coroutines should complete their work within
several milliseconds and yield control to the other coroutines as soon as
possible.
For delays longer than 32767 milliseconds, we can use an explicit for-loop. For
example, to delay for 100,000 seconds, we can do this:
(32-bit processors) compared to using an `uint32_t`. The largest number that
can be represented by a `uint16_t` is 65535, but the actual maximum delay is
limited to 32767 milliseconds to avoid overflow situations if the other
coroutines in the system take too much time for their work before returning
control to the waiting coroutine. With this limit, the other coroutines have as
much as 32767 milliseconds before it must yield, which should be more than
enough time for any conceivable situation. In practice, coroutines should
complete their work within several milliseconds and yield control to the other
coroutines as soon as possible.
**Delay Microseconds**
On faster microcontrollers, it might be useful to yield for microseconds using
the `COROUTINE_DELAY_MICROS(delayMicros)`. The following example waits for 300
microseconds:
```C++
COROUTINE(waitMicros) {
COROUTINE_BEGIN();
...
COROUTINE_DELAY_MICROS(300);
...
COROUTINE_END();
}
```
This macro has a number constraints:

* The maximum delay is 32767 micros.
* All other coroutines in the program *must* yield within 32767 microsecond,
otherwise the internal timing variable will overflow and an incorrect delay
will occur.
* The accuracy of `COROUTINE_DELAY_MICROS()` is not guaranteed because the
overhead of context switching and checking the delay's expiration may
consume a significant portion of the requested delay in microseconds.

**Delay Seconds**

For delays greater than 32767 milliseconds, we can use the
`COROUTINE_DELAY_SECONDS(seconds)` convenience macro. The following example
waits for 200 seconds:

```C++
COROUTINE(waitSeconds) {
COROUTINE_BEGIN();
...
COROUTINE_DELAY_SECONDS(200);
...
COROUTINE_END();
}
```
This macro has some constraints and caveats:
* The maximum number of seconds is 32767 seconds.
* The delay is implemented using the `millis()` clock divided by 1000.
* On 8-bit processors without hardware division instruction, the software
division consumes CPU time and flash memory, about 100 bytes on an AVR.
* The `unsigned long` returned by `millis()` rolls over every
4294967.296 seconds (49.7 days). During that rollover, the
`COROUTINE_DELAY_SECONDS()` will return 0.704 seconds too early. If the
delay value is relatively large, e.g. 100 seconds, this inaccuracy
probably won't matter too much.
We can also use an explicit for-loop. For example, to delay for 1000 seconds, we
can loop 100 times over a `COROUTINE_DELAY()` of 10 seconds, like this:
```C++
COROUTINE(waitThousandSeconds) {
static uint16_t i;
COROUTINE_BEGIN();
for (i = 0; i < 10000; i++) {
for (i = 0; i < 100; i++) {
COROUTINE_DELAY(10000); // 10 seconds
}
...
Expand Down
2 changes: 1 addition & 1 deletion docs/doxygen.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ PROJECT_NAME = "AceRoutine"
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER = 1.3.0
PROJECT_NUMBER = 1.3.1

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
Expand Down
2 changes: 1 addition & 1 deletion docs/html/AceRoutine_8h_source.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">AceRoutine
&#160;<span id="projectnumber">1.3.0</span>
&#160;<span id="projectnumber">1.3.1</span>
</div>
<div id="projectbrief">A low-memory, fast-switching, cooperative multitasking library using stackless coroutines on Arduino platforms.</div>
</td>
Expand Down
2 changes: 1 addition & 1 deletion docs/html/Channel_8h_source.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">AceRoutine
&#160;<span id="projectnumber">1.3.0</span>
&#160;<span id="projectnumber">1.3.1</span>
</div>
<div id="projectbrief">A low-memory, fast-switching, cooperative multitasking library using stackless coroutines on Arduino platforms.</div>
</td>
Expand Down
Loading

0 comments on commit 256dfc3

Please sign in to comment.