From eb06ee919a1c80cdbb0f418c8a1d2c004cb96be2 Mon Sep 17 00:00:00 2001 From: skarg Date: Mon, 4 May 2009 22:14:45 +0000 Subject: [PATCH] Added demo port for BACnetDevelopmentKit.com board. --- bacnet-stack/ports/bdk-atxx4-mstp/Makefile | 226 +++ bacnet-stack/ports/bdk-atxx4-mstp/ai.c | 159 +++ bacnet-stack/ports/bdk-atxx4-mstp/bacnet.aps | 1 + bacnet-stack/ports/bdk-atxx4-mstp/bi.c | 192 +++ bacnet-stack/ports/bdk-atxx4-mstp/bits.h | 89 ++ bacnet-stack/ports/bdk-atxx4-mstp/bo.c | 425 ++++++ bacnet-stack/ports/bdk-atxx4-mstp/device.c | 552 +++++++ bacnet-stack/ports/bdk-atxx4-mstp/dlmstp.c | 1342 ++++++++++++++++++ bacnet-stack/ports/bdk-atxx4-mstp/eeprom.c | 70 + bacnet-stack/ports/bdk-atxx4-mstp/eeprom.h | 46 + bacnet-stack/ports/bdk-atxx4-mstp/h_rp.c | 180 +++ bacnet-stack/ports/bdk-atxx4-mstp/h_wp.c | 134 ++ bacnet-stack/ports/bdk-atxx4-mstp/hardware.h | 48 + bacnet-stack/ports/bdk-atxx4-mstp/iar2gcc.h | 239 ++++ bacnet-stack/ports/bdk-atxx4-mstp/init.c | 63 + bacnet-stack/ports/bdk-atxx4-mstp/init.h | 39 + bacnet-stack/ports/bdk-atxx4-mstp/input.c | 67 + bacnet-stack/ports/bdk-atxx4-mstp/input.h | 41 + bacnet-stack/ports/bdk-atxx4-mstp/led.c | 211 +++ bacnet-stack/ports/bdk-atxx4-mstp/led.h | 52 + bacnet-stack/ports/bdk-atxx4-mstp/main.c | 146 ++ bacnet-stack/ports/bdk-atxx4-mstp/rs485.c | 207 +++ bacnet-stack/ports/bdk-atxx4-mstp/rs485.h | 64 + bacnet-stack/ports/bdk-atxx4-mstp/seeprom.c | 424 ++++++ bacnet-stack/ports/bdk-atxx4-mstp/seeprom.h | 48 + bacnet-stack/ports/bdk-atxx4-mstp/serial.c | 173 +++ bacnet-stack/ports/bdk-atxx4-mstp/serial.h | 53 + bacnet-stack/ports/bdk-atxx4-mstp/stack.c | 52 + bacnet-stack/ports/bdk-atxx4-mstp/timer.h | 67 + bacnet-stack/ports/bdk-atxx4-mstp/timer2.c | 222 +++ 30 files changed, 5632 insertions(+) create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/Makefile create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/ai.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/bacnet.aps create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/bi.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/bits.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/bo.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/device.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/dlmstp.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/eeprom.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/eeprom.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/h_rp.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/h_wp.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/hardware.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/iar2gcc.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/init.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/init.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/input.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/input.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/led.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/led.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/main.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/rs485.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/rs485.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/seeprom.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/seeprom.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/serial.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/serial.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/stack.c create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/timer.h create mode 100644 bacnet-stack/ports/bdk-atxx4-mstp/timer2.c diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/Makefile b/bacnet-stack/ports/bdk-atxx4-mstp/Makefile new file mode 100644 index 00000000..65aac914 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/Makefile @@ -0,0 +1,226 @@ +############################################################################### +# Makefile for LM Room Controller - AVR +############################################################################### + +## General Flags +MCU = atmega644p +AVRDUDE_MCU = m644p +# ATmega644 bootload is at word address 7000h, 7800h, 7C00h, or 7E00h +# Double that value to get the byte address +BOOTLOAD = 0xF800 +TARGET = bacnet +## Tools +CC = avr-gcc +AR = avr-ar +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size +AVRDUDE = avrdude +LINT = splint + +# programmer id--check the avrdude for complete list +# # of available opts. These should include stk500, +# # avr910, avrisp, bsd, pony and more. Set this to +# # one of the valid "-c PROGRAMMER-ID" values +# # described in the avrdude info page. +# # jtag2fast = Atmel JTAG ICE mkII, running at 115200 Bd +# # jtag2slow = Atmel JTAG ICE mkII, running at 19200 Bd +AVRDUDE_PROGRAMMERID = jtag2slow +# +# # port--serial or parallel port to which your +# # hardware programmer is attached +# # usb can just be usb +AVRDUDE_PORT = usb + +# Source locations +BACNET_CORE = ../../src +BACNET_INCLUDE = ../../include +BACNET_DEMO = ../../demo + +# local files for this project +CSRC = main.c \ + init.c \ + input.c \ + serial.c \ + rs485.c \ + timer2.c \ + led.c \ + eeprom.c \ + seeprom.c \ + dlmstp.c \ + h_wp.c \ + h_rp.c \ + device.c \ + ai.c \ + bi.c \ + bo.c + +# common demo files needed +DEMOSRC = $(BACNET_DEMO)/handler/txbuf.c \ + $(BACNET_DEMO)/handler/h_npdu.c \ + $(BACNET_DEMO)/handler/h_whois.c \ + $(BACNET_DEMO)/handler/h_rd.c \ + $(BACNET_DEMO)/handler/h_dcc.c \ + $(BACNET_DEMO)/handler/s_iam.c \ + $(BACNET_DEMO)/handler/noserv.c + +# core BACnet stack files +CORESRC = \ + $(BACNET_CORE)/fifo.c \ + $(BACNET_CORE)/crc.c \ + $(BACNET_CORE)/apdu.c \ + $(BACNET_CORE)/npdu.c \ + $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacint.c \ + $(BACNET_CORE)/bacreal.c \ + $(BACNET_CORE)/bacstr.c \ + $(BACNET_CORE)/iam.c \ + $(BACNET_CORE)/dcc.c \ + $(BACNET_CORE)/rp.c \ + $(BACNET_CORE)/wp.c \ + $(BACNET_CORE)/whois.c \ + $(BACNET_CORE)/rd.c \ + $(BACNET_CORE)/bacaddr.c \ + $(BACNET_CORE)/abort.c \ + $(BACNET_CORE)/reject.c \ + $(BACNET_CORE)/bacerror.c \ + $(BACNET_CORE)/bacapp.c + +# $(BACNET_CORE)/version.c +# $(BACNET_CORE)/bacprop.c \ +# $(BACNET_CORE)/bactext.c \ +# $(BACNET_CORE)/datetime.c \ +# $(BACNET_CORE)/indtext.c \ +# $(BACNET_CORE)/bigend.c \ +# $(BACNET_CORE)/arf.c \ +# $(BACNET_CORE)/awf.c \ +# $(BACNET_CORE)/cov.c \ +# $(BACNET_CORE)/iam/iam_client.c \ +# $(BACNET_CORE)/ihave.c \ +# $(BACNET_CORE)/rd.c \ +# $(BACNET_CORE)/rpm.c \ +# $(BACNET_CORE)/timesync.c \ +# $(BACNET_CORE)/whohas.c \ +# $(BACNET_CORE)/filename.c \ +# $(BACNET_CORE)/tsm.c \ +# $(BACNET_CORE)/address.c \ + +## Include Directories +INCLUDES = -I. -I$(BACNET_INCLUDE) + +# Source to Object conversion +COBJ = $(CSRC:%.c=%.o) +DEMOOBJ = $(DEMOSRC:.c=.o) +COREOBJ = $(CORESRC:.c=.o) + +LIBRARY = lib$(TARGET).a + +## Options common to compile, link and assembly rules +COMMON = -mmcu=$(MCU) + +OPTIMIZE_FLAGS = -mcall-prologues +OPTIMIZE_FLAGS += -finline-functions-called-once +# default optimization is for debugging from AVR Studio +OPTIMIZATION = -O0 +#OPTIMIZATION = -Os $(OPTIMIZE_FLAGS) +DEBUGGING = -g +# define something from the Makefile or batch file +DEFINES = + +## Compile options common for all C compilation units. +BFLAGS = -DBACDL_MSTP +BFLAGS += -DMAX_APDU=50 +BFLAGS += -DBIG_ENDIAN=0 +BFLAGS += -DMAX_TSM_TRANSACTIONS=0 +#BFLAGS += -DCRC_USE_TABLE +BFLAGS += -DBACAPP_BOOLEAN +BFLAGS += -DBACAPP_REAL +BFLAGS += -DBACAPP_OBJECT_ID +BFLAGS += -DBACAPP_UNSIGNED +BFLAGS += -DBACAPP_ENUMERATED +BFLAGS += -DBACAPP_CHARACTER_STRING +BFLAGS += -DWRITE_PROPERTY +CFLAGS = $(COMMON) +CFLAGS += $(DEFINES) +CFLAGS += $(DEBUGGING) +# dead code removal +CFLAGS += -ffunction-sections -fdata-sections +CFLAGS += -Wall -gdwarf-2 $(BFLAGS) $(OPTIMIZATION) -fsigned-char +CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d + +## Assembly specific flags +ASMFLAGS = $(COMMON) +ASMFLAGS += $(CFLAGS) +ASMFLAGS += -x assembler-with-cpp -Wa,-gdwarf2 + +## Linker flags +LDFLAGS = $(COMMON) +#dead code removal +#LDFLAGS += -Wl,-nostartfiles,-nostdlib +LDFLAGS += -Wl,--gc-sections,-static +LDFLAGS += -Wl,--section-start=.bootloader=$(BOOTLOAD) +LDFLAGS += -Wl,-Map=$(TARGET).map +LDFLAGS += -Wl,-L=.,-l$(TARGET) + +## Intel Hex file production flags +HEX_FLASH_FLAGS = -R .eeprom -R .fuse -R .lock -R .signature +HEX_EEPROM_FLAGS = -j .eeprom +HEX_EEPROM_FLAGS += --set-section-flags=.eeprom="alloc,load" +HEX_EEPROM_FLAGS += --change-section-lma .eeprom=0 --no-change-warnings + +## Objects that must be built in order to link +OBJECTS = $(COBJ) $(DEMOOBJ) + +## Build +TARGET_ELF=$(TARGET).elf + +all: $(LIBRARY) \ + $(TARGET_ELF) \ + $(TARGET).hex \ + $(TARGET).eep \ + $(TARGET).lst \ + size Makefile + +##Link +$(TARGET_ELF): $(OBJECTS) $(LIBRARY) + $(CC) $(OBJECTS) $(LDFLAGS) -o $@ + +%.hex: $(TARGET_ELF) + $(OBJCOPY) -O ihex $(HEX_FLASH_FLAGS) $< $@ + +%.eep: $(TARGET_ELF) + -$(OBJCOPY) $(HEX_EEPROM_FLAGS) -O ihex $< $@ || exit 0 + +%.lst: $(TARGET_ELF) + $(OBJDUMP) -h -S $< > $@ + +lib: $(LIBRARY) + +$(LIBRARY): $(COREOBJ) Makefile + $(AR) rcs $@ $(COREOBJ) + $(OBJDUMP) --syms $@ > $(LIBRARY:.a=.lst) + +%.o: %.c + $(CC) -c $(INCLUDES) $(CFLAGS) $*.c -o $@ + +size: ${TARGET_ELF} + @echo + @${SIZE} ${TARGET_ELF} + +lint: + $(LINT) $(BFLAGS) $(CSRC) + +install: $(TARGET_ELF) + $(AVRDUDE) -c $(AVRDUDE_PROGRAMMERID) \ + -p $(AVRDUDE_MCU) -P $(AVRDUDE_PORT) -e \ + -U flash:w:$(TARGET).hex + +## Clean target +.PHONY: clean +clean: + -rm -rf $(OBJECTS) $(TARGET_ELF) dep/* + -rm -rf $(LIBRARY) $(COREOBJ) $(LIBRARY:.a=.lst) + -rm -rf $(TARGET).hex $(TARGET).eep $(TARGET).lst $(TARGET).map + +## Other dependencies +-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*) diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/ai.c b/bacnet-stack/ports/bdk-atxx4-mstp/ai.c new file mode 100644 index 00000000..ff329247 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/ai.c @@ -0,0 +1,159 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" + +/* Analog Input = Photocell */ +#define MAX_ANALOG_INPUTS 2 +#if (MAX_ANALOG_INPUTS > 9) +#error Modify the Analog_Input_Name to handle multiple digits +#endif + +static uint8_t Present_Value[MAX_ANALOG_INPUTS]; + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Analog_Input_Count( + void) +{ + return MAX_ANALOG_INPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Analog_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +char *Analog_Input_Name( + uint32_t object_instance) +{ + static char text_string[16] = "AI-0"; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_INPUTS) { + text_string[3] = '0' + (uint8_t) object_instance; + return text_string; + } + + return NULL; +} + +static float Analog_Input_Present_Value( + uint32_t object_instance) +{ + float value = 0.0; + + if (object_instance < MAX_ANALOG_INPUTS) + value = Present_Value[object_instance]; + + return value; +} + +/* return apdu length, or -1 on error */ +/* assumption - object has already exists */ +int Analog_Input_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + + (void) array_index; + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_INPUT, + object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Input_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_INPUT); + break; + case PROP_PRESENT_VALUE: + apdu_len = + encode_application_real(&apdu[0], + Analog_Input_Present_Value(object_instance)); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/bacnet.aps b/bacnet-stack/ports/bdk-atxx4-mstp/bacnet.aps new file mode 100644 index 00000000..4736fd42 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/bacnet.aps @@ -0,0 +1 @@ +bacnet29-Apr-2009 08:16:5303-May-2009 15:12:17241029-Apr-2009 08:16:5344, 15, 0, 623AVR GCCD:\code\bacnet-stack\ports\bdk-atxx4-mstp\JTAGICE mkIIATmega644P.xmlfalseR00R01R02R03R04R05R06R07R08R09R10R11R12R13R14R15R16R17R18R19R20R21R22R23R24R25R26R27R28R29R30R31Auto000main.ctimer2.ceeprom.cinit.cinput.cled.crs485.cseeprom.cserial.cstack.cdlmstp.cbo.cbi.cai.cdevice.ch_rp.ch_wp.cMakefiledefaultYESMakefileatmega644p111bacnet.elfdefault\1-Wall -gdwarf-2 -std=gnu99 -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enumsdefault1C:\WinAVR-20090313\bin\avr-gcc.exeC:\WinAVR-20090313\utils\bin\make.exeD:\code\bacnet-stack\ports\bdk-atxx4-mstp\main.cD:\code\bacnet-stack\ports\bdk-atxx4-mstp\timer2.cD:\code\bacnet-stack\ports\bdk-atxx4-mstp\eeprom.cD:\code\bacnet-stack\ports\bdk-atxx4-mstp\init.cD:\code\bacnet-stack\ports\bdk-atxx4-mstp\input.cD:\code\bacnet-stack\ports\bdk-atxx4-mstp\led.cD:\code\bacnet-stack\ports\bdk-atxx4-mstp\rs485.cD:\code\bacnet-stack\ports\bdk-atxx4-mstp\seeprom.cD:\code\bacnet-stack\ports\bdk-atxx4-mstp\serial.cD:\code\bacnet-stack\ports\bdk-atxx4-mstp\stack.c00000main.c1 diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/bi.c b/bacnet-stack/ports/bdk-atxx4-mstp/bi.c new file mode 100644 index 00000000..65d4163c --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/bi.c @@ -0,0 +1,192 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Input Objects customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" + +#define MAX_BINARY_INPUTS 8 +#if (MAX_BINARY_INPUTS > 9) +#error Modify the Binary_Input_Name to handle multiple digits +#endif + +static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS]; + +static void Binary_Input_Initialize( + void) +{ + static bool initialized = false; + unsigned i; + + if (!initialized) { + initialized = true; + for (i = 0; i < MAX_BINARY_INPUTS; i++) { + Present_Value[i] = BINARY_INACTIVE; + } + } +} + +/* we simply have 0-n object instances. */ +bool Binary_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Input_Count( + void) +{ + return MAX_BINARY_INPUTS; +} + +/* we simply have 0-n object instances.*/ +uint32_t Binary_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Input_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_INPUTS; + + if (object_instance < MAX_BINARY_INPUTS) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Binary_Input_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + unsigned index = 0; + + Binary_Input_Initialize(); + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) { + value = Present_Value[index]; + } + + return value; +} + +char *Binary_Input_Name( + uint32_t object_instance) +{ + static char text_string[16] = "BI-0"; /* okay for single thread */ + + if (object_instance < MAX_BINARY_INPUTS) { + text_string[3] = '0' + (uint8_t) object_instance; + return text_string; + } + + return NULL; +} + +/* return apdu length, or -1 on error */ +/* assumption - object already exists, and has been bounds checked */ +int Binary_Input_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_POLARITY polarity = POLARITY_NORMAL; + BACNET_BINARY_PV value = BINARY_INACTIVE; + + + (void) array_index; + Binary_Input_Initialize(); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_INPUT, + object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + /* note: object name must be unique in our device */ + characterstring_init_ansi(&char_string, + Binary_Input_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_INPUT); + break; + case PROP_PRESENT_VALUE: + value = Binary_Input_Present_Value(object_instance); + apdu_len = encode_application_enumerated(&apdu[0], value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_POLARITY: + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/bits.h b/bacnet-stack/ports/bdk-atxx4-mstp/bits.h new file mode 100644 index 00000000..2a9b62d2 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/bits.h @@ -0,0 +1,89 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 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####*/ +#ifndef BITS_H +#define BITS_H + +/******************************************************************** +* Bit Masks +*********************************************************************/ +#define BIT0 (0x01) +#define BIT1 (0x02) +#define BIT2 (0x04) +#define BIT3 (0x08) +#define BIT4 (0x10) +#define BIT5 (0x20) +#define BIT6 (0x40) +#define BIT7 (0x80) +#define BIT8 (0x0100) +#define BIT9 (0x0200) +#define BIT10 (0x0400) +#define BIT11 (0x0800) +#define BIT12 (0x1000) +#define BIT13 (0x2000) +#define BIT14 (0x4000) +#define BIT15 (0x8000) +#define BIT16 (0x010000UL) +#define BIT17 (0x020000UL) +#define BIT18 (0x040000UL) +#define BIT19 (0x080000UL) +#define BIT20 (0x100000UL) +#define BIT21 (0x200000UL) +#define BIT22 (0x400000UL) +#define BIT23 (0x800000UL) +#define BIT24 (0x01000000UL) +#define BIT25 (0x02000000UL) +#define BIT26 (0x04000000UL) +#define BIT27 (0x08000000UL) +#define BIT28 (0x10000000UL) +#define BIT29 (0x20000000UL) +#define BIT30 (0x40000000UL) +#define BIT31 (0x80000000UL) + +/* a=register, b=bit number to act upon 0-n */ +#define BIT_SET(a,b) ((a) |= (1<<(b))) +#define BIT_CLEAR(a,b) ((a) &= ~(1<<(b))) +#define BIT_FLIP(a,b) ((a) ^= (1<<(b))) +#define BIT_CHECK(a,b) ((a) & (1<<(b))) + +/* x=target variable, y=mask */ +#define BITMASK_SET(x,y) ((x) |= (y)) +#define BITMASK_CLEAR(x,y) ((x) &= (~(y))) +#define BITMASK_FLIP(x,y) ((x) ^= (y)) +#define BITMASK_CHECK(x,y) ((x) & (y)) + +#ifndef _BV +#define _BV(x) (1<<(x)) +#endif + +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/bo.c b/bacnet-stack/ports/bdk-atxx4-mstp/bo.c new file mode 100644 index 00000000..1c111dbd --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/bo.c @@ -0,0 +1,425 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" + +#define MAX_BINARY_OUTPUTS 8 +#if (MAX_BINARY_OUTPUTS > 9) +#error Modify the Binary_Output_Name to handle multiple digits +#endif + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define RELINQUISH_DEFAULT BINARY_INACTIVE +/* Here is our Priority Array.*/ +static BACNET_BINARY_PV + Binary_Output_Level[MAX_BINARY_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Binary_Output_Out_Of_Service[MAX_BINARY_OUTPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Binary_Output_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_ACTIVE_TEXT, + PROP_INACTIVE_TEXT, + -1 +}; + +static const int Binary_Output_Properties_Proprietary[] = { + -1 +}; + +void Binary_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Binary_Output_Properties_Required; + if (pOptional) + *pOptional = Binary_Output_Properties_Optional; + if (pProprietary) + *pProprietary = Binary_Output_Properties_Proprietary; + + return; +} + +void Binary_Output_Init( + void) +{ + unsigned i, j; + static bool initialized = false; + + if (!initialized) { + initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Binary_Output_Level[i][j] = BINARY_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. */ +bool Binary_Output_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Count( + void) +{ + return MAX_BINARY_OUTPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Binary_Output_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_OUTPUTS; + + if (object_instance < MAX_BINARY_OUTPUTS) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Binary_Output_Present_Value( + uint32_t object_instance) +{ + BACNET_BINARY_PV value = RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + Binary_Output_Init(); + index = Binary_Output_Instance_To_Index(object_instance); + if (index < MAX_BINARY_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Binary_Output_Level[index][i] != BINARY_NULL) { + value = Binary_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Binary_Output_Name( + uint32_t object_instance) +{ + static char text_string[16] = "BV-0"; /* okay for single thread */ + + if (object_instance < MAX_BINARY_OUTPUTS) { + text_string[3] = '0' + (uint8_t) object_instance; + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Binary_Output_Encode_Property_APDU( + uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + BACNET_POLARITY polarity = POLARITY_NORMAL; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + + Binary_Output_Init(); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_BINARY_OUTPUT, + object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Binary_Output_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_BINARY_OUTPUT); + break; + case PROP_PRESENT_VALUE: + present_value = Binary_Output_Present_Value(object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = Binary_Output_Instance_To_Index(object_instance); + state = Binary_Output_Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_POLARITY: + /* FIXME: figure out the polarity */ + apdu_len = encode_application_enumerated(&apdu[0], polarity); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + object_index = + Binary_Output_Instance_To_Index(object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Binary_Output_Level[object_index][i] == BINARY_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + present_value = Binary_Output_Level[object_index][i]; + len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + object_index = + Binary_Output_Instance_To_Index(object_instance); + if (array_index <= BACNET_MAX_PRIORITY) { + if (Binary_Output_Level[object_index][array_index] == + BINARY_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + present_value = + Binary_Output_Level[object_index][array_index]; + len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + present_value = RELINQUISH_DEFAULT; + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_ACTIVE_TEXT: + characterstring_init_ansi(&char_string, "on"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_INACTIVE_TEXT: + characterstring_init_ansi(&char_string, "off"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Binary_Output_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = (BACNET_BINARY_PV) value.type.Enumerated; + object_index = + Binary_Output_Instance_To_Index(wp_data-> + object_instance); + priority--; + Binary_Output_Level[object_index][priority] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + level = BINARY_NULL; + object_index = + Binary_Output_Instance_To_Index(wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Output_Level[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Binary_Output_Instance_To_Index(wp_data->object_instance); + Binary_Output_Out_Of_Service[object_index] = + value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/device.c b/bacnet-stack/ports/bdk-atxx4-mstp/device.c new file mode 100644 index 00000000..9187421b --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/device.c @@ -0,0 +1,552 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "apdu.h" +#include "dcc.h" +#include "datalink.h" +#include "rs485.h" +#include "version.h" +/* objects */ +#include "device.h" +#include "ai.h" +#include "bi.h" +#include "bo.h" + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = BACNET_MAX_INSTANCE; +static char Object_Name[32] = "bdk-atxx4-mstp"; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; + +BACNET_REINITIALIZED_STATE_OF_DEVICE Reinitialize_State = + REINITIALIZED_STATE_IDLE; + +void Device_Reinit( + void) +{ + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); +} + +void Device_Init( + void) +{ + Reinitialize_State = REINITIALIZED_STATE_IDLE; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* FIXME: Get the data from the eeprom */ + /* I2C_Read_Block(EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number( + void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + Object_Instance_Number = object_id; + /* FIXME: Write the data to the eeprom */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return ((Object_Instance_Number == object_id) || + (object_id == BACNET_MAX_INSTANCE)); +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +void Device_Set_System_Status( + BACNET_DEVICE_STATUS status) +{ + if (status < MAX_DEVICE_STATUS) + System_Status = status; +} + +uint16_t Device_Vendor_Identifier( + void) +{ + return BACNET_VENDOR_ID; +} + +uint8_t Device_Protocol_Version( + void) +{ + return 1; +} + +uint8_t Device_Protocol_Revision( + void) +{ + return 5; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint8_t Device_Database_Revision( + void) +{ + return 0; +} + +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 1; /* at least 1 for device object */ + + /* FIXME: add objects as needed */ + count += Binary_Input_Count(); + count += Binary_Output_Count(); + count += Analog_Input_Count(); + + return count; +} + +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned object_index = 0; + unsigned object_count = 0; + + /* device object */ + if (array_index == 1) { + *object_type = OBJECT_DEVICE; + *instance = Object_Instance_Number; + status = true; + } + /* normalize the index since + we know it is not the previous objects */ + /* array index starts at 1 */ + object_index = array_index - 1; + /* 1 for the device object */ + object_count = 1; + /* FIXME: add objects as needed */ + /* binary value objects */ + if (!status) { + object_index -= object_count; + object_count = Binary_Output_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_OUTPUT; + *instance = Binary_Output_Index_To_Instance(object_index); + status = true; + } + } + /* analog input objects */ + if (!status) { + /* array index starts at 1, and 1 for the device object */ + object_index -= object_count; + object_count = Analog_Input_Count(); + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_INPUT; + *instance = Analog_Input_Index_To_Instance(object_index); + status = true; + } + } + /* binary input objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Binary_Input_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_INPUT; + *instance = Binary_Input_Index_To_Instance(object_index); + status = true; + } + } + + return status; +} + +/* return the length of the apdu encoded or -1 for error */ +int Device_Encode_Property_APDU( + uint8_t * apdu, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + BACNET_TIME local_time; + BACNET_DATE local_date; + + /* FIXME: change the hardcoded names to suit your application */ + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, Object_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, "BACnet Demo"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_System_Status()); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Vendor_Identifier()); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, "GNU Demo"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACnet_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, "1.0"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + characterstring_init_ansi(&char_string, "USA"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Protocol_Revision()); + break; + /* BACnet Legacy Support */ + case PROP_PROTOCOL_CONFORMANCE_CLASS: + apdu_len = encode_application_unsigned(&apdu[0], 1); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported(i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* FIXME: if ReadProperty used an array of Functions... */ + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* FIXME: indicate the objects that YOU support */ + bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); + bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true); + bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true); + bitstring_set_bit(&bit_string, OBJECT_ANALOG_INPUT, true); + bitstring_set_bit(&bit_string, OBJECT_BINARY_INPUT, true); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } else { + /* error: internal error? */ + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_OTHER; + apdu_len = -1; + break; + } + } + } else { + if (Device_Object_List_Identifier(array_index, &object_type, + &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Database_Revision()); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; + case PROP_LOCAL_TIME: + /* FIXME: if you support time */ + local_time.hour = 0; + local_time.min = 0; + local_time.sec = 0; + local_time.hundredths = 0; + apdu_len = encode_application_time(&apdu[0], &local_time); + break; + case PROP_UTC_OFFSET: + /* Note: BACnet Time Zone is inverse of everybody else */ + apdu_len = encode_application_signed(&apdu[0], 5 /* EST */ ); + break; + case PROP_LOCAL_DATE: + /* FIXME: if you support date */ + local_date.year = 2006; /* AD */ + local_date.month = 4; /* Jan=1..Dec=12 */ + local_date.day = 11; /* 1..31 */ + local_date.wday = 0; /* 1=Mon..7=Sun */ + apdu_len = encode_application_date(&apdu[0], &local_date); + break; + case PROP_DAYLIGHT_SAVINGS_STATUS: + /* FIXME: if you support time/date */ + apdu_len = encode_application_boolean(&apdu[0], false); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type.Object_Id. + instance))) { + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_INFO_FRAMES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_MASTER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + dlmstp_set_max_master(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + uint8_t encoding; + size_t len; + + encoding = + characterstring_encoding(&value.type.Character_String); + len = characterstring_length(&value.type.Character_String); + if (encoding == CHARACTER_ANSI_X34) { + if (len <= 20) { + /* FIXME: set the name */ + /* Display_Set_Name( + characterstring_value(&value.type.Character_String)); */ + /* FIXME: All the object names in a device must be unique. + Disallow setting the Device Object Name to any objects in + the device. */ + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/dlmstp.c b/bacnet-stack/ports/bdk-atxx4-mstp/dlmstp.c new file mode 100644 index 00000000..cd17ced0 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/dlmstp.c @@ -0,0 +1,1342 @@ +/*####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 +#include "bacdef.h" +#include "dlmstp.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bacaddr.h" +#include "timer.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one MS/TP datalink layer +*/ + +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#define MSTP_BROADCAST_ADDRESS 255 + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define FRAME_TYPE_TOKEN 0 +#define FRAME_TYPE_POLL_FOR_MASTER 1 +#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 +#define FRAME_TYPE_TEST_REQUEST 3 +#define FRAME_TYPE_TEST_RESPONSE 4 +#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 +#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define FRAME_TYPE_REPLY_POSTPONED 7 +/* Frame Types 128 through 255: Proprietary Frames */ +/* These frames are available to vendors as proprietary (non-BACnet) frames. */ +/* The first two octets of the Data field shall specify the unique vendor */ +/* identification code, most significant octet first, for the type of */ +/* vendor-proprietary frame to be conveyed. The length of the data portion */ +/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +#define FRAME_TYPE_PROPRIETARY_MIN 128 +#define FRAME_TYPE_PROPRIETARY_MAX 255 + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_DATA = 3 +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE = 0, + MSTP_MASTER_STATE_IDLE = 1, + MSTP_MASTER_STATE_USE_TOKEN = 2, + MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, + MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, + MSTP_MASTER_STATE_PASS_TOKEN = 5, + MSTP_MASTER_STATE_NO_TOKEN = 6, + MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 +} MSTP_MASTER_STATE; + +/* 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; + /* 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 transmit if a + frame is pending */ + unsigned TransmitPacketPending:1; + /* A Boolean flag set TRUE by the datalink transmit if a + pending packet is DataExpectingReply */ + unsigned TransmitPacketDER: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 uint16_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 broadcast when used as a */ +/* destination address but is not allowed as a value for TS. */ +static uint8_t 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. */ +static uint8_t 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. */ +static uint8_t Nmax_master; +/* An array of octets, used to store octets for transmitting */ +/* OutputBuffer is indexed from 0 to OutputBufferSize-1. */ +/* The MAX_PDU size of a frame is MAX_APDU + MAX_NPDU octets. */ +/* 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 *TransmitPacket; +static uint16_t TransmitPacketLen; +static uint8_t TransmitPacketDest; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* 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.) */ +#define Treply_timeout 260 + +/* The minimum 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 100 milliseconds.) */ +#define Tusage_timeout 25 + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* 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); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* 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. */ +#define Treply_delay 250 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool dlmstp_init( + char *ifname) +{ + ifname = ifname; + + 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; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + 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 (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* 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 */ + uint8_t buffer[8]; /* stores the header and data crc */ + uint16_t i = 0; /* used to calculate CRC for data */ + + /* create the MS/TP header */ + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = data_len / 256; + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = data_len % 256; + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + rs485_turnaround_delay(); + rs485_rts_enable(true); + rs485_bytes_send(buffer, 8); + /* send any data */ + if (data_len) { + /* calculate CRC for any data */ + for (i = 0; i < data_len; i++) { + crc16 = CRC_Calc_Data(data[i], crc16); + } + crc16 = ~crc16; + buffer[0] = (crc16 & 0x00FF); + buffer[1] = ((crc16 & 0xFF00) >> 8); + rs485_bytes_send(data, data_len); + rs485_bytes_send(buffer, 2); + } + rs485_rts_enable(false); +} + +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 */ + timer_reset(TIMER_SILENCE); + INCREMENT_AND_LIMIT_UINT8(EventCount); + } else if (rs485_byte_available(&DataRegister)) { + timer_reset(TIMER_SILENCE); + 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 (timer_elapsed_milliseconds(TIMER_SILENCE, 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 */ + timer_reset(TIMER_SILENCE); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + timer_reset(TIMER_SILENCE); + 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 (timer_elapsed_milliseconds(TIMER_SILENCE, 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 */ + timer_reset(TIMER_SILENCE); + 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)) { + timer_reset(TIMER_SILENCE); + 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 { + /* Note: proposed change to BACnet MSTP state machine! + If we don't decode data that is not for us, we could + get confused about the start if the Preamble 55 FF + is part of the data. */ + if ((DataLength) && (DataLength <= InputBufferSize)) { + /* Data */ + Index = 0; + DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } 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 - drop */ + } + } else { + /* FrameTooLong */ + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + MSTP_Flag.ReceivedInvalidFrame = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + } 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: + /* In the DATA state, the node waits + for the data portion of a frame. */ + if (timer_elapsed_milliseconds(TIMER_SILENCE, 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 */ + timer_reset(TIMER_SILENCE); + 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)) { + timer_reset(TIMER_SILENCE); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + 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 ((DestinationAddress == This_Station) || + (DestinationAddress == MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with no data + has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } + } 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; +} + +/* 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; + + /* 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: + /* 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 (timer_elapsed_milliseconds(TIMER_SILENCE, Tno_token)) { + /* LostToken */ + /* assume that the token has been lost */ + EventCount = 0; /* Addendum 135-2004d-8 */ + 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 */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to higher layers */ + MSTP_Flag.ReceivePacketPending = true; + /* broadcast DER just remains IDLE */ + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + 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 (!MSTP_Flag.TransmitPacketPending) { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t frame_type; + if (MSTP_Flag.TransmitPacketDER) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, TransmitPacketDest, This_Station, + (uint8_t *) & TransmitPacket[0], TransmitPacketLen); + MSTP_Flag.TransmitPacketPending = false; + FrameCount++; + switch (frame_type) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (TransmitPacketDest == 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; + } + } + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (timer_elapsed_milliseconds(TIMER_SILENCE, 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_IDLE; + 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; + } + /* 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 (timer_elapsed_milliseconds(TIMER_SILENCE, 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. */ + 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 (timer_elapsed_milliseconds(TIMER_SILENCE, my_timeout)) { + ns_timeout = Tno_token + (Tslot * (This_Station + 1)); + if (timer_elapsed_milliseconds(TIMER_SILENCE, ns_timeout)) { + /* should never get here unless timer resolution is bad */ + timer_reset(TIMER_SILENCE); + 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 ((timer_elapsed_milliseconds(TIMER_SILENCE, + 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: + /* Note: we could wait for up to Treply_delay */ + if (MSTP_Flag.TransmitPacketPending) { + matched = + dlmstp_compare_data_expecting_reply(&InputBuffer[0], + DataLength, SourceAddress, &TransmitPacket[0], + TransmitPacketLen, TransmitPacketDest); + } + if (MSTP_Flag.TransmitPacketPending && 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 (MSTP_Flag.TransmitPacketDER) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, TransmitPacketDest, This_Station, + (uint8_t *) & TransmitPacket[0], TransmitPacketLen); + MSTP_Flag.TransmitPacketPending = false; + Master_State = MSTP_MASTER_STATE_IDLE; + } else { + /* 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; + + if (MSTP_Flag.TransmitPacketPending == false) { + MSTP_Flag.TransmitPacketDER = npdu_data->data_expecting_reply; + TransmitPacket = pdu; + TransmitPacketLen = pdu_len; + bytes_sent = pdu_len; + TransmitPacketDest = dest->mac[0]; + MSTP_Flag.TransmitPacketPending = true; + } + + return bytes_sent; +} + +/* 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 */ + + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedInvalidFrame == false)) { + for (;;) { + MSTP_Receive_Frame_FSM(); + if (MSTP_Flag.ReceivedValidFrame || MSTP_Flag.ReceivedInvalidFrame) + break; + /* if we are not idle, then we are + receiving a frame or timing out */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) + break; + } + } + /* only do master state machine while rx is idle */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + 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; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > Nmax_master) + dlmstp_set_max_master(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 >= 1) { + Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + 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 <= max_master) { + Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + 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; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/eeprom.c b/bacnet-stack/ports/bdk-atxx4-mstp/eeprom.c new file mode 100644 index 00000000..5351139e --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/eeprom.c @@ -0,0 +1,70 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "eeprom.h" + +/* Internal EEPROM of the AVR - http://supp.iar.com/Support/?note=45745 */ + +#if defined(__GNUC__) + /* bug in WinAVR - not quite IAR compatible */ + #define __EEPUT _EEPUT + #define __EEGET _EEGET +#endif + +int eeprom_bytes_read( + uint16_t eeaddr, /* EEPROM starting memory address (offset of zero) */ + uint8_t * buf, /* data to store */ + int len) /* number of bytes of data to read */ +{ + int count = 0; /* return value */ + + while (len) { + __EEGET(buf[count], eeaddr); + count++; + eeaddr++; + len--; + } + + return count; +} + +int eeprom_bytes_write( + uint16_t eeaddr, /* EEPROM starting memory address */ + uint8_t * buf, /* data to send */ + int len) /* number of bytes of data */ +{ + int count = 0; + + while (len) { + __EEPUT(eeaddr, buf[count]); + count++; + eeaddr++; + len--; + } + + return count; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/eeprom.h b/bacnet-stack/ports/bdk-atxx4-mstp/eeprom.h new file mode 100644 index 00000000..59fc61f7 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/eeprom.h @@ -0,0 +1,46 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef EEPROM_H +#define EEPROM_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int eeprom_bytes_read( + uint16_t ee_address, /* EEPROM starting memory address */ + uint8_t * buffer, /* data to store */ + int nbytes); /* number of bytes of data to read */ +int eeprom_bytes_write( + uint16_t ee_address, /* EEPROM starting memory address */ + uint8_t * buffer, /* data to send */ + int nbytes); /* number of bytes of data */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/h_rp.c b/bacnet-stack/ports/bdk-atxx4-mstp/h_rp.c new file mode 100644 index 00000000..db3b742c --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/h_rp.c @@ -0,0 +1,180 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "rp.h" +/* demo objects */ +#include "device.h" +#include "ai.h" +#include "bi.h" +#include "bo.h" + +static uint8_t Temp_Buf[MAX_APDU] = { 0 }; + +/* Encodes the property APDU and returns the length, + or sets the error, and returns -1 */ +int Encode_Property_APDU( + uint8_t * apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = -1; + + /* initialize the default return values */ + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + /* handle each object type */ + switch (object_type) { + case OBJECT_DEVICE: + if (Device_Valid_Object_Instance_Number(object_instance)) { + apdu_len = + Device_Encode_Property_APDU(&apdu[0], property, + array_index, error_class, error_code); + } + break; + case OBJECT_ANALOG_INPUT: + if (Analog_Input_Valid_Instance(object_instance)) { + apdu_len = + Analog_Input_Encode_Property_APDU(&apdu[0], + object_instance, property, array_index, error_class, + error_code); + } + break; + case OBJECT_BINARY_INPUT: + if (Binary_Input_Valid_Instance(object_instance)) { + apdu_len = + Binary_Input_Encode_Property_APDU(&apdu[0], + object_instance, property, array_index, error_class, + error_code); + } + break; + case OBJECT_BINARY_OUTPUT: + if (Binary_Output_Valid_Instance(object_instance)) { + apdu_len = + Binary_Output_Encode_Property_APDU(&Temp_Buf[0], + object_instance, property, array_index, error_class, + error_code); + } + break; + default: + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + break; + } + + return apdu_len; +} + +void handler_read_property( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_READ_PROPERTY_DATA data; + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + bool error = false; + int bytes_sent = 0; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + goto RP_ABORT; + } + len = rp_decode_service_request(service_request, service_len, &data); + if (len < 0) { + /* bad decoding - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); + goto RP_ABORT; + } + /* most cases will be error */ + error = true; + len = + Encode_Property_APDU(&Temp_Buf[0], data.object_type, + data.object_instance, data.object_property, data.array_index, + &error_class, &error_code); + if (len >= 0) { + /* encode the APDU portion of the packet */ + data.application_data = &Temp_Buf[0]; + data.application_data_len = len; + /* FIXME: probably need a length limitation sent with encode */ + len = + rp_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, &data); + error = false; + } + if (error) { + if (len == -2) { + /* BACnet APDU too small to fit data, so proper response is Abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); + goto RP_ABORT; + } + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, + error_class, error_code); + } + RP_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); + + return; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/h_wp.c b/bacnet-stack/ports/bdk-atxx4-mstp/h_wp.c new file mode 100644 index 00000000..a3580fa3 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/h_wp.c @@ -0,0 +1,134 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "wp.h" +/* demo objects */ +#include "device.h" +#include "ai.h" +#include "bi.h" +#include "bo.h" + +/* too big to reside on stack frame for PIC */ +static BACNET_WRITE_PROPERTY_DATA wp_data; + +void handler_write_property( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + int bytes_sent = 0; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + goto WP_ABORT; + } + /* decode the service request only */ + len = wp_decode_service_request(service_request, service_len, &wp_data); + /* bad decoding or something we didn't understand - send an abort */ + if (len <= 0) { + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); + goto WP_ABORT; + } + /* handle the object type */ + switch (wp_data.object_type) { + case OBJECT_DEVICE: + if (Device_Write_Property(&wp_data, &error_class, &error_code)) { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY); + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY, + error_class, error_code); + } + break; + case OBJECT_ANALOG_INPUT: + case OBJECT_BINARY_INPUT: + error_class = ERROR_CLASS_PROPERTY; + error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY, + error_class, error_code); + break; + case OBJECT_BINARY_OUTPUT: + if (Binary_Output_Write_Property(&wp_data, &error_class, + &error_code)) { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY); + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY, + error_class, error_code); + } + break; + default: + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY, + error_class, error_code); + break; + } + WP_ABORT: + pdu_len += len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); + + return; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/hardware.h b/bacnet-stack/ports/bdk-atxx4-mstp/hardware.h new file mode 100644 index 00000000..8425e0c3 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/hardware.h @@ -0,0 +1,48 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +#if !defined(F_CPU) + /* The processor clock frequency */ +#define F_CPU 18430000UL +#endif + +#if defined(__ICCAVR__) +#include +#endif + +#if defined(__GNUC__) + #if !defined(__AVR_ATmega644P__) + #error Firmware is configured for ATmega644P only (-mmcu=atmega644p) + #endif + /* GCC specific configuration */ + #include +#endif + +#include "iar2gcc.h" +#include "bits.h" + +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/iar2gcc.h b/bacnet-stack/ports/bdk-atxx4-mstp/iar2gcc.h new file mode 100644 index 00000000..10a97aa0 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/iar2gcc.h @@ -0,0 +1,239 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 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####*/ +#ifndef IAR2GCC_H +#define IAR2GCC_H + +#if !defined(F_CPU) +#define F_CPU (7372800) +#endif + +/* IAR */ +#if defined(__ICCAVR__) +#include +#include + +/* inline function */ +static inline void _delay_us( + uint8_t microseconds) +{ + do { + __delay_cycles(F_CPU / 1000000UL); + } while (microseconds--); +} +#endif + +/* Input/Output Registers */ +#if defined(__GNUC__) +#include + +typedef struct { + unsigned char bit0:1; + unsigned char bit1:1; + unsigned char bit2:1; + unsigned char bit3:1; + unsigned char bit4:1; + unsigned char bit5:1; + unsigned char bit6:1; + unsigned char bit7:1; +} BitRegisterType; + +#ifndef true +#define true 1 +#endif + +#ifndef false +#define false 0 +#endif + +#define GPIO_BITREG(port,bitnum) \ + ((volatile BitRegisterType*)_SFR_MEM_ADDR(port) \ + )->bit ## bitnum + +#define PINA_Bit0 GPIO_BITREG(PINA,0) +#define PINA_Bit1 GPIO_BITREG(PINA,1) +#define PINA_Bit2 GPIO_BITREG(PINA,2) +#define PINA_Bit3 GPIO_BITREG(PINA,3) +#define PINA_Bit4 GPIO_BITREG(PINA,4) +#define PINA_Bit5 GPIO_BITREG(PINA,5) +#define PINA_Bit6 GPIO_BITREG(PINA,6) +#define PINA_Bit7 GPIO_BITREG(PINA,7) + +#define PORTA_Bit0 GPIO_BITREG(PORTA,0) +#define PORTA_Bit1 GPIO_BITREG(PORTA,1) +#define PORTA_Bit2 GPIO_BITREG(PORTA,2) +#define PORTA_Bit3 GPIO_BITREG(PORTA,3) +#define PORTA_Bit4 GPIO_BITREG(PORTA,4) +#define PORTA_Bit5 GPIO_BITREG(PORTA,5) +#define PORTA_Bit6 GPIO_BITREG(PORTA,6) +#define PORTA_Bit7 GPIO_BITREG(PORTA,7) + +#define PINB_Bit0 GPIO_BITREG(PINB,0) +#define PINB_Bit1 GPIO_BITREG(PINB,1) +#define PINB_Bit2 GPIO_BITREG(PINB,2) +#define PINB_Bit3 GPIO_BITREG(PINB,3) +#define PINB_Bit4 GPIO_BITREG(PINB,4) +#define PINB_Bit5 GPIO_BITREG(PINB,5) +#define PINB_Bit6 GPIO_BITREG(PINB,6) +#define PINB_Bit7 GPIO_BITREG(PINB,7) + +#define PORTB_Bit0 GPIO_BITREG(PORTB,0) +#define PORTB_Bit1 GPIO_BITREG(PORTB,1) +#define PORTB_Bit2 GPIO_BITREG(PORTB,2) +#define PORTB_Bit3 GPIO_BITREG(PORTB,3) +#define PORTB_Bit4 GPIO_BITREG(PORTB,4) +#define PORTB_Bit5 GPIO_BITREG(PORTB,5) +#define PORTB_Bit6 GPIO_BITREG(PORTB,6) +#define PORTB_Bit7 GPIO_BITREG(PORTB,7) + +#define PINC_Bit0 GPIO_BITREG(PINC,0) +#define PINC_Bit1 GPIO_BITREG(PINC,1) +#define PINC_Bit2 GPIO_BITREG(PINC,2) +#define PINC_Bit3 GPIO_BITREG(PINC,3) +#define PINC_Bit4 GPIO_BITREG(PINC,4) +#define PINC_Bit5 GPIO_BITREG(PINC,5) +#define PINC_Bit6 GPIO_BITREG(PINC,6) +#define PINC_Bit7 GPIO_BITREG(PINC,7) + +#define PORTC_Bit0 GPIO_BITREG(PORTC,0) +#define PORTC_Bit1 GPIO_BITREG(PORTC,1) +#define PORTC_Bit2 GPIO_BITREG(PORTC,2) +#define PORTC_Bit3 GPIO_BITREG(PORTC,3) +#define PORTC_Bit4 GPIO_BITREG(PORTC,4) +#define PORTC_Bit5 GPIO_BITREG(PORTC,5) +#define PORTC_Bit6 GPIO_BITREG(PORTC,6) +#define PORTC_Bit7 GPIO_BITREG(PORTC,7) + +#define PIND_Bit0 GPIO_BITREG(PIND,0) +#define PIND_Bit1 GPIO_BITREG(PIND,1) +#define PIND_Bit2 GPIO_BITREG(PIND,2) +#define PIND_Bit3 GPIO_BITREG(PIND,3) +#define PIND_Bit4 GPIO_BITREG(PIND,4) +#define PIND_Bit5 GPIO_BITREG(PIND,5) +#define PIND_Bit6 GPIO_BITREG(PIND,6) +#define PIND_Bit7 GPIO_BITREG(PIND,7) + +#define PORTD_Bit0 GPIO_BITREG(PORTD,0) +#define PORTD_Bit1 GPIO_BITREG(PORTD,1) +#define PORTD_Bit2 GPIO_BITREG(PORTD,2) +#define PORTD_Bit3 GPIO_BITREG(PORTD,3) +#define PORTD_Bit4 GPIO_BITREG(PORTD,4) +#define PORTD_Bit5 GPIO_BITREG(PORTD,5) +#define PORTD_Bit6 GPIO_BITREG(PORTD,6) +#define PORTD_Bit7 GPIO_BITREG(PORTD,7) + +#define GPIOR0_Bit0 GPIO_BITREG(GPIOR0,0) +#define GPIOR0_Bit1 GPIO_BITREG(GPIOR0,1) +#define GPIOR0_Bit2 GPIO_BITREG(GPIOR0,2) +#define GPIOR0_Bit3 GPIO_BITREG(GPIOR0,3) +#define GPIOR0_Bit4 GPIO_BITREG(GPIOR0,4) +#define GPIOR0_Bit5 GPIO_BITREG(GPIOR0,5) +#define GPIOR0_Bit6 GPIO_BITREG(GPIOR0,6) +#define GPIOR0_Bit7 GPIO_BITREG(GPIOR0,7) + +#define GPIOR1_Bit0 GPIO_BITREG(GPIOR1,0) +#define GPIOR1_Bit1 GPIO_BITREG(GPIOR1,1) +#define GPIOR1_Bit2 GPIO_BITREG(GPIOR1,2) +#define GPIOR1_Bit3 GPIO_BITREG(GPIOR1,3) +#define GPIOR1_Bit4 GPIO_BITREG(GPIOR1,4) +#define GPIOR1_Bit5 GPIO_BITREG(GPIOR1,5) +#define GPIOR1_Bit6 GPIO_BITREG(GPIOR1,6) +#define GPIOR1_Bit7 GPIO_BITREG(GPIOR1,7) + +#define GPIOR2_Bit0 GPIO_BITREG(GPIOR2,0) +#define GPIOR2_Bit1 GPIO_BITREG(GPIOR2,1) +#define GPIOR2_Bit2 GPIO_BITREG(GPIOR2,2) +#define GPIOR2_Bit3 GPIO_BITREG(GPIOR2,3) +#define GPIOR2_Bit4 GPIO_BITREG(GPIOR2,4) +#define GPIOR2_Bit5 GPIO_BITREG(GPIOR2,5) +#define GPIOR2_Bit6 GPIO_BITREG(GPIOR2,6) +#define GPIOR2_Bit7 GPIO_BITREG(GPIOR2,7) + +#endif + +/* Global Interrupts */ +#if defined(__GNUC__) +#define __enable_interrupt() sei() +#define __disable_interrupt() cli() +#endif + +/* Interrupts */ +#if defined(__ICCAVR__) +#define PRAGMA(x) _Pragma( #x ) +#define ISR(vec) PRAGMA( vector=vec ) __interrupt void handler_##vec(void) +#endif +#if defined(__GNUC__) +#include +#endif + +/* Flash */ +#if defined(__ICCAVR__) +#define FLASH_DECLARE(x) __flash x +#endif +#if defined(__GNUC__) +#define FLASH_DECLARE(x) x __attribute__((__progmem__)) +#endif + +/* EEPROM */ +#if defined(__ICCAVR__) +#define EEPROM_DECLARE(x) __eeprom x +#endif +#if defined(__GNUC__) +#include +#define EEPROM_DECLARE(x) x __attribute__((section (".eeprom"))) +#endif + +/* IAR intrinsic routines */ +#if defined(__GNUC__) + /* FIXME: intrinsic routines: map to assembler for size/speed */ +#define __multiply_unsigned(x,y) ((x)*(y)) + /* FIXME: __root means to not optimize or strip */ +#define __root +#endif + + +/* watchdog */ +#if defined(__ICCAVR__) + #include + #define watchdog_reset __watchdog_reset + #define WDTO_15MS 0 + #define WDTO_30MS 1 + #define WDTO_60MS 2 + #define WDTO_120MS 3 + #define WDTO_250MS 4 + #define WDTO_500MS 5 + #define WDTO_1S 6 + #define WDTO_2S 7 +#endif + +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/init.c b/bacnet-stack/ports/bdk-atxx4-mstp/init.c new file mode 100644 index 00000000..29345819 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/init.c @@ -0,0 +1,63 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include "hardware.h" + +void init( + void) +{ + /* Initialize the Clock Prescaler for ATmega48/88/168 */ + /* The default CLKPSx bits are factory set to 0011 */ + /* Enbable the Clock Prescaler */ + CLKPR = _BV(CLKPCE); + /* CLKPS3 CLKPS2 CLKPS1 CLKPS0 Clock Division Factor + ------ ------ ------ ------ --------------------- + 0 0 0 0 1 + 0 0 0 1 2 + 0 0 1 0 4 + 0 0 1 1 8 + 0 1 0 0 16 + 0 1 0 1 32 + 0 1 1 0 64 + 0 1 1 1 128 + 1 0 0 0 256 + 1 x x x Reserved + */ + /* Set the CLKPS3..0 bits to Prescaler of 1 */ + CLKPR = 0; + /* Initialize I/O ports */ + /* For Port DDRx (Data Direction) Input=0, Output=1 */ + /* For Port PORTx (Bit Value) TriState=0, High=1 */ + DDRA = 0; + PORTA = 0; + DDRB = 0; + PORTB = 0; + DDRC = 0; + PORTC = 0; + DDRD = 0; + PORTD = 0; + /* Configure the watchdog timer - Disabled for testing */ + BIT_CLEAR(MCUSR, WDRF); + WDTCSR = 0; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/init.h b/bacnet-stack/ports/bdk-atxx4-mstp/init.h new file mode 100644 index 00000000..1ab5cc2e --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/init.h @@ -0,0 +1,39 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef INIT_H +#define INIT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +void init(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/input.c b/bacnet-stack/ports/bdk-atxx4-mstp/input.c new file mode 100644 index 00000000..52173fc5 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/input.c @@ -0,0 +1,67 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include "hardware.h" + +static uint8_t Address_Switch; + +void input_task( + void) +{ + uint8_t value; + static uint8_t old_value = 0; + + value = BITMASK_CHECK(PINA, 0x7F); + if (value != old_value) { + old_value = value; + } else { + if (old_value != Address_Switch) { + Address_Switch = old_value; +#if defined(BACDL_MSTP) +// dlmstp_set_mac_address(Address_Switch); +#endif +// Device_Set_Object_Instance_Number(86000 + Address_Switch); +// Send_I_Am_Flag = true; + } + } +} + +uint8_t input_address(void) +{ + return Address_Switch; +} + +void input_init(void) +{ + /* configure the port pins */ + BITMASK_CLEAR(DDRA, + _BV(DDA0) | + _BV(DDA1) | + _BV(DDA2) | + _BV(DDA3) | + _BV(DDA4) | + _BV(DDA5) | + _BV(DDA6) + ); +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/input.h b/bacnet-stack/ports/bdk-atxx4-mstp/input.h new file mode 100644 index 00000000..1bea5cb8 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/input.h @@ -0,0 +1,41 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef INPUT_H +#define INPUT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +void input_init(void); +void input_task(void); +uint8_t input_address(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/led.c b/bacnet-stack/ports/bdk-atxx4-mstp/led.c new file mode 100644 index 00000000..cf3f25e0 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/led.c @@ -0,0 +1,211 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include "hardware.h" +#include "timer.h" +#include "led.h" + +static uint32_t Off_Delay_Milliseconds_1; +static uint32_t Off_Delay_Milliseconds_2; +static uint32_t Off_Delay_Milliseconds_3; +static uint32_t Off_Delay_Milliseconds_4; + +/************************************************************************* +* Description: Turn on an LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_on(uint8_t index) +{ + switch (index) { + case LED_1: + BIT_SET(PORTD, PD7); + break; + case LED_2: + BIT_SET(PORTD, PD6); + break; + case LED_3: + BIT_SET(PORTC, PC7); + break; + case LED_4: + BIT_SET(PORTC, PC6); + break; + default: + break; + } +} + +/************************************************************************* +* Description: Turn off an LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_off(uint8_t index) +{ + switch (index) { + case LED_1: + BIT_CLEAR(PORTD, PD7); + break; + case LED_2: + BIT_CLEAR(PORTD, PD6); + break; + case LED_3: + BIT_CLEAR(PORTC, PC7); + break; + case LED_4: + BIT_CLEAR(PORTC, PC6); + break; + default: + break; + } +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_state(uint8_t index) +{ + switch (index) { + case LED_1: + return (BIT_CHECK(PIND, PD7)); + break; + case LED_2: + return (BIT_CHECK(PIND, PD6)); + break; + case LED_3: + return (BIT_CHECK(PINC, PC7)); + break; + case LED_4: + return (BIT_CHECK(PINC, PC6)); + break; + default: + break; + } + + return false; +} + +/************************************************************************* +* Description: Toggle the state of the setup LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_toggle(uint8_t index) +{ + if (led_state(index)) { + led_off(index); + } else { + led_on(index); + } +} + +/************************************************************************* +* Description: Delay before going off to give minimum brightness. +* Returns: none +* Notes: none +*************************************************************************/ +void led_off_delay(uint8_t index, uint32_t delay_ms) +{ + switch (index) { + case LED_1: + Off_Delay_Milliseconds_1 = delay_ms; + timer_reset(TIMER_LED_1); + break; + case LED_2: + Off_Delay_Milliseconds_2 = delay_ms; + timer_reset(TIMER_LED_2); + break; + case LED_3: + Off_Delay_Milliseconds_3 = delay_ms; + timer_reset(TIMER_LED_3); + break; + case LED_4: + Off_Delay_Milliseconds_4 = delay_ms; + timer_reset(TIMER_LED_4); + break; + default: + break; + } +} + +/************************************************************************* +* Description: Task for blinking LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_task(void) +{ + if (Off_Delay_Milliseconds_1) { + if (timer_elapsed_milliseconds( + TIMER_LED_1, + Off_Delay_Milliseconds_1)) { + Off_Delay_Milliseconds_1 = 0; + led_off(LED_1); + } + } + if (Off_Delay_Milliseconds_2) { + if (timer_elapsed_milliseconds( + TIMER_LED_2, + Off_Delay_Milliseconds_2)) { + Off_Delay_Milliseconds_2 = 0; + led_off(LED_2); + } + } + if (Off_Delay_Milliseconds_3) { + if (timer_elapsed_milliseconds( + TIMER_LED_3, + Off_Delay_Milliseconds_3)) { + Off_Delay_Milliseconds_3 = 0; + led_off(LED_3); + } + } + if (Off_Delay_Milliseconds_4) { + if (timer_elapsed_milliseconds( + TIMER_LED_4, + Off_Delay_Milliseconds_4)) { + Off_Delay_Milliseconds_4 = 0; + led_off(LED_4); + } + } +} + +/************************************************************************* +* Description: Initialize the LED hardware +* Returns: none +* Notes: none +*************************************************************************/ +void led_init(void) +{ + /* configure the port pins as outputs */ + BIT_SET(DDRC, DDC7); + BIT_SET(DDRC, DDC6); + BIT_SET(DDRD, DDD7); + BIT_SET(DDRD, DDD6); + led_off(LED_1); + led_off(LED_2); + led_off(LED_3); + led_off(LED_4); +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/led.h b/bacnet-stack/ports/bdk-atxx4-mstp/led.h new file mode 100644 index 00000000..a2b66df4 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/led.h @@ -0,0 +1,52 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef LED_H +#define LED_H + +#include +#include + +#define LED_1 0 +#define LED_2 1 +#define LED_3 2 +#define LED_4 3 +#define MAX_LEDS 4 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +void led_on(uint8_t index); +void led_off(uint8_t index); +void led_off_delay(uint8_t index, uint32_t delay_ms); +void led_toggle(uint8_t index); +bool led_state(uint8_t index); +void led_task(void); +void led_init(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/main.c b/bacnet-stack/ports/bdk-atxx4-mstp/main.c new file mode 100644 index 00000000..dac96e61 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/main.c @@ -0,0 +1,146 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include "hardware.h" +#include "init.h" +#include "timer.h" +#include "input.h" +#include "led.h" +#include "seeprom.h" +#include "timer.h" +#include "dcc.h" +#include "rs485.h" +#include "serial.h" +#include "datalink.h" +#include "npdu.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "iam.h" +#include "device.h" +#include "ai.h" +#include "bi.h" +#include "bo.h" + +/* local version override */ +const char *BACnet_Version = "1.0"; +/* MAC Address of MS/TP */ +static uint8_t MSTP_MAC_Address; + +/* For porting to IAR, see: + http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC/IarToAvrgcc*/ + +static void bacnet_init( + void) +{ +#if defined(BACDL_MSTP) + MSTP_MAC_Address = input_address(); + /* configure the BACnet Datalink */ + rs485_baud_rate_set(9600); + dlmstp_set_max_master(127); + dlmstp_set_max_info_frames(1); + dlmstp_set_mac_address(MSTP_MAC_Address); + dlmstp_init(NULL); +#endif + Device_Set_Object_Instance_Number(4194303); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + + Send_I_Am(&Handler_Transmit_Buffer[0]); +} + +static uint8_t PDUBuffer[MAX_MPDU]; +static void bacnet_task(void) +{ + uint8_t mstp_mac_address = 0; + uint16_t pdu_len = 0; + BACNET_ADDRESS src; /* source address */ + + mstp_mac_address = input_address(); + if (MSTP_MAC_Address != mstp_mac_address) { + MSTP_MAC_Address = mstp_mac_address; + Send_I_Am(&Handler_Transmit_Buffer[0]); + } + if (timer_elapsed_seconds(TIMER_DCC, 1)) { + dcc_timer_seconds(1); + } + pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); + if (pdu_len) { + npdu_handler(&src, &PDUBuffer[0], pdu_len); + } +} + +void idle_init(void) +{ + timer_reset(TIMER_LED_3); + timer_reset(TIMER_LED_4); +} + +void idle_task(void) +{ + if (timer_elapsed_seconds(TIMER_LED_3, 1)) { + timer_reset(TIMER_LED_3); + led_toggle(LED_3); + } + if (timer_elapsed_milliseconds(TIMER_LED_4, 125)) { + timer_reset(TIMER_LED_4); + led_toggle(LED_4); + } +} + +int main( + void) +{ + init(); + led_init(); + input_init(); + timer_init(); + seeprom_init(); + rs485_init(); + serial_init(); + bacnet_init(); + idle_init(); + /* Enable global interrupts */ + __enable_interrupt(); + for (;;) { + input_task(); + bacnet_task(); + led_task(); + idle_task(); + } +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/rs485.c b/bacnet-stack/ports/bdk-atxx4-mstp/rs485.c new file mode 100644 index 00000000..c16ce208 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/rs485.c @@ -0,0 +1,207 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "fifo.h" +#include "timer.h" +#include "led.h" + +/* baud rate */ +static uint32_t Baud_Rate = 9600; + +/* 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) +/* turnaround_time_milliseconds = (Tturnaround*1000UL)/Baud_Rate; */ + +/* buffer for storing received bytes - size must be power of two */ +static uint8_t Receive_Buffer_Data[128]; +static FIFO_BUFFER Receive_Buffer; + +static void rs485_rts_init(void) +{ + /* configure the port pin as an output */ + BIT_SET(DDRD, DDD4); +} + +/* enable the transmit-enable line on the RS-485 transceiver */ +void rs485_rts_enable( + bool enable) +{ + if (enable) { + BIT_SET(PORTD, PD4); + } else { + BIT_CLEAR(PORTD, PD4); + } +} + +static void rs485_receiver_enable(void) +{ + UCSR0B = _BV(TXEN0) | _BV(RXEN0) | _BV(RXCIE0); +} + +void rs485_turnaround_delay( + void) +{ + uint16_t turnaround_time; + + /* delay after reception before trasmitting - per MS/TP spec */ + /* wait a minimum 40 bit times since reception */ + /* at least 1 ms for errors: rounding, clock tick */ + turnaround_time = 1 + ((Tturnaround * 1000UL) / Baud_Rate); + while (!timer_elapsed_milliseconds(TIMER_SILENCE, turnaround_time)) { + /* do nothing - wait for timer to increment */ + }; +} + +ISR(USART0_RX_vect) +{ + uint8_t data_byte; + + if (BIT_CHECK(UCSR0A, RXC0)) { + /* data is available */ + data_byte = UDR0; + (void)FIFO_Put(&Receive_Buffer, data_byte); + timer_reset(TIMER_SILENCE); + } +} + +bool rs485_byte_available( + uint8_t * data_register) +{ + bool data_available = false; /* return value */ + + if (!FIFO_Empty(&Receive_Buffer)) { + led_on(LED_1); + *data_register = FIFO_Get(&Receive_Buffer); + data_available = true; + led_off_delay(LED_1, 10); + } + + return data_available; +} + +bool rs485_receive_error(void) +{ + return false; +} + +void rs485_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) /* number of bytes of data */ +{ + led_on(LED_2); + while (!BIT_CHECK(UCSR0A, UDRE0)) { + /* do nothing - wait until Tx buffer is empty */ + } + while (nbytes) { + /* Send the data byte */ + UDR0 = *buffer; + while (!BIT_CHECK(UCSR0A, UDRE0)) { + /* do nothing - wait until Tx buffer is empty */ + } + buffer++; + nbytes--; + } + /* was the frame sent? */ + while (!BIT_CHECK(UCSR0A, TXC0)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSR0A, TXC0); + timer_reset(TIMER_SILENCE); + led_off_delay(LED_2, 1); + + return; +} + +uint32_t rs485_baud_rate( + void) +{ + return Baud_Rate; +} + +bool rs485_baud_rate_set( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + Baud_Rate = baud; + /* 2x speed mode */ + BIT_SET(UCSR0A, U2X0); + /* configure baud rate */ + UBRR0 = (F_CPU / (8UL * Baud_Rate)) - 1; + /* FIXME: store the baud rate */ + break; + default: + valid = false; + break; + } + + return valid; +} + +static void rs485_usart_init(void) +{ + /* enable Transmit and Receive */ + UCSR0B = _BV(TXEN0) | _BV(RXEN0); + /* Set USART Control and Status Register n C */ + /* Asynchronous USART 8-bit data, No parity, 1 stop */ + /* Set USART Mode Select: UMSELn1 UMSELn0 = 00 for Asynchronous USART */ + /* Set Parity Mode: UPMn1 UPMn0 = 00 for Parity Disabled */ + /* Set Stop Bit Select: USBSn = 0 for 1 stop bit */ + /* Set Character Size: UCSZn2 UCSZn1 UCSZn0 = 011 for 8-bit */ + /* Clock Polarity: UCPOLn = 0 when asynchronous mode is used. */ + UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); + /* Clear Power Reduction */ + BIT_CLEAR(PRR, PRUSART0); +} + +void rs485_init(void) +{ + FIFO_Init(&Receive_Buffer, &Receive_Buffer_Data[0], + (unsigned)sizeof(Receive_Buffer_Data)); + rs485_rts_init(); + rs485_usart_init(); + rs485_baud_rate_set(Baud_Rate); + rs485_receiver_enable(); + rs485_rts_enable(false); +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/rs485.h b/bacnet-stack/ports/bdk-atxx4-mstp/rs485.h new file mode 100644 index 00000000..beaf12d4 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/rs485.h @@ -0,0 +1,64 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 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####*/ + +#ifndef RS485_H +#define RS485_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void rs485_init(void); + void rs485_rts_enable( + bool enable); + 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 */ + uint32_t rs485_baud_rate(void); + bool rs485_baud_rate_set( + uint32_t baud); + + void rs485_turnaround_delay( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/seeprom.c b/bacnet-stack/ports/bdk-atxx4-mstp/seeprom.c new file mode 100644 index 00000000..92194963 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/seeprom.c @@ -0,0 +1,424 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "seeprom.h" + +/* the SEEPROM chip select bits A2, A1, and A0 are grounded */ +/* control byte is 0xAx */ +#ifndef SEEPROM_I2C_ADDRESS +#define SEEPROM_I2C_ADDRESS 0xA0 +#endif + +/* SEEPROM Clock Frequency */ +#ifndef SEEPROM_I2C_CLOCK +#define SEEPROM_I2C_CLOCK 400000UL +#endif + +/* max number of bytes that can be written in a single write */ +#ifndef SEEPROM_PAGE_SIZE +#define SEEPROM_PAGE_SIZE 128 +#endif + +/* word addressing - is it 8-bit or 16-bit */ +#ifndef SEEPROM_WORD_ADDRESS_16BIT +#define SEEPROM_WORD_ADDRESS_16BIT 1 +#endif + +/* The lower 3 bits of TWSR are reserved on the ATmega163 */ +#define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3)) +/* start condition transmitted */ +#define TW_START 0x08 +/* repeated start condition transmitted */ +#define TW_REP_START 0x10 +/* ***Master Transmitter*** */ +/* SLA+W transmitted, ACK received */ +#define TW_MT_SLA_ACK 0x18 +/* SLA+W transmitted, NACK received */ +#define TW_MT_SLA_NACK 0x20 +/* data transmitted, ACK received */ +#define TW_MT_DATA_ACK 0x28 +/* data transmitted, NACK received */ +#define TW_MT_DATA_NACK 0x30 +/* arbitration lost in SLA+W or data */ +#define TW_MT_ARB_LOST 0x38 +/* ***Master Receiver*** */ +/* arbitration lost in SLA+R or NACK */ +#define TW_MR_ARB_LOST 0x38 +/* SLA+R transmitted, ACK received */ +#define TW_MR_SLA_ACK 0x40 +/* SLA+R transmitted, NACK received */ +#define TW_MR_SLA_NACK 0x48 +/* data received, ACK returned */ +#define TW_MR_DATA_ACK 0x50 +/* data received, NACK returned */ +#define TW_MR_DATA_NACK 0x58 + +/* SLA+R address */ +#define TW_READ 1 +/* SLA+W address */ +#define TW_WRITE 0 + +/* + * Maximal number of iterations to wait for a device to respond for a + * selection. Should be large enough to allow for a pending write to + * complete, but low enough to properly abort an infinite loop in case + * a slave is broken or not present at all. With 100 kHz TWI clock, + * transfering the start condition and SLA+R/W packet takes about 10 + * µs. The longest write period is supposed to not exceed ~ 10 ms. + * Thus, normal operation should not require more than 100 iterations + * to get the device to respond to a selection. + */ +#define MAX_ITER 200 + +/************************************************************************* +* DESCRIPTION: Return bytes from SEEPROM memory at address +* RETURN: number of bytes read, or -1 on error +* NOTES: The 24LC128 SEEPROM uses a two byte memory address +**************************************************************************/ +int seeprom_bytes_read( + uint16_t eeaddr, /* SEEPROM starting memory address */ + uint8_t * buf, /* data to store */ + int len) /* number of bytes of data to read */ +{ + uint8_t sla, twcr, n = 0; + int rv = 0; + uint8_t twst; /* status - only valid while TWINT is set. */ + +#if SEEPROM_WORD_ADDRESS_16BIT + /* 16bit address devices need only TWI Device Address */ + sla = SEEPROM_I2C_ADDRESS; +#else + /* patch high bits of EEPROM address into SLA */ + sla = SEEPROM_I2C_ADDRESS | (((eeaddr >> 8) & 0x07) << 1); +#endif + /* Note [8] First cycle: master transmitter mode */ +restart: + if (n++ >= MAX_ITER) { + return -1; + } +begin: + /* send start condition */ + TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_REP_START: + /* OK, but should not happen */ + case TW_START: + break; + case TW_MT_ARB_LOST: + /* Note [9] */ + goto begin; + default: + /* error: not in start condition */ + /* NB: do /not/ send stop condition */ + return -1; + } + + /* Note [10] */ + /* send SLA+W */ + TWDR = sla | TW_WRITE; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_SLA_ACK: + break; + case TW_MT_SLA_NACK: + /* nack during select: device busy writing */ + /* Note [11] */ + goto restart; + case TW_MT_ARB_LOST: + /* re-arbitrate */ + goto begin; + default: + /* must send stop condition */ + goto error; + } +#if SEEPROM_WORD_ADDRESS_16BIT + /* 16 bit word address device, send high 8 bits of addr */ + TWDR = (eeaddr>>8); + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_ACK: + break; + case TW_MT_DATA_NACK: + goto quit; + case TW_MT_ARB_LOST: + goto begin; + default: + /* must send stop condition */ + goto error; + } +#endif + /* low 8 bits of addr */ + TWDR = eeaddr; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_ACK: + break; + case TW_MT_DATA_NACK: + goto quit; + case TW_MT_ARB_LOST: + goto begin; + default: + /* must send stop condition */ + goto error; + } + + /* Note [12] Next cycle(s): master receiver mode */ + /* send repeated start condition */ + TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_START: + /* OK, but should not happen */ + case TW_REP_START: + break; + case TW_MT_ARB_LOST: + goto begin; + default: + goto error; + } + + /* send SLA+R */ + TWDR = sla | TW_READ; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MR_SLA_ACK: + break; + case TW_MR_SLA_NACK: + goto quit; + case TW_MR_ARB_LOST: + goto begin; + default: + goto error; + } + /* Note [13] */ + twcr = _BV(TWINT) | _BV(TWEN) | _BV(TWEA); + for (;len > 0; len--) { + if (len == 1) { + /* send NAK this time */ + twcr = _BV(TWINT) | _BV(TWEN); + } + /* clear int to start transmission */ + TWCR = twcr; + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MR_DATA_NACK: + /* force end of loop */ + len = 0; + /* FALLTHROUGH */ + case TW_MR_DATA_ACK: + *buf= TWDR; + buf++; + rv++; + break; + default: + goto error; + } + } +quit: + /* Note [14] */ + /* send stop condition */ + TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); + return rv; +error: + rv = -1; + goto quit; +} + +/************************************************************************* +* DESCRIPTION: Write some data and wait until it is sent +* RETURN: number of bytes written, or -1 on error +* NOTES: The 24LC128 SEEPROM uses a two byte memory address +**************************************************************************/ +int seeprom_bytes_write( + uint16_t eeaddr, /* SEEPROM starting memory address */ + uint8_t * buf, /* data to send */ + int len) /* number of bytes of data */ +{ + uint8_t sla, n = 0; + int rv = 0; + uint16_t endaddr; + uint8_t twst; /* status - only valid while TWINT is set. */ + + if ((eeaddr + len) < (eeaddr | (SEEPROM_PAGE_SIZE - 1))) { + endaddr = eeaddr + len; + } else { + endaddr = (eeaddr | (SEEPROM_PAGE_SIZE - 1)) + 1; + } + len = endaddr - eeaddr; +#if SEEPROM_WORD_ADDRESS_16BIT + /* 16bit address devices need only TWI Device Address */ + sla = SEEPROM_I2C_ADDRESS; +#else + /* patch high bits of EEPROM address into SLA */ + sla = SEEPROM_I2C_ADDRESS | (((eeaddr >> 8) & 0x07) << 1); +#endif +restart: + if (n++ >= MAX_ITER) { + return -1; + } +begin: + /* Note [15] */ + TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* send start condition */ + while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */ + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_REP_START: + /* OK, but should not happen */ + case TW_START: + break; + case TW_MT_ARB_LOST: + goto begin; + default: + /* error: not in start condition */ + /* NB: do /not/ send stop condition */ + return -1; + } + /* send SLA+W */ + TWDR = sla | TW_WRITE; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_SLA_ACK: + break; + case TW_MT_SLA_NACK: + /* nack during select: device busy writing */ + goto restart; + case TW_MT_ARB_LOST: + /* re-arbitrate */ + goto begin; + default: + /* must send stop condition */ + goto error; + } +#if SEEPROM_WORD_ADDRESS_16BIT + /* 16 bit word address device, send high 8 bits of addr */ + TWDR = (eeaddr>>8); + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_ACK: + break; + case TW_MT_DATA_NACK: + goto quit; + case TW_MT_ARB_LOST: + goto begin; + default: + /* must send stop condition */ + goto error; + } +#endif + /* low 8 bits of addr */ + TWDR = eeaddr; + /* clear interrupt to start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) {}; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_ACK: + break; + case TW_MT_DATA_NACK: + goto quit; + case TW_MT_ARB_LOST: + goto begin; + default: + /* must send stop condition */ + goto error; + } + for (; len > 0; len--) { + TWDR = *buf++; + /* start transmission */ + TWCR = _BV(TWINT) | _BV(TWEN); + /* wait for transmission */ + while ((TWCR & _BV(TWINT)) == 0) ; + twst = TWSR & TW_STATUS_MASK; + switch (twst) { + case TW_MT_DATA_NACK: + /* device write protected -- Note [16] */ + goto error; + case TW_MT_DATA_ACK: + rv++; + break; + default: + goto error; + } + } +quit: + /* send stop condition */ + TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); + + return rv; + +error: + rv = -1; + goto quit; +} + +/************************************************************************* +* Description: Initialize the SEEPROM TWI connection +* Returns: none +* Notes: none +**************************************************************************/ +void seeprom_init(void) +{ + /* bit rate prescaler */ + TWSR=0; + TWCR=_BV(TWEN) | _BV(TWEA); + /* bit rate */ + TWBR = (F_CPU / SEEPROM_I2C_CLOCK - 16) / 2; + /* my address */ + TWAR=0; +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/seeprom.h b/bacnet-stack/ports/bdk-atxx4-mstp/seeprom.h new file mode 100644 index 00000000..5b9053e5 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/seeprom.h @@ -0,0 +1,48 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef SEEPROM_H +#define SEEPROM_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int seeprom_bytes_read( + uint16_t ee_address, /* SEEPROM starting memory address */ + uint8_t * buffer, /* data to store */ + int nbytes); /* number of bytes of data to read */ +int seeprom_bytes_write( + uint16_t ee_address, /* SEEPROM starting memory address */ + uint8_t * buffer, /* data to send */ + int nbytes); /* number of bytes of data */ +void seeprom_init( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/serial.c b/bacnet-stack/ports/bdk-atxx4-mstp/serial.c new file mode 100644 index 00000000..82b4abfb --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/serial.c @@ -0,0 +1,173 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "fifo.h" +#include "serial.h" + +/* baud rate */ +static uint32_t Baud_Rate = 9600; + +/* buffer for storing received bytes - size must be power of two */ +static uint8_t Receive_Buffer_Data[128]; +static FIFO_BUFFER Receive_Buffer; + +static void serial_receiver_enable(void) +{ + UCSR0B = _BV(TXEN0) | _BV(RXEN0) | _BV(RXCIE0); +} + +ISR(USART1_RX_vect) +{ + uint8_t data_byte; + + if (BIT_CHECK(UCSR1A, RXC1)) { + /* data is available */ + data_byte = UDR1; + (void)FIFO_Put(&Receive_Buffer, data_byte); + } +} + +bool serial_byte_get( + uint8_t * data_register) +{ + bool data_available = false; /* return value */ + + if (!FIFO_Empty(&Receive_Buffer)) { + *data_register = FIFO_Get(&Receive_Buffer); + data_available = true; + } + + return data_available; +} + +bool serial_byte_peek( + uint8_t * data_register) +{ + bool data_available = false; /* return value */ + + if (!FIFO_Empty(&Receive_Buffer)) { + *data_register = FIFO_Peek(&Receive_Buffer); + data_available = true; + } + + return data_available; +} + +void serial_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) /* number of bytes of data */ +{ + while (!BIT_CHECK(UCSR1A, UDRE1)) { + /* do nothing - wait until Tx buffer is empty */ + } + while (nbytes) { + /* Send the data byte */ + UDR1 = *buffer; + while (!BIT_CHECK(UCSR1A, UDRE1)) { + /* do nothing - wait until Tx buffer is empty */ + } + buffer++; + nbytes--; + } + /* was the frame sent? */ + while (!BIT_CHECK(UCSR1A, TXC1)) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + /* Clear the Transmit Complete flag by writing a one to it. */ + BIT_SET(UCSR1A, TXC1); + + return; +} + +void serial_byte_send(uint8_t ch) +{ + uint8_t buffer[1]; + + buffer[0] = ch; + serial_bytes_send(&buffer[0], 1); + + return; +} + +uint32_t serial_baud_rate( + void) +{ + return Baud_Rate; +} + +bool serial_baud_rate_set( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + Baud_Rate = baud; + /* 2x speed mode */ + BIT_SET(UCSR1A, U2X1); + /* configure baud rate */ + UBRR1 = (F_CPU / (8UL * Baud_Rate)) - 1; + /* FIXME: store the baud rate */ + break; + default: + valid = false; + break; + } + + return valid; +} + +static void serial_usart_init(void) +{ + /* enable Transmit and Receive */ + UCSR1B = _BV(TXEN1) | _BV(RXEN1); + /* Set USART Control and Status Register n C */ + /* Asynchronous USART 8-bit data, No parity, 1 stop */ + /* Set USART Mode Select: UMSELn1 UMSELn0 = 00 for Asynchronous USART */ + /* Set Parity Mode: UPMn1 UPMn0 = 00 for Parity Disabled */ + /* Set Stop Bit Select: USBSn = 0 for 1 stop bit */ + /* Set Character Size: UCSZn2 UCSZn1 UCSZn0 = 011 for 8-bit */ + /* Clock Polarity: UCPOLn = 0 when asynchronous mode is used. */ + UCSR0C = _BV(UCSZ11) | _BV(UCSZ10); + /* Clear Power Reduction */ + BIT_CLEAR(PRR, PRUSART1); +} + +void serial_init(void) +{ + FIFO_Init(&Receive_Buffer, &Receive_Buffer_Data[0], + (unsigned)sizeof(Receive_Buffer_Data)); + serial_usart_init(); + serial_baud_rate_set(Baud_Rate); + serial_receiver_enable(); +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/serial.h b/bacnet-stack/ports/bdk-atxx4-mstp/serial.h new file mode 100644 index 00000000..f32ff6f6 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/serial.h @@ -0,0 +1,53 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef SERIAL_H +#define SERIAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +bool serial_byte_get( + uint8_t * data_register); +bool serial_byte_peek( + uint8_t * data_register); + +void serial_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ +void serial_byte_send(uint8_t ch); + +uint32_t serial_baud_rate(void); +bool serial_baud_rate_set( + uint32_t baud); + +void serial_init(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/stack.c b/bacnet-stack/ports/bdk-atxx4-mstp/stack.c new file mode 100644 index 00000000..18d86177 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/stack.c @@ -0,0 +1,52 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include "hardware.h" + +/* stack checking */ +extern uint8_t _end; +extern uint8_t __stack; + +#define STACK_CANARY (0xC5) +void stack_paint( + void) __attribute__ ((naked)) __attribute__ ((section(".init1"))); + +void stack_paint( + void) +{ +#if 0 + uint8_t *p = &_end; + + while (p <= &__stack) { + *p = STACK_CANARY; + p++; + } +#else + __asm volatile ( + " ldi r30,lo8(_end)\n" " ldi r31,hi8(_end)\n" " ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */ + " ldi r25,hi8(__stack)\n" " rjmp .cmp\n" ".loop:\n" + " st Z+,r24\n" ".cmp:\n" " cpi r30,lo8(__stack)\n" + " cpc r31,r25\n" " brlo .loop\n" " breq .loop"::); +#endif +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/timer.h b/bacnet-stack/ports/bdk-atxx4-mstp/timer.h new file mode 100644 index 00000000..61519ac3 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/timer.h @@ -0,0 +1,67 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#include +#include + +/* Timer Module */ +/* reserve the millisecond timer indexes as needed for each module */ +#define TIMER_SILENCE 0 +#define TIMER_DEBOUNCE 1 +#define TIMER_LED_1 2 +#define TIMER_LED_2 3 +#define TIMER_LED_3 4 +#define TIMER_LED_4 5 +#define TIMER_DCC 6 +#define MAX_MILLISECOND_TIMERS 7 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void timer_init( + void); + unsigned long timer_milliseconds( + unsigned index); + bool timer_elapsed_milliseconds( + unsigned index, + unsigned long value); + bool timer_elapsed_seconds( + unsigned index, + unsigned long value); + bool timer_elapsed_minutes( + unsigned index, + unsigned long seconds); + unsigned long timer_milliseconds_set( + unsigned index, + unsigned long value); + unsigned long timer_reset( + unsigned index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/timer2.c b/bacnet-stack/ports/bdk-atxx4-mstp/timer2.c new file mode 100644 index 00000000..8b3acd45 --- /dev/null +++ b/bacnet-stack/ports/bdk-atxx4-mstp/timer2.c @@ -0,0 +1,222 @@ +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "hardware.h" +#include "timer.h" + +/* define various timers in timer.h file */ +#ifndef MAX_MILLISECOND_TIMERS +#define MAX_MILLISECOND_TIMERS 2 +#endif + +/* Timer2 Prescaling: 1, 8, 32, 64, 128, 256, or 1024 */ +#define TIMER2_PRESCALER 128 +/* Count: Timer counts up to 0xFF and then signals overflow */ +#define TIMER2_TICKS (F_CPU/TIMER2_PRESCALER/1000) +#if (TIMER2_TICKS > 0xFF) +#error Timer2 Prescaler value is too small +#endif +#define TIMER2_COUNT (0xFF-TIMER2_TICKS) + +/* counter for the various timers */ +static volatile unsigned long Millisecond_Counter[MAX_MILLISECOND_TIMERS]; + +/************************************************************************* +* Description: Timer Interrupt Handler +* Returns: none +* Notes: Global interupts must be enabled +*************************************************************************/ +static inline void timer_interrupt_handler(void) +{ + unsigned i; /* loop counter */ + + /* increment the tick count */ + for (i = 0; i < MAX_MILLISECOND_TIMERS; i++) { + Millisecond_Counter[i]++; + } +} + +/************************************************************************* +* Description: Timer Interrupt Service Routine - Timer Overflowed! +* Returns: none +* Notes: Global interupts must be enabled +*************************************************************************/ +ISR(TIMER2_OVF_vect) +{ + /* Set the counter for the next interrupt */ + TCNT2 = TIMER2_COUNT; + timer_interrupt_handler(); +} + +/************************************************************************* +* Description: sets the current time count with a value +* Returns: none +* Notes: none +*************************************************************************/ +unsigned long timer_milliseconds_set( + unsigned index, + unsigned long value) +{ + uint8_t sreg = 0; /* holds interrupts pending */ + unsigned long old_value = 0; /* return value */ + + if (index < MAX_MILLISECOND_TIMERS) { + sreg = SREG; + __disable_interrupt(); + old_value = Millisecond_Counter[index]; + Millisecond_Counter[index] = value; + SREG = sreg; + } + + return old_value; +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: none +*************************************************************************/ +unsigned long timer_milliseconds( + unsigned index) +{ + unsigned long timer_value = 0; /* return value */ + uint8_t sreg = 0; /* holds interrupts pending */ + + if (index < MAX_MILLISECOND_TIMERS) { + sreg = SREG; + __disable_interrupt(); + timer_value = Millisecond_Counter[index]; + SREG = sreg; + } + + return timer_value; +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds( + unsigned index, + unsigned long value) +{ + return (timer_milliseconds(index) >= value); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds( + unsigned index, + unsigned long seconds) +{ + return ((timer_milliseconds(index)/1000UL) >= seconds); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes( + unsigned index, + unsigned long minutes) +{ + return ((timer_milliseconds(index)/(1000UL*60UL)) >= minutes); +} + +/************************************************************************* +* Description: Sets the timer counter to zero. +* Returns: none +* Notes: none +*************************************************************************/ +unsigned long timer_reset( + unsigned index) +{ + return timer_milliseconds_set(index,0); +} + +/************************************************************************* +* Description: Initialization for Timer +* Returns: none +* Notes: none +*************************************************************************/ +static void timer2_init( + void) +{ + /* Normal Operation */ + TCCR2A = 0; + /* Timer2: prescale selections: + CSn2 CSn1 CSn0 Description + ---- ---- ---- ----------- + 0 0 0 No Clock Source + 0 0 1 No prescaling + 0 1 0 CLKt2s/8 + 0 1 1 CLKt2s/32 + 1 0 0 CLKt2s/64 + 1 0 1 CLKt2s/128 + 1 1 0 CLKt2s/256 + 1 1 1 CLKt2s/1024 + */ +#if (TIMER2_PRESCALER==1) + TCCR2B = _BV(CS20); +#elif (TIMER2_PRESCALER==8) + TCCR2B = _BV(CS21); +#elif (TIMER2_PRESCALER==32) + TCCR2B = _BV(CS21) | _BV(CS20); +#elif (TIMER2_PRESCALER==64) + TCCR2B = _BV(CS22); +#elif (TIMER2_PRESCALER==128) + TCCR2B = _BV(CS22) | _BV(CS20); +#elif (TIMER2_PRESCALER==256) + TCCR2B = _BV(CS22) | _BV(CS21); +#elif (TIMER2_PRESCALER==1024) + TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); +#else + #error Timer2 Prescale: Invalid Value +#endif + /* Clear any TOV Flag set when the timer overflowed */ + BIT_CLEAR(TIFR2, TOV2); + /* Initial value */ + TCNT2 = TIMER2_COUNT; + /* Enable the overflow interrupt */ + BIT_SET(TIMSK2, TOIE2); + /* Clear the Power Reduction Timer/Counter0 */ + BIT_CLEAR(PRR, PRTIM2); +} + +/************************************************************************* +* Description: Initialization for Timer +* Returns: none +* Notes: none +*************************************************************************/ +void timer_init( + void) +{ + timer2_init(); +} \ No newline at end of file