Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/avr/1-wire/ds18b20/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

#include <modm/platform.hpp>
#include <modm/driver/temperature/ds18b20.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

using namespace modm::platform;
using namespace modm::literals;
Expand Down
2 changes: 1 addition & 1 deletion examples/avr/adc/basic/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// ----------------------------------------------------------------------------

#include <modm/platform.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

using namespace modm::platform;
using namespace modm::literals;
Expand Down
2 changes: 1 addition & 1 deletion examples/avr/adc/oversample/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ using namespace std::chrono_literals;

// Create a new UART object

#include <modm/io/iostream.hpp>
#include <modm/io.hpp>
// Create a IOStream for complex formatting tasks
modm::IODeviceWrapper< Uart0, modm::IOBuffer::BlockIfFull > device;
modm::IOStream output(device);
Expand Down
2 changes: 1 addition & 1 deletion examples/avr/display/dogm128/touch/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

#include <modm/platform.hpp>
#include <modm/driver/display/ea_dog.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>
#include <modm/architecture/interface/clock.hpp>

using namespace modm::platform;
Expand Down
2 changes: 1 addition & 1 deletion examples/avr/uart/extended/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// ----------------------------------------------------------------------------

#include <modm/platform.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>
using namespace modm::literals;

using namespace modm::platform;
Expand Down
2 changes: 1 addition & 1 deletion examples/rp_pico/adc_simple/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#include <modm/board.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>
#include <modm/platform.hpp>

int
Expand Down
1 change: 1 addition & 0 deletions examples/rp_pico/st7789/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// ----------------------------------------------------------------------------

#include <modm/board.hpp>
#include <modm/io.hpp>
#include <modm/driver/display/st7789.hpp>
#include <modm/driver/display/st7789/st7789_spi_interface.hpp>
#include <modm/processing/timer/periodic_timer.hpp>
Expand Down
2 changes: 1 addition & 1 deletion examples/same70_xplained/adc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

#include <modm/board.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

using namespace modm::platform;
using namespace modm::literals;
Expand Down
4 changes: 2 additions & 2 deletions examples/samg55_xplained_pro/adc-uart/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#include <modm/board.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

using namespace modm::platform;
using namespace modm::literals;
Expand Down Expand Up @@ -52,4 +52,4 @@ main()

modm::delay(500ms);
}
}
}
4 changes: 2 additions & 2 deletions examples/samg55_xplained_pro/timer/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#include <modm/board.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>
#include <modm/platform.hpp>

using namespace modm::platform;
Expand Down Expand Up @@ -83,4 +83,4 @@ main()

while (true)
;
}
}
2 changes: 1 addition & 1 deletion examples/samv71_xplained_ultra/adc/dma/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#include <modm/board.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>
#include <array>
#include <atomic>

Expand Down
2 changes: 1 addition & 1 deletion examples/samv71_xplained_ultra/adc/multi-channel/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#include <modm/board.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

using namespace modm::platform;

Expand Down
2 changes: 1 addition & 1 deletion examples/samv71_xplained_ultra/adc/simple/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/

#include <modm/board.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

using namespace modm::platform;
using namespace modm::literals;
Expand Down
2 changes: 1 addition & 1 deletion examples/stm32f072_discovery/tmp102/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include <modm/processing.hpp>
#include <modm/driver/temperature/tmp102.hpp>

#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

using Usart1 = BufferedUart<UsartHal1>;
modm::IODeviceWrapper< Usart1, modm::IOBuffer::BlockIfFull > device;
Expand Down
2 changes: 1 addition & 1 deletion examples/stm32f4_discovery/temperature_ltc2984/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <modm/board.hpp>
#include <modm/processing.hpp>
#include <modm/driver/temperature/ltc2984.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

using Usart2 = BufferedUart<UsartHal2, UartTxBuffer<2048>>;
modm::IODeviceWrapper< Usart2, modm::IOBuffer::BlockIfFull > device;
Expand Down
2 changes: 1 addition & 1 deletion examples/stm32f4_discovery/tmp102/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <modm/board.hpp>
#include <modm/processing.hpp>
#include <modm/driver/temperature/tmp102.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

using Usart2 = BufferedUart<UsartHal2>;
modm::IODeviceWrapper< Usart2, modm::IOBuffer::BlockIfFull > device;
Expand Down
2 changes: 1 addition & 1 deletion src/modm/debug/logger/logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#define MODM_LOGGER_HPP

#include <modm/architecture/utils.hpp>
#include <modm/io/iostream.hpp>
#include <modm/io.hpp>

#include "level.hpp"
#include "style.hpp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
#define MODM_IODEVICE_WRAPPER_HPP

#include <stdint.h>

%% if with_fiber
#include <modm/processing/fiber.hpp>
%% endif
%#
#include "iodevice.hpp"

