.. _a-more-complex-program: A more complex program ---------------------- Now that we know, how the basic structure of a Circle application looks like, we want to add some more often used classes and thus functionality. The following program is based on the *sample/04-timer*. You will need a HDMI display or a serial terminal, connected to your Raspberry Pi, to try it out. First create a new subdirectory below *app/* and copy the files *main.cpp* and *Makefile* from the previously discussed program. These files remain unchanged. Only our ``CKernel`` class will be modified and extended. The class definition looks now as follows: .. code-block:: c++ :caption: kernel.h #ifndef _kernel_h #define _kernel_h #include #include #include #include #include #include #include #include #include #include enum TShutdownMode { ShutdownNone, ShutdownHalt, ShutdownReboot }; class CKernel { public: CKernel (void); ~CKernel (void); boolean Initialize (void); TShutdownMode Run (void); private: static void TimerHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext); private: CActLED m_ActLED; CKernelOptions m_Options; CDeviceNameService m_DeviceNameService; CScreenDevice m_Screen; CSerialDevice m_Serial; CExceptionHandler m_ExceptionHandler; CInterruptSystem m_Interrupt; CTimer m_Timer; CLogger m_Logger; }; #endif We add the following classes as member objects to ``CKernel``: ====================== ====================================================== Class Purpose ====================== ====================================================== CKernelOptions Provides command line options from *cmdline.txt* CDeviceNameService Maps device names to a pointer to the device object CScreenDevice Access to the HDMI display (screen) CSerialDevice Access to the serial interface (UART) CExceptionHandler Reports system faults (abort exceptions) for debugging CInterruptSystem Interrupt (IRQ and FIQ) handling CTimer Provides several time services CLogger System logging facility ====================== ====================================================== Furthermore a private static ``TimerHandler()`` callback function is added, which is used to show the function of kernel timers, implemented by the ``CTimer`` class. The file *kernel.cpp* has been updated like this: .. code-block:: c++ :caption: kernel.cpp #include "kernel.h" static const char FromKernel[] = "kernel"; CKernel::CKernel (void) : m_Screen (m_Options.GetWidth (), m_Options.GetHeight ()), m_Timer (&m_Interrupt), m_Logger (m_Options.GetLogLevel (), &m_Timer) { m_ActLED.Blink (5); } CKernel::~CKernel (void) { } In the constructor of ``CKernel`` the ``CScreenDevice`` member is explicitly initialized using the display width and height from the configuration file *cmdline.txt* on the SD card. The display resolution can be selected in the first line of this file for example like this: ``width=640 height=480``. The ``CTimer`` member uses interrupts (IRQ) to implement a system tick of 100 Hz and hence gets a pointer to the ``CInterruptSystem`` member object. .. note:: All Circle options for *cmdline.txt* are listed in `doc/cmdline.txt `_. All options must be specified in the first line, separated with a space. The system logging facility ``CLogger`` is initialized with the wanted logging level and a pointer to the timer, so that it can log the system time. The logging level can be set in *cmdline.txt* by adding ``loglevel=N``, where N is a number between 0 (panic) and 4 (debug, default). Only the log messages with a severity of smaller or equal then this value will be logged. .. code-block:: c++ :caption: kernel.cpp (continued) boolean CKernel::Initialize (void) { boolean bOK = TRUE; if (bOK) { bOK = m_Screen.Initialize (); } if (bOK) { bOK = m_Serial.Initialize (115200); } if (bOK) { CDevice *pTarget = m_DeviceNameService.GetDevice ( m_Options.GetLogDevice (), FALSE); if (pTarget == 0) { pTarget = &m_Screen; } bOK = m_Logger.Initialize (pTarget); } if (bOK) { bOK = m_Interrupt.Initialize (); } if (bOK) { bOK = m_Timer.Initialize (); } return bOK; } In the ``Initialize()`` method the second step of the class member initialization is done. The call to ``m_Logger.Initialize()`` gets a pointer to the logging device as a parameter, which is ``&m_Screen`` by default. If you add ``logdev=ttyS1`` to *cmdline.txt* you can read the messages on a connected serial terminal. The mapping from device name to device object pointer takes place in ``m_DeviceNameService.GetDevice()``, which returns 0, if the device name is not found. .. important:: The order of initialization is important. The same applies to the constructor and the order of member objects in the class definition in *kernel.h*. .. code-block:: c++ :caption: kernel.cpp (continued) TShutdownMode CKernel::Run (void) { m_Logger.Write (FromKernel, LogNotice, "An exception will occur after 15 seconds from now"); m_Timer.StartKernelTimer (15 * HZ, TimerHandler); unsigned nTime = m_Timer.GetTime (); while (1) { while (nTime == m_Timer.GetTime ()) { // just wait a second } nTime = m_Timer.GetTime (); m_Logger.Write (FromKernel, LogNotice, "Time is %u", nTime); } return ShutdownHalt; } ``m_Logger.Write()`` writes a message of the given severity to the system log. ``FromKernel`` names the source of the message (see definition above). ``m_Timer.StartKernelTimer()`` triggers, that the ``TimerHandler()`` gets called after 15 seconds. ``m_Timer.GetTime()`` returns the current local system time in seconds since 1970-01-01 00:00:00. Because we do not use a real-time clock, the actual time is equal to the uptime of the system. The program generates a log message every second on the screen or serial terminal, if it is selected as logging device. .. code-block:: c++ :caption: kernel.cpp (continued) void CKernel::TimerHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext) { void (*pInvalid) (void) = (void (*) (void)) 0x500000; (*pInvalid) (); } After 15 seconds the ``TimerHandler()`` is called and generates a "Prefetch abort" exception by jumping to the address 0x500000, because the memory region at this address is marked as "not executable". The Appendix :ref:`analyzing-exceptions` explains using this program, how the information can be analyzed, which is displayed, when an abort exception occurs.