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)