diff --git a/.gitignore b/.gitignore
index d3b11730..0be69d3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+*.su
+*.nm
+*.pyc
*.log
*.a
*.o
diff --git a/ports/at91sam7s/CMakeLists.txt b/ports/at91sam7s/CMakeLists.txt
index 2ad90c84..0b57d69c 100644
--- a/ports/at91sam7s/CMakeLists.txt
+++ b/ports/at91sam7s/CMakeLists.txt
@@ -48,7 +48,12 @@ set(CMAKE_AR arm-none-eabi-ar)
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
set(CMAKE_OBJDUMP arm-none-eabi-objdump)
set(SIZE arm-none-eabi-size)
+set(CMAKE_SIZE arm-none-eabi-size)
+set(CMAKE_NM arm-none-eabi-nm)
+set(CMAKE_CSTACK "${CMAKE_SOURCE_DIR}/../../tools/check-stack-usage/checkStackUsage.py")
+set(CMAKE_MEMAP "${CMAKE_SOURCE_DIR}/../../tools/memap/memap.py")
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+set(EXECUTABLE ${PROJECT_NAME}.elf)
project(bacnet-mstp)
@@ -65,6 +70,7 @@ add_link_options(-mno-thumb-interwork)
# Code size reduction using garbage collection sections
add_compile_options(-ffunction-sections -fdata-sections)
add_compile_options(-fno-common -fmessage-length=0)
+add_compile_options(-fstack-usage -fdump-rtl-dfinish)
add_link_options(-Wl,-gc-sections,--print-memory-usage)
# Build types
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
@@ -240,3 +246,30 @@ add_custom_command(TARGET ${EXECUTABLE}
COMMAND arm-none-eabi-objcopy -O ihex ${EXECUTABLE} ${PROJECT_NAME}.hex
COMMAND arm-none-eabi-objcopy -O binary ${EXECUTABLE} ${PROJECT_NAME}.bin
)
+
+# sort the RAM usage by size and place into a file
+add_custom_target(symbols
+ DEPENDS ${EXECUTABLE}
+ COMMENT "Print memory symbols by size"
+ COMMAND ${CMAKE_NM} -t d -S --size-sort ${EXECUTABLE} 1> ${PROJECT_NAME}.nm
+ COMMAND echo "RAM usage by size analysis in ${PROJECT_NAME}.nm"
+ COMMAND echo "=ADDRESS= ==RAM=== = ==VARIABLE-NAME=="
+ COMMAND tail ${PROJECT_NAME}.nm
+)
+
+# calculate the worst case CSTACK memory usage by size and place into a file
+add_custom_target(cstack
+ DEPENDS ${EXECUTABLE}
+ COMMENT "Print CSTACK memory depth by size"
+ COMMAND ${CMAKE_CSTACK} ${EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR} 1> ${PROJECT_NAME}.su
+ COMMAND echo "C-Stack maxium depth analysis in ${PROJECT_NAME}.su"
+ COMMAND echo "==DEPTH== : == Functions called =="
+ COMMAND tail ${PROJECT_NAME}.su
+)
+
+# Print file and library sizes
+add_custom_target(memmap
+ DEPENDS ${PROJECT_NAME}.map
+ COMMENT "Print file and library memory usage by size"
+ COMMAND ${CMAKE_MEMAP} -t GCC_ARM ${PROJECT_NAME}.map
+)
diff --git a/ports/stm32f10x/CMakeLists.txt b/ports/stm32f10x/CMakeLists.txt
new file mode 100644
index 00000000..262c63e4
--- /dev/null
+++ b/ports/stm32f10x/CMakeLists.txt
@@ -0,0 +1,311 @@
+# This is a CMake example for STM32 ARM Cortex-M3 STM32F103RGT6 on
+# a STM32 Discovery Kit evaluation board using the ARM GCC compiler
+# and STM32 CMSIS library.
+#
+# Board STM32F103 Discovery Kit
+# MCU STM32F103RGT6
+# CPU Cortex-M3
+# RAM 96KB
+# Flash 1024KB
+#
+# To build this project you need to install:
+# - ARM GCC compiler
+# - CMake
+#
+# To build this project you need to run:
+# - cmake -S . -B build
+# - cmake --build build
+#
+# To flash this project you need to run:
+# - st-flash write build/bacnet-mstp.hex 0x8000000
+#
+# To debug this project you need to run:
+# - arm-none-eabi-gdb -q build/bacnet-mstp.out
+# - (gdb) target extended-remote localhost:3333
+# - (gdb) monitor reset halt
+# - (gdb) load
+# - (gdb) monitor reset halt
+# - (gdb) monitor reset init
+# - (gdb) monitor reset run
+# - (gdb) monitor reset exit
+# - (gdb) quit
+#
+# You can also use VSCode with Cortex-Debug extension
+#
+# This example was tested with:
+# - ARM GCC 10.3.1
+# - CMake 3.22.1
+# - stm32f10x_StdPeriph_Driver V1.0.0
+# - BACnet Stack V1.3.2
+#
+cmake_minimum_required(VERSION 3.20)
+
+# Cross compilers and tools
+set(CMAKE_SYSTEM_NAME Generic)
+set(CMAKE_SYSTEM_VERSION 1)
+set(CMAKE_C_COMPILER arm-none-eabi-gcc)
+set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
+set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
+set(CMAKE_AR arm-none-eabi-ar)
+set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
+set(CMAKE_OBJDUMP arm-none-eabi-objdump)
+set(CMAKE_SIZE arm-none-eabi-size)
+set(CMAKE_NM arm-none-eabi-nm)
+set(CMAKE_CSTACK "${CMAKE_SOURCE_DIR}/../../tools/check-stack-usage/checkStackUsage.py")
+set(CMAKE_MEMAP "${CMAKE_SOURCE_DIR}/../../tools/memap/memap.py")
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+set(EXECUTABLE ${PROJECT_NAME}.elf)
+
+project(bacnet-mstp)
+
+enable_language(C ASM)
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_C_STANDARD_REQUIRED ON)
+set(CMAKE_C_EXTENSIONS ON)
+
+# Specific ARM microcontroller compiler and linker settings
+add_compile_options(-mcpu=cortex-m3)
+add_compile_options(-mthumb -mno-thumb-interwork -mabi=aapcs)
+add_link_options(-mcpu=cortex-m3)
+add_link_options(-mthumb -mno-thumb-interwork -mabi=aapcs)
+# Compiler and linker settings for garbage collection and memory usage
+add_compile_options(-ffunction-sections -fdata-sections)
+add_compile_options(-fno-common -fmessage-length=0)
+add_compile_options(-fstack-usage -fdump-rtl-dfinish)
+add_link_options(-Wl,-gc-sections,--print-memory-usage)
+
+# Build types
+if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
+ message(STATUS "Maximum optimization for speed")
+ add_compile_options(-Ofast)
+elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
+ message(STATUS "Maximum optimization for speed, debug info included")
+ add_compile_options(-Ofast -g)
+elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel")
+ message(STATUS "Maximum optimization for size")
+ add_compile_options(-Os)
+else ()
+ message(STATUS "Minimal optimization, debug info included")
+ add_compile_definitions(DEBUG)
+ add_compile_options(-Og -g3)
+endif ()
+
+# eliminate the deprecated function warnings
+option(BACNET_STACK_DEPRECATED_DISABLE "Disable deprecation compile warnings" ON)
+if(BACNET_STACK_DEPRECATED_DISABLE)
+ add_definitions(-DBACNET_STACK_DEPRECATED_DISABLE)
+endif()
+
+set(LIBRARY_BACNET_INC "${CMAKE_SOURCE_DIR}/../../src")
+set(LIBRARY_BACNET_CORE "${CMAKE_SOURCE_DIR}/../../src/bacnet")
+set(LIBRARY_BACNET_BASIC "${CMAKE_SOURCE_DIR}/../../src/bacnet/basic")
+set(LIBRARY_STM32_SRC "${CMAKE_SOURCE_DIR}/drivers/src")
+set(LIBRARY_STM32_INC "${CMAKE_SOURCE_DIR}/drivers/inc")
+set(LIBRARY_CMSIS_INC "${CMAKE_SOURCE_DIR}/CMSIS")
+set(LIBRARY_CMSIS_GCC_INC "${CMAKE_SOURCE_DIR}/CMSIS/gcc_ride7")
+
+set(BACNET_PROJECT_SOURCE
+ ${LIBRARY_STM32_SRC}/stm32f10x_adc.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_bkp.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_can.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_cec.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_crc.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_dac.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_dbgmcu.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_dma.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_exti.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_flash.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_fsmc.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_gpio.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_i2c.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_iwdg.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_misc.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_pwr.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_rcc.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_rtc.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_sdio.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_spi.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_tim.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_usart.c
+ ${LIBRARY_STM32_SRC}/stm32f10x_wwdg.c
+ ${LIBRARY_STM32_SRC}/syscalls.c
+
+ ${CMAKE_SOURCE_DIR}/stm32f10x_conf.h
+ # main entry and STM32 hardware setup
+ ${CMAKE_SOURCE_DIR}/main.c
+ ${CMAKE_SOURCE_DIR}/stm32f10x_it.c
+ ${CMAKE_SOURCE_DIR}/stm32f10x_it.h
+ ${CMAKE_SOURCE_DIR}/system_stm32f10x.c
+ # BACnet specific hardware abstraction, configuration and tasks
+ ${CMAKE_SOURCE_DIR}/bacnet.c
+ ${CMAKE_SOURCE_DIR}/led.c
+ ${CMAKE_SOURCE_DIR}/mstimer-init.c
+ ${CMAKE_SOURCE_DIR}/rs485.c
+ # BACnet objects in this device
+ ${CMAKE_SOURCE_DIR}/device.c
+ ${CMAKE_SOURCE_DIR}/netport.c
+ ${CMAKE_SOURCE_DIR}/bo.c
+ # BACnet services library
+ ${LIBRARY_BACNET_BASIC}/service/h_dcc.c
+ ${LIBRARY_BACNET_BASIC}/service/h_apdu.c
+ ${LIBRARY_BACNET_BASIC}/npdu/h_npdu.c
+ ${LIBRARY_BACNET_BASIC}/service/h_rd.c
+ ${LIBRARY_BACNET_BASIC}/service/h_rp.c
+ ${LIBRARY_BACNET_BASIC}/service/h_rpm.c
+ ${LIBRARY_BACNET_BASIC}/service/h_whohas.c
+ ${LIBRARY_BACNET_BASIC}/service/h_whois.c
+ ${LIBRARY_BACNET_BASIC}/service/h_wp.c
+ ${LIBRARY_BACNET_BASIC}/service/h_noserv.c
+ ${LIBRARY_BACNET_BASIC}/service/s_iam.c
+ ${LIBRARY_BACNET_BASIC}/service/s_ihave.c
+ ${LIBRARY_BACNET_BASIC}/tsm/tsm.c
+ ${LIBRARY_BACNET_BASIC}/sys/debug.c
+ ${LIBRARY_BACNET_BASIC}/sys/ringbuf.c
+ ${LIBRARY_BACNET_BASIC}/sys/fifo.c
+ ${LIBRARY_BACNET_BASIC}/sys/mstimer.c
+ # BACnet core library
+ ${LIBRARY_BACNET_CORE}/abort.c
+ ${LIBRARY_BACNET_CORE}/bacaddr.c
+ ${LIBRARY_BACNET_CORE}/bacapp.c
+ ${LIBRARY_BACNET_CORE}/bacdcode.c
+ ${LIBRARY_BACNET_CORE}/bacdest.c
+ ${LIBRARY_BACNET_CORE}/bacdevobjpropref.c
+ ${LIBRARY_BACNET_CORE}/bacerror.c
+ ${LIBRARY_BACNET_CORE}/bacint.c
+ ${LIBRARY_BACNET_CORE}/bacreal.c
+ ${LIBRARY_BACNET_CORE}/bacstr.c
+ ${LIBRARY_BACNET_CORE}/datalink/cobs.c
+ ${LIBRARY_BACNET_CORE}/datalink/crc.c
+ ${LIBRARY_BACNET_CORE}/datalink/dlmstp.c
+ ${LIBRARY_BACNET_CORE}/datalink/mstp.c
+ ${LIBRARY_BACNET_CORE}/datalink/mstptext.c
+ ${LIBRARY_BACNET_CORE}/datetime.c
+ ${LIBRARY_BACNET_CORE}/dcc.c
+ ${LIBRARY_BACNET_CORE}/indtext.c
+ ${LIBRARY_BACNET_CORE}/iam.c
+ ${LIBRARY_BACNET_CORE}/ihave.c
+ ${LIBRARY_BACNET_CORE}/hostnport.c
+ ${LIBRARY_BACNET_CORE}/lighting.c
+ ${LIBRARY_BACNET_CORE}/memcopy.c
+ ${LIBRARY_BACNET_CORE}/npdu.c
+ ${LIBRARY_BACNET_CORE}/proplist.c
+ ${LIBRARY_BACNET_CORE}/rd.c
+ ${LIBRARY_BACNET_CORE}/reject.c
+ ${LIBRARY_BACNET_CORE}/rp.c
+ ${LIBRARY_BACNET_CORE}/rpm.c
+ ${LIBRARY_BACNET_CORE}/timestamp.c
+ ${LIBRARY_BACNET_CORE}/weeklyschedule.c
+ ${LIBRARY_BACNET_CORE}/dailyschedule.c
+ ${LIBRARY_BACNET_CORE}/calendar_entry.c
+ ${LIBRARY_BACNET_CORE}/special_event.c
+ ${LIBRARY_BACNET_CORE}/bactimevalue.c
+ ${LIBRARY_BACNET_CORE}/whohas.c
+ ${LIBRARY_BACNET_CORE}/whois.c
+ ${LIBRARY_BACNET_CORE}/wp.c
+
+ ${LIBRARY_CMSIS_GCC_INC}/startup_stm32f10x_xl.s
+ CMSIS/stm32f10x.h
+)
+
+set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/stm32f10x.ld)
+
+set(EXECUTABLE ${PROJECT_NAME}.elf)
+
+add_executable(${EXECUTABLE} ${BACNET_PROJECT_SOURCE})
+
+target_compile_definitions(${EXECUTABLE} PRIVATE
+ -DNDEBUG
+ # STM32 CMSIS library
+ -DUSE_STDPERIPH_DRIVER
+ -DSTM32F10X_CL
+ -DHSE_VALUE=25000000
+ # BACnet Stack library
+ -DBACDL_MSTP
+ -DMAX_APDU=1476
+ -DBIG_ENDIAN=0
+ -DMAX_TSM_TRANSACTIONS=0
+ -DBACAPP_MINIMAL
+ -DMAX_CHARACTER_STRING_BYTES=64
+ -DMAX_OCTET_STRING_BYTES=64
+)
+
+# inhibit pedantic warnings
+target_compile_options(${EXECUTABLE} PRIVATE
+ -Wall -Wextra -pedantic
+ -Wfloat-equal -Wconversion -Wredundant-decls
+ -Wswitch-default
+ # don't warn about conversion, sign, compares, long long and attributes
+ # since they are common in embedded
+ -Wno-sign-conversion
+ -Wno-conversion
+ -Wno-sign-compare
+ -Wno-long-long
+ -Wno-attributes
+ # don't warn about implicit fallthrough since it is common in network protocols
+ -Wno-implicit-fallthrough
+ # the SDK does not meet coding guidelines
+ -Wno-comment
+ -Wno-missing-braces
+ -Wno-unused-variable
+ # reference the linker file for CSTACK size
+ -Wstack-usage=16384
+ -Wno-unused-parameter
+ -Wno-char-subscripts
+)
+
+target_include_directories(${EXECUTABLE} PRIVATE
+ ${CMAKE_SOURCE_DIR}
+ ${LIBRARY_CMSIS_INC}
+ ${LIBRARY_CMSIS_GCC_INC}
+ ${LIBRARY_STM32_INC}
+ ${LIBRARY_BACNET_INC}
+)
+
+target_link_options(${EXECUTABLE} PRIVATE
+ -T${LINKER_SCRIPT}
+ -specs=nano.specs
+ -lm
+ -lnosys
+ -Wl,-Map=${PROJECT_NAME}.map,--cref
+ -Wl,--gc-sections
+)
+
+# Create hex and bin files
+add_custom_command(TARGET ${EXECUTABLE}
+ POST_BUILD
+ COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE} ${PROJECT_NAME}.hex
+ COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE} ${PROJECT_NAME}.bin
+)
+
+# Print executable size
+add_custom_command(TARGET ${EXECUTABLE}
+ POST_BUILD
+ COMMAND ${CMAKE_SIZE} ${EXECUTABLE}
+)
+
+# sort the RAM usage by size and place into a file
+add_custom_target(symbols
+ DEPENDS ${EXECUTABLE}
+ COMMENT "Print memory symbols by size"
+ COMMAND ${CMAKE_NM} -t d -S --size-sort ${EXECUTABLE} 1> ${PROJECT_NAME}.nm
+ COMMAND echo "RAM usage by size analysis in ${PROJECT_NAME}.nm"
+ COMMAND echo "=ADDRESS= ==RAM=== = ==VARIABLE-NAME=="
+ COMMAND tail ${PROJECT_NAME}.nm
+)
+
+# calculate the worst case CSTACK memory usage by size and place into a file
+add_custom_target(cstack
+ DEPENDS ${EXECUTABLE}
+ COMMENT "Print CSTACK memory depth by size"
+ COMMAND ${CMAKE_CSTACK} ${EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR} 1> ${PROJECT_NAME}.su
+ COMMAND echo "C-Stack maxium depth analysis in ${PROJECT_NAME}.su"
+ COMMAND echo "==DEPTH== : == Functions called =="
+ COMMAND tail ${PROJECT_NAME}.su
+)
+
+# Print file and library sizes
+add_custom_target(memmap
+ DEPENDS ${PROJECT_NAME}.map
+ COMMENT "Print file and library memory usage by size"
+ COMMAND ${CMAKE_MEMAP} -t GCC_ARM ${PROJECT_NAME}.map
+)
diff --git a/ports/stm32f10x/Makefile b/ports/stm32f10x/Makefile
index 47de1dc7..3269dfee 100644
--- a/ports/stm32f10x/Makefile
+++ b/ports/stm32f10x/Makefile
@@ -13,6 +13,9 @@ LIBRARY_STM32 = ./drivers/src
LIBRARY_STM32_INCLUDES = ./drivers/inc
LIBRARY_CMSIS = ./CMSIS
+CSTACK_TOOL := $(BACNET_DIR)/tools/avstack/avstack.pl
+MEMAP_TOOL := $(BACNET_DIR)/tools/memap/memap.py
+
INCLUDES = -I$(PLATFORM_DIR)
INCLUDES += -I$(LIBRARY_STM32_INCLUDES)
INCLUDES += -I$(LIBRARY_CMSIS)
@@ -23,7 +26,6 @@ PLATFORM_SRC = \
$(PLATFORM_DIR)/bacnet.c \
$(PLATFORM_DIR)/bo.c \
$(PLATFORM_DIR)/device.c \
- $(PLATFORM_DIR)/dlmstp.c \
$(PLATFORM_DIR)/netport.c \
$(PLATFORM_DIR)/led.c \
$(PLATFORM_DIR)/rs485.c \
@@ -61,13 +63,15 @@ BACNET_SRC = \
$(BACNET_CORE)/bacint.c \
$(BACNET_CORE)/bacreal.c \
$(BACNET_CORE)/bacstr.c \
- $(BACNET_CORE)/datalink/automac.c \
- $(BACNET_CORE)/datalink/crc.c \
+ $(BACNET_CORE)/bactimevalue.c \
+ $(BACNET_CORE)/calendar_entry.c \
$(BACNET_CORE)/datetime.c \
+ $(BACNET_CORE)/dailyschedule.c \
$(BACNET_CORE)/dcc.c \
+ $(BACNET_CORE)/hostnport.c \
$(BACNET_CORE)/iam.c \
$(BACNET_CORE)/ihave.c \
- $(BACNET_CORE)/hostnport.c \
+ $(BACNET_CORE)/indtext.c \
$(BACNET_CORE)/lighting.c \
$(BACNET_CORE)/memcopy.c \
$(BACNET_CORE)/npdu.c \
@@ -76,16 +80,20 @@ BACNET_SRC = \
$(BACNET_CORE)/reject.c \
$(BACNET_CORE)/rp.c \
$(BACNET_CORE)/rpm.c \
+ $(BACNET_CORE)/special_event.c \
$(BACNET_CORE)/timestamp.c \
$(BACNET_CORE)/weeklyschedule.c \
- $(BACNET_CORE)/dailyschedule.c \
- $(BACNET_CORE)/bactimevalue.c \
- $(BACNET_CORE)/calendar_entry.c \
- $(BACNET_CORE)/special_event.c \
$(BACNET_CORE)/whohas.c \
$(BACNET_CORE)/whois.c \
$(BACNET_CORE)/wp.c
+DATALINK_SRC = \
+ $(BACNET_CORE)/datalink/cobs.c \
+ $(BACNET_CORE)/datalink/crc.c \
+ $(BACNET_CORE)/datalink/dlmstp.c \
+ $(BACNET_CORE)/datalink/mstp.c \
+ $(BACNET_CORE)/datalink/mstptext.c
+
STM32_SRC = \
$(LIBRARY_STM32)/stm32f10x_adc.c \
$(LIBRARY_STM32)/stm32f10x_bkp.c \
@@ -115,17 +123,20 @@ STM32_SRC = \
CSRC = $(PLATFORM_SRC)
CSRC += $(BASIC_SRC)
CSRC += $(BACNET_SRC)
+CSRC += $(DATALINK_SRC)
CSRC += $(STM32_SRC)
ASRC = $(LIBRARY_CMSIS)/gcc_ride7/startup_stm32f10x_xl.s
#Set the toolchain command names (only the ones needed are defined)
+# sudo apt install gcc-arm-none-eabi
PREFIX ?= arm-none-eabi-
CC = $(PREFIX)gcc
OBJCOPY = $(PREFIX)objcopy
OBJDUMP = $(PREFIX)objdump
AR = $(PREFIX)ar
+NM = $(PREFIX)nm
SIZE = $(PREFIX)size
LDSCRIPT = $(PLATFORM_DIR)/stm32f10x.ld
@@ -142,7 +153,7 @@ OPTIMIZE_FLAGS += -DNDEBUG
BACNET_FLAGS = -DBACDL_MSTP=1
BACNET_FLAGS += -DBACAPP_MINIMAL
-BACNET_FLAGS += -DMAX_APDU=480
+BACNET_FLAGS += -DMAX_APDU=1476
BACNET_FLAGS += -DBIG_ENDIAN=0
BACNET_FLAGS += -DMAX_TSM_TRANSACTIONS=0
BACNET_FLAGS += -DMAX_CHARACTER_STRING_BYTES=64
@@ -161,6 +172,8 @@ CFLAGS += $(BACNET_FLAGS)
CFLAGS += $(INCLUDES)
# enable garbage collection of unused functions and data to shrink binary
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
+# enable stack usage tracking
+CFLAGS += -fstack-usage
# function calls will not use any special __builtin_xx to allow debug/linking
CFLAGS += -fno-builtin
# place uninitialized global variables in the data section of the object file.
@@ -199,6 +212,7 @@ ODFLAGS = -x --syms
AOBJ = $(ASRC:.s=.o)
COBJ = $(CSRC:.c=.o)
+CSTACK = $(CSRC:.c=.su)
all: $(TARGET).bin $(TARGET).elf
$(OBJDUMP) $(ODFLAGS) $(TARGET).elf > $(TARGET).dmp
@@ -210,18 +224,22 @@ $(TARGET).bin: $(TARGET).elf
$(TARGET).elf: $(COBJ) $(AOBJ) Makefile
$(CC) $(CFLAGS) $(AOBJ) $(COBJ) $(LDFLAGS) -o $@
-# allow a single file to be unoptimized for debugging purposes
-#dlmstp.o:
-# $(CC) -c $(CFLAGS) $*.c -o $@
-#
-#main.o:
-# $(CC) -c $(CFLAGS) $*.c -o $@
-#
-#$(BACNET_CORE)/npdu.o:
-# $(CC) -c $(CFLAGS) $*.c -o $@
-#
-#$(BACNET_CORE)/apdu.o:
-# $(CC) -c $(CFLAGS) $*.c -o $@
+.PHONY: ram-usage
+ram-usage:
+ @$(NM) -t d -S --size-sort $(TARGET).elf 1> $(TARGET).nm
+ @echo "=ADDRESS= ==SIZE== = ==VARIABLE NAME=="
+ @tail $(TARGET).nm
+
+.PHONY: cstack
+cstack:
+ @$(CSTACK_TOOL) $(COBJ) 2> /dev/null 1> $(TARGET).su
+ @head -n 25 $(TARGET).su
+
+.PHONY: memap
+memap:
+ # memmap needs Python and PrettyPrint and IntelHex
+ # sudo apt install python3-prettytable python3-intelhex
+ $(MEMAP_TOOL) -t GCC_ARM $(TARGET).map
.c.o:
$(CC) -c $(OPTIMIZATION) $(CFLAGS) $*.c -o $@
@@ -231,8 +249,9 @@ $(TARGET).elf: $(COBJ) $(AOBJ) Makefile
.PHONY: clean
clean:
- -rm -rf $(COBJ) $(AOBJ) $(COREOBJ)
+ -rm -rf $(COBJ) $(AOBJ) $(COREOBJ) $(CSTACK)
-rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).dmp $(TARGET).map
+ -rm -rf $(TARGET).su $(TARGET).nm
-rm -rf *.lst
## Other dependencies
diff --git a/ports/stm32f10x/bacnet.ewp b/ports/stm32f10x/bacnet.ewp
index 448b5f0b..84b9c01b 100644
--- a/ports/stm32f10x/bacnet.ewp
+++ b/ports/stm32f10x/bacnet.ewp
@@ -227,7 +227,7 @@
STM32F10X_XL
USE_STDPERIPH_DRIVER
BACDL_MSTP
- MAX_APDU=480
+ MAX_APDU=1476
MAX_TSM_TRANSACTIONS=0
BACAPP_MINIMAL
@@ -1079,6 +1079,9 @@
$PROJ_DIR$\..\..\src\bacnet\dcc.c
+
+ $PROJ_DIR$\..\..\src\bacnet\indtext.c
+
$PROJ_DIR$\..\..\src\bacnet\iam.c
@@ -1131,13 +1134,19 @@
BACnet-Datalink
- $PROJ_DIR$\..\..\src\bacnet\datalink\automac.c
+ $PROJ_DIR$\..\..\src\bacnet\datalink\cobs.c
$PROJ_DIR$\..\..\src\bacnet\datalink\crc.c
- $PROJ_DIR$\dlmstp.c
+ $PROJ_DIR$\..\..\src\bacnet\datalink\dlmstp.c
+
+
+ $PROJ_DIR$\..\..\src\bacnet\datalink\mstp.c
+
+
+ $PROJ_DIR$\..\..\src\bacnet\datalink\mstptext.c
diff --git a/ports/stm32f10x/device.c b/ports/stm32f10x/device.c
index d1e41c14..bd43f7cc 100644
--- a/ports/stm32f10x/device.c
+++ b/ports/stm32f10x/device.c
@@ -45,10 +45,6 @@
#include "bacnet/basic/object/netport.h"
#endif
-/* forward prototype */
-int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata);
-bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data);
-
static struct my_object_functions {
BACNET_OBJECT_TYPE Object_Type;
object_init_function Object_Init;
diff --git a/ports/stm32f10x/dlmstp.c b/ports/stm32f10x/dlmstp.c
deleted file mode 100644
index cf546855..00000000
--- a/ports/stm32f10x/dlmstp.c
+++ /dev/null
@@ -1,1778 +0,0 @@
-/*####COPYRIGHTBEGIN####
- -------------------------------------------
- Copyright (C) 2009 Steve Karg
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to:
- The Free Software Foundation, Inc.
- 59 Temple Place - Suite 330
- Boston, MA 02111-1307
- USA.
-
- As a special exception, if other files instantiate templates or
- use macros or inline functions from this file, or you compile
- this file and link it with other works to produce a work based
- on this file, this file does not by itself cause the resulting
- work to be covered by the GNU General Public License. However
- the source code for this file must still be made available in
- accordance with section (3) of the GNU General Public License.
-
- This exception does not invalidate any other reasons why a work
- based on this file might be covered by the GNU General Public
- License.
- -------------------------------------------
-####COPYRIGHTEND####*/
-#include
-#include
-#include
-#include
-/* BACnet Stack defines - first */
-#include "bacnet/bacdef.h"
-/* BACnet Stack API */
-#include "bacnet/datalink/dlmstp.h"
-#include "bacnet/npdu.h"
-#include "bacnet/bacaddr.h"
-#include "bacnet/basic/sys/mstimer.h"
-#include "bacnet/basic/sys/ringbuf.h"
-#include "bacnet/datalink/crc.h"
-#include "bacnet/datalink/mstpdef.h"
-#include "bacnet/datalink/automac.h"
-#include "bacnet/basic/object/device.h"
-/* port specific */
-#include "rs485.h"
-
-/* This file has been customized for use with small microprocessors */
-/* Assumptions:
- Only one MS/TP datalink layer
-*/
-
-/* The state of the Receive State Machine */
-static MSTP_RECEIVE_STATE Receive_State;
-/* When a master node is powered up or reset, */
-/* it shall unconditionally enter the INITIALIZE state. */
-static MSTP_MASTER_STATE Master_State;
-/* bit-sized boolean flags */
-static struct mstp_flag_t {
- /* A Boolean flag set to TRUE by the Receive State Machine */
- /* if an invalid frame is received. */
- /* Set to FALSE by the main state machine. */
- unsigned ReceivedInvalidFrame : 1;
- /* A Boolean flag set to TRUE by the Receive State Machine */
- /* if a valid frame is received. */
- /* Set to FALSE by the main state machine. */
- unsigned ReceivedValidFrame : 1;
- /* set to TRUE when we get a frame not for us */
- unsigned ReceivedValidFrameNotForUs : 1;
- /* A Boolean flag set to TRUE by the master machine if this node is the */
- /* only known master node. */
- unsigned SoleMaster : 1;
- /* A Boolean flag set TRUE by the datalink if a
- packet has been received, but not processed. */
- unsigned ReceivePacketPending : 1;
-} MSTP_Flag;
-
-/* Used to store the data length of a received frame. */
-static uint32_t DataLength;
-/* Used to store the destination address of a received frame. */
-static uint8_t DestinationAddress;
-/* Used to count the number of received octets or errors. */
-/* This is used in the detection of link activity. */
-/* Compared to Nmin_octets */
-static uint8_t EventCount;
-/* Used to store the frame type of a received frame. */
-static uint8_t FrameType;
-/* An array of octets, used to store octets as they are received. */
-/* InputBuffer is indexed from 0 to InputBufferSize-1. */
-/* FIXME: assign this to an actual array of bytes! */
-/* Note: the buffer is designed as a pointer since some compilers
- and microcontroller architectures have limits as to places to
- hold contiguous memory. */
-static uint8_t *InputBuffer;
-static uint16_t InputBufferSize;
-/* Used to store the Source Address of a received frame. */
-static uint8_t SourceAddress;
-/* "This Station," the MAC address of this node. TS is generally read from a */
-/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */
-/* 0 to 254. The value 255 is used to denote AutoMAC addressing */
-static uint8_t This_Station = 255;
-/* This parameter represents the value of the Max_Master property of the */
-/* node's Device object. The value of Max_Master specifies the highest */
-/* allowable address for master nodes. The value of Max_Master shall be */
-/* less than or equal to 127. If Max_Master is not writable in a node, */
-/* its value shall be 127. */
-static uint8_t Nmax_master = 127;
-
-/* The minimum time without a DataAvailable or ReceiveError event */
-/* that a node must wait for a station to begin replying to a */
-/* confirmed request: 255 milliseconds. (Implementations may use */
-/* larger values for this timeout, not to exceed 300 milliseconds.) */
-#ifndef Treply_timeout
-#define Treply_timeout 260
-#endif
-
-/* The time without a DataAvailable or ReceiveError event that a node must */
-/* wait for a remote node to begin using a token or replying to a Poll For */
-/* Master frame: 20 milliseconds. (Implementations may use larger values for */
-/* this timeout, not to exceed 35 milliseconds.) */
-#ifndef Tusage_timeout
-#define Tusage_timeout 30
-#endif
-
-/* The minimum number of DataAvailable or ReceiveError events that must be */
-/* seen by a receiving node in order to declare the line "active": 4. */
-#define Nmin_octets 4
-
-/* The minimum time without a DataAvailable or ReceiveError event within */
-/* a frame before a receiving node may discard the frame: 60 bit times. */
-/* (Implementations may use larger values for this timeout, */
-/* not to exceed 100 milliseconds.) */
-/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */
-/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */
-/* At 115200 baud, 60 bit times would be about 0.5 milliseconds */
-/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 115200); */
-#ifndef Tframe_abort
-#define Tframe_abort 30
-#endif
-
-/* The maximum time a node may wait after reception of a frame that expects */
-/* a reply before sending the first octet of a reply or Reply Postponed */
-/* frame: 250 milliseconds. */
-#ifndef Treply_delay
-#define Treply_delay (250 - 50)
-#endif
-
-/* we need to be able to increment without rolling over */
-#define INCREMENT_AND_LIMIT_UINT8(x) \
- { \
- if (x < 0xFF) \
- x++; \
- }
-
-/* data structure for MS/TP transmit packet */
-struct mstp_tx_packet {
- uint16_t length;
- uint16_t index;
- uint8_t buffer[DLMSTP_MPDU_MAX];
-};
-/* count must be a power of 2 for ringbuf library */
-#ifndef MSTP_TRANSMIT_PACKET_COUNT
-#define MSTP_TRANSMIT_PACKET_COUNT 1
-#endif
-static struct mstp_tx_packet Transmit_Buffer[MSTP_TRANSMIT_PACKET_COUNT];
-static RING_BUFFER Transmit_Queue;
-
-/* data structure for MS/TP PDU Queue */
-struct mstp_pdu_packet {
- bool data_expecting_reply;
- uint8_t destination_mac;
- uint16_t length;
- uint8_t buffer[DLMSTP_MPDU_MAX];
-};
-/* count must be a power of 2 for ringbuf library */
-#ifndef MSTP_PDU_PACKET_COUNT
-#define MSTP_PDU_PACKET_COUNT 2
-#endif
-static struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT];
-static RING_BUFFER PDU_Queue;
-/* This parameter represents the value of the Max_Info_Frames property of */
-/* the node's Device object. The value of Max_Info_Frames specifies the */
-/* maximum number of information frames the node may send before it must */
-/* pass the token. Max_Info_Frames may have different values on different */
-/* nodes. This may be used to allocate more or less of the available link */
-/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */
-/* node, its value shall be 1. */
-static uint8_t Nmax_info_frames = MSTP_PDU_PACKET_COUNT;
-
-void dlmstp_automac_hander(void);
-
-bool dlmstp_init(char *ifname)
-{
- (void)ifname;
- Ringbuf_Init(&Transmit_Queue, (uint8_t *)Transmit_Buffer,
- sizeof(struct mstp_tx_packet), MSTP_TRANSMIT_PACKET_COUNT);
- Ringbuf_Init(&PDU_Queue, (uint8_t *)&PDU_Buffer,
- sizeof(struct mstp_pdu_packet), MSTP_PDU_PACKET_COUNT);
- rs485_init();
- automac_init();
-
- return true;
-}
-
-void dlmstp_cleanup(void)
-{
- /* nothing to do for static buffers */
-}
-
-void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address)
-{
- int i = 0;
-
- if (mstp_address == MSTP_BROADCAST_ADDRESS) {
- /* mac_len = 0 if broadcast address */
- src->mac_len = 0;
- src->mac[0] = 0;
- } else {
- src->mac_len = 1;
- src->mac[0] = mstp_address;
- }
- /* fill with 0's starting with index 1; index 0 filled above */
- for (i = 1; i < MAX_MAC_LEN; i++) {
- src->mac[i] = 0;
- }
- src->net = 0;
- src->len = 0;
- for (i = 0; i < MAX_MAC_LEN; i++) {
- src->adr[i] = 0;
- }
-}
-
-static bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu,
- uint16_t request_pdu_len,
- uint8_t src_address,
- uint8_t *reply_pdu,
- uint16_t reply_pdu_len,
- uint8_t dest_address)
-{
- uint16_t offset;
- /* One way to check the message is to compare NPDU
- src, dest, along with the APDU type, invoke id.
- Seems a bit overkill */
- struct DER_compare_t {
- BACNET_NPDU_DATA npdu_data;
- BACNET_ADDRESS address;
- uint8_t pdu_type;
- uint8_t invoke_id;
- uint8_t service_choice;
- };
- struct DER_compare_t request;
- struct DER_compare_t reply;
- bool request_segmented = false;
- bool reply_segmented = false;
-
- /* decode the request data */
- request.address.mac[0] = src_address;
- request.address.mac_len = 1;
- offset = bacnet_npdu_decode(request_pdu, request_pdu_len, NULL,
- &request.address, &request.npdu_data);
- if (request.npdu_data.network_layer_message) {
- return false;
- }
- if (offset >= request_pdu_len) {
- return false;
- }
- request.pdu_type = request_pdu[offset] & 0xF0;
- if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) {
- return false;
- }
- if (request_pdu[offset] & BIT(3)) {
- request_segmented = true;
- }
- if ((offset + 2) >= request_pdu_len) {
- return false;
- }
- request.invoke_id = request_pdu[offset + 2];
- /* segmented message? */
- if (request_segmented) {
- if ((offset + 5) >= request_pdu_len) {
- return false;
- }
- request.service_choice = request_pdu[offset + 5];
- } else {
- if ((offset + 3) >= request_pdu_len) {
- return false;
- }
- request.service_choice = request_pdu[offset + 3];
- }
- /* decode the reply data */
- reply.address.mac[0] = dest_address;
- reply.address.mac_len = 1;
- offset = bacnet_npdu_decode(
- reply_pdu, reply_pdu_len, &reply.address, NULL, &reply.npdu_data);
- if (reply.npdu_data.network_layer_message) {
- return false;
- }
- if (offset >= request_pdu_len) {
- return false;
- }
- reply.pdu_type = reply_pdu[offset] & 0xF0;
- if (reply_pdu[offset] & BIT(3)) {
- reply_segmented = true;
- }
- /* reply could be a lot of things:
- confirmed, simple ack, abort, reject, error */
- switch (reply.pdu_type) {
- case PDU_TYPE_SIMPLE_ACK:
- if ((offset + 2) >= request_pdu_len) {
- return false;
- }
- reply.invoke_id = reply_pdu[offset + 1];
- reply.service_choice = reply_pdu[offset + 2];
- break;
- case PDU_TYPE_COMPLEX_ACK:
- /* segmented message? */
- if (reply_segmented) {
- if ((offset + 4) >= request_pdu_len) {
- return false;
- }
- reply.invoke_id = reply_pdu[offset + 1];
- reply.service_choice = reply_pdu[offset + 4];
- } else {
- if ((offset + 2) >= request_pdu_len) {
- return false;
- }
- reply.invoke_id = reply_pdu[offset + 1];
- reply.service_choice = reply_pdu[offset + 2];
- }
- break;
- case PDU_TYPE_ERROR:
- if ((offset + 2) >= request_pdu_len) {
- return false;
- }
- reply.invoke_id = reply_pdu[offset + 1];
- reply.service_choice = reply_pdu[offset + 2];
- break;
- case PDU_TYPE_REJECT:
- case PDU_TYPE_ABORT:
- if ((offset + 1) >= request_pdu_len) {
- return false;
- }
- reply.invoke_id = reply_pdu[offset + 1];
- break;
- default:
- return false;
- }
- if (request.invoke_id != reply.invoke_id) {
- return false;
- }
- /* these services don't have service choice included */
- if ((reply.pdu_type != PDU_TYPE_REJECT) &&
- (reply.pdu_type != PDU_TYPE_ABORT)) {
- if (request.service_choice != reply.service_choice) {
- return false;
- }
- }
- if (request.npdu_data.protocol_version !=
- reply.npdu_data.protocol_version) {
- return false;
- }
-#if 0
- /* the NDPU priority doesn't get passed through the stack, and
- all outgoing messages have NORMAL priority */
- if (request.npdu_data.priority != reply.npdu_data.priority) {
- return false;
- }
-#endif
- if (!bacnet_address_same(&request.address, &reply.address)) {
- return false;
- }
-
- return true;
-}
-
-typedef enum {
- MSTP_TX_STATE_IDLE,
- MSTP_TX_STATE_SILENCE_WAIT,
- MSTP_TX_STATE_SEND_WAIT,
- MSTP_TX_STATE_STOP
-} MSTP_TX_STATE;
-static bool MSTP_Transmit_FSM(void)
-{
- static MSTP_TX_STATE state = MSTP_TX_STATE_IDLE;
- static struct mstp_tx_packet *pkt;
-
-MSTP_TX_START:
- switch (state) {
- case MSTP_TX_STATE_IDLE:
- if (!Ringbuf_Empty(&Transmit_Queue)) {
- /* get the packet - but don't remove it from queue */
- pkt = (struct mstp_tx_packet *)Ringbuf_Peek(&Transmit_Queue);
- state = MSTP_TX_STATE_SILENCE_WAIT;
- }
- break;
- case MSTP_TX_STATE_SILENCE_WAIT:
- if (rs485_turnaround_elapsed()) {
- rs485_rts_enable(true);
- pkt->index = 0;
- rs485_byte_send(pkt->buffer[pkt->index]);
- state = MSTP_TX_STATE_SEND_WAIT;
- /* optimize a little - for slower CPUs */
- goto MSTP_TX_START;
- }
- break;
- case MSTP_TX_STATE_SEND_WAIT:
- if (rs485_byte_sent()) {
- pkt->index++;
- if (pkt->index < pkt->length) {
- /* send next byte */
- rs485_byte_send(pkt->buffer[pkt->index]);
- /* optimize a little - for slower CPUs */
- goto MSTP_TX_START;
- } else {
- state = MSTP_TX_STATE_STOP;
- }
- }
- break;
- case MSTP_TX_STATE_STOP:
- if (rs485_byte_sent() && rs485_frame_sent()) {
- rs485_rts_enable(false);
- /* remove the packet from the queue */
- (void)Ringbuf_Pop(&Transmit_Queue, NULL);
- state = MSTP_TX_STATE_IDLE;
- }
- break;
- default:
- state = MSTP_TX_STATE_IDLE;
- break;
- }
-
- return (state != MSTP_TX_STATE_IDLE);
-}
-
-/* MS/TP Frame Format */
-/* All frames are of the following format: */
-/* */
-/* Preamble: two octet preamble: X`55', X`FF' */
-/* Frame Type: one octet */
-/* Destination Address: one octet address */
-/* Source Address: one octet address */
-/* Length: two octets, most significant octet first, of the Data field */
-/* Header CRC: one octet */
-/* Data: (present only if Length is non-zero) */
-/* Data CRC: (present only if Length is non-zero) two octets, */
-/* least significant octet first */
-/* (pad): (optional) at most one octet of padding: X'FF' */
-static void MSTP_Send_Frame(
- uint8_t frame_type, /* type of frame to send - see defines */
- uint8_t destination, /* destination address */
- uint8_t source, /* source address */
- uint8_t *data, /* any data to be sent - may be null */
- uint16_t data_len)
-{ /* number of bytes of data (up to 501) */
- uint8_t crc8 = 0xFF; /* used to calculate the crc value */
- uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */
- struct mstp_tx_packet *pkt;
- uint16_t i = 0; /* used to calculate CRC for data */
-
- pkt = (struct mstp_tx_packet *)Ringbuf_Data_Peek(&Transmit_Queue);
- if (pkt) {
- /* create the MS/TP header */
- pkt->buffer[0] = 0x55;
- pkt->buffer[1] = 0xFF;
- pkt->buffer[2] = frame_type;
- crc8 = CRC_Calc_Header(pkt->buffer[2], crc8);
- pkt->buffer[3] = destination;
- crc8 = CRC_Calc_Header(pkt->buffer[3], crc8);
- pkt->buffer[4] = source;
- crc8 = CRC_Calc_Header(pkt->buffer[4], crc8);
- pkt->buffer[5] = data_len / 256;
- crc8 = CRC_Calc_Header(pkt->buffer[5], crc8);
- pkt->buffer[6] = data_len % 256;
- crc8 = CRC_Calc_Header(pkt->buffer[6], crc8);
- pkt->buffer[7] = (uint8_t)(~crc8);
- pkt->length = 8;
- if (data_len) {
- /* calculate CRC for any data */
- for (i = 0; i < data_len; i++) {
- crc16 = CRC_Calc_Data(data[i], crc16);
- pkt->buffer[8 + i] = data[i];
- }
- crc16 = ~crc16;
- pkt->buffer[8 + data_len] = (crc16 & 0x00FF);
- pkt->buffer[8 + data_len + 1] = ((crc16 & 0xFF00) >> 8);
- pkt->length += data_len;
- pkt->length += 2;
- }
- Ringbuf_Data_Put(&Transmit_Queue, (uint8_t *)pkt);
- } else {
- pkt = NULL;
- }
-}
-
-static void MSTP_Receive_Frame_FSM(void)
-{
- /* stores the latest received data octet */
- uint8_t DataRegister = 0;
- /* Used to accumulate the CRC on the data field of a frame. */
- static uint16_t DataCRC = 0;
- /* Used to accumulate the CRC on the header of a frame. */
- static uint8_t HeaderCRC = 0;
- /* Used as an index by the Receive State Machine,
- up to a maximum value of the MPDU */
- static uint16_t Index = 0;
-
- switch (Receive_State) {
- case MSTP_RECEIVE_STATE_IDLE:
- /* In the IDLE state, the node waits
- for the beginning of a frame. */
- if (rs485_receive_error()) {
- /* EatAnError */
- rs485_silence_reset();
- INCREMENT_AND_LIMIT_UINT8(EventCount);
- } else if (rs485_byte_available(&DataRegister)) {
- rs485_silence_reset();
- INCREMENT_AND_LIMIT_UINT8(EventCount);
- if (DataRegister == 0x55) {
- /* Preamble1 */
- /* receive the remainder of the frame. */
- Receive_State = MSTP_RECEIVE_STATE_PREAMBLE;
- }
- }
- break;
- case MSTP_RECEIVE_STATE_PREAMBLE:
- /* In the PREAMBLE state, the node waits for the
- second octet of the preamble. */
- if (rs485_silence_elapsed(Tframe_abort)) {
- /* Timeout */
- /* a correct preamble has not been received */
- /* wait for the start of a frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- } else if (rs485_receive_error()) {
- /* Error */
- rs485_silence_reset();
- INCREMENT_AND_LIMIT_UINT8(EventCount);
- /* wait for the start of a frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- } else if (rs485_byte_available(&DataRegister)) {
- rs485_silence_reset();
- INCREMENT_AND_LIMIT_UINT8(EventCount);
- if (DataRegister == 0xFF) {
- /* Preamble2 */
- Index = 0;
- HeaderCRC = 0xFF;
- /* receive the remainder of the frame. */
- Receive_State = MSTP_RECEIVE_STATE_HEADER;
- } else if (DataRegister == 0x55) {
- /* ignore RepeatedPreamble1 */
- /* wait for the second preamble octet. */
- Receive_State = MSTP_RECEIVE_STATE_PREAMBLE;
- } else {
- /* NotPreamble */
- /* wait for the start of a frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- }
- }
- break;
- case MSTP_RECEIVE_STATE_HEADER:
- /* In the HEADER state, the node waits
- for the fixed message header. */
- if (rs485_silence_elapsed(Tframe_abort)) {
- /* Timeout */
- /* indicate that an error has occurred
- during the reception of a frame */
- MSTP_Flag.ReceivedInvalidFrame = true;
- /* wait for the start of a frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- } else if (rs485_receive_error()) {
- /* Error */
- rs485_silence_reset();
- INCREMENT_AND_LIMIT_UINT8(EventCount);
- /* indicate that an error has occurred
- during the reception of a frame */
- MSTP_Flag.ReceivedInvalidFrame = true;
- /* wait for the start of a frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- } else if (rs485_byte_available(&DataRegister)) {
- rs485_silence_reset();
- INCREMENT_AND_LIMIT_UINT8(EventCount);
- if (Index == 0) {
- /* FrameType */
- HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC);
- FrameType = DataRegister;
- Index = 1;
- } else if (Index == 1) {
- /* Destination */
- HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC);
- DestinationAddress = DataRegister;
- Index = 2;
- } else if (Index == 2) {
- /* Source */
- HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC);
- SourceAddress = DataRegister;
- Index = 3;
- } else if (Index == 3) {
- /* Length1 */
- HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC);
- DataLength = DataRegister * 256;
- Index = 4;
- } else if (Index == 4) {
- /* Length2 */
- HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC);
- DataLength += DataRegister;
- Index = 5;
- } else if (Index == 5) {
- /* HeaderCRC */
- HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC);
- /* In the HEADER_CRC state, the node validates the CRC
- on the fixed message header. */
- if (HeaderCRC != 0x55) {
- /* BadCRC */
- /* indicate that an error has occurred during
- the reception of a frame */
- MSTP_Flag.ReceivedInvalidFrame = true;
- /* wait for the start of the next frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- } else {
- if (DataLength == 0) {
- /* NoData */
- if ((DestinationAddress == This_Station) ||
- (DestinationAddress ==
- MSTP_BROADCAST_ADDRESS)) {
- /* ForUs */
- /* indicate that a frame with
- no data has been received */
- MSTP_Flag.ReceivedValidFrame = true;
- } else {
- /* NotForUs */
- MSTP_Flag.ReceivedValidFrameNotForUs = true;
- }
- /* wait for the start of the next frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- } else {
- /* receive the data portion of the frame. */
- if ((DestinationAddress == This_Station) ||
- (DestinationAddress ==
- MSTP_BROADCAST_ADDRESS)) {
- if (DataLength <= InputBufferSize) {
- /* Data */
- Receive_State = MSTP_RECEIVE_STATE_DATA;
- } else {
- /* FrameTooLong */
- Receive_State =
- MSTP_RECEIVE_STATE_SKIP_DATA;
- }
- } else {
- /* NotForUs */
- Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA;
- }
- Index = 0;
- DataCRC = 0xFFFF;
- }
- }
- } else {
- /* indicate that an error has occurred during */
- /* the reception of a frame */
- MSTP_Flag.ReceivedInvalidFrame = true;
- /* wait for the start of a frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- }
- }
- break;
- case MSTP_RECEIVE_STATE_DATA:
- case MSTP_RECEIVE_STATE_SKIP_DATA:
- /* In the DATA state, the node waits
- for the data portion of a frame. */
- if (rs485_silence_elapsed(Tframe_abort)) {
- /* Timeout */
- /* indicate that an error has occurred
- during the reception of a frame */
- MSTP_Flag.ReceivedInvalidFrame = true;
- /* wait for the start of the next frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- } else if (rs485_receive_error()) {
- /* Error */
- rs485_silence_reset();
- INCREMENT_AND_LIMIT_UINT8(EventCount);
- /* indicate that an error has occurred during
- the reception of a frame */
- MSTP_Flag.ReceivedInvalidFrame = true;
- /* wait for the start of the next frame. */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- } else if (rs485_byte_available(&DataRegister)) {
- rs485_silence_reset();
- INCREMENT_AND_LIMIT_UINT8(EventCount);
- if (Index < DataLength) {
- /* DataOctet */
- DataCRC = CRC_Calc_Data(DataRegister, DataCRC);
- if (Index < InputBufferSize) {
- InputBuffer[Index] = DataRegister;
- }
- Index++;
- } else if (Index == DataLength) {
- /* CRC1 */
- DataCRC = CRC_Calc_Data(DataRegister, DataCRC);
- Index++;
- } else if (Index == (DataLength + 1)) {
- /* CRC2 */
- DataCRC = CRC_Calc_Data(DataRegister, DataCRC);
- /* STATE DATA CRC - no need for new state */
- /* indicate the complete reception of a valid frame */
- if (DataCRC == 0xF0B8) {
- if (Receive_State == MSTP_RECEIVE_STATE_DATA) {
- /* ForUs */
- MSTP_Flag.ReceivedValidFrame = true;
- } else {
- /* NotForUs */
- MSTP_Flag.ReceivedValidFrameNotForUs = true;
- }
- } else {
- MSTP_Flag.ReceivedInvalidFrame = true;
- }
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- } else {
- MSTP_Flag.ReceivedInvalidFrame = true;
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- }
- }
- break;
- default:
- /* shouldn't get here - but if we do... */
- Receive_State = MSTP_RECEIVE_STATE_IDLE;
- break;
- }
-
- return;
-}
-
-static void MSTP_Slave_Node_FSM(void)
-{
- Master_State = MSTP_MASTER_STATE_IDLE;
- if (MSTP_Flag.ReceivedInvalidFrame == true) {
- /* ReceivedInvalidFrame */
- /* invalid frame was received */
- MSTP_Flag.ReceivedInvalidFrame = false;
- } else if (MSTP_Flag.ReceivedValidFrame) {
- MSTP_Flag.ReceivedValidFrame = false;
- switch (FrameType) {
- case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
- if (DestinationAddress != MSTP_BROADCAST_ADDRESS) {
- /* indicate successful reception to the higher layers */
- MSTP_Flag.ReceivePacketPending = true;
- }
- break;
- case FRAME_TYPE_TEST_REQUEST:
- MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, SourceAddress,
- This_Station, &InputBuffer[0], DataLength);
- break;
- case FRAME_TYPE_TOKEN:
- case FRAME_TYPE_POLL_FOR_MASTER:
- case FRAME_TYPE_TEST_RESPONSE:
- case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
- default:
- break;
- }
- } else if (MSTP_Flag.ReceivePacketPending) {
- if (!Ringbuf_Empty(&PDU_Queue)) {
- /* packet from the PDU Queue */
- struct mstp_pdu_packet *pkt;
- /* did the frame in the queue match the last request? */
- bool matched;
-
- pkt = (struct mstp_pdu_packet *)Ringbuf_Peek(&PDU_Queue);
- matched = dlmstp_compare_data_expecting_reply(&InputBuffer[0],
- DataLength, SourceAddress, &pkt->buffer[0], pkt->length,
- pkt->destination_mac);
- if (matched) {
- /* Reply */
- /* If a reply is available from the higher layers */
- /* within Treply_delay after the reception of the */
- /* final octet of the requesting frame */
- /* (the mechanism used to determine this is a local matter), */
- /* then call MSTP_Send_Frame to transmit the reply frame */
- /* and enter the IDLE state to wait for the next frame. */
- uint8_t frame_type;
- if (pkt->data_expecting_reply) {
- frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY;
- } else {
- frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY;
- }
- MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station,
- (uint8_t *)&pkt->buffer[0], pkt->length);
- (void)Ringbuf_Pop(&PDU_Queue, NULL);
- }
- /* clear our flag we were holding for comparison */
- MSTP_Flag.ReceivePacketPending = false;
- } else if ((rs485_silence_elapsed(Treply_delay))) {
- /* If no reply will be available from the higher layers
- within Treply_delay after the reception of the final octet
- of the requesting frame (the mechanism used to determine
- this is a local matter), then no reply is possible. */
- /* clear our flag we were holding for comparison */
- MSTP_Flag.ReceivePacketPending = false;
- }
- }
-}
-
-/* returns true if we need to transition immediately */
-static bool MSTP_Master_Node_FSM(void)
-{
- /* The number of frames sent by this node during a single token hold. */
- /* When this counter reaches the value Nmax_info_frames, the node must */
- /* pass the token. */
- static uint8_t FrameCount;
- /* "Next Station," the MAC address of the node to which This Station
- passes the token. If the Next_Station is unknown, Next_Station shall
- be equal to This_Station. */
- static uint8_t Next_Station;
- /* "Poll Station," the MAC address of the node to which This Station last */
- /* sent a Poll For Master. This is used during token maintenance. */
- static uint8_t Poll_Station;
- /* A counter of transmission retries used for Token and Poll For Master */
- /* transmission. */
- static unsigned RetryCount;
- /* The number of tokens received by this node. When this counter reaches */
- /* the value Npoll, the node polls the address range between TS and NS */
- /* for additional master nodes. TokenCount is set to zero at the end of */
- /* the polling process. */
- static unsigned TokenCount;
- /* next-x-station calculations */
- uint8_t next_poll_station = 0;
- uint8_t next_this_station = 0;
- uint8_t next_next_station = 0;
- /* timeout values */
- uint16_t my_timeout = 10, ns_timeout = 0;
- bool matched = false;
- /* transition immediately to the next state */
- bool transition_now = false;
- /* packet from the PDU Queue */
- struct mstp_pdu_packet *pkt;
-
- /* auto mode is active */
- if (This_Station == 255) {
- Master_State = MSTP_MASTER_STATE_INITIALIZE;
- }
- /* some calculations that several states need */
- next_poll_station = (Poll_Station + 1) % (Nmax_master + 1);
- next_this_station = (This_Station + 1) % (Nmax_master + 1);
- next_next_station = (Next_Station + 1) % (Nmax_master + 1);
- switch (Master_State) {
- case MSTP_MASTER_STATE_INITIALIZE:
- if (This_Station == 255) {
- dlmstp_automac_hander();
- if (This_Station != 255) {
- Next_Station = automac_next_station(This_Station);
- if (Next_Station == 255) {
- /* indicate that the next station is unknown */
- Next_Station = This_Station;
- Poll_Station = This_Station;
- TokenCount = Npoll;
- EventCount = 0;
- MSTP_Flag.SoleMaster = true;
- Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER;
- } else {
- Poll_Station = This_Station;
- TokenCount = 1;
- EventCount = 0;
- RetryCount = 0;
- MSTP_Flag.SoleMaster = false;
- Master_State = MSTP_MASTER_STATE_PASS_TOKEN;
- }
- }
- } else {
- /* DoneInitializing */
- /* indicate that the next station is unknown */
- Next_Station = This_Station;
- Poll_Station = This_Station;
- /* cause a Poll For Master to be sent when this node first */
- /* receives the token */
- TokenCount = Npoll;
- MSTP_Flag.SoleMaster = false;
- Master_State = MSTP_MASTER_STATE_IDLE;
- transition_now = true;
- }
- break;
- case MSTP_MASTER_STATE_IDLE:
- /* In the IDLE state, the node waits for a frame. */
- if (rs485_silence_elapsed(Tno_token)) {
- /* LostToken */
- /* assume that the token has been lost */
- EventCount = 0; /* Addendum 135-2004d-8 */
- /* set the receive frame flags to false in case we received
- some bytes and had a timeout for some reason */
- MSTP_Flag.ReceivedValidFrame = false;
- MSTP_Flag.ReceivedInvalidFrame = false;
- MSTP_Flag.ReceivedValidFrameNotForUs = false;
- Master_State = MSTP_MASTER_STATE_NO_TOKEN;
- transition_now = true;
- } else if (MSTP_Flag.ReceivedInvalidFrame == true) {
- /* ReceivedInvalidFrame */
- /* invalid frame was received */
- MSTP_Flag.ReceivedInvalidFrame = false;
- /* wait for the next frame - remain in IDLE */
- } else if (MSTP_Flag.ReceivedValidFrame == true) {
- switch (FrameType) {
- case FRAME_TYPE_TOKEN:
- /* ReceivedToken */
- /* tokens can't be broadcast */
- if (DestinationAddress == MSTP_BROADCAST_ADDRESS)
- break;
- MSTP_Flag.ReceivedValidFrame = false;
- FrameCount = 0;
- MSTP_Flag.SoleMaster = false;
- Master_State = MSTP_MASTER_STATE_USE_TOKEN;
- transition_now = true;
- break;
- case FRAME_TYPE_POLL_FOR_MASTER:
- /* ReceivedPFM */
- /* DestinationAddress is equal to TS */
- if (DestinationAddress == This_Station) {
- MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER,
- SourceAddress, This_Station, NULL, 0);
- }
- break;
- case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
- if ((DestinationAddress == MSTP_BROADCAST_ADDRESS) &&
- (npdu_confirmed_service(InputBuffer, DataLength))) {
- /* BTL test: verifies that the IUT will quietly
- discard any Confirmed-Request-PDU, whose
- destination address is a multicast or
- broadcast address, received from the
- network layer. */
- } else {
- /* indicate successful reception to higher layer */
- MSTP_Flag.ReceivePacketPending = true;
- }
- break;
- case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
- if (DestinationAddress == MSTP_BROADCAST_ADDRESS) {
- /* broadcast DER just remains IDLE */
- } else {
- /* indicate successful reception to higher layers */
- MSTP_Flag.ReceivePacketPending = true;
- Master_State =
- MSTP_MASTER_STATE_ANSWER_DATA_REQUEST;
- }
- break;
- case FRAME_TYPE_TEST_REQUEST:
- MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, SourceAddress,
- This_Station, &InputBuffer[0], DataLength);
- break;
- case FRAME_TYPE_TEST_RESPONSE:
- default:
- break;
- }
- /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for
- reference, and the flag will be cleared in the next state */
- if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) {
- MSTP_Flag.ReceivedValidFrame = false;
- }
- }
- break;
- /* In the USE_TOKEN state, the node is allowed to send one or */
- /* more data frames. These may be BACnet Data frames or */
- /* proprietary frames. */
- case MSTP_MASTER_STATE_USE_TOKEN:
- /* Note: We could wait for up to Tusage_delay */
- if (Ringbuf_Empty(&PDU_Queue)) {
- /* NothingToSend */
- FrameCount = Nmax_info_frames;
- Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
- transition_now = true;
- } else {
- uint8_t frame_type;
- pkt = (struct mstp_pdu_packet *)Ringbuf_Peek(&PDU_Queue);
- if (pkt->data_expecting_reply) {
- frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY;
- } else {
- frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY;
- }
- MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station,
- (uint8_t *)&pkt->buffer[0], pkt->length);
- FrameCount++;
- switch (frame_type) {
- case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
- /* SendAndWait */
- if (pkt->destination_mac == MSTP_BROADCAST_ADDRESS)
- Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
- else
- Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY;
- break;
- case FRAME_TYPE_TEST_REQUEST:
- Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY;
- break;
- case FRAME_TYPE_TEST_RESPONSE:
- case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
- default:
- /* SendNoWait */
- Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
- break;
- }
- (void)Ringbuf_Pop(&PDU_Queue, NULL);
- }
- break;
- case MSTP_MASTER_STATE_WAIT_FOR_REPLY:
- /* In the WAIT_FOR_REPLY state, the node waits for */
- /* a reply from another node. */
- if (rs485_silence_elapsed(Treply_timeout)) {
- /* ReplyTimeout */
- /* assume that the request has failed */
- FrameCount = Nmax_info_frames;
- Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
- /* Any retry of the data frame shall await the next entry */
- /* to the USE_TOKEN state. */
- /* (Because of the length of the timeout, */
- /* this transition will cause the token to be */
- /* passed regardless */
- /* of the initial value of FrameCount.) */
- transition_now = true;
- } else {
- if (MSTP_Flag.ReceivedInvalidFrame == true) {
- /* InvalidFrame */
- /* error in frame reception */
- MSTP_Flag.ReceivedInvalidFrame = false;
- Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
- transition_now = true;
- } else if (MSTP_Flag.ReceivedValidFrame == true) {
- if (DestinationAddress == This_Station) {
- /* What did we receive? */
- switch (FrameType) {
- case FRAME_TYPE_REPLY_POSTPONED:
- /* ReceivedReplyPostponed */
- Master_State =
- MSTP_MASTER_STATE_DONE_WITH_TOKEN;
- break;
- case FRAME_TYPE_TEST_RESPONSE:
- Master_State =
- MSTP_MASTER_STATE_DONE_WITH_TOKEN;
- break;
- case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
- /* ReceivedReply */
- /* or a proprietary type that indicates
- a reply */
- /* indicate successful reception to
- the higher layers */
- MSTP_Flag.ReceivePacketPending = true;
- Master_State =
- MSTP_MASTER_STATE_DONE_WITH_TOKEN;
- break;
- default:
- /* if proprietary frame was expected, you might
- need to transition to DONE WITH TOKEN */
- Master_State = MSTP_MASTER_STATE_IDLE;
- break;
- }
- } else {
- /* ReceivedUnexpectedFrame */
- /* an unexpected frame was received */
- /* This may indicate the presence of multiple tokens */
- /* or a device that didn't see activity after passing */
- /* a token (how lame!). */
- /* Synchronize with the network. */
- /* This action drops the token. */
- Master_State = MSTP_MASTER_STATE_IDLE;
- }
- MSTP_Flag.ReceivedValidFrame = false;
- transition_now = true;
- }
- }
- break;
- /* The DONE_WITH_TOKEN state either sends another data frame, */
- /* passes the token, or initiates a Poll For Master cycle. */
- case MSTP_MASTER_STATE_DONE_WITH_TOKEN:
- /* SendAnotherFrame */
- if (FrameCount < Nmax_info_frames) {
- /* then this node may send another information frame */
- /* before passing the token. */
- Master_State = MSTP_MASTER_STATE_USE_TOKEN;
- transition_now = true;
- } else if ((MSTP_Flag.SoleMaster == false) &&
- (Next_Station == This_Station)) {
- /* NextStationUnknown - added in Addendum 135-2008v-1 */
- /* then the next station to which the token
- should be sent is unknown - so PollForMaster */
- Poll_Station = next_this_station;
- MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
- This_Station, NULL, 0);
- RetryCount = 0;
- Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER;
- }
- /* Npoll changed in Errata SSPC-135-2004 */
- else if (TokenCount < (Npoll - 1)) {
- if ((MSTP_Flag.SoleMaster == true) &&
- (Next_Station != next_this_station)) {
- /* SoleMaster */
- /* there are no other known master nodes to */
- /* which the token may be sent
- (true master-slave operation). */
- FrameCount = 0;
- TokenCount++;
- Master_State = MSTP_MASTER_STATE_USE_TOKEN;
- transition_now = true;
- } else {
- /* SendToken */
- /* Npoll changed in Errata SSPC-135-2004 */
- /* The comparison of NS and TS+1
- eliminates the Poll For Master
- if there are no addresses between
- TS and NS, since there is no
- address at which a new master node
- may be found in that case. */
- TokenCount++;
- /* transmit a Token frame to NS */
- MSTP_Send_Frame(
- FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL, 0);
- RetryCount = 0;
- EventCount = 0;
- Master_State = MSTP_MASTER_STATE_PASS_TOKEN;
- }
- } else if (next_poll_station == Next_Station) {
- if (MSTP_Flag.SoleMaster == true) {
- /* SoleMasterRestartMaintenancePFM */
- Poll_Station = next_next_station;
- MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
- This_Station, NULL, 0);
- /* no known successor node */
- Next_Station = This_Station;
- RetryCount = 0;
- TokenCount = 1; /* changed in Errata SSPC-135-2004 */
- /* EventCount = 0; removed in Addendum 135-2004d-8 */
- /* find a new successor to TS */
- Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER;
- } else {
- /* ResetMaintenancePFM */
- Poll_Station = This_Station;
- /* transmit a Token frame to NS */
- MSTP_Send_Frame(
- FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL, 0);
- RetryCount = 0;
- TokenCount = 1; /* changed in Errata SSPC-135-2004 */
- EventCount = 0;
- Master_State = MSTP_MASTER_STATE_PASS_TOKEN;
- }
- } else {
- /* SendMaintenancePFM */
- Poll_Station = next_poll_station;
- MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
- This_Station, NULL, 0);
- RetryCount = 0;
- Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER;
- }
- break;
- /* The PASS_TOKEN state listens for a successor to begin using */
- /* the token that this node has just attempted to pass. */
- case MSTP_MASTER_STATE_PASS_TOKEN:
- if (rs485_silence_elapsed(Tusage_timeout)) {
- if (RetryCount < Nretry_token) {
- /* RetrySendToken */
- RetryCount++;
- /* Transmit a Token frame to NS */
- MSTP_Send_Frame(
- FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL, 0);
- EventCount = 0;
- /* re-enter the current state to listen for NS */
- /* to begin using the token. */
- } else {
- /* FindNewSuccessor */
- /* Assume that NS has failed. */
- /* note: if NS=TS-1, this node could send PFM to self! */
- Poll_Station = next_next_station;
- /* Transmit a Poll For Master frame to PS. */
- MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
- This_Station, NULL, 0);
- /* no known successor node */
- Next_Station = This_Station;
- RetryCount = 0;
- TokenCount = 0;
- /* EventCount = 0; removed in Addendum 135-2004d-8 */
- /* find a new successor to TS */
- Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER;
- }
- } else {
- if (EventCount > Nmin_octets) {
- /* SawTokenUser */
- /* Assume that a frame has been sent by
- the new token user. */
- /* Enter the IDLE state to process the frame. */
- Master_State = MSTP_MASTER_STATE_IDLE;
- transition_now = true;
- }
- }
- break;
- /* The NO_TOKEN state is entered if Silence Timer
- becomes greater than Tno_token, indicating that
- there has been no network activity for that period
- of time. The timeout is continued to determine
- whether or not this node may create a token. */
- case MSTP_MASTER_STATE_NO_TOKEN:
- my_timeout = Tno_token + (Tslot * This_Station);
- if (rs485_silence_elapsed(my_timeout)) {
- ns_timeout = Tno_token + (Tslot * (This_Station + 1));
- if (rs485_silence_elapsed(ns_timeout)) {
- /* should never get here unless timer resolution is bad */
- rs485_silence_reset();
- Master_State = MSTP_MASTER_STATE_IDLE;
- } else {
- /* GenerateToken */
- /* Assume that this node is the lowest numerical address */
- /* on the network and is empowered to create a token. */
- Poll_Station = next_this_station;
- /* Transmit a Poll For Master frame to PS. */
- MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
- This_Station, NULL, 0);
- /* indicate that the next station is unknown */
- Next_Station = This_Station;
- RetryCount = 0;
- TokenCount = 0;
- /* EventCount = 0; removed Addendum 135-2004d-8 */
- /* enter the POLL_FOR_MASTER state
- to find a new successor to TS. */
- Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER;
- }
- } else {
- if (EventCount > Nmin_octets) {
- /* SawFrame */
- /* Some other node exists at a lower address. */
- /* Enter the IDLE state to receive and
- process the incoming frame. */
- Master_State = MSTP_MASTER_STATE_IDLE;
- transition_now = true;
- }
- }
- break;
- /* In the POLL_FOR_MASTER state, the node listens for a reply to */
- /* a previously sent Poll For Master frame in order to find */
- /* a successor node. */
- case MSTP_MASTER_STATE_POLL_FOR_MASTER:
- if (MSTP_Flag.ReceivedValidFrame == true) {
- if ((DestinationAddress == This_Station) &&
- (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) {
- /* ReceivedReplyToPFM */
- MSTP_Flag.SoleMaster = false;
- Next_Station = SourceAddress;
- EventCount = 0;
- /* Transmit a Token frame to NS */
- MSTP_Send_Frame(
- FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL, 0);
- Poll_Station = This_Station;
- TokenCount = 0;
- RetryCount = 0;
- Master_State = MSTP_MASTER_STATE_PASS_TOKEN;
- } else {
- /* ReceivedUnexpectedFrame */
- /* An unexpected frame was received. */
- /* This may indicate the presence of multiple tokens. */
- /* enter the IDLE state to synchronize with the network. */
- /* This action drops the token. */
- Master_State = MSTP_MASTER_STATE_IDLE;
- transition_now = true;
- }
- MSTP_Flag.ReceivedValidFrame = false;
- } else if ((rs485_silence_elapsed(Tusage_timeout)) ||
- (MSTP_Flag.ReceivedInvalidFrame == true)) {
- if (MSTP_Flag.SoleMaster == true) {
- /* SoleMaster */
- /* There was no valid reply to the periodic poll */
- /* by the sole known master for other masters. */
- FrameCount = 0;
- /* TokenCount++; removed in 2004 */
- Master_State = MSTP_MASTER_STATE_USE_TOKEN;
- transition_now = true;
- } else {
- if (Next_Station != This_Station) {
- /* DoneWithPFM */
- /* There was no valid reply to the maintenance */
- /* poll for a master at address PS. */
- EventCount = 0;
- /* transmit a Token frame to NS */
- MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station,
- This_Station, NULL, 0);
- RetryCount = 0;
- Master_State = MSTP_MASTER_STATE_PASS_TOKEN;
- } else {
- if (next_poll_station != This_Station) {
- /* SendNextPFM */
- Poll_Station = next_poll_station;
- /* Transmit a Poll For Master frame to PS. */
- MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER,
- Poll_Station, This_Station, NULL, 0);
- RetryCount = 0;
- /* Re-enter the current state. */
- } else {
- /* DeclareSoleMaster */
- /* to indicate that this station
- is the only master */
- MSTP_Flag.SoleMaster = true;
- FrameCount = 0;
- Master_State = MSTP_MASTER_STATE_USE_TOKEN;
- transition_now = true;
- }
- }
- }
- MSTP_Flag.ReceivedInvalidFrame = false;
- }
- break;
- /* The ANSWER_DATA_REQUEST state is entered when a */
- /* BACnet Data Expecting Reply, a Test_Request, or */
- /* a proprietary frame that expects a reply is received. */
- case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST:
- pkt = (struct mstp_pdu_packet *)Ringbuf_Peek(&PDU_Queue);
- if (pkt != NULL) {
- matched = dlmstp_compare_data_expecting_reply(&InputBuffer[0],
- DataLength, SourceAddress, &pkt->buffer[0], pkt->length,
- pkt->destination_mac);
- } else {
- matched = false;
- }
- if (matched) {
- /* Reply */
- /* If a reply is available from the higher layers */
- /* within Treply_delay after the reception of the */
- /* final octet of the requesting frame */
- /* (the mechanism used to determine this is a local matter), */
- /* then call MSTP_Send_Frame to transmit the reply frame */
- /* and enter the IDLE state to wait for the next frame. */
- uint8_t frame_type;
- if (pkt->data_expecting_reply) {
- frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY;
- } else {
- frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY;
- }
- MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station,
- (uint8_t *)&pkt->buffer[0], pkt->length);
- Master_State = MSTP_MASTER_STATE_IDLE;
- /* clear our flag we were holding for comparison */
- MSTP_Flag.ReceivedValidFrame = false;
- /* clear the queue */
- (void)Ringbuf_Pop(&PDU_Queue, NULL);
- } else if (rs485_silence_elapsed(Treply_delay) || (pkt != NULL)) {
- /* DeferredReply */
- /* If no reply will be available from the higher layers */
- /* within Treply_delay after the reception of the */
- /* final octet of the requesting frame (the mechanism */
- /* used to determine this is a local matter), */
- /* then an immediate reply is not possible. */
- /* Any reply shall wait until this node receives the token. */
- /* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */
- /* and enter the IDLE state. */
- MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress,
- This_Station, NULL, 0);
- Master_State = MSTP_MASTER_STATE_IDLE;
- /* clear our flag we were holding for comparison */
- MSTP_Flag.ReceivedValidFrame = false;
- }
- break;
- default:
- Master_State = MSTP_MASTER_STATE_IDLE;
- break;
- }
-
- return transition_now;
-}
-
-/* returns number of bytes sent on success, zero on failure */
-int dlmstp_send_pdu(BACNET_ADDRESS *dest, /* destination address */
- BACNET_NPDU_DATA *npdu_data, /* network information */
- uint8_t *pdu, /* any data to be sent - may be null */
- unsigned pdu_len)
-{ /* number of bytes of data */
- int bytes_sent = 0;
- struct mstp_pdu_packet *pkt;
- uint16_t i = 0;
-
- pkt = (struct mstp_pdu_packet *)Ringbuf_Data_Peek(&PDU_Queue);
- if (pkt) {
- pkt->data_expecting_reply = npdu_data->data_expecting_reply;
- for (i = 0; i < pdu_len; i++) {
- pkt->buffer[i] = pdu[i];
- }
- pkt->length = pdu_len;
- if (dest && dest->mac_len) {
- pkt->destination_mac = dest->mac[0];
- } else {
- pkt->destination_mac = MSTP_BROADCAST_ADDRESS;
- }
- if (Ringbuf_Data_Put(&PDU_Queue, (uint8_t *)pkt)) {
- bytes_sent = pdu_len;
- }
- }
-
- return bytes_sent;
-}
-
-/* returns true if the Send PDU Queue is Empty */
-bool dlmstp_send_pdu_queue_empty(void)
-{
- return Ringbuf_Empty(&PDU_Queue);
-}
-
-/* returns true if the Send PDU Queue is Full */
-bool dlmstp_send_pdu_queue_full(void)
-{
- return Ringbuf_Full(&PDU_Queue);
-}
-
-/* master node FSM states */
-typedef enum {
- AUTOMAC_STATE_IDLE = 0,
- AUTOMAC_STATE_PFM = 1,
- AUTOMAC_STATE_TOKEN = 2,
- AUTOMAC_STATE_TESTING = 3,
- AUTOMAC_STATE_CONFIRM = 4
-} AUTOMAC_STATE;
-/* buffer used to send and validate a response - size is min APDU size */
-static uint8_t AutoMAC_Test_Buffer[50];
-void dlmstp_automac_hander(void)
-{
- static AUTOMAC_STATE state = AUTOMAC_STATE_IDLE;
- uint8_t mac = 0;
- uint32_t serial_number = 0;
- uint16_t vendor_id = 0;
- bool take_address = false;
- bool start_over = false;
-
- switch (state) {
- case AUTOMAC_STATE_IDLE:
- if ((MSTP_Flag.ReceivedValidFrame) ||
- (MSTP_Flag.ReceivedValidFrameNotForUs)) {
- MSTP_Flag.ReceivedValidFrame = false;
- MSTP_Flag.ReceivedValidFrameNotForUs = false;
- /* store stats until we get a MAC */
- automac_emitter_set(SourceAddress);
- switch (FrameType) {
- case FRAME_TYPE_TOKEN:
- automac_token_set(SourceAddress);
- break;
- case FRAME_TYPE_POLL_FOR_MASTER:
- automac_pfm_set(DestinationAddress);
- break;
- default:
- break;
- }
- } else if (MSTP_Flag.ReceivedInvalidFrame) {
- MSTP_Flag.ReceivedInvalidFrame = false;
- } else if (automac_pfm_cycle_complete()) {
- mac = automac_free_address_random();
- if (automac_free_address_valid(mac)) {
- automac_address_set(mac);
- state = AUTOMAC_STATE_PFM;
- } else {
- /* start over again */
- automac_init();
- state = AUTOMAC_STATE_IDLE;
- }
- } else if (rs485_silence_elapsed(automac_time_slot())) {
- /* long silence indicates we are alone or
- with other silent devices */
- SourceAddress = automac_address();
- state = AUTOMAC_STATE_TESTING;
- }
- break;
- case AUTOMAC_STATE_PFM:
- if ((MSTP_Flag.ReceivedValidFrame) ||
- (MSTP_Flag.ReceivedValidFrameNotForUs)) {
- MSTP_Flag.ReceivedValidFrame = false;
- MSTP_Flag.ReceivedValidFrameNotForUs = false;
- /* store stats until we get a MAC */
- switch (FrameType) {
- case FRAME_TYPE_POLL_FOR_MASTER:
- mac = automac_address();
- if (mac == SourceAddress) {
- /* start over again */
- automac_init();
- state = AUTOMAC_STATE_IDLE;
- } else if (mac == DestinationAddress) {
- MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER,
- SourceAddress, mac, NULL, 0);
- state = AUTOMAC_STATE_TOKEN;
- }
- break;
- case FRAME_TYPE_TEST_REQUEST:
- mac = automac_address();
- if ((mac == DestinationAddress) ||
- (mac == SourceAddress)) {
- /* start over again */
- automac_init();
- state = AUTOMAC_STATE_IDLE;
- }
- break;
- case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
- case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
- case FRAME_TYPE_TEST_RESPONSE:
- case FRAME_TYPE_TOKEN:
- mac = automac_address();
- if (mac == SourceAddress) {
- /* start over again */
- automac_init();
- state = AUTOMAC_STATE_IDLE;
- }
- break;
- default:
- break;
- }
- } else if (MSTP_Flag.ReceivedInvalidFrame) {
- MSTP_Flag.ReceivedInvalidFrame = false;
- } else if (rs485_silence_elapsed(automac_time_slot())) {
- /* start over again */
- automac_init();
- state = AUTOMAC_STATE_IDLE;
- }
- break;
- case AUTOMAC_STATE_TOKEN:
- if ((MSTP_Flag.ReceivedValidFrame) ||
- (MSTP_Flag.ReceivedValidFrameNotForUs)) {
- MSTP_Flag.ReceivedValidFrame = false;
- MSTP_Flag.ReceivedValidFrameNotForUs = false;
- switch (FrameType) {
- case FRAME_TYPE_TOKEN:
- mac = automac_address();
- if (mac == SourceAddress) {
- /* start over again */
- automac_init();
- state = AUTOMAC_STATE_IDLE;
- } else if (mac == DestinationAddress) {
- state = AUTOMAC_STATE_TESTING;
- }
- break;
- default:
- /* start over again */
- automac_init();
- state = AUTOMAC_STATE_IDLE;
- break;
- }
- } else if (MSTP_Flag.ReceivedInvalidFrame) {
- MSTP_Flag.ReceivedInvalidFrame = false;
- } else if (rs485_silence_elapsed(automac_time_slot())) {
- /* start over again */
- automac_init();
- state = AUTOMAC_STATE_IDLE;
- }
- break;
- case AUTOMAC_STATE_TESTING:
- /* I have the token - confirm my MAC with a quick test */
- mac = automac_address();
- vendor_id = Device_Vendor_Identifier();
- encode_unsigned16(&AutoMAC_Test_Buffer[0], vendor_id);
- serial_number = Device_Object_Instance_Number();
- encode_unsigned32(&AutoMAC_Test_Buffer[2], serial_number);
- MSTP_Send_Frame(FRAME_TYPE_TEST_REQUEST, SourceAddress, mac,
- &AutoMAC_Test_Buffer[0], 6);
- state = AUTOMAC_STATE_CONFIRM;
- break;
- case AUTOMAC_STATE_CONFIRM:
- /* we may timeout if our chosen MAC is unique */
- if (MSTP_Flag.ReceivedInvalidFrame) {
- MSTP_Flag.ReceivedInvalidFrame = false;
- start_over = true;
- } else if ((MSTP_Flag.ReceivedValidFrame) ||
- (MSTP_Flag.ReceivedValidFrameNotForUs)) {
- MSTP_Flag.ReceivedValidFrame = false;
- MSTP_Flag.ReceivedValidFrameNotForUs = false;
- mac = automac_address();
- if ((mac == DestinationAddress) && (DataLength >= 6)) {
- decode_unsigned16(&InputBuffer[0], &vendor_id);
- decode_unsigned32(&InputBuffer[2], &serial_number);
- if ((vendor_id == Device_Vendor_Identifier()) &&
- (serial_number == Device_Object_Instance_Number())) {
- take_address = true;
- } else {
- start_over = true;
- }
- } else {
- start_over = true;
- }
- } else if (rs485_silence_elapsed(300)) {
- /* use maximum possible value for Treply_timeout */
- /* in case validating device doesn't support Test Request */
- /* no response and no collission */
- take_address = true;
- }
- if (take_address) {
- /* take the address */
- This_Station = automac_address();
- DestinationAddress = automac_next_station(This_Station);
- if (DestinationAddress < 128) {
- MSTP_Send_Frame(FRAME_TYPE_TOKEN, DestinationAddress,
- This_Station, NULL, 0);
- }
- state = AUTOMAC_STATE_IDLE;
- } else if (start_over) {
- /* start over again */
- automac_init();
- state = AUTOMAC_STATE_IDLE;
- }
- break;
- default:
- break;
- }
-}
-
-/* Return the length of the packet */
-uint16_t dlmstp_receive(BACNET_ADDRESS *src, /* source address */
- uint8_t *pdu, /* PDU data */
- uint16_t max_pdu, /* amount of space available in the PDU */
- unsigned timeout)
-{ /* milliseconds to wait for a packet */
- uint16_t pdu_len = 0; /* return value */
- bool transmitting = false;
-
- /* set the input buffer to the same data storage for zero copy */
- if (!InputBuffer) {
- InputBuffer = pdu;
- InputBufferSize = max_pdu;
- }
- if (This_Station == 255) {
- automac_enabled_set(true);
- }
- if (Receive_State == MSTP_RECEIVE_STATE_IDLE) {
- transmitting = MSTP_Transmit_FSM();
- }
- if (transmitting == false) {
- while ((MSTP_Flag.ReceivedValidFrame == false) &&
- (MSTP_Flag.ReceivedValidFrameNotForUs == false) &&
- (MSTP_Flag.ReceivedInvalidFrame == false)) {
- /* only do receive state machine while we don't have a frame */
- MSTP_Receive_Frame_FSM();
- /* process another byte, if available */
- if (!rs485_byte_available(NULL)) {
- break;
- }
- }
- }
- /* only do master state machine while rx is idle */
- if ((Receive_State == MSTP_RECEIVE_STATE_IDLE) && (transmitting == false)) {
- if ((This_Station != 255) && (MSTP_Flag.ReceivedValidFrameNotForUs)) {
- MSTP_Flag.ReceivedValidFrameNotForUs = false;
- if ((SourceAddress == This_Station) && automac_enabled()) {
- /* duplicate MAC on the wire */
- automac_init();
- This_Station = 255;
- }
- } else {
- if ((This_Station > 127) && (This_Station < 255)) {
- MSTP_Slave_Node_FSM();
- } else if (This_Station <= 127) {
- while (MSTP_Master_Node_FSM()) {
- /* do nothing while some states fast transition */
- };
- }
- }
- }
- if (This_Station != 255) {
- /* if there is a packet that needs processed, do it now. */
- if (MSTP_Flag.ReceivePacketPending) {
- if (This_Station <= 127) {
- /* master nodes clear immediately */
- MSTP_Flag.ReceivePacketPending = false;
- }
- pdu_len = DataLength;
- src->mac_len = 1;
- src->mac[0] = SourceAddress;
- /* data is already in the pdu pointer */
- }
- }
-
- return pdu_len;
-}
-
-void dlmstp_set_mac_address(uint8_t mac_address)
-{
- /* Master Nodes can only have address 0-127 */
- if (mac_address <= 127) {
- This_Station = mac_address;
- if (mac_address > Nmax_master) {
- dlmstp_set_max_master(127);
- }
- } else if (mac_address == 255) {
- /* auto-MAC provision */
- This_Station = mac_address;
- }
-
- return;
-}
-
-uint8_t dlmstp_mac_address(void)
-{
- return This_Station;
-}
-
-/* This parameter represents the value of the Max_Info_Frames property of */
-/* the node's Device object. The value of Max_Info_Frames specifies the */
-/* maximum number of information frames the node may send before it must */
-/* pass the token. Max_Info_Frames may have different values on different */
-/* nodes. This may be used to allocate more or less of the available link */
-/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */
-/* node, its value shall be 1. */
-void dlmstp_set_max_info_frames(uint8_t max_info_frames)
-{
- if (max_info_frames >= MSTP_PDU_PACKET_COUNT) {
- Nmax_info_frames = max_info_frames;
- }
-
- return;
-}
-
-uint8_t dlmstp_max_info_frames(void)
-{
- return Nmax_info_frames;
-}
-
-/* This parameter represents the value of the Max_Master property of the */
-/* node's Device object. The value of Max_Master specifies the highest */
-/* allowable address for master nodes. The value of Max_Master shall be */
-/* less than or equal to 127. If Max_Master is not writable in a node, */
-/* its value shall be 127. */
-void dlmstp_set_max_master(uint8_t max_master)
-{
- if (max_master <= 127) {
- if ((This_Station == 255) || (This_Station <= max_master)) {
- Nmax_master = max_master;
- }
- }
-
- return;
-}
-
-uint8_t dlmstp_max_master(void)
-{
- return Nmax_master;
-}
-
-void dlmstp_get_my_address(BACNET_ADDRESS *my_address)
-{
- int i = 0; /* counter */
-
- my_address->mac_len = 1;
- my_address->mac[0] = This_Station;
- my_address->net = 0; /* local only, no routing */
- my_address->len = 0;
- for (i = 0; i < MAX_MAC_LEN; i++) {
- my_address->adr[i] = 0;
- }
-
- return;
-}
-
-void dlmstp_get_broadcast_address(BACNET_ADDRESS *dest)
-{ /* destination address */
- int i = 0; /* counter */
-
- if (dest) {
- dest->mac_len = 1;
- dest->mac[0] = MSTP_BROADCAST_ADDRESS;
- dest->net = BACNET_BROADCAST_NETWORK;
- dest->len = 0; /* always zero when DNET is broadcast */
- for (i = 0; i < MAX_MAC_LEN; i++) {
- dest->adr[i] = 0;
- }
- }
-
- return;
-}
-
-bool dlmstp_sole_master(void)
-{
- if (MSTP_Flag.SoleMaster) {
- return true;
- }
-
- return false;
-}
-
-uint8_t dlmstp_max_info_frames_limit(void)
-{
- return MSTP_PDU_PACKET_COUNT;
-}
-
-uint8_t dlmstp_max_master_limit(void)
-{
- return 127;
-}
diff --git a/ports/stm32f10x/drivers/src/syscalls.c b/ports/stm32f10x/drivers/src/syscalls.c
index 121622c7..c6067ab1 100644
--- a/ports/stm32f10x/drivers/src/syscalls.c
+++ b/ports/stm32f10x/drivers/src/syscalls.c
@@ -20,8 +20,6 @@
extern int errno;
extern int __io_putchar(int ch);
-register char * stack_ptr asm("sp");
-
char *__env[1] = { 0 };
char **environ = __env;
@@ -61,6 +59,7 @@ int _write(int file, char *ptr, int len)
caddr_t _sbrk(int incr)
{
+ register char * stack_ptr asm("sp");
extern char end asm("end");
static char *heap_end;
char *prev_heap_end;
diff --git a/ports/stm32f10x/main.c b/ports/stm32f10x/main.c
index 9beb8f04..927c9d14 100644
--- a/ports/stm32f10x/main.c
+++ b/ports/stm32f10x/main.c
@@ -27,13 +27,40 @@
#include
#include "hardware.h"
#include "bacnet/basic/sys/mstimer.h"
-#include "bacnet/basic/sys/mstimer.h"
+#include "bacnet/datalink/datalink.h"
+#include "bacnet/datalink/dlmstp.h"
+#include "bacnet/datalink/mstp.h"
#include "rs485.h"
#include "led.h"
#include "bacnet.h"
/* local version override */
char *BACnet_Version = "1.0";
+/* MS/TP port */
+static struct mstp_port_struct_t MSTP_Port;
+static struct dlmstp_rs485_driver RS485_Driver = {
+ .init = rs485_init,
+ .send = rs485_bytes_send,
+ .read = rs485_byte_available,
+ .transmitting = rs485_rts_enabled,
+ .baud_rate = rs485_baud_rate,
+ .baud_rate_set = rs485_baud_rate_set,
+ .silence_milliseconds = rs485_silence_milliseconds,
+ .silence_reset = rs485_silence_reset
+};
+static struct dlmstp_user_data_t MSTP_User_Data;
+static uint8_t Input_Buffer[DLMSTP_MPDU_MAX];
+static uint8_t Output_Buffer[DLMSTP_MPDU_MAX];
+
+/**
+ * @brief Called from _write() function from printf and friends
+ * @param[in] ch Character to send
+ */
+int __io_putchar(int ch)
+{
+ (void)ch;
+ return 0;
+}
#ifdef USE_FULL_ASSERT
@@ -102,6 +129,37 @@ void lse_init(void)
}
}
+/**
+ * @brief Configure the MSTP datalink layer
+ */
+static void mstp_configure(void)
+{
+ /* initialize MSTP datalink layer */
+ MSTP_Port.Nmax_info_frames = DLMSTP_MAX_INFO_FRAMES;
+ MSTP_Port.Nmax_master = DLMSTP_MAX_MASTER;
+ MSTP_Port.InputBuffer = Input_Buffer;
+ MSTP_Port.InputBufferSize = sizeof(Input_Buffer);
+ MSTP_Port.OutputBuffer = Output_Buffer;
+ MSTP_Port.OutputBufferSize = sizeof(Output_Buffer);
+ /* user data */
+ MSTP_Port.ZeroConfigEnabled = true;
+ MSTP_Port.SlaveNodeEnabled = false;
+ MSTP_User_Data.RS485_Driver = &RS485_Driver;
+ MSTP_Port.UserData = &MSTP_User_Data;
+ dlmstp_init((char *)&MSTP_Port);
+ if (MSTP_Port.ZeroConfigEnabled) {
+ dlmstp_set_mac_address(255);
+ } else {
+ /* FIXME: get the address from hardware DIP or from EEPROM */
+ dlmstp_set_mac_address(1);
+ }
+ /* FIXME: get the baud rate from hardware DIP or from EEPROM */
+ dlmstp_set_baud_rate(DLMSTP_BAUD_RATE_DEFAULT);
+}
+
+/**
+ * @brief Main entry point of the C application
+ */
int main(void)
{
struct mstimer Blink_Timer;
@@ -116,9 +174,13 @@ int main(void)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE,
ENABLE);
+ /* initialize hardware layer */
mstimer_init();
lse_init();
led_init();
+ /* initialize MSTP datalink layer */
+ mstp_configure();
+ /* initialize application layer*/
bacnet_init();
mstimer_set(&Blink_Timer, 125);
for (;;) {
diff --git a/ports/stm32f10x/rs485.c b/ports/stm32f10x/rs485.c
index b5872151..717bb0c3 100644
--- a/ports/stm32f10x/rs485.c
+++ b/ports/stm32f10x/rs485.c
@@ -45,70 +45,35 @@ static FIFO_BUFFER Receive_Buffer;
static struct mstimer Silence_Timer;
/* baud rate */
static uint32_t Baud_Rate = 38400;
+/* flag to track RTS status */
+static volatile bool Transmitting;
-/* The minimum time after the end of the stop bit of the final octet of a */
-/* received frame before a node may enable its EIA-485 driver: 40 bit times. */
-/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */
-/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */
-/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */
-/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */
-/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */
-/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */
-/* 40 bits is 4 octets including a start and stop bit with each octet */
-#define Tturnaround (40UL)
+/* statistics */
+static volatile uint32_t RS485_Transmit_Bytes;
+static volatile uint32_t RS485_Receive_Bytes;
-/*************************************************************************
- * Description: Reset the silence on the wire timer.
- * Returns: nothing
- * Notes: none
- **************************************************************************/
+
+/**
+ * @brief Reset the silence on the wire timer.
+ */
void rs485_silence_reset(void)
{
mstimer_set(&Silence_Timer, 0);
}
-/*************************************************************************
- * Description: Determine the amount of silence on the wire from the timer.
- * Returns: true if the amount of time has elapsed
- * Notes: none
- **************************************************************************/
-bool rs485_silence_elapsed(uint32_t interval)
+/**
+ * @brief Return the RS-485 silence time in milliseconds
+ * @return silence time in milliseconds
+ */
+uint32_t rs485_silence_milliseconds(void)
{
- return (mstimer_elapsed(&Silence_Timer) > interval);
+ return mstimer_elapsed(&Silence_Timer);
}
-/*************************************************************************
- * Description: Baud rate determines turnaround time.
- * Returns: amount of milliseconds
- * Notes: none
- **************************************************************************/
-static uint16_t rs485_turnaround_time(void)
-{
- /* delay after reception before transmitting - per MS/TP spec */
- /* wait a minimum 40 bit times since reception */
- /* at least 2 ms for errors: rounding, clock tick */
- if (Baud_Rate) {
- return (2 + ((Tturnaround * 1000UL) / Baud_Rate));
- } else {
- return 2;
- }
-}
-
-/*************************************************************************
- * Description: Use the silence timer to determine turnaround time.
- * Returns: true if turnaround time has expired.
- * Notes: none
- **************************************************************************/
-bool rs485_turnaround_elapsed(void)
-{
- return (mstimer_elapsed(&Silence_Timer) > rs485_turnaround_time());
-}
-
-/*************************************************************************
- * Description: Determines if an error occured while receiving
- * Returns: true an error occurred.
- * Notes: none
- **************************************************************************/
+/**
+ * @brief Determines if an error occured while receiving
+ * @return true an error occurred
+ */
bool rs485_receive_error(void)
{
return false;
@@ -125,6 +90,7 @@ void USART2_IRQHandler(void)
/* Read one byte from the receive data register */
data_byte = USART_ReceiveData(USART2);
(void)FIFO_Put(&Receive_Buffer, data_byte);
+ RS485_Receive_Bytes++;
}
if (USART_GetFlagStatus(USART2, USART_FLAG_ORE) != RESET) {
/* note: enabling RXNE interrupt also enables the ORE interrupt! */
@@ -134,11 +100,35 @@ void USART2_IRQHandler(void)
}
}
-/*************************************************************************
- * DESCRIPTION: Return true if a byte is available
- * RETURN: true if a byte is available, with the byte in the parameter
- * NOTES: none
- **************************************************************************/
+/**
+ * @brief Control the DE and /RE pins on the RS-485 transceiver
+ * @param enable - true to set DE and /RE high, false to set /DE and RE low
+ */
+void rs485_rts_enable(bool enable)
+{
+ if (enable) {
+ led_tx_on_interval(10);
+ GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
+ } else {
+ GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
+ }
+}
+
+/**
+ * @brief Determine the status of the transmit-enable line on the RS-485
+ * transceiver
+ * @return true if RTS is enabled, false if RTS is disabled
+ */
+bool rs485_rts_enabled(void)
+{
+ return Transmitting;
+}
+
+/**
+ * @brief Return true if a byte is available
+ * @param data_register - byte in this parameter if there is one available
+ * @return true if a byte is available, with the byte in the parameter
+ */
bool rs485_byte_available(uint8_t *data_register)
{
bool data_available = false; /* return value */
@@ -155,50 +145,37 @@ bool rs485_byte_available(uint8_t *data_register)
return data_available;
}
-/*************************************************************************
- * DESCRIPTION: Sends a byte of data
- * RETURN: nothing
- * NOTES: none
- **************************************************************************/
-void rs485_byte_send(uint8_t tx_byte)
-{
- led_tx_on_interval(10);
- USART_SendData(USART2, tx_byte);
- rs485_silence_reset();
-}
-
-/*************************************************************************
- * Description: Determines if a byte in the USART has been shifted from
- * register
- * Returns: true if the USART register is empty
- * Notes: none
- **************************************************************************/
+/**
+ * @brief Determines if a byte in the USART has been shifted from register
+ * @return true if the USART register is empty
+ */
bool rs485_byte_sent(void)
{
return USART_GetFlagStatus(USART2, USART_FLAG_TXE);
}
-/*************************************************************************
- * Description: Determines if the entire frame is sent from USART FIFO
- * Returns: true if the USART FIFO is empty
- * Notes: none
- **************************************************************************/
+/**
+ * @brief Determines if the entire frame is sent from USART FIFO
+ * @brief true if the USART FIFO is empty
+ */
bool rs485_frame_sent(void)
{
return USART_GetFlagStatus(USART2, USART_FLAG_TC);
}
-/*************************************************************************
- * DESCRIPTION: Send some data and wait until it is sent
- * RETURN: true if a collision or timeout occurred
- * NOTES: none
- **************************************************************************/
+/**
+ * @brief Transmit one or more bytes on RS-485.
+ * @param buffer - array of one or more bytes to transmit
+ * @param nbytes - number of bytes to transmit
+ * @return true if added to queue
+ */
void rs485_bytes_send(uint8_t *buffer, /* data to send */
uint16_t nbytes)
{ /* number of bytes of data */
uint8_t tx_byte;
while (nbytes) {
+ rs485_rts_enable(true);
/* Send the data byte */
tx_byte = *buffer;
/* Send one byte */
@@ -208,22 +185,22 @@ void rs485_bytes_send(uint8_t *buffer, /* data to send */
}
buffer++;
nbytes--;
+ RS485_Transmit_Bytes++;
}
/* was the frame sent? */
while (!rs485_frame_sent()) {
/* do nothing - wait until the entire frame in the
Transmit Shift Register has been shifted out */
}
+ rs485_rts_enable(false);
rs485_silence_reset();
return;
}
-/*************************************************************************
- * Description: Configures the baud rate of the USART
- * Returns: nothing
- * Notes: none
- **************************************************************************/
+/**
+ * @brief Configures the baud rate of the USART
+ */
static void rs485_baud_rate_configure(void)
{
USART_InitTypeDef USART_InitStructure;
@@ -240,11 +217,10 @@ static void rs485_baud_rate_configure(void)
USART_Init(USART2, &USART_InitStructure);
}
-/*************************************************************************
- * Description: Sets the baud rate to non-volatile storeage and configures USART
- * Returns: true if a value baud rate was saved
- * Notes: none
- **************************************************************************/
+/**
+ * @brief Sets the baud rate to non-volatile storeage and configures USART
+ * @return true if a value baud rate was saved
+ */
bool rs485_baud_rate_set(uint32_t baud)
{
bool valid = true;
@@ -267,35 +243,36 @@ bool rs485_baud_rate_set(uint32_t baud)
return valid;
}
-/*************************************************************************
- * Description: Determines the baud rate in bps
- * Returns: baud rate in bps
- * Notes: none
- **************************************************************************/
+/**
+ * @brief Return the RS-485 baud rate
+ * @return baud - RS-485 baud rate in bits per second (bps)
+ */
uint32_t rs485_baud_rate(void)
{
return Baud_Rate;
}
-/*************************************************************************
- * Description: Enable the Request To Send (RTS) aka Transmit Enable pin
- * Returns: nothing
- * Notes: none
- **************************************************************************/
-void rs485_rts_enable(bool enable)
+/**
+ * @brief Return the RS-485 statistics for transmit bytes
+ * @return number of bytes transmitted
+ */
+uint32_t rs485_bytes_transmitted(void)
{
- if (enable) {
- GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
- } else {
- GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
- }
+ return RS485_Transmit_Bytes;
}
-/*************************************************************************
- * Description: Initialize the room network USART
- * Returns: nothing
- * Notes: none
- **************************************************************************/
+/**
+ * @brief Return the RS-485 statistics for receive bytes
+ * @return number of bytes received
+ */
+uint32_t rs485_bytes_received(void)
+{
+ return RS485_Receive_Bytes;
+}
+
+/**
+ * @brief Initialize the room network USART
+ */
void rs485_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
diff --git a/ports/stm32f10x/rs485.h b/ports/stm32f10x/rs485.h
index b055de2a..ff8094c4 100644
--- a/ports/stm32f10x/rs485.h
+++ b/ports/stm32f10x/rs485.h
@@ -36,33 +36,34 @@ extern "C" {
void rs485_init(
void);
+
void rs485_rts_enable(
bool enable);
+ bool rs485_rts_enabled(
+ void);
+
bool rs485_byte_available(
uint8_t * data_register);
bool rs485_receive_error(
void);
void rs485_bytes_send(
- uint8_t * buffer, /* data to send */
- uint16_t nbytes); /* number of bytes of data */
+ uint8_t * buffer,
+ uint16_t nbytes);
+
uint32_t rs485_baud_rate(
void);
bool rs485_baud_rate_set(
uint32_t baud);
- /* a granular approach */
- void rs485_byte_send(
- uint8_t data_register);
- bool rs485_byte_sent(
- void);
- bool rs485_frame_sent(
- void);
- bool rs485_turnaround_elapsed(
- void);
+ uint32_t rs485_silence_milliseconds(
+ void);
void rs485_silence_reset(
void);
- bool rs485_silence_elapsed(
- uint32_t interval);
+
+ uint32_t rs485_bytes_transmitted(
+ void);
+ uint32_t rs485_bytes_received(
+ void);
#ifdef __cplusplus
}
diff --git a/ports/stm32f4xx/CMakeLists.txt b/ports/stm32f4xx/CMakeLists.txt
index c428e6ef..ce7a089d 100644
--- a/ports/stm32f4xx/CMakeLists.txt
+++ b/ports/stm32f4xx/CMakeLists.txt
@@ -49,8 +49,12 @@ set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
set(CMAKE_AR arm-none-eabi-ar)
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
set(CMAKE_OBJDUMP arm-none-eabi-objdump)
-set(SIZE arm-none-eabi-size)
+set(CMAKE_SIZE arm-none-eabi-size)
+set(CMAKE_NM arm-none-eabi-nm)
+set(CMAKE_CSTACK "${CMAKE_SOURCE_DIR}/../../tools/check-stack-usage/checkStackUsage.py")
+set(CMAKE_MEMAP "${CMAKE_SOURCE_DIR}/../../tools/memap/memap.py")
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+set(EXECUTABLE ${PROJECT_NAME}.elf)
project(bacnet-mstp)
@@ -64,6 +68,7 @@ add_compile_options(-mcpu=cortex-m4)
add_compile_options(-mthumb -mthumb-interwork)
add_compile_options(-ffunction-sections -fdata-sections)
add_compile_options(-fno-common -fmessage-length=0)
+add_compile_options(-fstack-usage -fdump-rtl-dfinish)
add_link_options(-mcpu=cortex-m4)
add_link_options(-mthumb -mthumb-interwork)
add_link_options(-Wl,-gc-sections,--print-memory-usage)
@@ -95,13 +100,13 @@ if(BACNET_STACK_DEPRECATED_DISABLE)
add_definitions(-DBACNET_STACK_DEPRECATED_DISABLE)
endif()
-set(LIBRARY_BACNET_INC "../../src")
-set(LIBRARY_BACNET_CORE "../../src/bacnet")
-set(LIBRARY_BACNET_BASIC "../../src/bacnet/basic")
-set(LIBRARY_STM32_SRC "./STM32F4xx_StdPeriph_Driver/src")
-set(LIBRARY_STM32_INC "./STM32F4xx_StdPeriph_Driver/inc")
-set(LIBRARY_CMSIS_INC "./CMSIS")
-set(LIBRARY_CMSIS_GCC_INC "./CMSIS/gcc_ride7")
+set(LIBRARY_BACNET_INC "${CMAKE_SOURCE_DIR}/../../src")
+set(LIBRARY_BACNET_CORE "${CMAKE_SOURCE_DIR}/../../src/bacnet")
+set(LIBRARY_BACNET_BASIC "${CMAKE_SOURCE_DIR}/../../src/bacnet/basic")
+set(LIBRARY_STM32_SRC "${CMAKE_SOURCE_DIR}/STM32F4xx_StdPeriph_Driver/src")
+set(LIBRARY_STM32_INC "${CMAKE_SOURCE_DIR}/STM32F4xx_StdPeriph_Driver/inc")
+set(LIBRARY_CMSIS_INC "${CMAKE_SOURCE_DIR}/CMSIS")
+set(LIBRARY_CMSIS_GCC_INC "${CMAKE_SOURCE_DIR}/CMSIS/gcc_ride7")
set(BACNET_PROJECT_SOURCE
${LIBRARY_STM32_SRC}/stm32f4xx_adc.c
@@ -242,6 +247,8 @@ target_compile_options(${EXECUTABLE} PRIVATE
-Wno-comment
-Wno-missing-braces
-Wno-unused-variable
+ # reference the linker file for CSTACK size
+ -Wstack-usage=16384
-Wno-unused-parameter
-Wno-char-subscripts
)
@@ -263,15 +270,42 @@ target_link_options(${EXECUTABLE} PRIVATE
-Wl,--gc-sections
)
+# Create hex and bin files
+add_custom_command(TARGET ${EXECUTABLE}
+ POST_BUILD
+ COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE} ${PROJECT_NAME}.hex
+ COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE} ${PROJECT_NAME}.bin
+)
+
# Print executable size
add_custom_command(TARGET ${EXECUTABLE}
POST_BUILD
- COMMAND arm-none-eabi-size ${EXECUTABLE}
+ COMMAND ${CMAKE_SIZE} ${EXECUTABLE}
)
-# Create hex file
-add_custom_command(TARGET ${EXECUTABLE}
- POST_BUILD
- COMMAND arm-none-eabi-objcopy -O ihex ${EXECUTABLE} ${PROJECT_NAME}.hex
- COMMAND arm-none-eabi-objcopy -O binary ${EXECUTABLE} ${PROJECT_NAME}.bin
+# sort the RAM usage by size and place into a file
+add_custom_target(symbols
+ DEPENDS ${EXECUTABLE}
+ COMMENT "Print memory symbols by size"
+ COMMAND ${CMAKE_NM} -t d -S --size-sort ${EXECUTABLE} 1> ${PROJECT_NAME}.nm
+ COMMAND echo "RAM usage by size analysis in ${PROJECT_NAME}.nm"
+ COMMAND echo "=ADDRESS= ==RAM=== = ==VARIABLE-NAME=="
+ COMMAND tail ${PROJECT_NAME}.nm
+)
+
+# calculate the worst case CSTACK memory usage by size and place into a file
+add_custom_target(cstack
+ DEPENDS ${EXECUTABLE}
+ COMMENT "Print CSTACK memory depth by size"
+ COMMAND ${CMAKE_CSTACK} ${EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR} 1> ${PROJECT_NAME}.su
+ COMMAND echo "C-Stack maxium depth analysis in ${PROJECT_NAME}.su"
+ COMMAND echo "==DEPTH== : == Functions called =="
+ COMMAND tail ${PROJECT_NAME}.su
+)
+
+# Print file and library sizes
+add_custom_target(memmap
+ DEPENDS ${PROJECT_NAME}.map
+ COMMENT "Print file and library memory usage by size"
+ COMMAND ${CMAKE_MEMAP} -t GCC_ARM ${PROJECT_NAME}.map
)
diff --git a/ports/stm32f4xx/Makefile b/ports/stm32f4xx/Makefile
index 37d16287..ce4b024c 100644
--- a/ports/stm32f4xx/Makefile
+++ b/ports/stm32f4xx/Makefile
@@ -13,6 +13,9 @@ LIBRARY_STM32 = ./STM32F4xx_StdPeriph_Driver/src
LIBRARY_STM32_INCLUDES = ./STM32F4xx_StdPeriph_Driver/inc
LIBRARY_CMSIS = ./CMSIS
+CSTACK_TOOL := $(BACNET_DIR)/tools/avstack/avstack.pl
+MEMAP_TOOL := $(BACNET_DIR)/tools/memap/memap.py
+
INCLUDES = -I$(PLATFORM_DIR)
INCLUDES += -I$(LIBRARY_STM32_INCLUDES)
INCLUDES += -I$(LIBRARY_CMSIS)
@@ -59,17 +62,15 @@ BACNET_SRC = \
$(BACNET_CORE)/bacint.c \
$(BACNET_CORE)/bacreal.c \
$(BACNET_CORE)/bacstr.c \
- $(BACNET_CORE)/datalink/cobs.c \
- $(BACNET_CORE)/datalink/crc.c \
- $(BACNET_CORE)/datalink/dlmstp.c \
- $(BACNET_CORE)/datalink/mstp.c \
- $(BACNET_CORE)/datalink/mstptext.c \
+ $(BACNET_CORE)/bactimevalue.c \
+ $(BACNET_CORE)/calendar_entry.c \
+ $(BACNET_CORE)/dailyschedule.c \
$(BACNET_CORE)/datetime.c \
$(BACNET_CORE)/dcc.c \
- $(BACNET_CORE)/indtext.c \
+ $(BACNET_CORE)/hostnport.c \
$(BACNET_CORE)/iam.c \
$(BACNET_CORE)/ihave.c \
- $(BACNET_CORE)/hostnport.c \
+ $(BACNET_CORE)/indtext.c \
$(BACNET_CORE)/lighting.c \
$(BACNET_CORE)/memcopy.c \
$(BACNET_CORE)/npdu.c \
@@ -78,16 +79,20 @@ BACNET_SRC = \
$(BACNET_CORE)/reject.c \
$(BACNET_CORE)/rp.c \
$(BACNET_CORE)/rpm.c \
+ $(BACNET_CORE)/special_event.c \
$(BACNET_CORE)/timestamp.c \
$(BACNET_CORE)/weeklyschedule.c \
- $(BACNET_CORE)/dailyschedule.c \
- $(BACNET_CORE)/calendar_entry.c \
- $(BACNET_CORE)/special_event.c \
- $(BACNET_CORE)/bactimevalue.c \
$(BACNET_CORE)/whohas.c \
$(BACNET_CORE)/whois.c \
$(BACNET_CORE)/wp.c
+DATALINK_SRC = \
+ $(BACNET_CORE)/datalink/cobs.c \
+ $(BACNET_CORE)/datalink/crc.c \
+ $(BACNET_CORE)/datalink/dlmstp.c \
+ $(BACNET_CORE)/datalink/mstp.c \
+ $(BACNET_CORE)/datalink/mstptext.c
+
STM32_SRC = \
$(LIBRARY_STM32)/stm32f4xx_adc.c \
$(LIBRARY_STM32)/stm32f4xx_can.c \
@@ -118,6 +123,7 @@ STM32_SRC = \
CSRC = $(PLATFORM_SRC)
CSRC += $(BASIC_SRC)
CSRC += $(BACNET_SRC)
+CSRC += $(DATALINK_SRC)
CSRC += $(STM32_SRC)
ASRC = $(LIBRARY_CMSIS)/gcc_ride7/startup_stm32f4xx.s
@@ -173,6 +179,8 @@ CFLAGS += $(BACNET_FLAGS)
CFLAGS += $(INCLUDES)
# enable garbage collection of unused functions and data to shrink binary
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
+# enable stack usage tracking
+CFLAGS += -fstack-usage
# function calls will not use any special __builtin_xx to allow debug/linking
CFLAGS += -fno-builtin
# place uninitialized global variables in the data section of the object file.
@@ -211,6 +219,7 @@ ODFLAGS = -x --syms
AOBJ = $(ASRC:.s=.o)
COBJ = $(CSRC:.c=.o)
+CSTACK = $(CSRC:.c=.su)
all: $(TARGET).bin $(TARGET).hex $(TARGET).elf
$(OBJDUMP) $(ODFLAGS) $(TARGET).elf > $(TARGET).dmp
@@ -225,9 +234,22 @@ $(TARGET).hex: $(TARGET).elf
$(TARGET).elf: $(COBJ) $(AOBJ) Makefile
$(CC) $(CFLAGS) $(AOBJ) $(COBJ) $(LDFLAGS) -o $@
-: ram-usage
+.PHONY: ram-usage
ram-usage:
- $(NM) -t d -S --size-sort $(TARGET).elf 1> $(TARGET).nm
+ @$(NM) -t d -S --size-sort $(TARGET).elf 1> $(TARGET).nm
+ @echo "=ADDRESS= ==SIZE== = ==VARIABLE NAME=="
+ @tail $(TARGET).nm
+
+.PHONY: cstack
+cstack:
+ @$(CSTACK_TOOL) $(COBJ) 2> /dev/null 1> $(TARGET).su
+ @head -n 25 $(TARGET).su
+
+.PHONY: memap
+memap:
+ # memmap needs Python and PrettyPrint and IntelHex
+ # sudo apt install python3-prettytable python3-intelhex
+ $(MEMAP_TOOL) -t GCC_ARM $(TARGET).map
# GDB using st-util (GDB server for ST Link)
GDB_PORT = 3333
@@ -282,6 +304,7 @@ install: $(TARGET).bin
.PHONY: clean
clean:
-rm -rf $(COBJ) $(AOBJ) $(COREOBJ)
+ -rm -rf $(CSTACK) $(CEXPAND)
-rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).dmp $(TARGET).map
-rm -rf $(TARGET).su $(TARGET).nm
-rm -rf *.lst
diff --git a/ports/stm32f4xx/STM32F4xx_StdPeriph_Driver/src/syscalls.c b/ports/stm32f4xx/STM32F4xx_StdPeriph_Driver/src/syscalls.c
index 121622c7..c6067ab1 100644
--- a/ports/stm32f4xx/STM32F4xx_StdPeriph_Driver/src/syscalls.c
+++ b/ports/stm32f4xx/STM32F4xx_StdPeriph_Driver/src/syscalls.c
@@ -20,8 +20,6 @@
extern int errno;
extern int __io_putchar(int ch);
-register char * stack_ptr asm("sp");
-
char *__env[1] = { 0 };
char **environ = __env;
@@ -61,6 +59,7 @@ int _write(int file, char *ptr, int len)
caddr_t _sbrk(int incr)
{
+ register char * stack_ptr asm("sp");
extern char end asm("end");
static char *heap_end;
char *prev_heap_end;
diff --git a/ports/stm32f4xx/device.c b/ports/stm32f4xx/device.c
index 1c70b706..f2056fb8 100644
--- a/ports/stm32f4xx/device.c
+++ b/ports/stm32f4xx/device.c
@@ -45,10 +45,6 @@
#endif
#include "bacnet/basic/object/device.h"
-/* forward prototype */
-int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata);
-bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data);
-
static struct my_object_functions {
BACNET_OBJECT_TYPE Object_Type;
object_init_function Object_Init;
diff --git a/tools/avstack/avstack.pl b/tools/avstack/avstack.pl
new file mode 100755
index 00000000..93d39fa1
--- /dev/null
+++ b/tools/avstack/avstack.pl
@@ -0,0 +1,251 @@
+#!/usr/bin/perl -w
+# avstack.pl: GCC C stack checker
+# Copyright (C) 2013 Daniel Beer
+#
+# Permission to use, copy, modify, and/or distribute this software for
+# any purpose with or without fee is hereby granted, provided that the
+# above copyright notice and this permission notice appear in all
+# copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+#
+# Usage
+# -----
+#
+# This script requires that you compile your code with -fstack-usage.
+# This results in GCC generating a .su file for each .o file. Once you
+# have these, do:
+#
+# ./avstack.pl