namespace modm
Expand All @@ -39,25 +42,49 @@ IOBuffer
template< class Device, IOBuffer behavior >
class IODeviceWrapper : public IODevice
{
%% if with_fiber
static constexpr fiber::id NoOwner{fiber::id(-1)};
static inline fiber::id id{NoOwner};
%% endif
public:
IODeviceWrapper() = default;
using IODevice::write;

void
write(char c) override
{
bool written;
do
if constexpr (behavior == IOBuffer::BlockIfFull)
%% if with_fiber
{
written = Device::write(uint8_t(c));
// lock this device to one fiber until a newline is received
const auto me = this_fiber::get_id();
this_fiber::poll([&]{ return id == NoOwner or id == me; });
id = me;

this_fiber::poll([&]{ return Device::write(uint8_t(c)); });

if (c == '\n') id = NoOwner;
}
while(behavior == IOBuffer::BlockIfFull and not written);
%% else
while (not Device::write(uint8_t(c))) ;
%% endif
else Device::write(uint8_t(c));
}

void
flush() override
{
%% if with_fiber
const auto me = this_fiber::get_id();
this_fiber::poll([&]{ return id == NoOwner; });
id = me;

this_fiber::poll([&]{ return Device::isWriteFinished(); });

id = NoOwner;
%% else
Device::flushWriteBuffer();
%% endif
}

bool
Expand All @@ -71,6 +98,10 @@ class IODeviceWrapper : public IODevice
template< class Device, IOBuffer behavior >
class IODeviceObjectWrapper : public IODevice
{
%% if with_fiber
static constexpr fiber::id NoOwner{fiber::id(-1)};
fiber::id id{NoOwner};
%% endif
Device &device;
public:
IODeviceObjectWrapper(Device& device) : device{device} {}
Expand All @@ -79,18 +110,38 @@ class IODeviceObjectWrapper : public IODevice
void
write(char c) override
{
bool written;
do
if constexpr (behavior == IOBuffer::BlockIfFull)
%% if with_fiber
{
written = device.write(uint8_t(c));
// lock this device to one fiber until a newline is received
const auto me = this_fiber::get_id();
this_fiber::poll([&]{ return id == NoOwner or id == me; });
id = me;

this_fiber::poll([&]{ return device.write(uint8_t(c)); });

if (c == '\n') id = NoOwner;
}
while(behavior == IOBuffer::BlockIfFull and not written);
%% else
while (not device.write(uint8_t(c))) ;
%% endif
else device.write(uint8_t(c));
}

void
flush() override
{
%% if with_fiber
const auto me = this_fiber::get_id();
this_fiber::poll([&]{ return id == NoOwner; });
id = me;

this_fiber::poll([&]{ return device.isWriteFinished(); });

id = NoOwner;
%% else
device.flushWriteBuffer();
%% endif
}

bool
Expand Down
1 change: 0 additions & 1 deletion src/modm/io/iostream.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#include <string_view>

#include "iodevice.hpp"
#include "iodevice_wrapper.hpp" // convenience

%% if options.with_printf
/// @cond
Expand Down
4 changes: 3 additions & 1 deletion src/modm/io/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def init(module):
def prepare(module, options):
module.depends(
":architecture:accessor",
":architecture:fiber",
":math:utils")

is_avr = options[":target"].identifier.platform in ["avr"]
Expand Down Expand Up @@ -45,12 +46,13 @@ def build(env):
env.substitutions = {
"is_hosted": target.platform == "hosted",
"is_avr": target.platform == "avr",
"with_fiber": env.has_module(":processing:fiber"),
"family": target.family,
"core": core,
}
env.outbasepath = "modm/src/modm/io"
env.copy("iodevice.hpp")
env.copy("iodevice_wrapper.hpp")
env.template("iodevice_wrapper.hpp.in")
env.template("iostream_printf.cpp.in")
env.template("iostream.hpp.in")
env.template("iostream_chrono.hpp.in")
Expand Down
46 changes: 17 additions & 29 deletions src/modm/io/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,41 +24,14 @@ stream.printf("format number 8: %u or as signed -100: %d", 8, -100);
## Redirecting IOStreams

The `modm::IODeviceWrapper` transforms any peripheral device that provides static
`write()` and `read()` functions into an `IODevice`.

You have to decide what happens when the device buffer is full and you cannot
write to it at the moment. There are two options:

1. busy wait until the buffer is free, or
2. discard the bytes that cannot be written.

Option 1 has the advantage, that none of your data will be lost,
however, busy-waiting can take a long time and can mess up your
program timings.
There is also a **high risk of deadlock**, when writing to a
IODevice inside of an interrupt and then busy-waiting forever
because the IODevice requires interrupts itself to send out
the data.

It is therefore highly recommended to use option 2, where surplus
data will be discarded.
You should increase the IODevice buffer size, if you experience
missing data from your connection.
This behavior is also deadlock safe when called from inside another
interrupt, and your program timing is minimally affected (essentially
only coping data into the buffer).

There is no default template argument, so that you hopefully make
a conscious decision and be aware of this behavior.

Example:
`write()` and `read()` functions into an `IODevice`:

```cpp
// configure a UART
using Uart = Uart0;

// wrap it into an IODevice
modm::IODeviceWrapper<Uart, modm::IOBuffer::DiscardIfFull> device;
modm::IODeviceWrapper<Uart, modm::IOBuffer::BlockIfFull> device;

// use this device to print a message
device.write("Hello");
Expand All @@ -67,3 +40,18 @@ device.write("Hello");
modm::IOStream stream(device);
stream << " World!";
```


## IODevice Buffer Behavior

The `modm::IODeviceWrapper` can be configured to discard or block in case the
device is full. Discarding data is always non-blocking, however, blocking on
write involves waiting in a loop until the device has space. This can cause a
deadlock when called inside an interrupt!

If compiled with the `modm:processing:fiber` module, the blocking behavior
allows the fiber to yield while waiting for the device. To prevent interlaced
output from different fibers, the `write(char)` function is protected by a
mutex that releases when a newline character `\n` is written. The `flush()`
function is also mutex protected to allow one fiber to reliably flush the
stream without having other fibers push data into the device.
Loading
Loading