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 #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)