Reference Manual For QP/C 8.1.5
Loading...
Searching...
No Matches
Blinky

QP/C TutorialDining Philosophers Problem (DPP)

The ultra-simple Blinky example is the embedded systems' equivalent of the venerable "Hello World!" program, that is, the simplest possible working application that does "something". In the case of Blinky, this "something" is blinking an LED at the rate of 1Hz, where an LED turns on and remains on for 0.5 seconds, then turns off and remains off for 0.5 seconds.

Blinky running in a console.


Blinky running on an embedded board.


The Blinky example demonstrates the following QP/C features:

Remarks
While it is highly recommended to begin with the Blinky example, it does not demonstrate many important QP features and, therefore, might be too simplistic as a starting point for real-life projects. A more complete starting point with multiple communicating Active Objects, mutable events, and publish-subscribe is the Dining Philosopher Problem (DPP) example.

Blinky Versions

The Blinky example is provided in many versions for various execution targets, including host computers and embedded boards. All of these Blinky versions contain essentially the same state machine code and differ only in the Board Support Package (BSP).

<qp>                   // QP/C or QP/C++ installation directory
|
+---examples           // QP examples
|   |
|   +---posix-win32    // examples for the host (POSIX, Windows)
|   |   +---blinky                   // Blinky for Windows, Linux, macOS <== TRY FIRST
|   |
|   +---arm-cm         // examples for ARM Cortex-M embedded boards
|   |   +---blinky_ek-tm4c123gxl     // Blinky for EK-TM4C123GXL (Cortex-M4)
|   |   +---blinky_nucleo-c031c6     // Blinky for STM32 NUCLEO-C031C6 (Cortex-M0+)
|   |   +---blinky_nucleo-u545re     // Blinky for STM32 NUCLEO-U545RE (Cortex-M33)
|   |
|   +---stm32cube      // examples for STM32Cube IDE
|   |   +---blinky_nucleo-u545re     // Blinky for STM32 NUCLEO-U545RE (Cortex-M33)
|   |
|   +---arm-cr         // examples for ARM Cortex-R embedded boards
|   |   +---blinky_launchxl2-tms57012 // Blinky for LAUNCHXL2-TMS57012 (Cortex-R4)
|   |
|   +---msp430         // examples for MSP430 embedded boards
|   |   +---blinky_msp-exp430g2      // Blinky for MSP-EXP430G2 (MSP430 classic)
|   |   +---blinky_msp-exp430f5529lp // Blinky for MSP-EXP430f5529LP (MSP430 extended)
|   |
|   +---zephyr         // examples for Zephyr RTOS
|   |   +---blinky                   // Blinky for all Zephyr-supported boards
Remarks
The Blinky version in the posix-win32 directory runs on the host computer and does not require any embedded hardware, and is the first one that you should try.

Build Configurations

The Blinky example demonstrate multiple build configurations:

  • Debug — this configuration is built with full debugging information and minimal optimization. The QS trace instrumentation is disabled.
  • Release — this configuration is built with high optimization. Debugging at the source-code level is severely impaired due to the highly optimized code, but the debugger can be used to download and start the executable. The QS trace instrumentation is disabled.
  • Spy — like the debug variant, this variant is built with full debugging information and minimal optimization. Additionally, it is built with the QS trace instrumentation enabled. The on-board serial port and the Q-Spy host application are used for sending and viewing trace data.
Why do you need multiple build configurations?
The different phases of the embedded software life cycle pose different challenges. During the development and maintenance phase, for example, the emphasis is on the ease of debugging and verifying the correctness of the code, which requires lower levels of optimization and special instrumentation code. In contrast, for releasing the code in the final product, the emphasis is on small memory footprint, CPU efficiency, and low power consumption, which requires a high level of optimization and removal of any code instrumentation. To address these conflicting objectives, the same source code is compiled into multiple build configurations that differ in the use of compiler options and activation of the code instrumentation.

Blinky Code

