Tutorial: Integrating the Mainloop Application on an XMC2Go Evaluation Board (Step 3)

Understanding the Anatomy of a Target Project

When a redBlocks project is integrated on the target, the high-level drivers and the application functionality that have been used in the simulation environment do not need to be altered in any way. It is merely necessary to initialize the target hardware, setup the interrupt callbacks and provide the required low-level hardware abstraction.

Let's have a look at file LowLevelPlatform.h first: It must provide the definitions that are required by the high-level platform definitions and the application modules, i. e. it needs to provide the same public interface as the file LowLevelPlatform.h that is used for simulation.

  • Instead of defining simulation driver stubs for our LEDs, the system tick driver, the TerminalComInterface and the means to control interrupts (IsrLock) and the processor's sleep mode (PowerSaveCallback) that are used by the mainloop scheduler, the appropriate target drivers need to be defined in LowLevelPlatform.h:
    typedef redBlocks::HAL::Drivers::TXmc1100DigitalOutputDriver<PORT1_BASE, 0> Led;
    typedef redBlocks::HAL::Drivers::TXmc1100DigitalOutputDriver<PORT1_BASE, 1> AwakeLed;
    typedef redBlocks::HAL::Drivers::TXmc1100SystemTickDriver<> SystemTickDriver;
    typedef redBlocks::HAL::Drivers::TXmc1100UartDriver<> TerminalComInterface;
    typedef redBlocks::HAL::Drivers::Xmc1100IsrLock IsrLock;
    typedef redBlocks::HAL::Drivers::Xmc1100PowerSaveCallback PowerSaveCallback;
    
  • The initialization code for these drivers is located in initLowLevelDrivers():
    static void initLowLevelDrivers()
    {
      // configure 1000 ticks per second and enable the system tick ISR
      SystemTickDriver::configure(1000);
      SystemTickDriver::enableCallback();
    
      // configure the UART for 19200 baud (no parity) and enable the 
      // receive ISR callback
      TerminalComInterface::configure(19200);
    
      // configure the pins, the LEDs are connected to as outputs
      Led::configure();
      AwakeLed::configure();
    }
    

Now let's turn to file main.cpp:

  • This is the place where the interrupt service routines are defined. When a uVision project is created, it defines empty default interrupt service routines for all entries of the processor's interrupt vector table; see uVision/RTE/Device/XMC1100-Q024x0064/startup_XMC1100.s

    We can override these default ISRs by simply redefining functions with the respective name somewhere in our program. Note that these redefined functions need to be declared with extern "C", if they are located in a C++ source file. In our example application we are interested in the UART's send and receive interrupt and in the system tick interrupt. Therefore, we define functions for these three interrupts and simply call the methods of the corresponding low-level drivers that are defined in LowLevelPlaform.h
    extern "C"
    {
      void USIC0_0_IRQHandler()
      {
        Platform::TerminalComInterface::onSendIsr();
      }
    
      void USIC0_1_IRQHandler()
      {
        Platform::TerminalComInterface::onRecvIsr();
      }
    
      void SysTick_Handler(void)
      {
        Platform::SystemTickDriver::onIsr();
      }
    }
    
  • The file also includes the application's entry routine main(). Here, the target's clock rate is configured, the HeapManager (if used) and the logging facility (if used) is set up. After that, the initialization sequences of the low-level and high-level platform modules are run. Finally, we instantiate the Application class, do some logging and call Application::run(). Note that the startup sequence for a much more sophisticated application that consists of several different application modules is typically identical, as the class Application should be in charge for instantiating the different application software modules (for an example of a more complex application refer to the VendingMachine example).
    int main( void )
    {
      // Clock configuration
      SCU_GENERAL->PASSWD = 0x000000C0UL;
      SCU_CLK->CLKCR = 0x3FF00400UL; // 8 MHz MCLK, 8 MHz PCLK
      SCU_GENERAL->PASSWD = 0x000000C3UL;
      SystemCoreClockUpdate();

      // We configure the heap size to 2000 bytes
      typedef redBlocks::Memory::HeapManager::THeapManagerStaticAlloc<2000> HeapManager;
      HeapManager::init();

      // Setup the logging facility
      new ComInterfaceLogger();

      // Driver initialization.
      Platform::initLowLevelDrivers();
      Platform::initHighLevelDrivers();

      // Initialize application.
      Application app;

      // Prevent dynamic memory allocation after this point.
      HeapManager::disableAllocation();
      RB_LOG_DEBUG( "Heap memory used: " << HeapManager::getUsed() );
      RB_LOG_DEBUG( "Application initialized, starting event processing" );

      app.run();
    }

Now it is time to start experimenting and enhancing the example application (e. g. use a RamRingBuffer in class Application to queue the incoming commands and support multiple character commands). When changing the example application, you can work conveniently within the redBlocks simulation environment on the host and switch smoothly to the target platform whenever you like. While all basic drivers (digital and analog I/Os, communication interfaces, flash memory, displays) are available in the simulation environment, only a small set of drivers for the XMC2Go board are part of the evaluation package (UART, system tick and digital ouputs). Their source code is included for reference and as a basis for other additional drivers (e. g. for digital inputs), you are certainly free to implement.

Note however, that there are certain restrictions in the redBlocks evaluation package. When you try to instantiate a redBlocks template with different arguments than the ones that are included in the evaluation package, you will run in linker errors.

For the anatomy of a more sophisticated redBlocks application, refer to the example VendingMachine that is located in the workspace of the redBlocks Workbench.