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)