The Blinky example demonstrates the general structure of QP/C applications. Most of the code is identical for any deployment target. The only target-dependent part is the Board Support Package (BSP) implementation (the file bsp.c ). Here are the annotated files comprising the Blinky example:

blinky            // Blinky example
|   blinky.qm     // QM model file for the Blinky application
|   app.h         // Application interface
|   bsp.h         // Board Support Package interface
|   blinky.c      // active object implementation
|   bsp.c         // Board Support Package implementatin
|   main.c        // main function
|   qp_config.h   // QP/C configuration
Remarks
In most cases, the files comprising the Blinky application have been generated by the QM modeling tool↑ from the blinky.qm model file. However, if you don't intend to use QM, you can edit the code manually since you don't care that your changes would be lost when the code is regenerated.

The main Function

The file main.c shows the steps to initialize and run the QP/C application. This main() function applies generally to all QP applications and is used in all examples.

#include "qpc.h" // QP/C real-time embedded framework
#include "bsp.h" // Board Support Package
//............................................................................
int main() {
QF_init(); // initialize the framework and the underlying RT kernel
BSP_init(); // initialize the BSP and instantiate/start AOs
return QF_run(); // run the framework
}
QP/C Framework in C interface including the backwards-compatibility layer \clearpage .

State Machine and blinky.c

The Blinky Active Object has a simple state machine depicted in the state diagram below. On the right, you can see the generated code for the state machine, with a very clear mapping of state machine elements to code. The mapping between the state machine elements and code is specified in the State Dynamics Viewpoint of the Software Design Specification.

Remarks
The state machine code in blinky.c has been generated by the QM modeling tool↑. However, the code can also be created/modified manually if you don't wish to use QM.

Board Support Package

Unlike the main() function and the state machine code, which are generic and can be written in a hardware-independent way, the BSP (Board Support Package) is hardware-specific. A minimal BSP for running Blinky in a console is presented below. For other targets, including embedded boards, the BSP needs to be implemented differently.

Note
QP/C enables and encourages applications, where most of the code is hardware-independent and thus easy to port and easy to test. All hardware-specific functions should be confined to the BSP, which is the only component that needs to be ported to a specific hardware. The Blinky example and all other QP/C examples demonstrate this design approach.

Blinky on the Host (Console)

The Blinky example for the host computers (Windows, Linux, macOS) is located in the directory qpc/examples/posix-win32/blinky (see also Blinky Versions). To build the Blinky example on the host, you need the make utility and the GNU C/C++ compiler. (On Windows, these tools are provided in the QP-bundle, which you should install beforehand).

Building Blinky on Windows

Deafault Debug build configuration:

cd \qp\qpc\examples\posix-win32\blinky
make

This assumes that the qpc framework exists in the directory \qp\qpc. Please check your installation and adjust the location as necessary.

Release build configuration:

cd \qp\qpc\examples\posix-win32\blinky
make CONF=rel

Spy build configuration (with QP/Spy software tracing enabled):

cd \qp\qpc\examples\posix-win32\blinky
make CONF=spy

Building Blinky on Linux or macOS

Deafault Debug build configuration:

cd ~/qp/qpc/examples/posix-win32/blinky
make

This assumes that the qpc framework exists in the directory ~/qp/qpc. Please check your installation and adjust the location as necessary.

Release build configuration:

cd ~/qp/qpc/examples/posix-win32/blinky
make CONF=rel

Spy build configuration (with QP/Spy software tracing enabled):

cd ~/qp/qpc/examples/posix-win32/blinky
make CONF=spy

Running the Example

The previous build step should produce the executable in the build subdirectory inside the blinky project. Assuming that your current directory is the blinky project, you run the executable as follows:

On Windows:

build\blinky

On Linux or macOS:

build/blinky

And here is a typical output:

Simple Blinky example
QP/C version: 8.1.2
Press Ctrl-C to quit...
LED OFF
LED ON
LED OFF
LED ON
LED OFF
^C
Remarks
The Blinky example keeps running and producing the output indefinitely. You terminate it by pressing Ctr-C on your keyboard.

