About this example:
-------------------
Simple "Blinky" console application for workstations (Windows, Linux, MacOS)
(see "QM Tutorial" at: https://www.state-machine.com/qm/gs_tut.html)
This example demonstrates:
- Active object (Blinky) with state machine
- Board Support Package abstraction for portability
- BSP implementation for desktop OS (Windows, Linux, MacOS)
- Platform-independent main() function
- Makefile to build the generated code on desktop OS (Windows, Linux, MacOS)
- Customized tools for building the generated code directly from QM
Building the example:
---------------------
To build this example, you will need the QP/C framework installed on your computer and the GNU-GCC compiler. Both of them will be available if you install the QP-bundle from:
https://www.state-machine.com/#Downloads
Running the example:
--------------------
This example is a simple console application, which you can run from a terminal.
For more QM examples for QP/C see:
https://www.state-machine.com/qpc/exa.html
The Blinky "constructor" is provided outside of the Blinky class, so that it can be used independently from the class. This is part of the "opaque pointer" design idiom.
Blinky *me = (Blinky *)AO_Blinky;
QActive_ctor(&me->super, Q_STATE_CAST(&Blinky_initial));
QTimeEvt_ctorX(&me->timeEvt, &me->super, TIMEOUT_SIG, 0U);
#include "qpc.h" /* QP/C framework API */
#include "bsp.h" /* Board Support Package interface */
/* ask QM to declare the Blinky class --------------------------------------*/
$declare${AOs::Blinky}
/* instantiate the Blinky active object ------------------------------------*/
static Blinky l_blinky;
QActive * const AO_Blinky = &l_blinky.super;
/* ask QM to define the Blinky class ---------------------------------------*/
$define${AOs::Blinky_ctor}
$define${AOs::Blinky}
#ifndef BSP_H
#define BSP_H
/* a very simple Board Support Package (BSP) -------------------------------*/
enum { BSP_TICKS_PER_SEC = 100 }; /* number of clock ticks in a second */
void BSP_init(void);
void BSP_ledOff(void);
void BSP_ledOn(void);
/* define the event signals used in the application ------------------------*/
enum BlinkySignals {
TIMEOUT_SIG = Q_USER_SIG, /* offset the first signal by Q_USER_SIG */
MAX_SIG /* keep last (the number of signals) */
};
/* active object(s) used in this application -------------------------------*/
extern QActive * const AO_Blinky; /* opaque pointer to the Blinky AO */
$declare${AOs::Blinky_ctor}
#endif /* BSP_H */
/* Board Support Package implementation for desktop OS (Windows, Linux, MacOS) */
#include "qpc.h" /* QP/C framework API */
#include "bsp.h" /* Board Support Package interface */
#include <stdio.h> /* for printf()/fprintf() */
#include <stdlib.h> /* for exit() */
void BSP_init(void) {
printf("Simple Blinky example\n"
"QP/C version: %s\n"
"Press Ctrl-C to quit...\n",
QP_VERSION_STR);
}
void BSP_ledOff(void) { printf("LED OFF\n"); }
void BSP_ledOn(void) { printf("LED ON\n"); }
/* callback functions needed by the framework ------------------------------*/
void QF_onStartup(void) {}
void QF_onCleanup(void) {}
void QF_onClockTick(void) {
QF_TICK_X(0U, (void *)0); /* QF clock tick processing for rate 0 */
}
void Q_onAssert(char const * const module, int loc) {
fprintf(stderr, "Assertion failed in %s:%d", module, loc);
exit(-1);
}
#include "qpc.h" /* QP/C framework API */
#include "bsp.h" /* Board Support Package interface */
Q_DEFINE_THIS_FILE
/* the main function -------------------------------------------------------*/
int main() {
static QEvt const *blinky_queueSto[10]; /* event queue buffer for Blinky */
QF_init(); /* initialize the framework */
BSP_init(); /* initialize the BSP */
/* instantiate and start the Blinky active object */
Blinky_ctor(); /* in C you must explicitly call the Blinky constructor */
QACTIVE_START(AO_Blinky, /* active object to start */
1U, /* priority of the active object */
blinky_queueSto, /* event queue buffer */
Q_DIM(blinky_queueSto), /* the length of the buffer */
(void *)0, 0U, /* private stack (not used) */
(QEvt *)0); /* initialization event (not used) */
return QF_run(); /* let the framework run the application */
}
# Makefile for building QP/C application on Windows and POSIX hosts
#
# examples of invoking this Makefile:
# building configurations: Debug (default), Release, and Spy
# make
# make CONF=rel
# make CONF=spy
# make clean # cleanup the build
# make CONF=spy clean # cleanup the build
#
# NOTE:
# To use this Makefile on Windows, you will need the GNU make utility, which
# is included in the QTools collection for Windows, see:
# https://sourceforge.net/projects/qpc/files/QTools/
#
#-----------------------------------------------------------------------------
# project name:
#
PROJECT := blinky
#-----------------------------------------------------------------------------
# project directories:
#
# list of all source directories used by this project
VPATH := . \
# list of all include directories needed by this project
INCLUDES := -I. \
# location of the QP/C framework (if not provided in an env. variable)
ifeq ($(QPC),)
QPC := ../../..
endif
#-----------------------------------------------------------------------------
# project files:
#
# C source files...
C_SRCS := \
blinky.c \
bsp.c \
main.c
# C++ source files...
CPP_SRCS :=
LIB_DIRS :=
LIBS :=
# defines...
# QP_API_VERSION controls the QP API compatibility; 9999 means the latest API
DEFINES := -DQP_API_VERSION=9999
ifeq (,$(CONF))
CONF := dbg
endif
#-----------------------------------------------------------------------------
# add QP/C framework (depends on the OS this Makefile runs on):
#
ifeq ($(OS),Windows_NT)
# NOTE:
# For Windows hosts, you can choose:
# - the single-threaded QP/C port (win32-qv) or
# - the multithreaded QP/C port (win32).
#
QP_PORT_DIR := $(QPC)/ports/win32-qv
#QP_PORT_DIR := $(QPC)/ports/win32
LIB_DIRS += -L$(QP_PORT_DIR)/$(CONF)
LIBS += -lqp -lws2_32
else
# NOTE:
# For POSIX hosts (Linux, MacOS), you can choose:
# - the single-threaded QP/C port (win32-qv) or
# - the multithreaded QP/C port (win32).
#
QP_PORT_DIR := $(QPC)/ports/posix-qv
#QP_PORT_DIR := $(QPC)/ports/posix
C_SRCS += \
qep_hsm.c \
qep_msm.c \
qf_act.c \
qf_actq.c \
qf_defer.c \
qf_dyn.c \
qf_mem.c \
qf_ps.c \
qf_qact.c \
qf_qeq.c \
qf_qmact.c \
qf_time.c \
qf_port.c
QS_SRCS := \
qs.c \
qs_64bit.c \
qs_rx.c \
qs_fp.c \
qs_port.c
LIBS += -lpthread
endif
#============================================================================
# Typically you should not need to change anything below this line
VPATH += $(QPC)/src/qf $(QP_PORT_DIR)
INCLUDES += -I$(QPC)/include -I$(QPC)/src -I$(QP_PORT_DIR)
#-----------------------------------------------------------------------------
# GNU toolset:
#
# NOTE:
# GNU toolset (MinGW) is included in the QTools collection for Windows, see:
# http://sourceforge.net/projects/qpc/files/QTools/
# It is assumed that %QTOOLS%\bin directory is added to the PATH
#
CC := gcc
CPP := g++
LINK := gcc # for C programs
#LINK := g++ # for C++ programs
#-----------------------------------------------------------------------------
# basic utilities (depends on the OS this Makefile runs on):
#
ifeq ($(OS),Windows_NT)
MKDIR := mkdir
RM := rm
TARGET_EXT := .exe
else ifeq ($(OSTYPE),cygwin)
MKDIR := mkdir -p
RM := rm -f
TARGET_EXT := .exe
else
MKDIR := mkdir -p
RM := rm -f
TARGET_EXT :=
endif
#-----------------------------------------------------------------------------
# build configurations...
ifeq (rel, $(CONF)) # Release configuration ..................................
BIN_DIR := build_rel
# gcc options:
CFLAGS = -c -O3 -fno-pie -std=c99 -pedantic -Wall -Wextra -W \
$(INCLUDES) $(DEFINES) -DNDEBUG
CPPFLAGS = -c -O3 -fno-pie -std=c++11 -pedantic -Wall -Wextra \
-fno-rtti -fno-exceptions \
$(INCLUDES) $(DEFINES) -DNDEBUG
else ifeq (spy, $(CONF)) # Spy configuration ................................
BIN_DIR := build_spy
C_SRCS += $(QS_SRCS)
VPATH += $(QPC)/src/qs
# gcc options:
CFLAGS = -c -g -O -fno-pie -std=c99 -pedantic -Wall -Wextra -W \
$(INCLUDES) $(DEFINES) -DQ_SPY
CPPFLAGS = -c -g -O -fno-pie -std=c++11 -pedantic -Wall -Wextra \
-fno-rtti -fno-exceptions \
$(INCLUDES) $(DEFINES) -DQ_SPY
else # default Debug configuration .........................................
BIN_DIR := build
# gcc options:
CFLAGS = -c -g -O -fno-pie -std=c99 -pedantic -Wall -Wextra -W \
$(INCLUDES) $(DEFINES)
CPPFLAGS = -c -g -O -fno-pie -std=c++11 -pedantic -Wall -Wextra \
-fno-rtti -fno-exceptions \
$(INCLUDES) $(DEFINES)
endif # .....................................................................
LINKFLAGS := -no-pie
#-----------------------------------------------------------------------------
C_OBJS := $(patsubst %.c,%.o, $(C_SRCS))
CPP_OBJS := $(patsubst %.cpp,%.o, $(CPP_SRCS))
TARGET_EXE := $(BIN_DIR)/$(PROJECT)$(TARGET_EXT)
C_OBJS_EXT := $(addprefix $(BIN_DIR)/, $(C_OBJS))
C_DEPS_EXT := $(patsubst %.o,%.d, $(C_OBJS_EXT))
CPP_OBJS_EXT := $(addprefix $(BIN_DIR)/, $(CPP_OBJS))
CPP_DEPS_EXT := $(patsubst %.o,%.d, $(CPP_OBJS_EXT))
# create $(BIN_DIR) if it does not exist
ifeq ("$(wildcard $(BIN_DIR))","")
$(shell $(MKDIR) $(BIN_DIR))
endif
#-----------------------------------------------------------------------------
# rules
#
all: $(TARGET_EXE)
$(TARGET_EXE) : $(C_OBJS_EXT) $(CPP_OBJS_EXT)
$(CC) $(CFLAGS) $(QPC)/include/qstamp.c -o $(BIN_DIR)/qstamp.o
$(LINK) $(LINKFLAGS) $(LIB_DIRS) -o $@ $^ $(BIN_DIR)/qstamp.o $(LIBS)
$(BIN_DIR)/%.d : %.c
$(CC) -MM -MT $(@:.d=.o) $(CFLAGS) $< > $@
$(BIN_DIR)/%.d : %.cpp
$(CPP) -MM -MT $(@:.d=.o) $(CPPFLAGS) $< > $@
$(BIN_DIR)/%.o : %.c
$(CC) $(CFLAGS) $< -o $@
$(BIN_DIR)/%.o : %.cpp
$(CPP) $(CPPFLAGS) $< -o $@
.PHONY : clean show
# include dependency files only if our goal depends on their existence
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),show)
-include $(C_DEPS_EXT) $(CPP_DEPS_EXT)
endif
endif
.PHONY : clean show
clean :
-$(RM) $(BIN_DIR)/*.o \
$(BIN_DIR)/*.d \
$(TARGET_EXE)
show :
@echo PROJECT = $(PROJECT)
@echo TARGET_EXE = $(TARGET_EXE)
@echo VPATH = $(VPATH)
@echo C_SRCS = $(C_SRCS)
@echo CPP_SRCS = $(CPP_SRCS)
@echo C_DEPS_EXT = $(C_DEPS_EXT)
@echo C_OBJS_EXT = $(C_OBJS_EXT)
@echo C_DEPS_EXT = $(C_DEPS_EXT)
@echo CPP_DEPS_EXT = $(CPP_DEPS_EXT)
@echo CPP_OBJS_EXT = $(CPP_OBJS_EXT)
@echo LIB_DIRS = $(LIB_DIRS)
@echo LIBS = $(LIBS)
@echo DEFINES = $(DEFINES)