diff --git a/bacnet-stack/bacenum.h b/bacnet-stack/bacenum.h index 0a7ac93b..2ea2464f 100644 --- a/bacnet-stack/bacenum.h +++ b/bacnet-stack/bacenum.h @@ -311,7 +311,8 @@ typedef enum { STATUS_OPERATIONAL_READ_ONLY = 1, STATUS_DOWNLOAD_REQUIRED = 2, STATUS_DOWNLOAD_IN_PROGRESS = 3, - STATUS_NON_OPERATIONAL = 4 + STATUS_NON_OPERATIONAL = 4, + MAX_DEVICE_STATUS = 5 } BACNET_DEVICE_STATUS; typedef enum { @@ -1033,7 +1034,8 @@ typedef enum { REINITIALIZED_STATE_END_BACKUP = 3, REINITIALIZED_STATE_START_RESTORE = 4, REINITIALIZED_STATE_END_RESTORE = 5, - REINITIALIZED_STATE_ABORT_RESTORE = 6 + REINITIALIZED_STATE_ABORT_RESTORE = 6, + REINITIALIZED_STATE_IDLE = 255 } BACNET_REINITIALIZED_STATE_OF_DEVICE; typedef enum { diff --git a/bacnet-stack/datalink.c b/bacnet-stack/datalink.c deleted file mode 100644 index c25eeae9..00000000 --- a/bacnet-stack/datalink.c +++ /dev/null @@ -1,118 +0,0 @@ -/*####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####*/ -#include -#include -#include -#include "datalink.h" -/* currently this is oriented to a single data link */ -/* However, it could handle multiple data links with the */ -/* addition of passing a network number or datalink number */ -/* as part of the calls. */ - -/* returns number of bytes sent on success, negative on failure */ -int datalink_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 */ -#if defined(BACDL_ARCNET) - return arcnet_send_pdu(dest, npdu_data, pdu, pdu_len); -#elif defined(BACDL_MSTP) - return dlmstp_send_pdu(dest, npdu_data, pdu, pdu_len); -#elif defined(BACDL_ETHERNET) - return ethernet_send_pdu(dest, npdu_data, pdu, pdu_len); -#elif defined(BACDL_BIP) - return bip_send_pdu(dest, npdu_data, pdu, pdu_len); -#else - return 0; -#endif -} - -/* returns the number of octets in the PDU, or zero on failure */ -uint16_t datalink_receive(BACNET_ADDRESS * src, /* source address */ - uint8_t * pdu, /* PDU data */ - uint16_t max_pdu, /* amount of space available in the PDU */ - unsigned timeout) -{ /* number of milliseconds to wait for a packet */ -#if defined(BACDL_ARCNET) - return arcnet_receive(src, pdu, max_pdu, timeout); -#elif defined(BACDL_MSTP) - return dlmstp_receive(src, pdu, max_pdu, timeout); -#elif defined(BACDL_ETHERNET) - return ethernet_receive(src, pdu, max_pdu, timeout); -#elif defined(BACDL_BIP) - return bip_receive(src, pdu, max_pdu, timeout); -#else - return 0; -#endif -} - -void datalink_cleanup(void) -{ -#if defined(BACDL_ARCNET) - arcnet_cleanup(); -#elif defined(BACDL_MSTP) - dlmstp_cleanup(); -#elif defined(BACDL_ETHERNET) - ethernet_cleanup(); -#elif defined(BACDL_BIP) - bip_cleanup(); -#endif -} - -void datalink_get_broadcast_address(BACNET_ADDRESS * dest) -{ /* destination address */ -#if defined(BACDL_ARCNET) - arcnet_get_broadcast_address(dest); -#elif defined(BACDL_MSTP) - dlmstp_get_broadcast_address(dest); -#elif defined(BACDL_ETHERNET) - ethernet_get_broadcast_address(dest); -#elif defined(BACDL_BIP) - bip_get_broadcast_address(dest); -#endif -} - -void datalink_get_my_address(BACNET_ADDRESS * my_address) -{ -#if defined(BACDL_ARCNET) - arcnet_get_my_address(my_address); -#elif defined(BACDL_MSTP) - dlmstp_get_my_address(my_address); -#elif defined(BACDL_ETHERNET) - ethernet_get_my_address(my_address); -#elif defined(BACDL_BIP) - bip_get_my_address(my_address); -#endif -} diff --git a/bacnet-stack/datalink.h b/bacnet-stack/datalink.h index 4764749a..11a682ac 100644 --- a/bacnet-stack/datalink.h +++ b/bacnet-stack/datalink.h @@ -34,45 +34,42 @@ #ifndef DATALINK_H #define DATALINK_H -#include -#include -#include -#include "bacdef.h" -#include "npdu.h" - #if defined(BACDL_ETHERNET) #include "ethernet.h" + +#define datalink_send_pdu ethernet_send_pdu +#define datalink_receive ethernet_receive +#define datalink_cleanup ethernet_cleanup +#define datalink_get_broadcast_address ethernet_get_broadcast_address +#define datalink_get_my_address ethernet_get_my_address + #elif defined(BACDL_ARCNET) #include "arcnet.h" + +#define datalink_send_pdu arcnet_send_pdu +#define datalink_receive arcnet_receive +#define datalink_cleanup arcnet_cleanup +#define datalink_get_broadcast_address arcnet_get_broadcast_address +#define datalink_get_my_address arcnet_get_my_address + #elif defined(BACDL_MSTP) #include "dlmstp.h" + +#define datalink_send_pdu dlmstp_send_pdu +#define datalink_receive dlmstp_receive +#define datalink_cleanup dlmstp_cleanup +#define datalink_get_broadcast_address dlmstp_get_broadcast_address +#define datalink_get_my_address dlmstp_get_my_address + #elif defined(BACDL_BIP) #include "bip.h" + +#define datalink_send_pdu bip_send_pdu +#define datalink_receive bip_receive +#define datalink_cleanup bip_cleanup +#define datalink_get_broadcast_address bip_get_broadcast_address +#define datalink_get_my_address bip_get_my_address + #endif -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* returns number of bytes sent on success, negative on failure */ - int datalink_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 */ - -/* returns the number of octets in the PDU, or zero on failure */ - uint16_t datalink_receive(BACNET_ADDRESS * src, /* source address */ - uint8_t * pdu, /* PDU data */ - uint16_t max_pdu, /* amount of space available in the PDU */ - unsigned timeout); /* number of milliseconds to wait for a packet */ - - void datalink_cleanup(void); - - void datalink_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */ - - void datalink_get_my_address(BACNET_ADDRESS * my_address); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ #endif diff --git a/bacnet-stack/demo/handler/h_rp.c b/bacnet-stack/demo/handler/h_rp.c index 1832c767..b73496a5 100644 --- a/bacnet-stack/demo/handler/h_rp.c +++ b/bacnet-stack/demo/handler/h_rp.c @@ -54,7 +54,8 @@ static uint8_t Temp_Buf[MAX_APDU] = { 0 }; void handler_read_property(uint8_t * service_request, uint16_t service_len, - BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data) + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) { BACNET_READ_PROPERTY_DATA data; int len = 0; @@ -76,14 +77,15 @@ void handler_read_property(uint8_t * service_request, if (len <= 0) fprintf(stderr, "Unable to decode Read-Property Request!\n"); #endif - /* bad decoding - send an abort */ 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); #if PRINT_ENABLED fprintf(stderr, "Sending Abort!\n"); #endif } else 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); diff --git a/bacnet-stack/ports/pic18f6720/18F6720.lkr b/bacnet-stack/ports/pic18f6720/18F6720.lkr new file mode 100644 index 00000000..83e05781 --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/18F6720.lkr @@ -0,0 +1,38 @@ +// $Id: 18f6720.lkr,v 1.1 2003/12/16 14:53:08 GrosbaJ Exp $ +// File: 18f6720.lkr +// Sample linker script for the PIC18F6720 processor + +LIBPATH . + +FILES c018i.o +FILES clib.lib +FILES p18f6720.lib + +CODEPAGE NAME=vectors START=0x0 END=0x29 PROTECTED +CODEPAGE NAME=page START=0x2A END=0x1FFFF +CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED +CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED +CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED +CODEPAGE NAME=eedata START=0xF00000 END=0xF003FF PROTECTED + +ACCESSBANK NAME=accessram START=0x0 END=0x5F +DATABANK NAME=gpr0 START=0x60 END=0xFF +DATABANK NAME=gpr1 START=0x100 END=0x1FF +DATABANK NAME=gpr2 START=0x200 END=0x2FF +DATABANK NAME=gpr3 START=0x300 END=0x3FF +DATABANK NAME=gpr4 START=0x400 END=0x4FF +DATABANK NAME=gpr5 START=0x500 END=0x5FF +DATABANK NAME=gpr6 START=0x600 END=0x6FF +DATABANK NAME=gpr7 START=0x700 END=0x7FF +DATABANK NAME=gpr8 START=0x800 END=0x8FF +DATABANK NAME=gpr9 START=0x900 END=0x9FF +DATABANK NAME=gpr10 START=0xA00 END=0xAFF +DATABANK NAME=gpr11 START=0xB00 END=0xBFF +DATABANK NAME=gpr12 START=0xC00 END=0xCFF +DATABANK NAME=gpr13 START=0xD00 END=0xDFF +DATABANK NAME=gpr14 START=0xE00 END=0xEFF +ACCESSBANK NAME=accesssfr START=0xF60 END=0xFFF PROTECTED + +SECTION NAME=CONFIG ROM=config + +STACK SIZE=0x100 RAM=gpr14 diff --git a/bacnet-stack/ports/pic18f6720/BACnet-Server.mcp b/bacnet-stack/ports/pic18f6720/BACnet-Server.mcp new file mode 100644 index 00000000..e6f4337f --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/BACnet-Server.mcp @@ -0,0 +1,145 @@ +[HEADER] +magic_cookie={66E99B07-E706-4689-9E80-9B2582898A13} +file_version=1.0 +[PATH_INFO] +dir_src= +dir_bin= +dir_tmp= +dir_sin= +dir_inc=C:\code\bacnet-stack;C:\code\bacnet-stack\demo\handler;C:\code\bacnet-stack\demo\object;C:\code\bacnet-stack\ports\pic18 +dir_lib=C:\mcc18\lib +dir_lkr= +[CAT_FILTERS] +filter_src=*.asm;*.c +filter_inc=*.h;*.inc +filter_obj=*.o +filter_lib=*.lib +filter_lkr=*.lkr +[OTHER_FILES] +file_000=no +file_001=no +file_002=no +file_003=no +file_004=no +file_005=no +file_006=no +file_007=no +file_008=no +file_009=no +file_010=no +file_011=no +file_012=no +file_013=no +file_014=no +file_015=no +file_016=no +file_017=no +file_018=no +file_019=no +file_020=no +file_021=no +file_022=no +file_023=no +file_024=no +file_025=no +file_026=no +file_027=no +file_028=no +file_029=no +file_030=no +file_031=no +file_032=no +file_033=no +file_034=no +file_035=no +file_036=no +file_037=no +file_038=no +file_039=no +file_040=no +file_041=no +file_042=no +file_043=no +file_044=no +file_045=no +file_046=no +file_047=no +file_048=no +file_049=no +file_050=no +file_051=no +file_052=no +file_053=no +file_054=no +file_055=no +file_056=no +file_057=no +file_058=no +[FILE_INFO] +file_000=C:\code\bacnet-stack\abort.c +file_001=C:\code\bacnet-stack\apdu.c +file_002=C:\code\bacnet-stack\bacapp.c +file_003=C:\code\bacnet-stack\bacdcode.c +file_004=C:\code\bacnet-stack\bacerror.c +file_005=C:\code\bacnet-stack\bacstr.c +file_006=C:\code\bacnet-stack\crc.c +file_007=C:\code\bacnet-stack\dcc.c +file_008=C:\code\bacnet-stack\iam.c +file_009=C:\code\bacnet-stack\npdu.c +file_010=C:\code\bacnet-stack\rd.c +file_011=C:\code\bacnet-stack\reject.c +file_012=C:\code\bacnet-stack\rp.c +file_013=C:\code\bacnet-stack\whois.c +file_014=C:\code\bacnet-stack\demo\handler\h_dcc.c +file_015=C:\code\bacnet-stack\demo\handler\h_rd.c +file_016=main.c +file_017=dlmstp.c +file_018=device.c +file_019=rs485.c +file_020=isr.c +file_021=C:\code\bacnet-stack\datetime.c +file_022=C:\code\bacnet-stack\demo\handler\txbuf.c +file_023=C:\code\bacnet-stack\demo\handler\h_whois.c +file_024=mstp.c +file_025=C:\code\bacnet-stack\demo\handler\h_rp_tiny.c +file_026=C:\code\bacnet-stack\wp.h +file_027=C:\code\bacnet-stack\abort.h +file_028=C:\code\bacnet-stack\apdu.h +file_029=C:\code\bacnet-stack\bacapp.h +file_030=C:\code\bacnet-stack\bacdcode.h +file_031=C:\code\bacnet-stack\bacdef.h +file_032=C:\code\bacnet-stack\bacenum.h +file_033=C:\code\bacnet-stack\bacerror.h +file_034=C:\code\bacnet-stack\bacstr.h +file_035=C:\code\bacnet-stack\config.h +file_036=C:\code\bacnet-stack\crc.h +file_037=C:\code\bacnet-stack\dcc.h +file_038=C:\code\bacnet-stack\dlmstp.h +file_039=C:\code\bacnet-stack\iam.h +file_040=C:\code\bacnet-stack\npdu.h +file_041=C:\code\bacnet-stack\rd.h +file_042=C:\code\bacnet-stack\reject.h +file_043=C:\code\bacnet-stack\rp.h +file_044=C:\code\bacnet-stack\whois.h +file_045=C:\code\bacnet-stack\demo\handler\client.h +file_046=C:\code\bacnet-stack\demo\handler\handlers.h +file_047=C:\code\bacnet-stack\demo\object\ai.h +file_048=C:\code\bacnet-stack\demo\object\ao.h +file_049=C:\code\bacnet-stack\demo\object\device.h +file_050=stdbool.h +file_051=stdint.h +file_052=hardware.h +file_053=rs485.h +file_054=C:\code\bacnet-stack\datetime.h +file_055=datalink.h +file_056=C:\code\bacnet-stack\demo\handler\txbuf.h +file_057=mstp.h +file_058=18F6720.lkr +[SUITE_INFO] +suite_guid={5B7D72DD-9861-47BD-9F60-2BE967BF8416} +suite_state= +[TOOL_SETTINGS] +TS{DD2213A8-6310-47B1-8376-9430CDFC013F}= +TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}=/m"$(BINDIR_)$(TARGETBASE).map" /o"$(TARGETBASE).cof" +TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}=-DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DTSM_ENABLED=0 -DBIG_ENDIAN=0 -mL -Ls -Ou- -Ot- -Ob- -Op- -Or- -Od- -Opa- +TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}= diff --git a/bacnet-stack/ports/pic18f6720/BACnet-Server.mcw b/bacnet-stack/ports/pic18f6720/BACnet-Server.mcw new file mode 100644 index 00000000..347980f8 Binary files /dev/null and b/bacnet-stack/ports/pic18f6720/BACnet-Server.mcw differ diff --git a/bacnet-stack/ports/pic18f6720/device.c b/bacnet-stack/ports/pic18f6720/device.c new file mode 100644 index 00000000..7e37e924 --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/device.c @@ -0,0 +1,567 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include /* for memmove */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "device.h" /* me */ +#include "dlmstp.h" +#include "rs485.h" +#include "ai.h" +#include "bi.h" +#include "bv.h" +#include "wp.h" +#include "dcc.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 = 12345; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; + +BACNET_REINITIALIZED_STATE_OF_DEVICE Reinitialize_State = + REINITIALIZED_STATE_IDLE; + +void Device_Reinit(void) +{ + dcc_set_status_duration(COMMUNICATION_ENABLE,0); + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); +} + +void Device_Init(void) +{ + Reinitialize_State = REINITIALIZED_STATE_IDLE; + dcc_set_status_duration(COMMUNICATION_ENABLE,0); + /* FIXME: Get the data from the eeprom */ + /* I2C_Read_Block(EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number(void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number(uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) + { + Object_Instance_Number = object_id; + /* FIXME: Write the data to the eeprom */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ + } + else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number(uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return ((Object_Instance_Number == object_id) || + (object_id == BACNET_MAX_INSTANCE)); +} + +BACNET_DEVICE_STATUS Device_System_Status(void) +{ + return System_Status; +} + +void Device_Set_System_Status(BACNET_DEVICE_STATUS status) +{ + if (status < MAX_DEVICE_STATUS) + System_Status = status; +} + +/* FIXME: put your vendor ID here! */ +uint16_t Device_Vendor_Identifier(void) +{ + return 0; +} + +uint8_t Device_Protocol_Version(void) +{ + return 1; +} + +uint8_t Device_Protocol_Revision(void) +{ + return 5; +} + +/* FIXME: MAX_APDU is defined in config.ini - set it! */ +uint16_t Device_Max_APDU_Length_Accepted(void) +{ + return MAX_APDU; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported(void) +{ + return SEGMENTATION_NONE; +} + +uint16_t Device_APDU_Timeout(void) +{ + return 60000; +} + + +uint8_t Device_Number_Of_APDU_Retries(void) +{ + return 0; +} + +uint8_t Device_Database_Revision(void) +{ + return 0; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count(void) +{ + unsigned count = 1;/* at least 1 for device object */ + +/* FIXME: add objects as needed */ +#if 0 + count += Binary_Value_Count(); + count += Analog_Input_Count(); + count += Binary_Input_Count(); +#endif + + return count; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +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; + } + #if 0 + /* FIXME: add objects as needed */ + /* binary input objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + /* array index starts at 1, and 1 for the device object */ + object_index = array_index - 2; + 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; + } + } + /* analog input objects */ + if (!status) { + /* array index starts at 1, and 1 for the device object */ + object_index -= object_count; + object_count = Analog_Input_Count(); + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_INPUT; + *instance = Analog_Input_Index_To_Instance(object_index); + status = true; + } + } + /* binary input objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Binary_Input_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_INPUT; + *instance = Binary_Input_Index_To_Instance(object_index); + status = true; + } + } + #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; + BACNET_TIME local_time; + BACNET_DATE local_date; + uint8_t year = 0; + char string_buffer[24]; + int16_t TimeZone = 0; + + /* FIXME: change the hardcoded names to suit your application */ + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + (void)strcpypgm2ram(&string_buffer[0], "PIC18F6720 Device"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + (void)strcpypgm2ram(&string_buffer[0], "BACnet Demo"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = encode_tagged_enumerated(&apdu[0], Device_System_Status()); + break; + case PROP_VENDOR_NAME: + (void)strcpypgm2ram(&string_buffer[0], "ASHRAE"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = encode_tagged_unsigned(&apdu[0], Device_Vendor_Identifier()); + break; + case PROP_MODEL_NAME: + (void)strcpypgm2ram(&string_buffer[0], "GNU Demo"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + (void)strcpypgm2ram(&string_buffer[0], "1.00"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + (void)strcpypgm2ram(&string_buffer[0], "1.00"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + (void)strcpypgm2ram(&string_buffer[0], "USA"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Protocol_Revision()); + break; + /* BACnet Legacy Support */ + case PROP_PROTOCOL_CONFORMANCE_CLASS: + apdu_len = encode_tagged_unsigned(&apdu[0], 1); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported(i)); + } + apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* 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 0 + bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true); + bitstring_set_bit(&bit_string, OBJECT_ANALOG_INPUT, true); + bitstring_set_bit(&bit_string, OBJECT_BINARY_INPUT, true); + #endif + apdu_len = encode_tagged_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 == BACNET_ARRAY_LENGTH_INDEX) + apdu_len = encode_tagged_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_tagged_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } else { + /* error: internal error? */ + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_OTHER; + apdu_len = -1; + break; + } + } + } else { + if (Device_Object_List_Identifier(array_index, &object_type, + &instance)) + apdu_len = + encode_tagged_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_tagged_unsigned(&apdu[0], + Device_Max_APDU_Length_Accepted()); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = encode_tagged_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_tagged_unsigned(&apdu[0], Device_APDU_Timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Number_Of_APDU_Retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = encode_tagged_unsigned(&apdu[0], Device_Database_Revision()); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = encode_tagged_unsigned(&apdu[0], dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = encode_tagged_unsigned(&apdu[0], dlmstp_max_master()); + break; + case PROP_LOCAL_TIME: + /* FIXME: if you support time */ + local_time.hour = 0; + local_time.min = 0; + local_time.sec = 0; + local_time.hundredths = 0; + apdu_len = encode_tagged_time(&apdu[0], &local_time); + break; + case PROP_UTC_OFFSET: + /* Note: BACnet Time Zone is inverse of everybody else */ + apdu_len = encode_tagged_signed(&apdu[0], 5 /* EST */); + break; + case PROP_LOCAL_DATE: + /* FIXME: if you support date */ + local_date.year = 2006; /* AD */ + local_date.month = 4; /* Jan=1..Dec=12 */ + local_date.day = 11; /* 1..31 */ + local_date.wday = 0; /* 1=Mon..7=Sun */ + apdu_len = encode_tagged_date(&apdu[0], &local_date); + break; + case PROP_DAYLIGHT_SAVINGS_STATUS: + /* FIXME: if you support time/date */ + apdu_len = encode_tagged_boolean(&apdu[0], false); + break; + case 9600: + apdu_len = encode_tagged_unsigned(&apdu[0], RS485_Get_Baud_Rate()); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data( + wp_data->application_data, + wp_data->application_data_len, + &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_INFO_FRAMES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_MASTER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + dlmstp_set_max_master(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + uint8_t encoding; + size_t len; + + encoding = + characterstring_encoding(&value.type.Character_String); + len = characterstring_length(&value.type.Character_String); + if (encoding == CHARACTER_ANSI_X34) { + if (len <= 20) { + /* FIXME: set the name */ + /* Display_Set_Name( + characterstring_value(&value.type.Character_String)); */ + + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case 9600: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int > 115200) { + RS485_Set_Baud_Rate(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + diff --git a/bacnet-stack/ports/pic18f6720/dlmstp.c b/bacnet-stack/ports/pic18f6720/dlmstp.c new file mode 100644 index 00000000..e1676dee --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/dlmstp.c @@ -0,0 +1,370 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "bacdef.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" + +// Number of MS/TP Packets Rx/Tx +uint16_t MSTP_Packets = 0; + +/* receive buffer */ +#pragma udata MSTP_RxData +static DLMSTP_PACKET Receive_Buffer; +/* temp buffer for NPDU insertion */ +/* local MS/TP port data - shared with RS-485 */ +#pragma udata MSTP_PortData +volatile struct mstp_port_struct_t MSTP_Port; +#pragma udata + +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} + +// This defines the number of edit fields for this module +#define MAX_EDIT_FIELD 1 +static uint8_t EditField = 0; +/* ************************************************************************* + DESCRIPTION: This function handles incrementing or decrementing our + EditField + RETURN: none + ALGORITHM: none + NOTES: Pass a #>0 to increment #<0 to decrement + *************************************************************************** */ +void dlmstp_SetEditField( + signed char state) /* direction our editfield is moving */ +{ + if (state > 0) + { + if (++EditField > MAX_EDIT_FIELD) + EditField = 0; + } + else if (state < 0) + { + if (EditField) + EditField--; + else + EditField = MAX_EDIT_FIELD; + } + else + EditField = 0; +} + +/* ************************************************************************* + DESCRIPTION: Gets the current edit field for this module + RETURN: the current edit field + ALGORITHM: none + NOTES: none + *************************************************************************** */ +uint8_t dlmstp_GetEditField(void) +{ + return (EditField); +} + +void dlmstp_millisecond_timer(void) +{ + INCREMENT_AND_LIMIT_UINT16(MSTP_Port.SilenceTimer); +} + +void dlmstp_reinit(void) +{ + RS485_Reinit(); + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); +} + +void dlmstp_init(void) +{ + uint8_t data; + + /* initialize buffer */ + Receive_Buffer.ready = false; + Receive_Buffer.pdu_len = 0; + /* initialize hardware */ + RS485_Initialize(); + MSTP_Port.InputBuffer = &Receive_Buffer.pdu[0]; + MSTP_Init(&MSTP_Port); + /* FIXME: implement your data storage */ + data = 64; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAC_ADDR); */ + if (data <= 127) + MSTP_Port.This_Station = data; + else + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + /* FIXME: implement your data storage */ + data = 127; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + if ((data <= 127) && (data >= MSTP_Port.This_Station)) + MSTP_Port.Nmax_master = data; + else + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + /* FIXME: implement your data storage */ + data = 1; + /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + if (data >= 1) + MSTP_Port.Nmax_info_frames = data; + else + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); +} + +void dlmstp_cleanup(void) +{ + /* nothing to do for static buffers */ +} + +/* 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; + unsigned npdu_len = 0; + uint8_t frame_type = 0; + uint8_t destination = 0; /* destination address */ + BACNET_ADDRESS src; + unsigned i = 0; /* loop counter */ + + if (MSTP_Port.TxReady == false) { + if (npdu_data->data_expecting_reply) + MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + else + MSTP_Port.TxFrameType = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + + /* load destination MAC address */ + if (dest && dest->mac_len == 1) { + destination = dest->mac[0]; + } else { + return -2; + } + dlmstp_get_my_address(&src); + if ((8 /* header len */ + pdu_len) > MAX_MPDU) { + return -4; + } + bytes_sent = MSTP_Create_Frame( + (uint8_t *) & MSTP_Port.TxBuffer[0], + sizeof(MSTP_Port.TxBuffer), + MSTP_Port.TxFrameType, + destination, + MSTP_Port.This_Station, pdu, pdu_len); + MSTP_Port.TxLength = bytes_sent; + MSTP_Port.TxReady = true; + MSTP_Packets++; + } + + return bytes_sent; +} + +void dlmstp_task(void) +{ + uint8_t bytes_remaining; + bool received_frame; + + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedInvalidFrame == false)) + { + do { + bytes_remaining = RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + received_frame = MSTP_Port.ReceivedValidFrame || + MSTP_Port.ReceivedInvalidFrame; + if (received_frame) + break; + } while (bytes_remaining); + } + /* only do master state machine while rx is idle */ + if (MSTP_Port.receive_state == MSTP_RECEIVE_STATE_IDLE) { + while (MSTP_Master_Node_FSM(&MSTP_Port)) {}; + //MSTP_Master_Node_FSM(&MSTP_Port); + } + /* see if there is a packet available, and a place + to put the reply (if necessary) and process it */ + if (Receive_Buffer.ready && !MSTP_Port.TxReady) { + if (Receive_Buffer.pdu_len) { + MSTP_Packets++; + npdu_handler( + &Receive_Buffer.address, + &Receive_Buffer.pdu[0], + Receive_Buffer.pdu_len); + } + Receive_Buffer.ready = false; + } + + return; +} + +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; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t dlmstp_put_receive( + uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) /* amount of PDU data */ +{ + /* PDU is already in the Receive_Buffer */ + dlmstp_fill_bacnet_address(&Receive_Buffer.address, src); + Receive_Buffer.pdu_len = pdu_len; + Receive_Buffer.ready = true; +} + +void dlmstp_set_my_address(uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + MSTP_Port.This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > MSTP_Port.Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_my_address(void) +{ + return MSTP_Port.This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames(uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + MSTP_Port.Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +unsigned dlmstp_max_info_frames(void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master(uint8_t max_master) +{ + if (max_master <= 127) { + if (MSTP_Port.This_Station <= max_master) { + MSTP_Port.Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master(void) +{ + return MSTP_Port.Nmax_master; +} + +void dlmstp_get_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.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; /* len=0 denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/bacnet-stack/ports/pic18f6720/dlmstp.h b/bacnet-stack/ports/pic18f6720/dlmstp.h new file mode 100644 index 00000000..a57dba59 --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/dlmstp.h @@ -0,0 +1,110 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 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 DLMSTP_H +#define DLMSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* defines specific to MS/TP */ +#define MAX_HEADER (2+1+1+1+2+1+2+1) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +typedef struct dlmstp_packet { + bool ready; /* true if ready to be sent or received */ + BACNET_ADDRESS address; /* source address */ + uint8_t frame_type; /* type of message */ + unsigned pdu_len; /* packet length */ + uint8_t pdu[MAX_MPDU]; /* packet */ +} DLMSTP_PACKET; + +/* number of MS/TP tx/rx packets */ +extern uint16_t MSTP_Packets; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void dlmstp_reinit(void); + void dlmstp_init(void); + void dlmstp_cleanup(void); + void dlmstp_millisecond_timer(void); + void dlmstp_task(void); + + /* returns number of bytes sent on success, negative 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 */ + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + void dlmstp_set_max_info_frames(uint8_t max_info_frames); + unsigned dlmstp_max_info_frames(void); + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + void dlmstp_set_max_master(uint8_t max_master); + uint8_t dlmstp_max_master(void); + + /* MAC address for MS/TP */ + void dlmstp_set_my_address(uint8_t my_address); + uint8_t dlmstp_my_address(void); + + /* BACnet address used in datalink */ + void dlmstp_get_my_address(BACNET_ADDRESS * my_address); + void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */ + + /* MS/TP state machine functions */ + uint16_t dlmstp_put_receive(uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/pic18f6720/hardware.h b/bacnet-stack/ports/pic18f6720/hardware.h new file mode 100644 index 00000000..28f04dd4 --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/hardware.h @@ -0,0 +1,268 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +#include +#include +#include + +/* PORTA.0 Photocell Input PORTA.1 LED Row6 PORTA.2 LED Row5 PORTA.3 LED + * Row4 PORTA.4 Square Wave input from RTC PORTA.5 LCD RW PORTB.0 Zero + * Cross PORTB.1 USB RXF# PORTB.2 USB TXE# PORTB.3 Keypad Row Enable + * (74HC373 Output Control) PORTB.4 Keypad Row Gate (74HC373 Gate) + * PORTB.5 Switch Input Latch & Keypad Column Gate (74HC373 Gate) PORTB.6 + * ICD connection PORTB.7 ICD connection PORTC.0 Pilot Latch PORTC.1 + * Pilot Output Enable (low) PORTC.2 Piezo PORTC.3 I2C clock PORTC.4 I2C + * data PORTC.5 RS232 enable (low) PORTC.6 RS232 Tx PORTC.7 RS232 Rx + * PORTD.0 Data bus PORTD.1 Data bus PORTD.2 Data bus PORTD.3 Data bus + * PORTD.4 Data bus PORTD.5 Data bus PORTD.6 Data bus PORTD.7 Data bus + * PORTE.0 USB RD PORTE.1 USB WR PORTE.2 LCD RS PORTE.3 485 transmit + * enable PORTE.4 Relay data latch PORTE.5 Switch Input Clock PORTE.6 + * Switch Input High/Low PORTE.7 Switch Input Data PORTF.0 LED Row2 + * PORTF.1 LED Row1 PORTF.2 LED Col5 PORTF.3 LED Col4 PORTF.4 LED Col3 + * PORTF.5 LED Col2 PORTF.6 LED Col1 PORTF.7 LED Col0 PORTG.0 485 receive + * enable PORTG.1 485 Tx PORTG.2 485 Rx PORTG.3 LCD E PORTG.4 LED Row0 */ +#define LCD_BUSY PORTDbits.RD7 +#define LCD_E PORTGbits.RG3 +#define LCD_RW PORTAbits.RA5 +#define LCD_RS PORTEbits.RE2 +#define LCD_DATA PORTD +#define LCD_TRIS TRISD + +#define PILOT_LATCH PORTCbits.RC0 +#define PILOT_ENABLE PORTCbits.RC1 +#define PILOT_PORT PORTD +#define PILOT_PORT_TRIS TRISD + +#define PIEZO PORTCbits.RC2 +#define PIEZO_ON() TRISCbits.TRISC2 = 0 +#define PIEZO_OFF() TRISCbits.TRISC2 = 1 + +#define RS232_ENABLE PORTCbits.RC5 + +#define RS485_TX_ENABLE PORTEbits.RE3 +#define RS485_RX_DISABLE PORTGbits.RG0 + +#define SWITCH_LOAD PORTBbits.RB5 +#define SWITCH_CLK PORTEbits.RE5 +#define SWITCH_COM PORTEbits.RE6 +#define SWITCH_DATA PORTEbits.RE7 + +#define LEDPORT PORTF +#define LEDTRIS TRISF +#define LED_ROW1 PORTGbits.RG4 +#define LED_ROW2 PORTFbits.RF1 +#define LED_ROW3 PORTFbits.RF0 +#define LED_ROW4 PORTAbits.RA3 +#define LED_ROW5 PORTAbits.RA2 +#define LED_ROW6 PORTAbits.RA1 + +#define RELAY_PORT PORTD +#define RELAY_PORT_TRIS TRISD +#define RELAY_LATCH PORTEbits.RE4 + +#define KEYPAD_DATA PORTD +#define KEYPAD_TRIS TRISD +#define KEYPAD_COL_LATCH PORTBbits.RB5 +#define KEYPAD_ROW_ENABLE PORTBbits.RB3 +#define KEYPAD_ROW_LATCH PORTBbits.RB4 +#define KEYPAD_ROW1 0 b00000001 +#define KEYPAD_ROW2 0 b00000010 +#define KEYPAD_ROW3 0 b00000100 +#define KEYPAD_ROW4 0 b00001000 +#define KEYPAD_ROW5 0 b00010000 +#define KEYPAD_ROW6 0 b00100000 + +#define USB_RD_EMPTY PORTBbits.RB1 +#define USB_WR_FULL PORTBbits.RB2 +#define USB_RD PORTEbits.RE0 +#define USB_WR PORTEbits.RE1 +#define USB_PORT PORTD +#define USB_PORT_TRIS TRISD + +#define ZERO_CROSS PORTBbits.RB0 + +#define PORT_A_TRIS_MASK 0x11 /* 0b00010001 */ +#define PORT_B_TRIS_MASK 0xC7 /* 0b11000111 */ +#define PORT_C_TRIS_MASK 0x9C /* 0b10011100 */ +#define PORT_D_TRIS_MASK 0xFF /* 0b11111111 */ +#define PORT_E_TRIS_MASK 0x88 /* 0b10001000 */ +#define PORT_F_TRIS_MASK 0x00 /* 0b00000000 */ +#define PORT_G_TRIS_MASK 0x04 /* 0b00000100 */ + +#define TURN_OFF_COMPARATORS() CMCON = 0x07 + +#define SHORT_BEEP 50 +#define LONG_BEEP 250 + +#define CLICK() Hardware_Sound_Piezo(SHORT_BEEP); +#define BEEP() Hardware_Sound_Piezo(LONG_BEEP); + +typedef union +{ + struct + { + uint8_t:1; + uint8_t:1; + uint8_t Thursday : 1; + uint8_t Wednesday : 1; + uint8_t Tuesday : 1; + uint8_t Monday : 1; + uint8_t Program : 1; + uint8_t Run : 1; + + uint8_t:1; + uint8_t:1; + uint8_t Input1 : 1; + uint8_t Input2 : 1; + uint8_t Input3 : 1; + uint8_t Input4 : 1; + uint8_t Input5 : 1; + uint8_t Input6 : 1; + + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t Input7 : 1; + uint8_t:1; + uint8_t:1; + uint8_t Input8 : 1; + uint8_t Photocell : 1; + + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t Remote : 1; + uint8_t Relay8 : 1; + + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t Relay7 : 1; + uint8_t Relay6 : 1; + uint8_t Relay5 : 1; + uint8_t Relay4 : 1; + uint8_t Relay3 : 1; + + uint8_t:1; + uint8_t:1; + uint8_t Relay2 : 1; + uint8_t Relay1 : 1; + uint8_t Holiday : 1; + uint8_t Sunday : 1; + uint8_t Saturday : 1; + uint8_t Friday : 1; + }; + struct + { + uint8_t row1; + uint8_t row2; + uint8_t row3; + uint8_t row4; + uint8_t row5; + uint8_t row6; + }; +} LED_REGS; + +union SWITCH_REGS +{ + struct + { + uint8_t All_On : 1; + uint8_t All_Off : 1; + uint8_t Addr : 4; + uint8_t Pilot_Fault : 1; + uint8_t Master : 1; + }; + uint8_t Sw_Byte; +}; + +enum INT_STATE { INT_DISABLED, INT_ENABLED, INT_RESTORE }; + +#define RESTART_WDT() { _asm CLRWDT _endasm } + +/* ************************************************************************* + define ENABLE_GLOBAL_INT() INTCONbits.GIE = 1 £ + #define DISABLE_GLOBAL_INT() INTCONbits.GIE = 0 £ + #define ENABLE_PERIPHERAL_INT() INTCONbits.PEIE = 1 £ + #define DISABLE_PERIPHERAL_INT() INTCONbits.PEIE = 0 + *************************************************************************** */ +#define ENABLE_HIGH_INT() INTCONbits.GIE = 1 +#define DISABLE_HIGH_INT() INTCONbits.GIE = 0 + +#define ENABLE_LOW_INT() INTCONbits.PEIE = 1 +#define DISABLE_LOW_INT() INTCONbits.PEIE = 0 + +#define ENABLE_TIMER0_INT() INTCONbits.TMR0IE = 1 +#define DISABLE_TIMER0_INT() INTCONbits.TMR0IE = 0 + +#define ENABLE_TIMER2_INT() PIE1bits.TMR2IE = 1 +#define DISABLE_TIMER2_INT() PIE1bits.TMR2IE = 0 + +#define ENABLE_TIMER4_INT() PIE3bits.TMR4IE = 1 +#define DISABLE_TIMER4_INT() PIE3bits.TMR4IE = 0 + +#define ENABLE_CCP2_INT() PIE2bits.CCP2IE = 1 +#define DISABLE_CCP2_INT() PIE2bits.CCP2IE = 0 + +#define ENABLE_CCP1_INT() PIE1bits.CCP1IE = 1 +#define DISABLE_CCP1_INT() PIE1bits.CCP1IE = 0 + +#define ENABLE_ABUS_INT() PIE1bits.SSPIE = 1 +#define DISABLE_ABUS_INT() PIE1bits.SSPIE = 0 +#define CLEAR_ABUS_FLAG() PIR1bits.SSPIF = 0 + +#define SETUP_CCP1(x) CCP1CON = x +#define SETUP_CCP2(x) CCP2CON = x + +#define DISABLE_RX_INT() PIE1bits.RCIE = 0 +#define ENABLE_RX_INT() PIE1bits.RCIE = 1 + +#define DISABLE_TX_INT() PIE1bits.TXIE = 0 +#define ENABLE_TX_INT() PIE1bits.TXIE = 1 + +#if CLOCKSPEED == 20 +#define DELAY_US(x) { _asm \ + MOVLW x \ + LOOP: \ + NOP \ + NOP \ + DECFSZ WREG, 1, 0 \ + BRA LOOP \ + _endasm } +#endif + +/* Global Vars */ +extern volatile LED_REGS LEDS; +extern volatile LED_REGS Blink; +extern uint8_t Piezo_Timer; +extern volatile bool DataPortLocked; + +#endif /* HARDWARE_H */ + diff --git a/bacnet-stack/ports/pic18f6720/isr.c b/bacnet-stack/ports/pic18f6720/isr.c new file mode 100644 index 00000000..43e28bd2 --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/isr.c @@ -0,0 +1,210 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include "stdint.h" +#include "hardware.h" +#include "rs485.h" +#include "dlmstp.h" + +/* from main.c */ +extern volatile uint8_t System_Seconds; +extern volatile uint8_t Milliseconds; + +void InterruptHandlerHigh(void); +void InterruptHandlerLow(void); +void Interrupt_Timer2(void); +void Interrupt_Timer3(void); +void Interrupt_Timer4(void); +void Interrupt_USART_Rx(void); +void Interrupt_USART_Tx(void); +void Interrupt_CCP2(void); +void INT0_Interrupt(void); + +#pragma code InterruptVectorHigh = 0x308 +void InterruptVectorHigh (void) +{ + _asm goto InterruptHandlerHigh /* jump to interrupt routine */ + _endasm +} +#pragma code + +#pragma code InterruptVectorLow = 0x318 +void InterruptVectorLow (void) +{ + _asm goto InterruptHandlerLow /* jump to interrupt routine */ + _endasm +} +#pragma code + +#pragma interrupt InterruptHandlerHigh +void InterruptHandlerHigh (void) +{ + #if 0 + /* check for USART Rx int */ + if ((PIR1bits.RCIF) && (PIE1bits.RCIE)) + { + if ((RCSTA1bits.FERR) || (RCSTA1bits.OERR)) + { + Comstat.Rx_Bufferoverrun = TRUE; + PIE1bits.RC1IE = 0; /* Disable Interrupt on receipt */ + } + else if (Comstat.Rx_Bytes++ < RX_BUFFER_SIZE - 1) + { + Rx_Buffer[Comstat.RxHead++] = RCREG1; + + /* Stick a Null on the end to let us use str functions on our + * buffer */ + Rx_Buffer[Comstat.RxHead] = 0; + } + else + { + Comstat.Rx_Bufferoverrun = TRUE; + PIE1bits.RC1IE = 0; /* Disable Interrupt on receipt */ + } + } + #endif + + /* check for timer0 int */ + if ((INTCONbits.TMR0IF) && (INTCONbits.TMR0IE)) + { + INTCONbits.TMR0IF = 0; + System_Seconds++; + } +} + +#pragma interruptlow InterruptHandlerLow save = PROD, section(".tmpdata"), TABLAT, TBLPTR, section \ + ("MATH_DATA") + +void InterruptHandlerLow(void) +{ + /* check for timer2 int */ + if ((PIR1bits.TMR2IF) && (PIE1bits.TMR2IE)) + { + PIR1bits.TMR2IF = 0; + Interrupt_Timer2(); + } + + /* check for timer3 int */ + if ((PIR2bits.TMR3IF) && (PIE2bits.TMR3IE)) + { + PIR2bits.TMR3IF = 0; + Interrupt_Timer3(); + } + + /* check for timer4 int */ + if ((PIR3bits.TMR4IF) && (PIE3bits.TMR4IE)) + { + PIR3bits.TMR4IF = 0; + dlmstp_millisecond_timer(); + Interrupt_Timer4(); + } + + /* check for compare int */ + if ((PIR2bits.CCP2IF) && (PIE2bits.CCP2IE)) + { + PIR2bits.CCP2IF = 0; + Interrupt_CCP2(); + } + + /* check for USART Tx int */ + if ((PIR3bits.TX2IF) && (PIE3bits.TX2IE)) + { + RS485_Interrupt_Tx(); + } + + /* check for USART Rx int */ + if ((PIR3bits.RC2IF) && (PIE3bits.RC2IE)) + { + RS485_Interrupt_Rx(); + } + +/* Unused Interrupts + //check for timer1 int + if ((PIR1bits.TMR1IF) && (PIE1bits.TMR1IE)) + { + PIR1bits.TMR1IF = 0; + Interrupt_Timer1(); + } + + //check for compare int + if ((PIR1bits.CCP1IF) && (PIE1bits.CCP1IE)) + { + PIR1bits.CCP1IF = 0; + Interrupt_CCP1(); + } + + //check for compare int + if ((PIR3bits.CCP3IF) && (PIE3bits.CCP3IE)) + { + PIR3bits.CCP3IF = 0; + Interrupt_CCP3(); + } + + //check for compare int + if ((PIR3bits.CCP4IF) && (PIE3bits.CCP4IE)) + { + PIR3bits.CCP4IF = 0; + + Interrupt_CCP4(); + } + + //check for AD int + if ((PIR1bits.ADIF) && (PIE1bits.ADIE)) + { + PIR1bits.ADIF = 0; + Interrupt_ADC(); + } + + //check for MSSP int + if ((PIR1bits.SSPIF) && (PIE1bits.SSPIE)) + { + PIR1bits.SSPIF = 0; + Interrupt_SSP(); + } + +*/ +} + +void Interrupt_Timer2(void) +{ +} + +void Interrupt_Timer3(void) +{ +} + +/* Timer4 is set to go off every 1ms. This is our system tick */ +void Interrupt_Timer4(void) +{ + /* Milisecond is our system tick */ + if (Milliseconds < 0xFF) + ++Milliseconds; +} + +void Interrupt_CCP2(void) +{ + +} + diff --git a/bacnet-stack/ports/pic18f6720/main.c b/bacnet-stack/ports/pic18f6720/main.c new file mode 100644 index 00000000..65815673 --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/main.c @@ -0,0 +1,169 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include /* for memmove */ +#include +#include +#include +#include "stdint.h" +#include "hardware.h" +/* BACnet */ +#include "apdu.h" +#include "datalink.h" +#include "dcc.h" +#include "handlers.h" +#include "iam.h" +#include "txbuf.h" + +volatile uint8_t Milliseconds = 0; +volatile uint8_t System_Seconds = 0; +volatile uint8_t Zero_Cross_Timeout = 0; + +static void BACnet_Service_Handlers_Init(void) +{ + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, + handler_who_is); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + //apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + // handler_write_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + //apdu_set_unconfirmed_handler + // (SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + // handler_timesync_utc); + //apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + // handler_timesync); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler + (SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); +} + +void Reinitialize(void) +{ + uint8_t i; + char name = 0; + + _asm reset _endasm +} + +void Global_Int( + enum INT_STATE state) /* FIX ME: add comment */ +{ + static uint8_t intstate = 0; + + switch (state) + { + case INT_DISABLED: + intstate >>= 2; + intstate |= (INTCON & 0xC0); + break; + case INT_ENABLED: + INTCONbits.GIE = 1; + INTCONbits.PEIE = 1; + intstate <<= 2; + break; + case INT_RESTORE: + INTCON |= (intstate & 0xC0); + intstate <<= 2; + break; + default: + break; + } +} + +void Initialize_Variables(void) +{ + /* Check to see if we need to initialize our eeproms */ + ENABLE_TIMER4_INT(); + /* interrupts must be enabled before we read our inputs */ + Global_Int(INT_ENABLED); + BACnet_Service_Handlers_Init(); + dlmstp_init(); + /* Start our time from now */ + Milliseconds = 0; + System_Seconds = 0; +} + +void Verify_Ints(void) +{ + /* Make sure the Abus data and clock lines are inputs. Also make sure + * that global interrupts are enabled. */ + if (!TRISCbits.TRISC3) + TRISCbits.TRISC3 = 1; + if (!TRISCbits.TRISC4) + TRISCbits.TRISC4 = 1; + if (!INTCONbits.GIE) + INTCONbits.GIE = 1; + if (!INTCONbits.PEIE) + INTCONbits.PEIE = 1; + /* if (!INTCONbits.INT0E) £ + * INTCONbits.INT0E=1; */ + if (!PIE1bits.TMR2IE) + PIE1bits.TMR2IE = 1; +} + +void MainTasks(void) +{ + /* Handle our millisecond counters */ + while (Milliseconds) + { + --Milliseconds; + } + /* Handle our seconds counters */ + while (System_Seconds) + { + dcc_timer_seconds(1); + System_Seconds--; + } +} + +void main(void) +{ + /* Note that before main is called, the SCL line is £ + * toggled 256 times to clear any I2C devices that £ + * may be holding the data line low £ + * Reset POR bit */ + RCONbits.NOT_POR = 1; + RCONbits.NOT_RI = 1; + Initialize_Variables(); + /* Handle anything that needs to be done on powerup */ + /* Greet the BACnet world! */ + iam_send(&Handler_Transmit_Buffer[0]); + /* Main loop */ + while (TRUE) + { + RESTART_WDT(); + Verify_Ints(); + dlmstp_task(); + MainTasks(); + } +} diff --git a/bacnet-stack/ports/pic18f6720/mstp.c b/bacnet-stack/ports/pic18f6720/mstp.c new file mode 100644 index 00000000..684e85cc --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/mstp.c @@ -0,0 +1,1702 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 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####*/ + +/* This clause describes a Master-Slave/Token-Passing (MS/TP) data link */ +/* protocol, which provides the same services to the network layer as */ +/* ISO 8802-2 Logical Link Control. It uses services provided by the */ +/* EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be */ +/* included in this standard by reference. The following hardware is assumed: */ +/* (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of */ +/* transmitting and receiving eight data bits with one stop bit */ +/* and no parity. */ +/* (b) An EIA-485 transceiver whose driver may be disabled. */ +/* (c) A timer with a resolution of five milliseconds or less */ + +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "mstp.h" +#include "bytes.h" +#include "crc.h" +#include "rs485.h" + +/* debug print statements */ +#if PRINT_ENABLED + #define PRINT_ENABLED_RECEIVE 0 + #define PRINT_ENABLED_RECEIVE_DATA 1 + #define PRINT_ENABLED_MASTER 0 +#else + #define PRINT_ENABLED_RECEIVE 0 + #define PRINT_ENABLED_RECEIVE_DATA 0 + #define PRINT_ENABLED_MASTER 0 +#endif + +/* 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' */ + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +/* note: we always send a reply postponed since a message other than + the reply may be in the transmit queue */ +#define Treply_delay 10 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 255 + +/* Repeater turnoff delay. The duration of a continuous logical one state */ +/* at the active input port of an MS/TP repeater after which the repeater */ +/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */ +#define Troff 30 + +/* 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 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 20 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port) +{ + return (mstp_port->EventCount > Nmin_octets); +} + +unsigned MSTP_Create_Frame(uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + unsigned index = 0; /* used to load the data portion of the frame */ + + /* not enough to do a header */ + if (buffer_len < 8) + return 0; + + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = data_len / 256; + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = data_len % 256; + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + + index = 8; + while (data_len && data && (index < buffer_len)) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + data++; + index++; + data_len--; + } + /* append the data CRC if necessary */ + if (index > 8) { + if ((index + 2) <= buffer_len) { + crc16 = ~crc16; + buffer[index] = LO_BYTE(crc16); + index++; + buffer[index] = HI_BYTE(crc16); + index++; + } else + return 0; + } + + return index; /* returns the frame length */ +} + +void MSTP_Create_And_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t buffer[MAX_MPDU] = { 0 }; /* buffer for sending */ + uint16_t len = 0; /* number of bytes to send */ + + len = (uint16_t) MSTP_Create_Frame(&buffer[0], /* where frame is loaded */ + sizeof(buffer), /* amount of space available */ + frame_type, /* type of frame to send - see defines */ + destination, /* destination address */ + source, /* source address */ + data, /* any data to be sent - may be null */ + data_len); /* number of bytes of data (up to 501) */ + + RS485_Send_Frame(mstp_port, &buffer[0], len); + /* FIXME: be sure to reset SilenceTimer after each octet is sent! */ +} + +#if PRINT_ENABLED_RECEIVE +char *mstp_receive_state_text(int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_RECEIVE_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + text = "PREAMBLE"; + break; + case MSTP_RECEIVE_STATE_HEADER: + text = "HEADER"; + break; + case MSTP_RECEIVE_STATE_HEADER_CRC: + text = "HEADER_CRC"; + break; + case MSTP_RECEIVE_STATE_DATA: + text = "DATA"; + break; + default: + break; + } + + return text; +} +#endif + +void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) +{ +#if PRINT_ENABLED_RECEIVE_DATA + static MSTP_RECEIVE_STATE receive_state = MSTP_RECEIVE_STATE_IDLE; +#endif +#if PRINT_ENABLED_RECEIVE + fprintf(stderr, + "MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u Silence=%u\n", + mstp_receive_state_text(mstp_port->receive_state), + mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index, + mstp_port->EventCount, mstp_port->DataLength, + mstp_port->SilenceTimer); +#endif + switch (mstp_port->receive_state) { + /* In the IDLE state, the node waits for the beginning of a frame. */ + case MSTP_RECEIVE_STATE_IDLE: + /* EatAnError */ + if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "MSTP Rx: %02X ", mstp_port->DataRegister); +#endif + /* Preamble1 */ + if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* EatAnOctet */ + else { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "\n"); +#endif + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the PREAMBLE state, the node waits for the second octet of the preamble. */ + case MSTP_RECEIVE_STATE_PREAMBLE: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* Preamble2 */ + if (mstp_port->DataRegister == 0xFF) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->Index = 0; + mstp_port->HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* ignore RepeatedPreamble1 */ + else if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the second preamble octet. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* NotPreamble */ + else { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER state, the node waits for the fixed message header. */ + case MSTP_RECEIVE_STATE_HEADER: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* FrameType */ + if (mstp_port->Index == 0) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->FrameType = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 1; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Destination */ + else if (mstp_port->Index == 1) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DestinationAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 2; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Source */ + else if (mstp_port->Index == 2) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->SourceAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 3; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length1 */ + else if (mstp_port->Index == 3) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength = mstp_port->DataRegister * 256; + mstp_port->DataAvailable = false; + mstp_port->Index = 4; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length2 */ + else if (mstp_port->Index == 4) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength += mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 5; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* HeaderCRC */ + else if (mstp_port->Index == 5) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataAvailable = false; + /* don't wait for next state - do it here */ + /* MSTP_RECEIVE_STATE_HEADER_CRC */ + if (mstp_port->HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else { + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* FrameTooLong */ + if (mstp_port->DataLength > MAX_MPDU) { + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* NoData */ + else if (mstp_port->DataLength == 0) { + /* indicate that a frame with no data has been received */ + mstp_port->ReceivedValidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Data */ + else { + mstp_port->Index = 0; + mstp_port->DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + } + /* NotForUs */ + else { + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + + + } + /* not per MS/TP standard, but it is a case not covered */ + else { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during */ + /* the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER_CRC state, the node validates the CRC on the fixed */ + /* message header. */ + case MSTP_RECEIVE_STATE_HEADER_CRC: + break; + /* In the DATA state, the node waits for the data portion of a frame. */ + case MSTP_RECEIVE_STATE_DATA: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* DataOctet */ + if (mstp_port->Index < mstp_port->DataLength) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->InputBuffer[mstp_port->Index] = + mstp_port->DataRegister; + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC1 */ + else if (mstp_port->Index == mstp_port->DataLength) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC2 */ + else if (mstp_port->Index == (mstp_port->DataLength + 1)) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (mstp_port->DataCRC == 0xF0B8) + mstp_port->ReceivedValidFrame = true; + else + mstp_port->ReceivedInvalidFrame = true; + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + } + break; + default: + /* shouldn't get here - but if we do... */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + break; + } +#if PRINT_ENABLED_RECEIVE_DATA + if ((receive_state != MSTP_RECEIVE_STATE_IDLE) && + (mstp_port->receive_state == MSTP_RECEIVE_STATE_IDLE)) { + fprintf(stderr, "\n"); + fflush(stderr); + } + receive_state = mstp_port->receive_state; +#endif + + return; +} + +#if PRINT_ENABLED +char *mstp_master_state_text(int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_MASTER_STATE_INITIALIZE: + text = "INITIALIZE"; + break; + case MSTP_MASTER_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_USE_TOKEN: + text = "USE_TOKEN"; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + text = "WAIT_FOR_REPLY"; + break; + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_PASS_TOKEN: + text = "DONE_WITH_TOKEN"; + break; + case MSTP_MASTER_STATE_NO_TOKEN: + text = "NO_TOKEN"; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + text = "ANSWER_DATA_REQUEST"; + break; + default: + break; + } + + return text; +} +#endif + +#if PRINT_ENABLED +char *mstp_frame_type_text(int type) +{ + char *text = "unknown"; + + switch (type) { + case FRAME_TYPE_TOKEN: + text = "TOKEN"; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER: + text = "REPLY_TO_POLL_FOR_MASTER"; + break; + case FRAME_TYPE_TEST_REQUEST: + text = "TEST_REQUEST"; + break; + case FRAME_TYPE_TEST_RESPONSE: + text = "TEST_RESPONSE"; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + text = "BACNET_DATA_EXPECTING_REPLY"; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + text = "BACNET_DATA_NOT_EXPECTING_REPLY"; + break; + case FRAME_TYPE_REPLY_POSTPONED: + text = "REPLY_POSTPONED"; + break; + default: + if ((type >= FRAME_TYPE_PROPRIETARY_MIN) && + (type <= FRAME_TYPE_PROPRIETARY_MAX)) + text = "PROPRIETARY"; + break; + } + + return text; +} +#endif + +/* returns true if we need to transition immediately */ +bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) +{ + int mtu_len = 0; + int frame_type = 0; + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + uint16_t my_timeout = 10, ns_timeout = 0; + /* transition immediately to the next state */ + bool transition_now = false; +#if PRINT_ENABLED_MASTER + static MSTP_MASTER_STATE master_state = MSTP_MASTER_STATE_INITIALIZE; +#endif + + /* some calculations that several states need */ + next_poll_station = (mstp_port->Poll_Station + 1) % + (mstp_port->Nmax_master + 1); + next_this_station = (mstp_port->This_Station + 1) % + (mstp_port->Nmax_master + 1); + next_next_station = (mstp_port->Next_Station + 1) % + (mstp_port->Nmax_master + 1); +#if PRINT_ENABLED_MASTER + if (mstp_port->master_state != master_state) { + master_state = mstp_port->master_state; + fprintf(stderr, + "MSTP: TS=%02X[%02X] NS=%02X[%02X] PS=%02X[%02X] EC=%u TC=%u ST=%u %s\n", + mstp_port->This_Station, + next_this_station, + mstp_port->Next_Station, + next_next_station, + mstp_port->Poll_Station, + next_poll_station, + mstp_port->EventCount, + mstp_port->TokenCount, + mstp_port->SilenceTimer, + mstp_master_state_text(mstp_port->master_state)); + } +#endif + + switch (mstp_port->master_state) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + mstp_port->TokenCount = Npoll; + mstp_port->SoleMaster = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + /* In the IDLE state, the node waits for a frame. */ + case MSTP_MASTER_STATE_IDLE: + /* LostToken */ + if (mstp_port->SilenceTimer >= Tno_token) { + /* assume that the token has been lost */ + mstp_port->EventCount = 0; /* Addendum 135-2004d-8 */ + mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } + /* ReceivedInvalidFrame */ + else if (mstp_port->ReceivedInvalidFrame == true) { + /* invalid frame was received */ + mstp_port->ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (mstp_port->ReceivedValidFrame == true) { +#if PRINT_ENABLED_MASTER + fprintf(stderr, + "MSTP: ReceivedValidFrame Src=%02X Dest=%02X DataLen=%u FC=%u ST=%u Type=%s\n", + mstp_port->SourceAddress, + mstp_port->DestinationAddress, + mstp_port->DataLength, + mstp_port->FrameCount, + mstp_port->SilenceTimer, + mstp_frame_type_text(mstp_port->FrameType)); +#endif + /* destined for me! */ + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) || + (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + switch (mstp_port->FrameType) { + /* ReceivedToken */ + case FRAME_TYPE_TOKEN: + /* tokens can't be broadcast */ + if (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS) + break; + mstp_port->ReceivedValidFrame = false; + mstp_port->FrameCount = 0; + mstp_port->SoleMaster = false; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + /* ReceivedPFM */ + case FRAME_TYPE_POLL_FOR_MASTER: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + mstp_port->SourceAddress, mstp_port->This_Station, + NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + //mstp_port->ReplyPostponedTimer = 0; + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + /* broadcast DER just remains IDLE */ + if (mstp_port->DestinationAddress != + MSTP_BROADCAST_ADDRESS) { + mstp_port->master_state = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TEST_RESPONSE, + mstp_port->SourceAddress, mstp_port->This_Station, + NULL, 0); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + } + mstp_port->ReceivedValidFrame = false; + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + if (!mstp_port->TxReady) { + /* NothingToSend */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->SilenceTimer > Tusage_delay) { + /* if we missed our timing deadline, another token will be sent */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } else { + /* don't send it if we are too late in getting out */ + uint8_t destination = mstp_port->TxBuffer[3]; + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], mstp_port->TxLength); + mstp_port->FrameCount++; + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (destination == MSTP_BROADCAST_ADDRESS) + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + mstp_port->master_state = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + mstp_port->TxReady = false; + } + break; + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + if (mstp_port->SilenceTimer >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. (Because of the length of the timeout, */ + /* this transition will cause the token to be passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (mstp_port->ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->ReceivedValidFrame == true) { + if (mstp_port->DestinationAddress == + mstp_port->This_Station) { + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, /* source MS/TP address */ + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens. */ + /* Synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (mstp_port->FrameCount < mstp_port->Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (mstp_port->TokenCount < (Npoll - 1)) { + if ((mstp_port->SoleMaster == true) && + (mstp_port->Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + mstp_port->FrameCount = 0; + mstp_port->TokenCount++; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 eliminates the Poll For Master */ + /* if there are no addresses between TS and NS, since there is no */ + /* address at which a new master node may be found in that case. */ + mstp_port->TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == mstp_port->Next_Station) { + if (mstp_port->SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + mstp_port->Poll_Station = mstp_port->This_Station; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (mstp_port->SilenceTimer < Tusage_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (mstp_port->RetryCount < Nretry_token) { + /* RetrySendToken */ + mstp_port->RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + mstp_port->Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, + 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* The NO_TOKEN state is entered if mstp_port->SilenceTimer becomes greater */ + /* than Tno_token, indicating that there has been no network activity */ + /* for that period of time. The timeout is continued to determine */ + /* whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * mstp_port->This_Station); + if (mstp_port->SilenceTimer < my_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = + Tno_token + (Tslot * (mstp_port->This_Station + 1)); + if (mstp_port->SilenceTimer < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + mstp_port->Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, + 0); + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (mstp_port->ReceivedValidFrame == true) { + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + && (mstp_port->FrameType == + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + mstp_port->SoleMaster = false; + mstp_port->Next_Station = mstp_port->SourceAddress; + mstp_port->EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->TokenCount = 0; + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + mstp_port->ReceivedValidFrame = false; + } else if ((mstp_port->SilenceTimer >= Tusage_timeout) || + (mstp_port->ReceivedInvalidFrame == true)) { + if (mstp_port->SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + mstp_port->FrameCount = 0; + /* mstp_port->TokenCount++; removed in 2004 */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (mstp_port->Next_Station != mstp_port->This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + mstp_port->EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, + NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != mstp_port->This_Station) { + /* SendNextPFM */ + mstp_port->Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + mstp_port->SoleMaster = true; + mstp_port->FrameCount = 0; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + mstp_port->ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + /* FIXME: if we knew the APDU type received, we could + see if the next message was that same APDU type */ + if ((mstp_port->SilenceTimer <= Treply_delay) && + mstp_port->TxReady) { + /* 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_Create_And_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + if ((mstp_port->FrameType == + FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) + && (mstp_port->TxReady)) { + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], + mstp_port->TxLength); + mstp_port->TxReady = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + } + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + else { + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_POSTPONED, + mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + break; + default: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* note: This_Station should be set with the MAC address */ +/* note: Nmax_info_frames should be set */ +/* note: Nmax_master should be set */ +void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port) +{ + int i; /*loop counter */ + + if (mstp_port) { + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE; + mstp_port->ReceiveError = false; + mstp_port->DataAvailable = false; + mstp_port->DataRegister = 0; + mstp_port->DataCRC = 0; + mstp_port->DataCRC = 0; + mstp_port->DataLength = 0; + mstp_port->DestinationAddress = 0; + mstp_port->EventCount = 0; + mstp_port->FrameType = FRAME_TYPE_TOKEN; + mstp_port->FrameCount = 0; + mstp_port->HeaderCRC = 0; + mstp_port->Index = 0; + mstp_port->Index = 0; + for (i = 0; i < sizeof(mstp_port->InputBuffer); i++) { + mstp_port->InputBuffer[i] = 0; + } + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->RetryCount = 0; + mstp_port->SilenceTimer = 0; +// mstp_port->ReplyPostponedTimer = 0; + mstp_port->SoleMaster = false; + mstp_port->SourceAddress = 0; + mstp_port->TokenCount = 0; + #if 0 + // these are adjustable, so should already be set + mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES; + mstp_port->Nmax_master = DEFAULT_MAX_MASTER; + #endif + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + for (i = 0; i < sizeof(mstp_port->TxBuffer); i++) { + mstp_port->TxBuffer[i] = 0; + } + mstp_port->TxLength = 0; + mstp_port->TxReady = false; + mstp_port->TxFrameType = 0; + + } +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* test stub functions */ +void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + (void) mstp_port; + (void) buffer; + (void) nbytes; +} + +#define RING_BUFFER_DATA_SIZE 1 +#define RING_BUFFER_SIZE MAX_MPDU +static RING_BUFFER Test_Buffer; +static uint8_t Test_Buffer_Data[RING_BUFFER_DATA_SIZE * RING_BUFFER_SIZE]; +static void Load_Input_Buffer(uint8_t * buffer, size_t len) +{ + static bool initialized = false; /* tracks our init */ + + + if (!initialized) { + initialized = true; + Ringbuf_Init(&Test_Buffer, + (char *) Test_Buffer_Data, + RING_BUFFER_DATA_SIZE, RING_BUFFER_SIZE); + } + /* empty any the existing data */ + while (!Ringbuf_Empty(&Test_Buffer)) { + (void) Ringbuf_Pop_Front(&Test_Buffer); + } + + if (buffer) { + while (len) { + (void) Ringbuf_Put(&Test_Buffer, (char *) buffer); + len--; + buffer++; + } + } +} + +void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port) +{ /* port specific data */ + char *data; + if (!Ringbuf_Empty(&Test_Buffer) && mstp_port && + (mstp_port->DataAvailable == false)) { + data = Ringbuf_Pop_Front(&Test_Buffer); + if (data) { + mstp_port->DataRegister = *data; + mstp_port->DataAvailable = true; + } + } +} + +void testReceiveNodeFSM(Test * pTest) +{ + volatile struct mstp_port_struct_t mstp_port; /* port data */ + unsigned EventCount = 0; /* local counter */ + uint8_t my_mac = 0x05; /* local MAC address */ + uint8_t HeaderCRC = 0; /* for local CRC calculation */ + uint8_t FrameType = 0; /* type of packet that was sent */ + unsigned len; /* used for the size of the message packet */ + size_t i; /* used to loop through the message bytes */ + uint8_t buffer[MAX_MPDU] = { 0 }; + uint8_t data[MAX_MPDU - 8 /*header */ - 2 /*CRC*/] = { 0 }; + + MSTP_Init(&mstp_port, my_mac); + + /* check the receive error during idle */ + mstp_port.receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port.ReceiveError = true; + mstp_port.SilenceTimer = 255; + mstp_port.EventCount = 0; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for bad packet header */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header, but timeout */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the timeout */ + mstp_port.SilenceTimer = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble, but receive error */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble1, but bad preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* bad data */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble, but timeout in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the timeout */ + mstp_port.SilenceTimer = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + + /* check for good packet header preamble, but error in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* Data is received - index is incremented */ + /* FrameType */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = FRAME_TYPE_TOKEN; + HeaderCRC = 0xFF; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 1); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, FrameType == FRAME_TYPE_TOKEN); + /* Destination */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x10; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 2); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DestinationAddress == 0x10); + /* Source */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = my_mac; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 3); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.SourceAddress == my_mac); + /* Length1 = length*256 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 4); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* Length2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* HeaderCRC */ + mstp_port.DataAvailable = true; + ct_test(pTest, HeaderCRC == 0x73); /* per Annex G example */ + mstp_port.DataRegister = ~HeaderCRC; /* one's compliment of CRC is sent */ + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + ct_test(pTest, mstp_port.HeaderCRC == 0x55); + /* NotForUs */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* BadCRC in header check */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, 0x10, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header CRC bad */ + buffer[7] = 0x00; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* NoData for us */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* FrameTooLong */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header data length bad */ + buffer[5] = 0x02; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* Data */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + memset(data, 0, sizeof(data)); + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_PROPRIETARY_MIN, my_mac, /* destination */ + my_mac, /* source */ + data, /* data */ + sizeof(data)); /* data size */ + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + while (mstp_port.receive_state != MSTP_RECEIVE_STATE_IDLE) { + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + } + ct_test(pTest, mstp_port.DataLength == sizeof(data)); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + return; +} + +void testMasterNodeFSM(Test * pTest) +{ + volatile struct mstp_port_struct_t mstp_port; /* port data */ + uint8_t my_mac = 0x05; /* local MAC address */ + + MSTP_Init(&mstp_port, my_mac); + ct_test(pTest, mstp_port.master_state == MSTP_MASTER_STATE_INITIALIZE); + +} + +#endif + +#ifdef TEST_MSTP +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("mstp", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testReceiveNodeFSM); + assert(rc); + rc = ct_addTestFunction(pTest, testMasterNodeFSM); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif diff --git a/bacnet-stack/ports/pic18f6720/mstp.h b/bacnet-stack/ports/pic18f6720/mstp.h new file mode 100644 index 00000000..5fd2a15a --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/mstp.h @@ -0,0 +1,246 @@ +/*####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 MSTP_H +#define MSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.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 +/* The initial CRC16 checksum value */ +#define CRC16_INITIAL_VALUE (0xFFFF) + + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_HEADER_CRC = 3, + MSTP_RECEIVE_STATE_DATA = 4 +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE = 0, + MSTP_MASTER_STATE_IDLE = 1, + MSTP_MASTER_STATE_USE_TOKEN = 2, + MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, + MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, + MSTP_MASTER_STATE_PASS_TOKEN = 5, + MSTP_MASTER_STATE_NO_TOKEN = 6, + MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 +} MSTP_MASTER_STATE; + +struct mstp_port_struct_t { + MSTP_RECEIVE_STATE receive_state; + /* When a master node is powered up or reset, */ + /* it shall unconditionally enter the INITIALIZE state. */ + MSTP_MASTER_STATE master_state; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an error is detected during the reception of a frame. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceiveError:1; + /* There is data in the buffer */ + unsigned DataAvailable:1; + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* stores the latest received data */ + uint8_t DataRegister; + /* Used to accumulate the CRC on the data field of a frame. */ + uint16_t DataCRC; + /* Used to store the data length of a received frame. */ + unsigned DataLength; + /* Used to store the destination address of a received frame. */ + uint8_t DestinationAddress; + /* Used to count the number of received octets or errors. */ + /* This is used in the detection of link activity. */ + /* Compared to Nmin_octets */ + uint8_t EventCount; + /* Used to store the frame type of a received frame. */ + uint8_t FrameType; + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + unsigned FrameCount; + /* Used to accumulate the CRC on the header of a frame. */ + uint8_t HeaderCRC; + /* Used as an index by the Receive State Machine, up to a maximum value of */ + /* InputBufferSize. */ + unsigned Index; + /* An array of octets, used to store octets as they are received. */ + /* InputBuffer is indexed from 0 to InputBufferSize-1. */ + /* The maximum size of a frame is 501 octets. */ + uint8_t *InputBuffer; + /* "Next Station," the MAC address of the node to which This Station passes */ + /* the token. If the Next_Station is unknown, Next_Station shall be equal to */ + /* This_Station. */ + uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + unsigned RetryCount; + /* A timer with nominal 5 millisecond resolution used to measure and */ + /* generate silence on the medium between octets. It is incremented by a */ + /* timer process and is cleared by the Receive State Machine when activity */ + /* is detected and by the SendFrame procedure as each octet is transmitted. */ + /* Since the timer resolution is limited and the timer is not necessarily */ + /* synchronized to other machine events, a timer value of N will actually */ + /* denote intervals between N-1 and N */ + uint16_t SilenceTimer; + + /* A timer used to measure and generate Reply Postponed frames. It is */ + /* incremented by a timer process and is cleared by the Master Node State */ + /* Machine when a Data Expecting Reply Answer activity is completed. */ +/* note: we always send a reply postponed since a message other than + the reply may be in the transmit queue */ +// uint16_t ReplyPostponedTimer; + + /* Used to store the Source Address of a received frame. */ + uint8_t SourceAddress; + + /* The number of tokens received by this node. When this counter reaches the */ + /* value Npoll, the node polls the address range between TS and NS for */ + /* additional master nodes. TokenCount is set to zero at the end of the */ + /* polling process. */ + unsigned TokenCount; + + /* "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. */ + uint8_t This_Station; + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + unsigned Nmax_info_frames; + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + unsigned Nmax_master; + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + uint8_t TxBuffer[MAX_MPDU]; + unsigned TxLength; + bool TxReady; /* true if ready to be sent or received */ + uint8_t TxFrameType; /* type of message - needed by MS/TP */ +}; + +#define DEFAULT_MAX_INFO_FRAMES 1 +#define DEFAULT_MAX_MASTER 127 +#define DEFAULT_MAC_ADDRESS 127 + +/* 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 40 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port); + void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t + *mstp_port); + bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t + *mstp_port); + + /* returns true if line is active */ + bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port); + + unsigned MSTP_Create_Frame(uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len); /* number of bytes of data (up to 501) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/pic18f6720/rs485.c b/bacnet-stack/ports/pic18f6720/rs485.c new file mode 100644 index 00000000..dcc0c9ed --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/rs485.c @@ -0,0 +1,385 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* 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 +#include +#include +#include +#include "hardware.h" +#include "mstp.h" +#include "rs485.h" + +/* public port info */ +extern volatile struct mstp_port_struct_t MSTP_Port; + +/* the baud rate is adjustable */ +uint32_t RS485_Baud_Rate = 9600; + +/* the ISR and other use this for status and control */ +COMSTAT RS485_Comstat; + +//#pragma udata MSTPPortData +/* the buffer for receiving characters */ +volatile uint8_t RS485_Rx_Buffer[MAX_MPDU]; + +/* UART transmission buffer and index */ +volatile uint8_t RS485_Tx_Buffer[MAX_MPDU]; + +/**************************************************************************** +* DESCRIPTION: Transmits a frame using the UART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) /* number of bytes of data (up to 501) */ +{ + uint16_t i = 0; /* loop counter */ + uint8_t turnaround_time; + + if (!buffer) + return; + + /* bounds check */ + if (nbytes >= sizeof(RS485_Tx_Buffer)) + return; + + /* buffer is full. Wait for ISR to transmit. */ + while (RS485_Comstat.Tx_Bytes) {}; + + /* wait 40 bit times since reception */ + if (RS485_Baud_Rate == 9600) + turnaround_time = 4; + else if (RS485_Baud_Rate == 19200) + turnaround_time = 2; + else + turnaround_time = 1; + + while (mstp_port->SilenceTimer < turnaround_time) {}; + + RS485_Comstat.TxHead = 0; + memcpy((void *)&RS485_Tx_Buffer[0], (void *)buffer, nbytes); + + //for (i = 0; i < nbytes; i++) { + // /* put the data into the buffer */ + // RS485_Tx_Buffer[i] = *buffer; + // buffer++; + //} + RS485_Comstat.Tx_Bytes = nbytes; + /* disable the receiver */ + PIE3bits.RC2IE = 0; + RCSTA2bits.CREN = 0; + /* enable the transceiver */ + RS485_TX_ENABLE = 1; + RS485_RX_DISABLE = 1; + /* enable the transmitter */ + TXSTA2bits.TXEN = 1; + PIE3bits.TX2IE = 1; + /* per MSTP spec, sort of */ + mstp_port->SilenceTimer = 0; + + return; +} + +/**************************************************************************** +* DESCRIPTION: Checks for data on the receive UART, and handles errors +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint8_t RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port) +{ + /* check for data */ + if (RS485_Comstat.Rx_Bytes) + { + mstp_port->DataRegister = RS485_Rx_Buffer[RS485_Comstat.RxTail]; + if (RS485_Comstat.RxTail >= (sizeof(RS485_Rx_Buffer)-1)) + RS485_Comstat.RxTail = 0; + else + RS485_Comstat.RxTail++; + RS485_Comstat.Rx_Bytes--; + /* errors? let the state machine know */ + if (RS485_Comstat.Rx_Bufferoverrun) + { + RS485_Comstat.Rx_Bufferoverrun = FALSE; + mstp_port->ReceiveError = TRUE; + } + /* We read a good byte */ + else + mstp_port->DataAvailable = TRUE; + } + + return RS485_Comstat.Rx_Bytes; +} +/* ************************************************************************* + DESCRIPTION: Receives RS485 data stream + + RETURN: none + + ALGORITHM: none + + NOTES: none + *************************************************************************** */ +void RS485_Interrupt_Rx(void) +{ + char dummy; + + if ((RCSTA2bits.FERR) || (RCSTA2bits.OERR)) + { + /* Clear the error */ + RCSTA2bits.CREN = 0; + RCSTA2bits.CREN = 1; + RS485_Comstat.Rx_Bufferoverrun = TRUE; + dummy = RCREG2; + } + else if (RS485_Comstat.Rx_Bytes < sizeof(RS485_Rx_Buffer)) + { + RS485_Rx_Buffer[RS485_Comstat.RxHead] = RCREG2; + if (RS485_Comstat.RxHead >= (sizeof(RS485_Rx_Buffer)-1)) + RS485_Comstat.RxHead = 0; + else + RS485_Comstat.RxHead++; + RS485_Comstat.Rx_Bytes++; + } + else + { + RS485_Comstat.Rx_Bufferoverrun = TRUE; + dummy = RCREG2; + (void)dummy; + } +} + +/* ************************************************************************* + DESCRIPTION: Transmits a byte using the UART out the RS485 port + + RETURN: none + + ALGORITHM: none + + NOTES: none + *************************************************************************** */ +void RS485_Interrupt_Tx(void) +{ + if (RS485_Comstat.Tx_Bytes) + { + /* Get the data byte */ + TXREG2 = RS485_Tx_Buffer[RS485_Comstat.TxHead]; + /* point to the next byte */ + RS485_Comstat.TxHead++; + /* reduce the buffer size */ + RS485_Comstat.Tx_Bytes--; + } + else + { + /* wait for the USART to be empty */ + while (!TXSTA2bits.TRMT); + /* disable this interrupt */ + PIE3bits.TX2IE = 0; + /* enable the receiver */ + RS485_TX_ENABLE = 0; + RS485_RX_DISABLE = 0; + // FIXME: might not be necessary + PIE3bits.RC2IE = 1; + RCSTA2bits.CREN = 1; + } +} + +/**************************************************************************** +* 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_Rate; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: none +* 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_Rate = baud; + break; + default: + valid = false; + break; + } + + if (valid) { + /* FIXME: store the baud rate */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&RS485_Baud_Rate, + sizeof(RS485_Baud_Rate), + EEPROM_MSTP_BAUD_RATE_ADDR); */ +} + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize_Port(void) +{ + + /* Reset USART registers to POR state */ + TXSTA2 = 0; + RCSTA2 = 0; + /* configure USART for receiving */ + /* since the TX will handle setting up for transmit */ + RCSTA2bits.CREN = 1; + /* Interrupt on receipt */ + PIE3bits.RC2IE = 1; + /* enable the transmitter, disable its interrupt */ + TXSTA2bits.TXEN = 1; + PIE3bits.TX2IE = 0; + /* setup USART Baud Rate Generator */ + /* see BAUD RATES FOR ASYNCHRONOUS MODE in Data Book */ + /* Fosc=20MHz + BRGH=1 BRGH=0 + Rate SPBRG Rate SPBRG + ------- ----- ------- ----- + 9615 129 9469 32 + 19230 64 19530 15 + 37878 32 78130 3 + 56818 21 104200 2 + 113630 10 312500 0 + 250000 4 + 625000 1 + 1250000 0 + */ + switch (RS485_Baud_Rate) + { + case 19200: + SPBRG2 = 64; + TXSTA2bits.BRGH = 1; + break; + case 38400: + SPBRG2 = 32; + TXSTA2bits.BRGH = 1; + break; + case 57600: + SPBRG2 = 21; + TXSTA2bits.BRGH = 1; + break; + case 76800: + SPBRG2 = 3; + TXSTA2bits.BRGH = 0; + break; + case 115200: + SPBRG2 = 10; + TXSTA2bits.BRGH = 1; + break; + case 9600: + SPBRG2 = 129; + TXSTA2bits.BRGH = 1; + break; + default: + SPBRG2 = 129; + TXSTA2bits.BRGH = 1; + RS485_Set_Baud_Rate(9600); + break; + } + /* select async mode */ + TXSTA2bits.SYNC = 0; + /* enable transmitter */ + TXSTA2bits.TXEN = 1; + /* serial port enable */ + RCSTA2bits.SPEN = 1; + /* since we are using RS485, + we need to explicitly say + transmit enable or not */ + RS485_RX_DISABLE = 0; + RS485_TX_ENABLE = 0; +} + +/**************************************************************************** +* DESCRIPTION: Disables the RS485 hardware +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Disable_Port(void) +{ + RCSTA2 &= 0x4F; /* Disable the receiver */ + TXSTA2bits.TXEN = 0; /* and transmitter */ + PIE3 &= 0xCF; /* Disable both interrupts */ +} + +void RS485_Reinit(void) +{ + RS485_Set_Baud_Rate(9600); +} + +/**************************************************************************** +* DESCRIPTION: Initializes the data and the port +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize(void) +{ + /* Init the Rs485 buffers */ + RS485_Comstat.RxHead = 0; + RS485_Comstat.RxTail = 0; + RS485_Comstat.Rx_Bytes = 0; + RS485_Comstat.Rx_Bufferoverrun = FALSE; + RS485_Comstat.TxHead = 0; + RS485_Comstat.TxTail = 0; + RS485_Comstat.Tx_Bytes = 0; + + /* FIXME: read the data from storage */ + /* I2C_Read_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&RS485_Baud_Rate, + sizeof(RS485_Baud_Rate), + EEPROM_MSTP_BAUD_RATE_ADDR); */ + + RS485_Initialize_Port(); +} diff --git a/bacnet-stack/ports/pic18f6720/rs485.h b/bacnet-stack/ports/pic18f6720/rs485.h new file mode 100644 index 00000000..90a95902 --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/rs485.h @@ -0,0 +1,85 @@ +/*####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 +#include "mstp.h" + +typedef struct +{ + uint8_t RxHead; + uint8_t RxTail; + uint8_t Rx_Bytes; + uint8_t TxHead; + uint8_t TxTail; + uint8_t Tx_Bytes; + uint8_t Rx_Bufferoverrun : 1; + uint8_t Tx_Bufferoverrun : 1; +} COMSTAT; + +extern COMSTAT RS485_Comstat; +extern volatile uint8_t RS485_Rx_Buffer[MAX_MPDU]; +extern volatile uint8_t RS485_Tx_Buffer[MAX_MPDU]; +extern uint32_t RS485_Baud_Rate; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Reinit(void); + void RS485_Initialize(void); + + void RS485_Disable(void); + + void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes); /* number of bytes of data (up to 501) */ + + uint8_t RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + + void RS485_Interrupt_Rx(void); + + void RS485_Interrupt_Tx(void); + + uint32_t RS485_Get_Baud_Rate(void); + bool RS485_Set_Baud_Rate(uint32_t baud); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/pic18f6720/stdbool.h b/bacnet-stack/ports/pic18f6720/stdbool.h new file mode 100644 index 00000000..716b66e0 --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/stdbool.h @@ -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 diff --git a/bacnet-stack/ports/pic18f6720/stdint.h b/bacnet-stack/ports/pic18f6720/stdint.h new file mode 100644 index 00000000..427414c1 --- /dev/null +++ b/bacnet-stack/ports/pic18f6720/stdint.h @@ -0,0 +1,18 @@ +/* Defines the standard integer types that are used in code */ + +#ifndef STDINT_H +#define STDINT_H 1 + +#include + +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 short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#endif /* STDINT_H */