Added port for ATmega8 - but not quite small enough yet. Compiled for ATmega168 (but at 8734 bytes of flash).
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
###############################################################################
|
||||
# Makefile for BACnet
|
||||
###############################################################################
|
||||
|
||||
## General Flags
|
||||
PROJECT = bacnet
|
||||
MCU = atmega168
|
||||
TARGET = bacnet
|
||||
## Tools
|
||||
CC = avr-gcc
|
||||
AR = avr-ar
|
||||
OBJCOPY = avr-objcopy
|
||||
OBJDUMP = avr-objdump
|
||||
SIZE = avr-size
|
||||
|
||||
# Source locations
|
||||
BACNET_CORE = ../../src
|
||||
BACNET_INCLUDE = ../../include
|
||||
BACNET_DEMO = ../../demo
|
||||
|
||||
# local files for this project
|
||||
CSRC = main.c \
|
||||
timer.c \
|
||||
rs485.c \
|
||||
dlmstp.c \
|
||||
apdu.c \
|
||||
$(BACNET_CORE)/crc.c
|
||||
|
||||
# common demo files needed
|
||||
DEMOSRC = h_rp.c \
|
||||
device.c \
|
||||
$(BACNET_DEMO)/handler/txbuf.c \
|
||||
$(BACNET_DEMO)/handler/h_npdu.c \
|
||||
$(BACNET_DEMO)/handler/noserv.c
|
||||
|
||||
# core BACnet stack files
|
||||
CORESRC = \
|
||||
$(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)/rp.c \
|
||||
$(BACNET_CORE)/bacaddr.c \
|
||||
$(BACNET_CORE)/abort.c \
|
||||
$(BACNET_CORE)/reject.c \
|
||||
$(BACNET_CORE)/bacerror.c \
|
||||
$(BACNET_CORE)/bacapp.c
|
||||
|
||||
# $(BACNET_CORE)/iam.c \
|
||||
# $(BACNET_CORE)/whois.c \
|
||||
# $(BACNET_CORE)/wp.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)/dcc.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
|
||||
OPTIMIZE_FLAGS += -finline-functions-called-once
|
||||
#OPTIMIZATION = -O0
|
||||
#OPTIMIZATION = -Os
|
||||
OPTIMIZATION = -Os $(OPTIMIZE_FLAGS)
|
||||
#OPTIMIZATION = -O3 $(OPTIMIZE_FLAGS)
|
||||
|
||||
## 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_REAL
|
||||
#BFLAGS += -DBACAPP_OBJECT_ID
|
||||
#BFLAGS += -DBACAPP_UNSIGNED
|
||||
#BFLAGS += -DBACAPP_ENUMERATED
|
||||
#BFLAGS += -DBACAPP_CHARACTER_STRING
|
||||
#BFLAGS += -DWRITE_PROPERTY
|
||||
BFLAGS += -DMAX_ANALOG_VALUES=0
|
||||
BFLAGS += -DMAX_BINARY_VALUES=0
|
||||
CFLAGS = $(COMMON)
|
||||
# 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,-Map=$(TARGET).map,-L=.,-l$(TARGET)
|
||||
#LDFLAGS += -Wl,-Map=$(TARGET).map
|
||||
|
||||
## Intel Hex file production flags
|
||||
HEX_FLASH_FLAGS = -R .eeprom
|
||||
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)
|
||||
#OBJECTS = $(COBJ)
|
||||
|
||||
## 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)
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(INCLUDES) $(CFLAGS) $*.c -o $@
|
||||
|
||||
size: ${TARGET_ELF}
|
||||
@echo
|
||||
@${SIZE} -C --mcu=${MCU} ${TARGET_ELF}
|
||||
|
||||
## 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/*)
|
||||
@@ -0,0 +1,159 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "config.h"
|
||||
|
||||
/* Analog Input = Photocell */
|
||||
#define MAX_ANALOG_INPUTS 9
|
||||
#if (MAX_ANALOG_INPUTS > 9)
|
||||
#error Modify the Analog_Input_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
float 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;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
unsigned Analog_Input_Instance_To_Index(
|
||||
uint32_t object_instance)
|
||||
{
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
|
||||
char *Analog_Input_Name(
|
||||
uint32_t object_instance)
|
||||
{
|
||||
static char text_string[5] = "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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
unsigned object_index;
|
||||
|
||||
|
||||
(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.
|
||||
Note that Object-Name must be unique in this device */
|
||||
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:
|
||||
object_index = Analog_Input_Instance_To_Index(object_instance);
|
||||
apdu_len =
|
||||
encode_application_real(&apdu[0], Present_Value[object_index]);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*####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####*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "bits.h"
|
||||
#include "apdu.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "handlers.h"
|
||||
|
||||
bool apdu_service_supported(
|
||||
BACNET_SERVICES_SUPPORTED service_supported)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
if (service_supported == SERVICE_SUPPORTED_READ_PROPERTY) {
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
uint16_t apdu_decode_confirmed_service_request(
|
||||
uint8_t * apdu, /* APDU data */
|
||||
uint16_t apdu_len,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data,
|
||||
uint8_t * service_choice,
|
||||
uint8_t ** service_request,
|
||||
uint16_t * service_request_len)
|
||||
{
|
||||
uint16_t len = 0; /* counts where we are in PDU */
|
||||
|
||||
service_data->segmented_message = (apdu[0] & BIT3) ? true : false;
|
||||
service_data->more_follows = (apdu[0] & BIT2) ? true : false;
|
||||
service_data->segmented_response_accepted =
|
||||
(apdu[0] & BIT1) ? true : false;
|
||||
service_data->max_segs = decode_max_segs(apdu[1]);
|
||||
service_data->max_resp = decode_max_apdu(apdu[1]);
|
||||
service_data->invoke_id = apdu[2];
|
||||
len = 3;
|
||||
if (service_data->segmented_message) {
|
||||
service_data->sequence_number = apdu[len++];
|
||||
service_data->proposed_window_number = apdu[len++];
|
||||
}
|
||||
*service_choice = apdu[len++];
|
||||
*service_request = &apdu[len];
|
||||
*service_request_len = apdu_len - len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void apdu_handler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t * apdu, /* APDU data */
|
||||
uint16_t apdu_len)
|
||||
{
|
||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||
uint8_t service_choice = 0;
|
||||
uint8_t *service_request = NULL;
|
||||
uint16_t service_request_len = 0;
|
||||
uint16_t len = 0; /* counts where we are in PDU */
|
||||
|
||||
if (apdu) {
|
||||
/* PDU Type */
|
||||
switch (apdu[0] & 0xF0) {
|
||||
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
||||
len = apdu_decode_confirmed_service_request(&apdu[0], /* APDU data */
|
||||
apdu_len, &service_data, &service_choice, &service_request,
|
||||
&service_request_len);
|
||||
if (service_choice == SERVICE_CONFIRMED_READ_PROPERTY) {
|
||||
handler_read_property(service_request, service_request_len,
|
||||
src, &service_data);
|
||||
} else {
|
||||
handler_unrecognized_service(service_request,
|
||||
service_request_len, src, &service_data);
|
||||
}
|
||||
break;
|
||||
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
|
||||
case PDU_TYPE_SIMPLE_ACK:
|
||||
case PDU_TYPE_COMPLEX_ACK:
|
||||
case PDU_TYPE_SEGMENT_ACK:
|
||||
case PDU_TYPE_ERROR:
|
||||
case PDU_TYPE_REJECT:
|
||||
case PDU_TYPE_ABORT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 Value Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "bacapp.h"
|
||||
#include "config.h" /* the custom stuff */
|
||||
#include "wp.h"
|
||||
#include "av.h"
|
||||
|
||||
#if (MAX_ANALOG_VALUES > 10)
|
||||
#error Modify the Analog_Value_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
float AV_Present_Value[MAX_ANALOG_VALUES];
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Analog_Value_Valid_Instance(
|
||||
uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_ANALOG_VALUES)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Analog_Value_Count(
|
||||
void)
|
||||
{
|
||||
return MAX_ANALOG_VALUES;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Analog_Value_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 Analog_Value_Instance_To_Index(
|
||||
uint32_t object_instance)
|
||||
{
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
char *Analog_Value_Name(
|
||||
uint32_t object_instance)
|
||||
{
|
||||
static char text_string[5] = "AV-"; /* okay for single thread */
|
||||
|
||||
text_string[3] = '0' + (uint8_t) object_instance;
|
||||
|
||||
return text_string;
|
||||
}
|
||||
|
||||
/* return apdu len, or -1 on error */
|
||||
int Analog_Value_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;
|
||||
unsigned object_index;
|
||||
|
||||
switch (property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len =
|
||||
encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE,
|
||||
object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
characterstring_init_ansi(&char_string,
|
||||
Analog_Value_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_VALUE);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
object_index = Analog_Value_Instance_To_Index(object_instance);
|
||||
apdu_len =
|
||||
encode_application_real(&apdu[0],
|
||||
AV_Present_Value[object_index]);
|
||||
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;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Analog_Value_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;
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
if (!Analog_Value_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_REAL) {
|
||||
object_index =
|
||||
Analog_Value_Instance_To_Index(wp_data->object_instance);
|
||||
AV_Present_Value[object_index] = value.type.Real;
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testAnalog_Value(
|
||||
Test * pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_VALUE;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint32_t instance = 123;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
len =
|
||||
Analog_Value_Encode_Property_APDU(&apdu[0], instance,
|
||||
PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL, &error_class, &error_code);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len =
|
||||
decode_object_id(&apdu[len], (int *) &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == OBJECT_ANALOG_VALUE);
|
||||
ct_test(pTest, decoded_instance == instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ANALOG_VALUE
|
||||
int main(
|
||||
void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Analog Value", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAnalog_Value);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void) ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ANALOG_VALUE */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef AVR035_H
|
||||
#define AVR035_H
|
||||
|
||||
/* from AVR035: Efficient C Coding for AVR */
|
||||
|
||||
/* a=register, b=bit number to act upon */
|
||||
#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))
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,302 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 Value Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "hardware.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "config.h" /* the custom stuff */
|
||||
#include "wp.h"
|
||||
#include "bv.h"
|
||||
|
||||
#if (MAX_BINARY_VALUES > 10)
|
||||
#error Modify the Binary_Value_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
static BACNET_BINARY_PV Present_Value[MAX_BINARY_VALUES];
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
bool Binary_Value_Valid_Instance(
|
||||
uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_BINARY_VALUES)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
unsigned Binary_Value_Count(
|
||||
void)
|
||||
{
|
||||
return MAX_BINARY_VALUES;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
uint32_t Binary_Value_Index_To_Instance(
|
||||
unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
unsigned Binary_Value_Instance_To_Index(
|
||||
uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_BINARY_VALUES;
|
||||
|
||||
if (object_instance < MAX_BINARY_VALUES)
|
||||
index = object_instance;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static BACNET_BINARY_PV Binary_Value_Present_Value(
|
||||
uint32_t object_instance)
|
||||
{
|
||||
BACNET_BINARY_PV value = BINARY_INACTIVE;
|
||||
|
||||
if (object_instance < MAX_BINARY_VALUES) {
|
||||
value = Present_Value[object_instance];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
char *Binary_Value_Name(
|
||||
uint32_t object_instance)
|
||||
{
|
||||
static char text_string[5] = "BV-0"; /* okay for single thread */
|
||||
|
||||
if (object_instance < MAX_BINARY_VALUES) {
|
||||
text_string[3] = '0' + (uint8_t) object_instance;
|
||||
return text_string;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return apdu len, or -1 on error */
|
||||
int Binary_Value_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_BINARY_PV present_value = BINARY_INACTIVE;
|
||||
BACNET_POLARITY polarity = POLARITY_NORMAL;
|
||||
|
||||
switch (property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len =
|
||||
encode_application_object_id(&apdu[0], OBJECT_BINARY_VALUE,
|
||||
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:
|
||||
characterstring_init_ansi(&char_string,
|
||||
Binary_Value_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_VALUE);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
present_value = Binary_Value_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:
|
||||
apdu_len = encode_application_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_POLARITY:
|
||||
/* FIXME: figure out the 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;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Binary_Value_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;
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
if (!Binary_Value_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) {
|
||||
if ((value.type.Enumerated == BINARY_ACTIVE) ||
|
||||
(value.type.Enumerated == BINARY_INACTIVE)) {
|
||||
object_index =
|
||||
Binary_Value_Instance_To_Index(wp_data->
|
||||
object_instance);
|
||||
/* NOTE: this Binary value has no priority array */
|
||||
Present_Value[object_index] =
|
||||
(BACNET_BINARY_PV) value.type.Enumerated;
|
||||
/* 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. */
|
||||
if (Present_Value[0] == BINARY_ACTIVE) {
|
||||
LED_GREEN_ON();
|
||||
} else {
|
||||
LED_GREEN_OFF();
|
||||
}
|
||||
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;
|
||||
#if 0
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) {
|
||||
object_index =
|
||||
Binary_Value_Instance_To_Index(wp_data->object_instance);
|
||||
Binary_Value_Out_Of_Service[object_index] = value.type.Boolean;
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testBinary_Value(
|
||||
Test * pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
BACNET_OBJECT_TYPE decoded_type = OBJECT_BINARY_VALUE;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint32_t instance = 123;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
|
||||
len =
|
||||
Binary_Value_Encode_Property_APDU(&apdu[0], instance,
|
||||
PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL, &error_class, &error_code);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len =
|
||||
decode_object_id(&apdu[len], (int *) &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == OBJECT_BINARY_VALUE);
|
||||
ct_test(pTest, decoded_instance == instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_BINARY_VALUE
|
||||
int main(
|
||||
void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Binary_Value", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBinary_Value);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void) ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_BINARY_VALUE */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,331 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacstr.h"
|
||||
#include "bacenum.h"
|
||||
#include "apdu.h"
|
||||
#include "dcc.h"
|
||||
#include "dlmstp.h"
|
||||
#include "rs485.h"
|
||||
#include "version.h"
|
||||
/* objects */
|
||||
#include "device.h"
|
||||
#include "av.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 = 260001;
|
||||
static char *Object_Name = "My Device";
|
||||
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
uint16_t Device_Vendor_Identifier(
|
||||
void)
|
||||
{
|
||||
return BACNET_VENDOR_ID;
|
||||
}
|
||||
|
||||
unsigned Device_Object_List_Count(
|
||||
void)
|
||||
{
|
||||
unsigned count = 1; /* at least 1 for device object */
|
||||
|
||||
#if MAX_ANALOG_VALUES
|
||||
/* FIXME: add objects as needed */
|
||||
count += Analog_Value_Count();
|
||||
#endif
|
||||
#if MAX_BINARY_VALUES
|
||||
/* FIXME: add objects as needed */
|
||||
count += Binary_Value_Count();
|
||||
#endif
|
||||
|
||||
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 */
|
||||
#if MAX_ANALOG_VALUES
|
||||
/* analog value objects */
|
||||
if (!status) {
|
||||
/* array index starts at 1, and 1 for the device object */
|
||||
object_index -= object_count;
|
||||
object_count = Analog_Value_Count();
|
||||
if (object_index < object_count) {
|
||||
*object_type = OBJECT_ANALOG_VALUE;
|
||||
*instance = Analog_Value_Index_To_Instance(object_index);
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if MAX_BINARY_VALUES
|
||||
/* binary value objects */
|
||||
if (!status) {
|
||||
object_index -= object_count;
|
||||
object_count = Binary_Value_Count();
|
||||
/* is it a valid index for this object? */
|
||||
if (object_index < object_count) {
|
||||
*object_type = OBJECT_BINARY_VALUE;
|
||||
*instance = Binary_Value_Index_To_Instance(object_index);
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
/* 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_SYSTEM_STATUS:
|
||||
apdu_len = encode_application_enumerated(&apdu[0], 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_TEXT);
|
||||
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_PROTOCOL_VERSION:
|
||||
apdu_len = encode_application_unsigned(&apdu[0], 1);
|
||||
break;
|
||||
case PROP_PROTOCOL_REVISION:
|
||||
apdu_len = encode_application_unsigned(&apdu[0], 5);
|
||||
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((BACNET_SERVICES_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);
|
||||
/* must have the bit string as big as it can be */
|
||||
for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) {
|
||||
/* 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);
|
||||
#if MAX_ANALOG_VALUES
|
||||
bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true);
|
||||
#endif
|
||||
#if MAX_BINARY_VALUES
|
||||
bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true);
|
||||
#endif
|
||||
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++) {
|
||||
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 {
|
||||
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], SEGMENTATION_NONE);
|
||||
break;
|
||||
case PROP_APDU_TIMEOUT:
|
||||
apdu_len = encode_application_unsigned(&apdu[0], 60000);
|
||||
break;
|
||||
case PROP_NUMBER_OF_APDU_RETRIES:
|
||||
apdu_len = encode_application_unsigned(&apdu[0], 0);
|
||||
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], 0);
|
||||
break;
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,765 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2008 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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "bacdef.h"
|
||||
#include "dlmstp.h"
|
||||
#include "rs485.h"
|
||||
#include "crc.h"
|
||||
#include "npdu.h"
|
||||
#include "bits.h"
|
||||
#include "bacaddr.h"
|
||||
#include "txbuf.h"
|
||||
|
||||
/* This file has been customized for use with small microprocessors */
|
||||
/* Assumptions:
|
||||
Only one slave node MS/TP datalink layer
|
||||
*/
|
||||
#include "hardware.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* 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;
|
||||
|
||||
/* The state of the Receive State Machine */
|
||||
static MSTP_RECEIVE_STATE Receive_State;
|
||||
static struct mstp_flag_t {
|
||||
/* A Boolean flag set to TRUE by the Receive State Machine */
|
||||
/* if an invalid or valid frame is received. */
|
||||
/* Set to FALSE by the main state machine. */
|
||||
unsigned ReceivedInvalidFrame:1;
|
||||
unsigned ReceivedValidFrame: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;
|
||||
|
||||
/* data passed to receive handler */
|
||||
struct mstp_packet_info_t
|
||||
{
|
||||
/* A Boolean flag set to TRUE by the Receive State Machine */
|
||||
/* if a valid frame is received. */
|
||||
unsigned valid_frame:1;
|
||||
/* preamble flags */
|
||||
unsigned preamble1:1;
|
||||
unsigned preamble2:1;
|
||||
uint8_t header[6]; /* buffer to put the header bytes */
|
||||
uint8_t *buffer; /* buffer to put the data */
|
||||
uint8_t buffer_len; /* buffer to put the data */
|
||||
uint8_t index; /* index into receive buffer */
|
||||
};
|
||||
static struct nitoo_packet_info_t MSTP_Receive_Packet;
|
||||
|
||||
/* 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 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;
|
||||
/* 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 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 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;
|
||||
/* initialize hardware */
|
||||
RS485_Initialize();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 * pdu, /* any data to be sent - may be null */
|
||||
uint16_t pdu_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 crc */
|
||||
uint8_t datacrc[2]; /* stores the 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] = pdu_len / 256;
|
||||
crc8 = CRC_Calc_Header(buffer[5], crc8);
|
||||
buffer[6] = pdu_len % 256;
|
||||
crc8 = CRC_Calc_Header(buffer[6], crc8);
|
||||
buffer[7] = ~crc8;
|
||||
if (pdu_len) {
|
||||
/* calculate CRC for any data */
|
||||
for (i = 0; i < pdu_len; i++) {
|
||||
crc16 = CRC_Calc_Data(pdu[i], crc16);
|
||||
}
|
||||
crc16 = ~crc16;
|
||||
datacrc[0] = (crc16 & 0x00FF);
|
||||
datacrc[1] = ((crc16 & 0xFF00) >> 8);
|
||||
}
|
||||
/* now transmit the frame */
|
||||
RS485_Turnaround_Delay();
|
||||
RS485_Transmitter_Enable(true);
|
||||
RS485_Send_Data(buffer, 8);
|
||||
/* send any data */
|
||||
if (pdu_len) {
|
||||
RS485_Send_Data(pdu, pdu_len);
|
||||
RS485_Send_Data(datacrc, 2);
|
||||
}
|
||||
RS485_Transmitter_Enable(false);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* return true if the packet is good. */
|
||||
/* note: buffer should include the CRC as the last byte */
|
||||
static bool crc_header_good(uint8_t *buffer, uint8_t len)
|
||||
{
|
||||
uint8_t i; /* loop counter */
|
||||
uint8_t crc8 = 0xFF; /* loop counter */
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
crc8 = CRC_Calc_Header(buffer[i], crc8);
|
||||
}
|
||||
|
||||
return (crc8 == 0x55);
|
||||
}
|
||||
|
||||
|
||||
static void mstp_receive_handler(void)
|
||||
{
|
||||
uint8_t data_register = 0; /* data from UART */
|
||||
|
||||
if (RS485_ReceiveError()) {
|
||||
timer_silence_reset();
|
||||
} else if (RS485_DataAvailable(&data_register)) {
|
||||
timer_silence_reset();
|
||||
if ((MSTP_Receive_Packet.preamble1 == false) &&
|
||||
(data_register == 0x55)) {
|
||||
MSTP_Receive_Packet.preamble1 = true;
|
||||
return;
|
||||
}
|
||||
if ((MSTP_Receive_Packet.preamble2 == false) &&
|
||||
(MSTP_Receive_Packet.preamble1 == true)) {
|
||||
if (data_register == 0xFF) {
|
||||
MSTP_Receive_Packet.preamble2 = true;
|
||||
MSTP_Receive_Packet.index = 0;
|
||||
MSTP_Receive_Packet.data_len = 0;
|
||||
} else if (data_register == 0x55) {
|
||||
/* repeated preamble1 */
|
||||
return;
|
||||
} else {
|
||||
MSTP_Receive_Packet.preamble1 = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (DataLength == 0) {
|
||||
MSTP_Receive_Packet.header[MSTP_Receive_Packet.index] = data_register;
|
||||
if (MSTP_Receive_Packet.index == 5) {
|
||||
if (crc_header_good(MSTP_Receive_Packet.header, 6)) {
|
||||
FrameType = MSTP_Receive_Packet.header[0];
|
||||
DestinationAddress = MSTP_Receive_Packet.header[1];
|
||||
SourceAddress = MSTP_Receive_Packet.header[2];
|
||||
DataLength = (MSTP_Receive_Packet.header[3] * 256) +
|
||||
MSTP_Receive_Packet.header[4];
|
||||
if (DataLength == 0) {
|
||||
MSTP_Receive_Packet.valid_frame = true;
|
||||
} else {
|
||||
MSTP_Receive_Packet.index = 0;
|
||||
}
|
||||
} else {
|
||||
MSTP_Receive_Packet.preamble2 = false;
|
||||
MSTP_Receive_Packet.preamble2 = false;
|
||||
MSTP_Receive_Packet.index = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MSTP_Receive_Packet.buffer[MSTP_Receive_Packet.index] = data_register;
|
||||
if (packet_info->index == packet_info->len) {
|
||||
/* PDU length ended */
|
||||
packet_info->ready = true;
|
||||
} else if (packet_info->index >= sizeof(packet_info->buffer)){
|
||||
/* exceeded the size of the storage */
|
||||
packet_info->len = packet_info->index;
|
||||
packet_info->ready = true;
|
||||
} else {
|
||||
packet_info->index++;
|
||||
}
|
||||
MSTP_Receive_Packet.index++;
|
||||
if (packet_info->ready) {
|
||||
/* validate the CRC */
|
||||
if (!lrc_packet_good(packet_info->buffer,packet_info->len+1)) {
|
||||
packet_info->ready = false;
|
||||
}
|
||||
/* pull off the CRC */
|
||||
packet_info->crc = packet_info->buffer[packet_info->len];
|
||||
/* get ready for the next packet */
|
||||
packet_info->index = 0;
|
||||
packet_info->preamble1 = false;
|
||||
packet_info->preamble2 = false;
|
||||
led_setup_off();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ReceivePreamble1 == true) {
|
||||
if (timer_silence_elapsed(Tframe_abort)) {
|
||||
/* we've been busy too long! Abort packet! */
|
||||
Index = 0;
|
||||
ReceivePreamble1 = false;
|
||||
ReceivePreamble2 = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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 uint8_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_ReceiveError()) {
|
||||
/* EatAnError */
|
||||
timer_silence_reset();
|
||||
} else if (RS485_DataAvailable(&DataRegister)) {
|
||||
timer_silence_reset();
|
||||
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_silence_elapsed(Tframe_abort)) {
|
||||
/* Timeout */
|
||||
/* a correct preamble has not been received */
|
||||
/* wait for the start of a frame. */
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_ReceiveError()) {
|
||||
/* Error */
|
||||
timer_silence_reset();
|
||||
/* wait for the start of a frame. */
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_DataAvailable(&DataRegister)) {
|
||||
timer_silence_reset();
|
||||
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_silence_elapsed(Tframe_abort)) {
|
||||
/* Timeout */
|
||||
/* indicate that an error has occurred during the reception of a frame */
|
||||
MSTP_Flag.ReceivedInvalidFrame = true;
|
||||
/* wait for the start of a frame. */
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_ReceiveError()) {
|
||||
/* Error */
|
||||
timer_silence_reset();
|
||||
/* 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_DataAvailable(&DataRegister)) {
|
||||
timer_silence_reset();
|
||||
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_silence_elapsed(Tframe_abort)) {
|
||||
/* Timeout */
|
||||
/* indicate that an error has occurred during the reception of a frame */
|
||||
MSTP_Flag.ReceivedInvalidFrame = true;
|
||||
/* wait for the start of the next frame. */
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_ReceiveError()) {
|
||||
/* Error */
|
||||
timer_silence_reset();
|
||||
/* 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_DataAvailable(&DataRegister)) {
|
||||
timer_silence_reset();
|
||||
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;
|
||||
}
|
||||
|
||||
static void MSTP_Slave_Node_FSM(
|
||||
void)
|
||||
{
|
||||
if (MSTP_Flag.ReceivedValidFrame) {
|
||||
MSTP_Flag.ReceivedValidFrame = false;
|
||||
switch (FrameType) {
|
||||
case FRAME_TYPE_TOKEN:
|
||||
break;
|
||||
case FRAME_TYPE_POLL_FOR_MASTER:
|
||||
break;
|
||||
case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
|
||||
break;
|
||||
case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
|
||||
/* indicate successful reception to the higher layers */
|
||||
MSTP_Flag.ReceivePacketPending = true;
|
||||
break;
|
||||
case FRAME_TYPE_TEST_REQUEST:
|
||||
MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE,
|
||||
SourceAddress, This_Station, &InputBuffer[0],
|
||||
DataLength);
|
||||
break;
|
||||
case FRAME_TYPE_TEST_RESPONSE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (MSTP_Flag.TransmitPacketPending) {
|
||||
/* 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. */
|
||||
/* Note: optimized such that we are never a client */
|
||||
MSTP_Send_Frame(FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY,
|
||||
TransmitPacketDest, This_Station,
|
||||
(uint8_t *) & TransmitPacket[0], TransmitPacketLen);
|
||||
MSTP_Flag.TransmitPacketPending = false;
|
||||
MSTP_Flag.ReceivePacketPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* dummy - unused parameter */
|
||||
timeout = timeout;
|
||||
/* 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) &&
|
||||
(MSTP_Flag.ReceivePacketPending == 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) {
|
||||
MSTP_Slave_Node_FSM();
|
||||
}
|
||||
/* 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); */
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t dlmstp_mac_address(
|
||||
void)
|
||||
{
|
||||
return This_Station;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#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"
|
||||
#if MAX_ANALOG_VALUES
|
||||
#include "av.h"
|
||||
#endif
|
||||
#if MAX_BINARY_VALUES
|
||||
#include "bv.h"
|
||||
#endif
|
||||
|
||||
/* Encodes the property APDU and returns the length,
|
||||
or sets the error, and returns -1 */
|
||||
int Encode_Property_APDU(
|
||||
uint8_t * apdu,
|
||||
BACNET_READ_PROPERTY_DATA * rp_data,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
int apdu_len = -1;
|
||||
|
||||
/* handle each object type */
|
||||
switch (rp_data->object_type) {
|
||||
case OBJECT_DEVICE:
|
||||
if (Device_Valid_Object_Instance_Number(rp_data->object_instance)) {
|
||||
apdu_len =
|
||||
Device_Encode_Property_APDU(&apdu[0],
|
||||
rp_data->object_property, rp_data->array_index,
|
||||
error_class, error_code);
|
||||
}
|
||||
break;
|
||||
#if MAX_ANALOG_VALUES
|
||||
case OBJECT_ANALOG_VALUE:
|
||||
if (Analog_Value_Valid_Instance(rp_data->object_instance)) {
|
||||
apdu_len =
|
||||
Analog_Value_Encode_Property_APDU(&apdu[0],
|
||||
rp_data->object_instance, rp_data->object_property,
|
||||
rp_data->array_index, error_class, error_code);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if MAX_BINARY_VALUES
|
||||
case OBJECT_BINARY_VALUE:
|
||||
if (Binary_Value_Valid_Instance(rp_data->object_instance)) {
|
||||
apdu_len =
|
||||
Binary_Value_Encode_Property_APDU(&apdu[0],
|
||||
rp_data->object_instance, rp_data->object_property,
|
||||
rp_data->array_index, error_class, error_code);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
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 ack_len = 0;
|
||||
int property_len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
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 */
|
||||
ack_len =
|
||||
rp_ack_encode_apdu_init(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
/* FIXME: add buffer len as passed into function or use smart buffer */
|
||||
property_len =
|
||||
Encode_Property_APDU(&Handler_Transmit_Buffer[pdu_len + ack_len],
|
||||
&data, &error_class, &error_code);
|
||||
if (property_len >= 0) {
|
||||
len =
|
||||
rp_ack_encode_apdu_object_property_end(&Handler_Transmit_Buffer
|
||||
[pdu_len + property_len + ack_len]);
|
||||
len += ack_len + property_len;
|
||||
} else {
|
||||
switch (property_len) {
|
||||
/* BACnet APDU too small to fit data, so proper response is Abort */
|
||||
case -2:
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
break;
|
||||
default:
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
error_class, error_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
RP_ABORT:
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "whois.h"
|
||||
#include "iam.h"
|
||||
#include "device.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
|
||||
bool Send_I_Am = true;
|
||||
|
||||
void handler_who_is(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
int len = 0;
|
||||
int32_t low_limit = 0;
|
||||
int32_t high_limit = 0;
|
||||
int32_t target_device;
|
||||
|
||||
(void) src;
|
||||
len =
|
||||
whois_decode_service_request(service_request, service_len, &low_limit,
|
||||
&high_limit);
|
||||
if (len == 0) {
|
||||
Send_I_Am = true;
|
||||
} else if (len != -1) {
|
||||
/* is my device id within the limits? */
|
||||
target_device = Device_Object_Instance_Number();
|
||||
if (((target_device >= low_limit) && (target_device <= high_limit))
|
||||
||
|
||||
/* BACnet wildcard is the max instance number - everyone responds */
|
||||
((BACNET_MAX_INSTANCE >= (uint32_t) low_limit) &&
|
||||
(BACNET_MAX_INSTANCE <= (uint32_t) high_limit))) {
|
||||
Send_I_Am = true;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#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 "av.h"
|
||||
#include "bv.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;
|
||||
|
||||
/* decode the service request only */
|
||||
len = wp_decode_service_request(service_request, service_len, &wp_data);
|
||||
/* 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);
|
||||
/* 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);
|
||||
} else if (service_data->segmented_message) {
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
} else {
|
||||
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_VALUE:
|
||||
if (Analog_Value_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_BINARY_VALUE:
|
||||
if (Binary_Value_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;
|
||||
}
|
||||
}
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 7372800UL
|
||||
#endif
|
||||
|
||||
#if defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ASM__)
|
||||
#include <iom168.h>
|
||||
#else
|
||||
#if !defined(__AVR_ATmega168__)
|
||||
#error Firmware is configured for ATmega168 only (-mmcu=atmega168)
|
||||
#endif
|
||||
#endif
|
||||
#include "iar2gcc.h"
|
||||
#include "avr035.h"
|
||||
|
||||
#define LED_NPDU_INIT() BIT_SET(DDRD, DDD5)
|
||||
#define LED_NPDU_ON() BIT_CLEAR(PORTD, PD5)
|
||||
#define LED_NPDU_OFF() BIT_SET(PORTD, PD5)
|
||||
/* #define LED_NPDU PORTD_Bit5 */
|
||||
/* #define LED_NPDU_OFF() {LED_NPDU = false;} */
|
||||
/* #define LED_NPDU_ON() {LED_NPDU = true;} */
|
||||
|
||||
#define LED_GREEN_INIT() BIT_SET(DDRD, DDD4)
|
||||
#define LED_GREEN_ON() BIT_CLEAR(PORTD, PD4)
|
||||
#define LED_GREEN_OFF() BIT_SET(PORTD, PD4)
|
||||
|
||||
#endif
|
||||
Binary file not shown.
@@ -0,0 +1,226 @@
|
||||
/*####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(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ASM__)
|
||||
#include <inavr.h>
|
||||
#include <ioavr.h>
|
||||
/* BitValue is used alot in GCC examples */
|
||||
#define _BV(bit_num) (1 << (bit_num))
|
||||
|
||||
/* 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 <avr/io.h>
|
||||
|
||||
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 <avr/interrupt.h>
|
||||
#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 <avr/eeprom.h>
|
||||
#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
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
#include "timer.h"
|
||||
#include "rs485.h"
|
||||
#include "datalink.h"
|
||||
#include "npdu.h"
|
||||
#include "txbuf.h"
|
||||
#include "iam.h"
|
||||
#include "device.h"
|
||||
#include "av.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/* local version override */
|
||||
const char *BACnet_Version = "1.0";
|
||||
|
||||
/* For porting to IAR, see:
|
||||
http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC/IarToAvrgcc*/
|
||||
|
||||
/* dummy function - so we can use default demo handlers */
|
||||
bool dcc_communication_enabled(
|
||||
void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void init(
|
||||
void)
|
||||
{
|
||||
/* FIXME: Initialize the Clock Prescaler for ATmega8 */
|
||||
#if defined(__AVR_ATmega168__)
|
||||
/* 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;
|
||||
#endif
|
||||
/* Initialize I/O ports */
|
||||
/* For Port DDRx (Data Direction) Input=0, Output=1 */
|
||||
/* For Port PORTx (Bit Value) TriState=0, High=1 */
|
||||
DDRB = 0;
|
||||
PORTB = 0;
|
||||
DDRC = 0;
|
||||
PORTC = 0;
|
||||
DDRD = 0;
|
||||
PORTD = 0;
|
||||
|
||||
/* Configure the watchdog timer - Disabled for testing */
|
||||
#if defined(__AVR_ATmega168__)
|
||||
BIT_CLEAR(MCUSR, WDRF);
|
||||
WDTCSR = 0;
|
||||
#else
|
||||
BIT_CLEAR(MCUCSR, WDRF);
|
||||
WDTCR = 0;
|
||||
#endif
|
||||
|
||||
/* Configure Specialized Hardware */
|
||||
RS485_Initialize();
|
||||
RS485_Set_Baud_Rate(38400);
|
||||
/* Configure Timer0 for millisecond timer */
|
||||
timer_init();
|
||||
/* Enable global interrupts */
|
||||
__enable_interrupt();
|
||||
}
|
||||
|
||||
static uint8_t PDUBuffer[MAX_MPDU];
|
||||
|
||||
int main(
|
||||
void)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
BACNET_ADDRESS src; /* source address */
|
||||
|
||||
init();
|
||||
datalink_init(NULL);
|
||||
for (;;) {
|
||||
/* other tasks */
|
||||
/* BACnet handling */
|
||||
pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0);
|
||||
if (pdu_len) {
|
||||
npdu_handler(&src, &PDUBuffer[0], pdu_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
This port was originally done with the Atmel ATmega168
|
||||
I used the following tools:
|
||||
1. The WinAVR compiler avr-gcc (GCC) 4.1.2 (WinAVR 20070525)
|
||||
and tools from <http://winavr.sourceforge.net/>, hints and
|
||||
sample code from <http://www.avrfreaks.net/> and
|
||||
<http://savannah.gnu.org/projects/avr-libc/>.
|
||||
"avr-binutils, avr-gcc, and avr-libc form the heart of the
|
||||
Free Software toolchain for the Atmel AVR microcontrollers."
|
||||
2. AVR Studio 4 from Atmel <http://atmel.com/>
|
||||
|
||||
The hardware is expected to utilize the signals as defined
|
||||
in the spreadsheet hardware.ods (OpenOffice.org calc).
|
||||
Attach a DS75176 RS-485 transceiver (or similar) to the USART.
|
||||
DS75176 ATmega168
|
||||
------ ---------
|
||||
RO RXD
|
||||
/RE --choice of I/O
|
||||
DE --choice of I/O
|
||||
DI TXD
|
||||
GND GND
|
||||
DO --to RS-485 wire
|
||||
DO --to RS-485 wire
|
||||
+5V From 5V Regulator
|
||||
|
||||
The makefile allows you to build a simple server.
|
||||
dlmstp is the datalink layer for MS/TP over RS-485.
|
||||
This project uses an MS/TP Slave Node.
|
||||
|
||||
I used the makefile from the command line on Windows:
|
||||
C:\code\bacnet-stack\ports\atmega168> make clean all
|
||||
|
||||
The BACnet Capabilities include ReadProperty support.
|
||||
The BACnet objects include only a Device object.
|
||||
All required object properties can be retrieved using ReadProperty.
|
||||
|
||||
With full optimization, the statistics on the demo are:
|
||||
|
||||
avr-gcc (GCC) 4.2.2 (WinAVR 20071221rc1)
|
||||
Device: atmega168
|
||||
Program: 8734 bytes (53.3% Full)
|
||||
Data: 254 bytes (24.8% Full) (does not include CStack)
|
||||
|
||||
Hopefully you find this code useful!
|
||||
|
||||
Steve Karg <skarg@users.sourceforge.net>
|
||||
@@ -0,0 +1,292 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* The module handles sending data out the RS-485 port */
|
||||
/* and handles receiving data from the RS-485 port. */
|
||||
/* Customize this file for your specific hardware */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
/*#include "mstp.h" */
|
||||
|
||||
/* This file has been customized for use with ATMEGA168 */
|
||||
#if defined(__AVR_ATmega168__)
|
||||
/* USART defines for RS-485 port */
|
||||
#define UCSRB UCSR0B
|
||||
#define TXEN TXEN0
|
||||
#define RXEN RXEN0
|
||||
#define UCSRC UCSR0C
|
||||
#define UCSZ1 UCSZ01
|
||||
#define UCSZ0 UCSZ00
|
||||
#define UCSRA UCSR0A
|
||||
#define U2X U2X0
|
||||
#define UBRRL UBRR0
|
||||
#define UCSRA UCSR0A
|
||||
#define UDRE UDRE0
|
||||
#define UDR UDR0
|
||||
#define TXC TXC0
|
||||
#define FE FE0
|
||||
#define DOR DOR0
|
||||
#define UPE UPE0
|
||||
#define DOR DOR0
|
||||
#define RXC RXC0
|
||||
#endif
|
||||
|
||||
#include "hardware.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* baud rate */
|
||||
static uint32_t RS485_Baud;
|
||||
|
||||
/* 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)/RS485_Baud; */
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in
|
||||
* receive mode.
|
||||
* RETURN: none
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
void RS485_Initialize(
|
||||
void)
|
||||
{
|
||||
/* enable Transmit and Receive */
|
||||
UCSRB = _BV(TXEN) | _BV(RXEN);
|
||||
|
||||
/* 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. */
|
||||
UCSRC = _BV(UCSZ1) | _BV(UCSZ0);
|
||||
#if defined(__AVR_ATmega168__)
|
||||
/* Clear Power Reduction USART0 */
|
||||
BIT_CLEAR(PRR, PRUSART0);
|
||||
#endif
|
||||
/* Use port PD2 for RTS - enable and disable of Transceiver Tx/Rx */
|
||||
/* Set port bit as Output - initially receiving */
|
||||
BIT_CLEAR(PORTD, PD2);
|
||||
BIT_SET(DDRD, DDD2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Returns the baud rate that we are currently running at
|
||||
* RETURN: none
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
uint32_t RS485_Get_Baud_Rate(
|
||||
void)
|
||||
{
|
||||
return RS485_Baud;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Sets the baud rate for the chip USART
|
||||
* RETURN: true if valid baud rate
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
bool RS485_Set_Baud_Rate(
|
||||
uint32_t baud)
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
switch (baud) {
|
||||
case 9600:
|
||||
case 19200:
|
||||
case 38400:
|
||||
case 57600:
|
||||
case 76800:
|
||||
case 115200:
|
||||
RS485_Baud = baud;
|
||||
/* 2x speed mode */
|
||||
BIT_SET(UCSRA, U2X);
|
||||
/* configure baud rate */
|
||||
UBRRL = (F_CPU / (8UL * RS485_Baud)) - 1;
|
||||
/* FIXME: store the baud rate */
|
||||
break;
|
||||
default:
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Waits on the SilenceTimer for 40 bits.
|
||||
* RETURN: none
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
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) / RS485_Baud);
|
||||
while (!timer_silence_elapsed(turnaround_time)) {
|
||||
/* do nothing - wait for timer to increment */
|
||||
};
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Enable or disable the transmitter
|
||||
* RETURN: none
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
void RS485_Transmitter_Enable(
|
||||
bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
BIT_SET(PORTD, PD2);
|
||||
} else {
|
||||
BIT_CLEAR(PORTD, PD2);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Send some data and wait until it is sent
|
||||
* RETURN: none
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
void RS485_Send_Data(
|
||||
uint8_t * buffer, /* data to send */
|
||||
uint16_t nbytes)
|
||||
{ /* number of bytes of data */
|
||||
while (nbytes) {
|
||||
while (!BIT_CHECK(UCSRA, UDRE)) {
|
||||
/* do nothing - wait until Tx buffer is empty */
|
||||
}
|
||||
/* Send the data byte */
|
||||
UDR = *buffer;
|
||||
buffer++;
|
||||
nbytes--;
|
||||
}
|
||||
/* was the frame sent? */
|
||||
while (!BIT_CHECK(UCSRA, TXC)) {
|
||||
/* 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(UCSRA, TXC);
|
||||
/* per MSTP spec, sort of */
|
||||
timer_silence_reset();
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Return true if a framing or overrun error is present
|
||||
* RETURN: true if error
|
||||
* ALGORITHM: autobaud - if there are a lot of errors, switch baud rate
|
||||
* NOTES: Clears any error flags.
|
||||
*****************************************************************************/
|
||||
bool RS485_ReceiveError(
|
||||
void)
|
||||
{
|
||||
bool ReceiveError = false;
|
||||
uint8_t dummy_data;
|
||||
|
||||
/* check for framing error */
|
||||
#if 0
|
||||
if (BIT_CHECK(UCSRA, FE0)) {
|
||||
/* FIXME: how do I clear the error flags? */
|
||||
BITMASK_CLEAR(UCSRA, (_BV(FE) | _BV(DOR) | _BV(UPE)));
|
||||
ReceiveError = true;
|
||||
}
|
||||
#endif
|
||||
/* check for overrun error */
|
||||
if (BIT_CHECK(UCSRA, DOR)) {
|
||||
/* flush the receive buffer */
|
||||
do {
|
||||
dummy_data = UDR;
|
||||
} while (BIT_CHECK(UCSRA, RXC));
|
||||
ReceiveError = true;
|
||||
}
|
||||
|
||||
return ReceiveError;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Return true if data is available
|
||||
* RETURN: true if data is available, with the data in the parameter set
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
bool RS485_DataAvailable(
|
||||
uint8_t * data)
|
||||
{
|
||||
bool DataAvailable = false;
|
||||
|
||||
/* check for data */
|
||||
if (BIT_CHECK(UCSRA, RXC)) {
|
||||
*data = UDR;
|
||||
DataAvailable = true;
|
||||
}
|
||||
|
||||
return DataAvailable;
|
||||
}
|
||||
|
||||
#ifdef TEST_RS485
|
||||
int main(
|
||||
void)
|
||||
{
|
||||
unsigned i = 0;
|
||||
uint8_t DataRegister;
|
||||
|
||||
RS485_Set_Baud_Rate(38400);
|
||||
RS485_Initialize();
|
||||
/* receive task */
|
||||
for (;;) {
|
||||
if (RS485_ReceiveError()) {
|
||||
fprintf(stderr, "ERROR ");
|
||||
} else if (RS485_DataAvailable(&DataRegister)) {
|
||||
fprintf(stderr, "%02X ", DataRegister);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* TEST_RS485 */
|
||||
@@ -0,0 +1,70 @@
|
||||
/*####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 <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void RS485_Initialize(
|
||||
void);
|
||||
|
||||
void RS485_Transmitter_Enable(
|
||||
bool enable);
|
||||
|
||||
void RS485_Send_Data(
|
||||
uint8_t * buffer, /* data to send */
|
||||
uint16_t nbytes); /* number of bytes of data */
|
||||
|
||||
bool RS485_ReceiveError(
|
||||
void);
|
||||
bool RS485_DataAvailable(
|
||||
uint8_t * data);
|
||||
|
||||
void RS485_Turnaround_Delay(
|
||||
void);
|
||||
uint32_t RS485_Get_Baud_Rate(
|
||||
void);
|
||||
bool RS485_Set_Baud_Rate(
|
||||
uint32_t baud);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef STDBOOL_H
|
||||
#define STDBOOL_H
|
||||
|
||||
/* C99 Boolean types for compilers without C99 support */
|
||||
|
||||
#ifndef __cplusplus
|
||||
/* typedef char _Bool; */
|
||||
#ifndef bool
|
||||
#define bool _Bool
|
||||
#endif
|
||||
#ifndef true
|
||||
#define true 1
|
||||
#endif
|
||||
#ifndef false
|
||||
#define false 0
|
||||
#endif
|
||||
#define __bool_true_false_are_defined 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
/* Defines the standard integer types that are used in code */
|
||||
|
||||
#ifndef STDINT_H
|
||||
#define STDINT_H 1
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef unsigned char uint8_t; /* 1 byte 0 to 255 */
|
||||
typedef signed char int8_t; /* 1 byte -127 to 127 */
|
||||
typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */
|
||||
typedef signed short int16_t; /* 2 bytes -32767 to 32767 */
|
||||
typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */
|
||||
typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */
|
||||
|
||||
#endif /* STDINT_H */
|
||||
@@ -0,0 +1,109 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
|
||||
/* This module is a 1 millisecond timer */
|
||||
|
||||
/* Prescaling: 1, 8, 64, 256, 1024 */
|
||||
#define TIMER_PRESCALER 64
|
||||
/* Count: Timer0 counts up to 0xFF and then signals overflow */
|
||||
#define TIMER_TICKS (F_CPU/TIMER_PRESCALER/1000)
|
||||
#if (TIMER_TICKS > 0xFF)
|
||||
#error Timer Prescaler value is too small
|
||||
#endif
|
||||
#define TIMER_COUNT (0xFF-TIMER_TICKS)
|
||||
/* millisecond timer count */
|
||||
static volatile uint16_t Timer_Silence;
|
||||
|
||||
/* FIXME: Configure the Timer */
|
||||
void timer_init(
|
||||
void)
|
||||
{
|
||||
/* Normal Operation */
|
||||
TCCR1A = 0;
|
||||
/* CSn2 CSn1 CSn0 Description
|
||||
---- ---- ---- -----------
|
||||
0 0 0 No Clock Source
|
||||
0 0 1 No prescaling
|
||||
0 1 0 CLKio/8
|
||||
0 1 1 CLKio/64
|
||||
1 0 0 CLKio/256
|
||||
1 0 1 CLKio/1024
|
||||
1 1 0 Falling Edge of T0 (external)
|
||||
1 1 1 Rising Edge of T0 (external)
|
||||
*/
|
||||
#if defined(__AVR_ATmega168__)
|
||||
TCCR0B = _BV(CS01) | _BV(CS00);
|
||||
/* Clear any TOV1 Flag set when the timer overflowed */
|
||||
BIT_CLEAR(TIFR0, TOV0);
|
||||
/* Initial value */
|
||||
TCNT0 = TIMER_COUNT;
|
||||
/* Enable the overflow interrupt */
|
||||
BIT_SET(TIMSK0, TOIE0);
|
||||
/* Clear the Power Reduction Timer/Counter0 */
|
||||
BIT_CLEAR(PRR, PRTIM0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Timer interupt */
|
||||
/* note: Global interupts must be enabled - sei() */
|
||||
/* Timer Overflowed! Increment the time. */
|
||||
ISR(TIMER0_OVF_vect)
|
||||
{
|
||||
/* Set the counter for the next interrupt */
|
||||
TCNT0 = TIMER_COUNT;
|
||||
/* Overflow Flag is automatically cleared */
|
||||
/* Update the global timer */
|
||||
Timer_Silence++;
|
||||
}
|
||||
|
||||
/* return true if time has expired */
|
||||
bool timer_silence_elapsed(
|
||||
uint16_t value)
|
||||
{
|
||||
bool status = false;
|
||||
uint8_t sreg;
|
||||
|
||||
sreg = SREG;
|
||||
__disable_interrupt();
|
||||
if (Timer_Silence >= value) {
|
||||
status = true;
|
||||
}
|
||||
SREG = sreg;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void timer_silence_reset(void)
|
||||
{
|
||||
uint8_t sreg;
|
||||
|
||||
sreg = SREG;
|
||||
__disable_interrupt();
|
||||
Timer_Silence = 0;
|
||||
SREG = sreg;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void timer_init(
|
||||
void);
|
||||
bool timer_silence_elapsed(
|
||||
uint16_t value);
|
||||
void timer_silence_reset(
|
||||
void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
Reference in New Issue
Block a user