Tutorial: Building a Mainloop Application (Step 2)

Create an empty Mainloop Project

To create an empty mainloop project, select menu "File -> New -> Project" and "redBlocks Project" in the following dialog within the redBlocks Workbench. Click through the wizard, choose MainloopProject for the project type, specify a suitable project name (e. g. "HelloMainloop") and accept the default values for the rest of the parameters.

The newly created project includes the files Application.h, ComInterfaceLogger.h and HeapManagerConfig.h.

HeapManager

The project is preconfigured with the redBlocks HeapManager (see the Doxygen documentation of the redBlocks Component Library that is shipped with the redBlocks Eval Package for the features of the redBlocks HeapManager). It is configured via the file HeapManagerConfig.h (note that changes to the default configuration are not supported in the eval version).

In main.cpp the HeapManager is initialized with the following lines:

typedef redBlocks::Memory::HeapManager::THeapManagerStaticAlloc<12000> HeapManager;
HeapManager::init();

With this setup, the HeapManager maintains 12000 bytes of RAM that can be dynamically allocated during initialization until memory allocation is disabled at the end of the initialization sequence in main.cpp:

HeapManager::disableAllocation();

This approach supports the widely chosen concept to allow dynamic memory allocation during application initialization (in order to set up memory pools, instantiate application classes, ...) and prevent the use of dynamic memory allocation after that, in order to avoid sporadic and hard to find runtime errors due to memory fragmentation.

Logging

The project is also prepared for logging: LowLevelPlatform.h defines the identifier LogComInterface for a communication interface with index 0 (which is typically a UART on the real target) and initializes the simulation stub. As we want to use this communication interface in our example application for logging and for receiving commands, it makes sense to change its name to TerminalComInterface (which requires to change all references to it, like shown during this tutorial) :

typedef redBlocks::HAL::Drivers::TComInterfaceDriverStub<0> TerminalComInterface;
// ...
static void initLowLevelDrivers()
{
  // ...
  SimulationStubs::ComInterface::add <TerminalComInterface> (1, 1);
}

In Platform.h a high-level driver of type TBufferdComInterfaceWriter is associated with our low-level driver TerminalComInterface. This high-level driver provides the capability to buffer data and send them out asynchronously (in the send interrupt of the low-level driver). We configure the send buffer to a size of 400 bytes.

As we use the communication interface in send direction for logging only, we stick with its name BufferedLogComInterfaceWriter but need to change the name of the low level driver it is associated with to TerminalComInterface in the already prepared code snippet:

// ...
typedef redBlocks::HAL::TBufferedComInterfaceWriter<Platform::TerminalComInterface> BufferedLogComInterfaceWriter;
// ...
typedef redBlocks::Core::Log::TGlobalStandardLogger<Platform::BufferedLogComInterfaceWriter> StandardLogger;
static void initHighLevelDrivers()
{
  // initializes the high level driver that buffers logging output with a buffer size of 400 bytes
  new BufferedLogComInterfaceWriter( 400 );
  // initializes the standard logger
  new StandardLogger();
}

The project template already includes a definition for the type StandardLogger and its initialization. This is the class that is used by all logging macros (see API documentation on "Components" -> "Core Components" -> "Logging" for further details).

The file Platform.h also associates the send ready callback (which is typically invoked from an interrupt service routine on a real target) of the low level driver TerminalComInterface with the high-level driver BufferedLogComInterfaceWriter. Make sure to change the name of the low level driver to TerminalComInterface here as well:

// associate the send callback of the communication interface that is used for
// logging output with the high level driver (the receive callback mapping is
// defined in PlatformCallbacks.h)
RB_CONNECT_ISR_CBK(Platform::TerminalComInterface,
  Platform::TerminalComInterface::CBK_ON_SEND_READY,
  Platform::BufferedLogComInterfaceWriter::onSendReadyCallback())

Mainloop Scheduler

As the redBlocks Mainloop Scheduler requires a system tick, there is a corresponding driver stub defined in LowLevelPlatform.h:

// provides the static methods enableCallback() and disableCallback() in
// order to switch periodic system tick callback on and off
typedef redBlocks::Simulators::SimulationStubs::SystemTick SystemTickDriver;

... and initialized:

// configure system ticks: per simulation tick we generate 10 system ticks
// (which results in a simulated system tick period of 1 ms, if 100 simulation
// ticks are configured in main.cpp)
SimulationStubs::SystemTick::configure(10,1);

The Mainloop Scheduler requires a class that provides the static methods lock() and unlock() in order to temporarily deactivate all interrupts that may interfere with each other or the system tick. The simulation platform  provides the class redBlocks::Simulators::SimulationStubs::IsrLock for this purpose. The generated code in LowLevelPlatform.h already defines the alias LowLevelPlaform::IsrLock for this class:

// provides the static methods lock() and unlock() that are used to lock all interrupts
typedef redBlocks::Simulators::SimulationStubs::IsrLock IsrLock;

In order to use the power saving facilities of the Mainloop Scheduler it also defines a static method enterSleepMode() and a class PowerSaveCallback.

// provides the means to the mainloop scheduler to control the processor's sleep mode
typedef redBlocks::OS::MainLoopScheduler::DefaultSimulationPowerSaveCallback PowerSaveCallback;
static void enterSleepMode()
{
  redBlocks::Simulators::SimulationStubs::SleepMode::enter();
}

In Platform.h the class Platform configures the class template redBlocks::OS::TMainLoopScheduler with these low level drivers.

typedef redBlocks::OS::TMainLoopScheduler<LowLevelPlatform::IsrLock, 1, 1, LowLevelPlatform::PowerSaveCallback> OS;

Refer to the Doxygen documentation of the redBlocks Component Library that is shipped with the redBlocks Eval Package for the features and configuration options of this class. Note that in the eval version TMainLoopScheduler cannot be instantiated with any other arguments than the ones shown here.

Further down in Platform.h, the system tick's callback is associated with the TMainLoopScheduler's tick() method:

RB_CONNECT_ISR_CBK(
  Platform::SystemTickDriver,
  Platform::SystemTickDriver::CBK_ON_SYSTEM_TICK,
  Platform::OS::tick())

There is also an application template prepared in Application.h which includes the method run() that is called from main.cpp. It runs in an endless loop that processes all events and sends the processor to sleep mode, when there are currently no more events pending:

void run()
{
  while (true)
  {
    Platform::OS::processEvents();
    Platform::enterSleepMode();
  }
}

Keep in mind that neither Platform.h nor any application classes need to be changed when integrating the embedded application on the hardware target. It is only necessary to provide a file LowLevelPlaform.h with a class LowLevelPlatform that offers the same interface as the simulation variant of this class and the right functionality for the hardware target.

As this infrastructure is already in place (except the name change from LogComInterface to TerminalComInterface), it is time to move on to the next step and implement the application specific code.