Board Support Package bsp.c

The BSP (Board Support Package) implements the hardware-specific functions (e.g., turning the LED on and off in case of Blinky) as well as the QP customization callbacks. A minimal BSP for Blinky running in a console is shown below.

#include "qpc.h" // QP/C real-time event framework
#include "bsp.h" // Board Support Package
#include "app.h" // Application
#include <stdio.h> // for printf()/fprintf()
#include <stdlib.h> // for exit()
//============================================================================
Q_DEFINE_THIS_FILE // file name for assertions
// local variables -----------------------------------------------------------
#ifdef Q_SPY
enum AppRecords { // application-specific trace records
LED_STAT = QS_USER,
};
// QSpy source IDs
static QSpyId const l_clock_tick = { QS_ID_AP };
#endif // Q_SPY
//============================================================================
// Error handler
Q_NORETURN Q_onError(char const * const module, int_t const id) {
// NOTE: this implementation of the error handler is intended only
// for debugging and MUST be changed for deployment of the application
// (assuming that you ship your production code with assertions enabled).
fprintf(stderr, "Assertion failed in %s:%d", module, id);
QS_ASSERTION(module, id, 10000U); // report assertion to QS
exit(-1);
}
//============================================================================
// BSP...
void BSP_init(void const * const arg) {
printf("Simple Blinky example\n"
"QP/C version: %s\n"
"Press Ctrl-C to quit...\n",
// initialize QS software tracing...
if (!QS_INIT(arg)) {
}
// QS dictionaries...
QS_OBJ_DICTIONARY(&l_clock_tick);
QS_SIG_DICTIONARY(TIMEOUT_SIG, (void *)0);
QS_USR_DICTIONARY(LED_STAT);
// setup the QS filters...
QS_GLB_FILTER(QS_GRP_ALL); // enable all records
QS_GLB_FILTER(-QS_QF_TICK); // exclude the tick record
// mutable events not used -- no need to call QF_poolInit()
// publish-subscribe not used -- no need to call QActive_psInit()
// instantiate and start AOs/threads...
Blinky_ctor();
static QEvtPtr blinkyQueueSto[10];
QActive_start(AO_Blinky,
1U, // QP prio. of the AO
blinkyQueueSto, // event queue storage
Q_DIM(blinkyQueueSto), // queue length [events]
(void *)0, 0U, // no stack storage
(void *)0); // no initialization param
}
//............................................................................
void BSP_ledOn(void) {
printf("LED ON\n");
// application-specific record
QS_BEGIN_ID(LED_STAT, AO_Blinky->prio)
QS_STR("ON"); // LED status
QS_END()
}
//............................................................................
void BSP_ledOff(void) {
printf("LED OFF\n");
// application-specific record
QS_BEGIN_ID(LED_STAT, AO_Blinky->prio)
QS_STR("OFF"); // LED status
QS_END()
}
//============================================================================
// QF...
void QF_onStartup(void) {
}
//............................................................................
void QF_onCleanup(void) {
}
//............................................................................
void QF_onClockTick(void) {
QTIMEEVT_TICK_X(0U, &l_clock_tick); // clock tick processing for rate 0
QS_RX_INPUT(); // handle the QS-RX input
QS_OUTPUT(); // handle the QS output
}
//============================================================================
// QS callbacks...
#ifdef Q_SPY
//............................................................................
void QS_onCommand(uint8_t cmdId,
uint32_t param1, uint32_t param2, uint32_t param3)
{
Q_UNUSED_PAR(cmdId);
Q_UNUSED_PAR(param1);
Q_UNUSED_PAR(param2);
Q_UNUSED_PAR(param3);
}
#endif // Q_SPY
#define Q_UNUSED_PAR(par_)
Helper macro to mark unused parameters of functions.
Definition qp.h:90
#define QP_VERSION_STR
Version string complying with Software Versioning specification.
Definition qp.h:33
int int_t
Alias for assertion-ID numbers in QP assertions and return from QF_run() \clearpage .
Definition qp.h:87
#define QTIMEEVT_TICK_X(tickRate_, sender_)
Invoke the system clock tick processing QTimeEvt_tick_().
Definition qp.h:767
#define Q_DIM(array_)
Helper macro to calculate static dimension of a 1-dim array.
Definition qp.h:91
#define Q_NORETURN
No-return specifier for the Q_onError() callback function.
Definition qp_port.h:45
#define QS_OBJ_DICTIONARY(obj_)
Produce object dictionary.
Definition qs.h:366
@ QS_QF_TICK
QTimeEvt tick was called.
Definition qs.h:95
#define QS_SIG_DICTIONARY(sig_, obj_)
Produce event signal dictionary.
Definition qs.h:363
#define QS_GLB_FILTER(rec_)
check the global filter
Definition qs.h:260
#define QS_USR_DICTIONARY(rec_)
Produce user-record dictionary for application-specific trace records.
Definition qs.h:375
#define QS_INIT(arg_)
Initialize the QS software tracing system.
Definition qs.h:256
Q_NORETURN Q_onError(char const *const module, int_t const id)
Custom Error Handler (fault management) Callback function invoked after detecting an error (part of Q...
#define Q_ERROR()
Assertion for a wrong path through the code (with ID provided in LINE).
Definition qsafe.h:94
The data type to store in the event queue ring-buffer.
Definition qequeue.h:46
QS ID type for applying local filtering \clearpage .
Definition qs.h:218

Blinky on the Embedded Targets

The Blinky examples for various embedded boards are located in the qpc/examples (see Blinky Versions). For embedded targets, you have many more choices than on the host. The choices include:

  • the real-time kernel used in QP (QV, QK, QXK or 3rd-part RTOS)
  • the compiler toolchain and IDE used (e.g., GNU, KEIL, IAR, etc.)

This section shows the Blinky version for one embedded board (NUCLEO-C031C6). Versions for other boards and kernels (including 3rd-party RTOSes) are organized similarly.

Blinky Code Organization

The following annotated directory structure explains the Blinky code for ARM Cortex-M, NUCLEO-C031C6 board (Blinky example for other boards have very similar structure):

qpc/examples/arm-cm/blinky_nucleo-c031c6 // Blinky for ARM Cortex-M, NUCLEO-C031C6
|   app.h         // Application interface
|   bsp.h         // BSP interface
|   blinky.c      // Blinky state machine
|   main.c        // main() function
|   qp_config.h   // QP/C configuration
|
+---qk            // Preemptive, non-blocking QK kernel
|   |   bsp.c                 // BSP implementation for the QK kernel
|   |
|   +---armclang              // ARM-CLANG toolchain (KEIL compiler 6)
|   |       blinky-qk.sct     // linker script for KEIL linker
|   |       blinky-qk.uvoptx  // KEIL uVision project
|   |       blinky-qk.uvprojx // KEIL uVision project
|   |
|   +---gnu                    // GNU-ARM toolchain
|   |       blinky-qk.ld       // linker script for GNU-ARM
|   |       Makefile           // Makefile to build from command-line
|   |
|   \---iar                    // IAR toolchain
|           blinky-qk.ewd      // IAR debugger configuration
|           blinky-qk.ewp      // IAR project
|           blinky-qk.eww      // IAR workspace
|           blinky-qk.icf      // linker script for IAR
|
+---qv            // Non-prreemptive QV kernel
|   |   bsp.c                  // BSP implementation for the QV kernel
|   |
|   +---armclang               // ARM-CLANG toolchain (KEIL compiler 6)
|   |       blinky-qk.sct      // linker script for KEIL linker
|   |       blinky-qk.uvoptx   // KEIL uVision project
|   |       blinky-qk.uvprojx  // KEIL uVision project
|   |
|   +---gnu                    // GNU-ARM toolchain
|   |       blinky-qv.ld       // linker script for GNU-ARM
|   |       Makefile           // Makefile to build from command-line
|   |
|   \---iar                    // IAR toolchain
|           blinky-qv.ewd      // IAR debugger configuration
|           blinky-qv.ewp      // IAR project
|           blinky-qv.eww      // IAR workspace
|           blinky-qv.icf      // linker script for IAR
|
\---qxk            // Preemptive, preemptive dual-mode QXK kernel
    |   bsp.c                  // BSP implementation for the QXK kernel
    |   xthr.c                 // extended-threads for QXK
    |
    +---armclang               // ARM-CLANG toolchain (KEIL compiler 6)
    |       blinky-qxk.sct     // linker script for KEIL linker
    |       blinky-qxk.uvoptx  // KEIL uVision project
    |       blinky-qxk.uvprojx // KEIL uVision project
    |
    +---gnu                    // GNU-ARM toolchain
    |       blinky-qxk.ld      // linker script for GNU-ARM
    |       Makefile           // Makefile to build from command-line
    |
    \---iar                    // IAR toolchain
            blinky-qxk.ewd     // IAR debugger configuration
            blinky-qxk.ewp     // IAR project
            blinky-qxk.eww     // IAR workspace
            blinky-qxk.icf     // linker script for IAR
Remarks
For example, to build Blinky for the QK kernel with the ARM-CLANG compiler (KEIL compiler 6), go to the directory qpc/examples/arm-cm/blinky_nucleo-c031c6/qk/armclang. If you wish to build Blinky for the QV kernel with the GNU-ARM compiler, go to the directory qpc/examples/arm-cm/blinky_nucleo-c031c6/qk/gnu, etc.

Building with GNU-ARM

To build Blinky for the QK kernel and GNU-ARM, open a terminal and type:

cd qpc\examples\arm-cm\blinky_nucleo-c031c6\qk\gnu
make
C:\qp\qpc\examples\arm-cm\blinky_nucleo-c031c6\qk\gnu>make
C:\qp\qtools/gnu_arm-none-eabi/bin/arm-none-eabi-gcc -c -g -mcpu=cortex-m0plus   -mthumb -Wall -ffunction-sections -fdata-sections -O -I../.. -I../../../../../include -I../../../../../ports/arm-cm/qk/gnu -I../../../../../3rd_party/CMSIS/Include -I../../../../../3rd_party/nucleo-c031c6 -DSTM32C031xx  ../bsp.c -o dbg/bsp.o
. . .
C:\qp\qtools/gnu_arm-none-eabi/bin/arm-none-eabi-gcc -Tblinky-qk.ld -mcpu=cortex-m0plus   -mthumb -specs=nosys.specs -specs=nano.specs -Wl,-Map,dbg/blinky-qk.map,--cref,--gc-sections  -o dbg/blinky-qk.elf dbg/bsp.o dbg/main.o dbg/blinky.o dbg/system_stm32c0xx.o dbg/startup_stm32c031xx.o dbg/qep_hsm.o dbg/qep_msm.o dbg/qf_act.o dbg/qf_actq.o dbg/qf_defer.o dbg/qf_dyn.o dbg/qf_mem.o dbg/qf_ps.o dbg/qf_qact.o dbg/qf_qeq.o dbg/qf_qmact.o dbg/qf_time.o dbg/qk.o dbg/qk_port.o dbg/syscalls.o dbg/qstamp.o
C:\qp\qtools/gnu_arm-none-eabi/bin/arm-none-eabi-objcopy -O binary dbg/blinky-qk.elf dbg/blinky-qk.bin

Building with KEIL

To build Blinky for the QK kernel and ARM-CLANG (KEIL compiler 6), launch the KEIL uVision and load the project: qpc/examples/arm-cm/blinky_nucleo-c031c6/qk/armclang/blink-qk.uvprojx

DPP project in IAR ARM-KEIL uVision

Building with IAR

To build Blinky for the QK kernel and IAR, launch the IAR IDE and load the project: qpc/examples/arm-cm/blinky_nucleo-c031c6/qk/iar/blinky-qk.eww

DPP project in IAR EWARM

QP/C TutorialDining Philosophers Problem (DPP)