diff --git a/bacnet-stack/demo/handler/h_wpm.c b/bacnet-stack/demo/handler/h_wpm.c index 2a205621..4eb8f2d9 100644 --- a/bacnet-stack/demo/handler/h_wpm.c +++ b/bacnet-stack/demo/handler/h_wpm.c @@ -1,187 +1,187 @@ -/************************************************************************** -* -* Copyright (C) 2011 Krzysztof Malorny -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#include -#include -#include -#include -#include -#include "config.h" -#include "txbuf.h" -#include "bacdef.h" -#include "bacdcode.h" -#include "bacerror.h" -#include "apdu.h" -#include "npdu.h" -#include "abort.h" -#include "wp.h" -#include "wpm.h" -/* device object has the handling for all objects */ -#include "device.h" -#include "handlers.h" - -/** @file h_wpm.c Handles Write Property Multiple requests. */ - - -/** Handler for a WriteProperty Service request. - * @ingroup DSWP - * This handler will be invoked by apdu_handler() if it has been enabled - * by a call to apdu_set_confirmed_handler(). - * This handler builds a response packet, which is - * - an Abort if - * - the message is segmented - * - if decoding fails - * - an ACK if Device_Write_Property_Multiple() succeeds - * - an Error if Device_Write_PropertyMultiple() encounters an error - * - * @param service_request [in] The contents of the service request. - * @param service_len [in] The length of the service_request. - * @param src [in] BACNET_ADDRESS of the source of the message - * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information - * decoded from the APDU header of this message. - */ - - - -void handler_write_property_multiple( - uint8_t * service_request, - uint16_t service_len, - BACNET_ADDRESS * src, - BACNET_CONFIRMED_SERVICE_DATA * service_data) -{ - int len = 0; - int apdu_len = 0; - int npdu_len = 0; - int pdu_len = 0; - int decode_len = 0; - bool error = false; - BACNET_WRITE_PROPERTY_DATA wp_data; - BACNET_NPDU_DATA npdu_data; - BACNET_ADDRESS my_address; - int bytes_sent = 0; - - - - if (service_data->segmented_message) { - len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], - service_data->invoke_id, - ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); -#if PRINT_ENABLED - fprintf(stderr, "WPM: Segmented message. Sending Abort!\n"); -#endif - goto WPM_ABORT; - } - - /* decode service request */ - decode_len = 0; - do - { +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "wp.h" +#include "wpm.h" +/* device object has the handling for all objects */ +#include "device.h" +#include "handlers.h" + +/** @file h_wpm.c Handles Write Property Multiple requests. */ + + +/** Handler for a WriteProperty Service request. + * @ingroup DSWP + * This handler will be invoked by apdu_handler() if it has been enabled + * by a call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - an ACK if Device_Write_Property_Multiple() succeeds + * - an Error if Device_Write_PropertyMultiple() encounters an error + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ + + + +void handler_write_property_multiple( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int apdu_len = 0; + int npdu_len = 0; + int pdu_len = 0; + int decode_len = 0; + bool error = false; + BACNET_WRITE_PROPERTY_DATA wp_data; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + int bytes_sent = 0; + + + + if (service_data->segmented_message) { + len = abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr, "WPM: Segmented message. Sending Abort!\n"); +#endif + goto WPM_ABORT; + } + + /* decode service request */ + decode_len = 0; + do + { /* decode Object Identifier */ - len = wpm_decode_object_id(&service_request[decode_len], - service_len - decode_len, &wp_data); - if (len > 0) - { - uint8_t tag_number = 0; - - decode_len += len; + len = wpm_decode_object_id(&service_request[decode_len], + service_len - decode_len, &wp_data); + if (len > 0) + { + uint8_t tag_number = 0; + + decode_len += len; /* Opening tag 1 - List of Properties */ - if (decode_is_opening_tag_number(&service_request[decode_len++], 1)) - { - do - { + if (decode_is_opening_tag_number(&service_request[decode_len++], 1)) + { + do + { /* decode a 'Property Identifier'; (3) an optional 'Property Array Index'; */ /* (4) a 'Property Value'; and (5) an optional 'Priority'. */ - len = wpm_decode_object_property(&service_request[decode_len], - service_len - decode_len, &wp_data); - if (len > 0) - { - decode_len += len; - if (Device_Write_Property(&wp_data) == false) - { - error = true; + len = wpm_decode_object_property(&service_request[decode_len], + service_len - decode_len, &wp_data); + if (len > 0) + { + decode_len += len; + if (Device_Write_Property(&wp_data) == false) + { + error = true; break; /* do while (decoding List of Properties) */ - } - } - else - { -#if PRINT_ENABLED - fprintf(stderr, "Bad Encoding!\n"); -#endif - wp_data.error_class = ERROR_CLASS_PROPERTY; - wp_data.error_code = ERROR_CODE_OTHER; - error = true; + } + } + else + { +#if PRINT_ENABLED + fprintf(stderr, "Bad Encoding!\n"); +#endif + wp_data.error_class = ERROR_CLASS_PROPERTY; + wp_data.error_code = ERROR_CODE_OTHER; + error = true; break; /* do while (decoding List of Properties) */ - } - + } + /* Closing tag 1 - List of Properties */ - if (decode_is_closing_tag_number(&service_request[decode_len], 1)) - { - tag_number = 1; - decode_len++; - } - else + if (decode_is_closing_tag_number(&service_request[decode_len], 1)) + { + tag_number = 1; + decode_len++; + } + else tag_number = 0; /* it was not tag 1, decode next Property Identifier ... */ - - } + + } while(tag_number != 1); /* end decoding List of Properties for "that" object */ - - if (error) + + if (error) break; /*do while (decode service request) */ - } - } - else - { -#if PRINT_ENABLED - fprintf(stderr, "Bad Encoding!\n"); -#endif - wp_data.error_class = ERROR_CLASS_OBJECT; - wp_data.error_code = ERROR_CODE_OTHER; - error = true; + } + } + else + { +#if PRINT_ENABLED + fprintf(stderr, "Bad Encoding!\n"); +#endif + wp_data.error_class = ERROR_CLASS_OBJECT; + wp_data.error_code = ERROR_CODE_OTHER; + error = true; break; /*do while (decode service request) */ - } - } - while(decode_len < service_len); - - - /* encode the NPDU portion of the packet */ - datalink_get_my_address(&my_address); - npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); - npdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, - &npdu_data); - - apdu_len = 0; - - if (error == false) - apdu_len = wpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len], - service_data->invoke_id); - else - apdu_len = wpm_error_ack_encode_apdu(&Handler_Transmit_Buffer[npdu_len], - service_data->invoke_id, &wp_data); - -WPM_ABORT: - - pdu_len = npdu_len + apdu_len; - bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], - pdu_len); -} + } + } + while(decode_len < service_len); + + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + npdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + + apdu_len = 0; + + if (error == false) + apdu_len = wpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id); + else + apdu_len = wpm_error_ack_encode_apdu(&Handler_Transmit_Buffer[npdu_len], + service_data->invoke_id, &wp_data); + +WPM_ABORT: + + pdu_len = npdu_len + apdu_len; + bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); +} diff --git a/bacnet-stack/demo/object/nc.c b/bacnet-stack/demo/object/nc.c index d65323d2..df9a70ae 100644 --- a/bacnet-stack/demo/object/nc.c +++ b/bacnet-stack/demo/object/nc.c @@ -1,109 +1,109 @@ -/************************************************************************** -* -* Copyright (C) 2011 Krzysztof Malorny -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#include -#include -#include -#include - -#include "address.h" -#include "bacdef.h" -#include "bacdcode.h" -#include "bacenum.h" -#include "bacapp.h" -#include "client.h" -#include "config.h" -#include "device.h" -#include "event.h" -#include "handlers.h" -#include "txbuf.h" -#include "wp.h" -#include "nc.h" - - -#ifndef MAX_NOTIFICATION_CLASSES -#define MAX_NOTIFICATION_CLASSES 2 -#endif - - -#if defined(INTRINSIC_REPORTING) -static NOTIFICATION_CLASS_INFO NC_Info[MAX_NOTIFICATION_CLASSES]; - -/* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Notification_Properties_Required[] = -{ - PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - PROP_OBJECT_TYPE, - PROP_NOTIFICATION_CLASS, - PROP_PRIORITY, - PROP_ACK_REQUIRED, - PROP_RECIPIENT_LIST, - -1 -}; - -static const int Notification_Properties_Optional[] = -{ - PROP_DESCRIPTION, - -1 -}; - -static const int Notification_Properties_Proprietary[] = -{ - -1 -}; - -void Notification_Class_Property_Lists( - const int **pRequired, - const int **pOptional, - const int **pProprietary) -{ - if (pRequired) - *pRequired = Notification_Properties_Required; - if (pOptional) - *pOptional = Notification_Properties_Optional; - if (pProprietary) - *pProprietary = Notification_Properties_Proprietary; - return; -} - -void Notification_Class_Init(void) -{ - uint8_t NotifyIdx = 0; - - for (NotifyIdx = 0; NotifyIdx < MAX_NOTIFICATION_CLASSES; NotifyIdx++) - { - /* init with zeros */ - memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO)); - /* set the basic parameters */ - NC_Info[NotifyIdx].Ack_Required = 0; - NC_Info[NotifyIdx].Priority[0] = 255; /* The lowest priority for Normal message. */ - NC_Info[NotifyIdx].Priority[1] = 255; /* The lowest priority for Normal message. */ - NC_Info[NotifyIdx].Priority[2] = 255; /* The lowest priority for Normal message. */ - } - - /* init with special values for tests */ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include + +#include "address.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "client.h" +#include "config.h" +#include "device.h" +#include "event.h" +#include "handlers.h" +#include "txbuf.h" +#include "wp.h" +#include "nc.h" + + +#ifndef MAX_NOTIFICATION_CLASSES +#define MAX_NOTIFICATION_CLASSES 2 +#endif + + +#if defined(INTRINSIC_REPORTING) +static NOTIFICATION_CLASS_INFO NC_Info[MAX_NOTIFICATION_CLASSES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Notification_Properties_Required[] = +{ + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_NOTIFICATION_CLASS, + PROP_PRIORITY, + PROP_ACK_REQUIRED, + PROP_RECIPIENT_LIST, + -1 +}; + +static const int Notification_Properties_Optional[] = +{ + PROP_DESCRIPTION, + -1 +}; + +static const int Notification_Properties_Proprietary[] = +{ + -1 +}; + +void Notification_Class_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Notification_Properties_Required; + if (pOptional) + *pOptional = Notification_Properties_Optional; + if (pProprietary) + *pProprietary = Notification_Properties_Proprietary; + return; +} + +void Notification_Class_Init(void) +{ + uint8_t NotifyIdx = 0; + + for (NotifyIdx = 0; NotifyIdx < MAX_NOTIFICATION_CLASSES; NotifyIdx++) + { + /* init with zeros */ + memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO)); + /* set the basic parameters */ + NC_Info[NotifyIdx].Ack_Required = 0; + NC_Info[NotifyIdx].Priority[0] = 255; /* The lowest priority for Normal message. */ + NC_Info[NotifyIdx].Priority[1] = 255; /* The lowest priority for Normal message. */ + NC_Info[NotifyIdx].Priority[2] = 255; /* The lowest priority for Normal message. */ + } + + /* init with special values for tests */ /* NC_Info[0].Ack_Required = TRANSITION_TO_NORMAL_MASKED; */ /* NC_Info[0].Priority[0] = 248; */ /* NC_Info[0].Priority[1] = 192; */ @@ -129,766 +129,766 @@ void Notification_Class_Init(void) /* NC_Info[0].Recipient_List[0].ConfirmedNotify = true; */ /* NC_Info[0].Recipient_List[0].Transitions = */ /* TRANSITION_TO_OFFNORMAL_MASKED | TRANSITION_TO_NORMAL_MASKED; */ - - - return; -} - - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need validate that the */ -/* given instance exists */ -bool Notification_Class_Valid_Instance( - uint32_t object_instance) -{ - unsigned int index; - - index = Notification_Class_Instance_To_Index(object_instance); - if (index < MAX_NOTIFICATION_CLASSES) - return true; - - return false; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then count how many you have */ -unsigned Notification_Class_Count(void) -{ - return MAX_NOTIFICATION_CLASSES; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the instance */ -/* that correlates to the correct index */ -uint32_t Notification_Class_Index_To_Instance(unsigned index) -{ - return index; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the index */ -/* that correlates to the correct instance number */ -unsigned Notification_Class_Instance_To_Index( - uint32_t object_instance) -{ - unsigned index = MAX_NOTIFICATION_CLASSES; - - if (object_instance < MAX_NOTIFICATION_CLASSES) - index = object_instance; - - return index; -} - -bool Notification_Class_Object_Name( - uint32_t object_instance, - BACNET_CHARACTER_STRING *object_name) -{ - static char text_string[32] = ""; /* okay for single thread */ - unsigned int index; - bool status = false; - - index = Notification_Class_Instance_To_Index(object_instance); - if (index < MAX_NOTIFICATION_CLASSES) { - sprintf(text_string, "NOTIFICATION CLASS %lu", (unsigned long) index); - status = characterstring_init_ansi(object_name, text_string); - } - - return status; -} - - - -int Notification_Class_Read_Property( - BACNET_READ_PROPERTY_DATA * rpdata) -{ - NOTIFICATION_CLASS_INFO *CurrentNotify; - BACNET_CHARACTER_STRING char_string; - BACNET_OCTET_STRING octet_string; - BACNET_BIT_STRING bit_string; - uint8_t *apdu = NULL; - uint8_t u8Val; - int idx; - int apdu_len = 0; /* return value */ - - - if ((rpdata == NULL) || (rpdata->application_data == NULL) || - (rpdata->application_data_len == 0)) { - return 0; - } - - apdu = rpdata->application_data; - CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(rpdata->object_instance)]; - - switch (rpdata->object_property) - { - case PROP_OBJECT_IDENTIFIER: - apdu_len = - encode_application_object_id(&apdu[0], OBJECT_NOTIFICATION_CLASS, - rpdata->object_instance); - break; - - case PROP_OBJECT_NAME: - case PROP_DESCRIPTION: - Notification_Class_Object_Name(rpdata->object_instance, &char_string); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - - case PROP_OBJECT_TYPE: - apdu_len = - encode_application_enumerated(&apdu[0], OBJECT_NOTIFICATION_CLASS); - break; - - case PROP_NOTIFICATION_CLASS: - apdu_len += encode_application_unsigned(&apdu[0], rpdata->object_instance); - break; - - case PROP_PRIORITY: - if (rpdata->array_index == 0) - apdu_len += encode_application_unsigned(&apdu[0], 3); - else - { - if (rpdata->array_index == BACNET_ARRAY_ALL) - { - /* TO-OFFNORMAL */ - apdu_len += encode_application_unsigned(&apdu[apdu_len], - CurrentNotify->Priority[0]); - /* TO-FAULT */ - apdu_len += encode_application_unsigned(&apdu[apdu_len], - CurrentNotify->Priority[1]); - /* TO-NORMAL */ - apdu_len += encode_application_unsigned(&apdu[apdu_len], - CurrentNotify->Priority[2]); - } - else if (rpdata->array_index <= 3) - { - apdu_len += encode_application_unsigned(&apdu[apdu_len], - CurrentNotify->Priority[rpdata->array_index - 1]); - } - else - { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = -1; - } - } - break; - - case PROP_ACK_REQUIRED: - u8Val = CurrentNotify->Ack_Required; - - bitstring_init(&bit_string); - bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, - (u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true : false ); - bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, - (u8Val & TRANSITION_TO_FAULT_MASKED ) ? true : false ); - bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, - (u8Val & TRANSITION_TO_NORMAL_MASKED ) ? true : false ); - /* encode bitstring */ - apdu_len += encode_application_bitstring(&apdu[apdu_len], &bit_string); - break; - - case PROP_RECIPIENT_LIST: - /* encode all entry of Recipient_List */ - for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) - { - BACNET_DESTINATION *RecipientEntry; - int i = 0; - - /* get pointer of current element for Recipient_List - easier for use */ - RecipientEntry = &CurrentNotify->Recipient_List[idx]; - if (RecipientEntry->Recipient.RecipientType != RECIPIENT_TYPE_NOTINITIALIZED) - { - /* Valid Days - BACnetDaysOfWeek - [bitstring] monday-sunday */ - u8Val = 0x01; - bitstring_init(&bit_string); - - for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) - { - if (RecipientEntry->ValidDays & u8Val) - bitstring_set_bit(&bit_string, i, true); - else - bitstring_set_bit(&bit_string, i, false); - u8Val <<= 1; /* next day */ - } - apdu_len += encode_application_bitstring(&apdu[apdu_len],&bit_string); - - /* From Time */ - apdu_len += encode_application_time(&apdu[apdu_len], - &RecipientEntry->FromTime); - - /* To Time */ - apdu_len += encode_application_time(&apdu[apdu_len], - &RecipientEntry->ToTime); - - /* - BACnetRecipient ::= CHOICE { - device [0] BACnetObjectIdentifier, - address [1] BACnetAddress - } */ - - /* CHOICE - device [0] BACnetObjectIdentifier */ - if(RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) - { - apdu_len += encode_context_object_id(&apdu[apdu_len], 0, OBJECT_DEVICE, - RecipientEntry->Recipient._.DeviceIdentifier); - } - /* CHOICE - address [1] BACnetAddress */ - else if (RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) - { - /* opening tag 1 */ - apdu_len += encode_opening_tag(&apdu[apdu_len], 1); - /* network-number Unsigned16, */ - apdu_len += encode_application_unsigned(&apdu[apdu_len], - RecipientEntry->Recipient._.Address.net); - - /* mac-address OCTET STRING */ - if (RecipientEntry->Recipient._.Address.net) - { - octetstring_init(&octet_string, - RecipientEntry->Recipient._.Address.adr, - RecipientEntry->Recipient._.Address.len); - } - else - { - octetstring_init(&octet_string, - RecipientEntry->Recipient._.Address.mac, - RecipientEntry->Recipient._.Address.mac_len); - } - apdu_len += encode_application_octet_string(&apdu[apdu_len], &octet_string); - - /* closing tag 1 */ - apdu_len += encode_closing_tag(&apdu[apdu_len], 1); - - } - else {;} /* shouldn't happen */ - - /* Process Identifier - Unsigned32 */ - apdu_len += encode_application_unsigned(&apdu[apdu_len], - RecipientEntry->ProcessIdentifier); - - /* Issue Confirmed Notifications - boolean */ - apdu_len += encode_application_boolean(&apdu[apdu_len], - RecipientEntry->ConfirmedNotify); - - /* Transitions - BACnet Event Transition Bits [bitstring] */ - u8Val = RecipientEntry->Transitions; - - bitstring_init(&bit_string); - bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, - (u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true : false ); - bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, - (u8Val & TRANSITION_TO_FAULT_MASKED ) ? true : false ); - bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, - (u8Val & TRANSITION_TO_NORMAL_MASKED ) ? true : false ); - - apdu_len += encode_application_bitstring(&apdu[apdu_len], &bit_string); - } - } - break; - - default: - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; - apdu_len = -1; - break; - } - - /* only array properties can have array options */ - if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY) && - (rpdata->array_index != BACNET_ARRAY_ALL)) - { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; - apdu_len = BACNET_STATUS_ERROR; - } - - return apdu_len; -} - - -bool Notification_Class_Write_Property( - BACNET_WRITE_PROPERTY_DATA * wp_data) -{ - NOTIFICATION_CLASS_INFO *CurrentNotify; - NOTIFICATION_CLASS_INFO TmpNotify; - BACNET_APPLICATION_DATA_VALUE value; - bool status = false; - int iOffset = 0; - uint8_t idx = 0; - int len = 0; - - - - CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(wp_data->object_instance)]; - + + + return; +} + + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Notification_Class_Valid_Instance( + uint32_t object_instance) +{ + unsigned int index; + + index = Notification_Class_Instance_To_Index(object_instance); + if (index < MAX_NOTIFICATION_CLASSES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Notification_Class_Count(void) +{ + return MAX_NOTIFICATION_CLASSES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Notification_Class_Index_To_Instance(unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Notification_Class_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_NOTIFICATION_CLASSES; + + if (object_instance < MAX_NOTIFICATION_CLASSES) + index = object_instance; + + return index; +} + +bool Notification_Class_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING *object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + unsigned int index; + bool status = false; + + index = Notification_Class_Instance_To_Index(object_instance); + if (index < MAX_NOTIFICATION_CLASSES) { + sprintf(text_string, "NOTIFICATION CLASS %lu", (unsigned long) index); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + + + +int Notification_Class_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + NOTIFICATION_CLASS_INFO *CurrentNotify; + BACNET_CHARACTER_STRING char_string; + BACNET_OCTET_STRING octet_string; + BACNET_BIT_STRING bit_string; + uint8_t *apdu = NULL; + uint8_t u8Val; + int idx; + int apdu_len = 0; /* return value */ + + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + apdu = rpdata->application_data; + CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(rpdata->object_instance)]; + + switch (rpdata->object_property) + { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_NOTIFICATION_CLASS, + rpdata->object_instance); + break; + + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Notification_Class_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_NOTIFICATION_CLASS); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len += encode_application_unsigned(&apdu[0], rpdata->object_instance); + break; + + case PROP_PRIORITY: + if (rpdata->array_index == 0) + apdu_len += encode_application_unsigned(&apdu[0], 3); + else + { + if (rpdata->array_index == BACNET_ARRAY_ALL) + { + /* TO-OFFNORMAL */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[0]); + /* TO-FAULT */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[1]); + /* TO-NORMAL */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[2]); + } + else if (rpdata->array_index <= 3) + { + apdu_len += encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[rpdata->array_index - 1]); + } + else + { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + break; + + case PROP_ACK_REQUIRED: + u8Val = CurrentNotify->Ack_Required; + + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (u8Val & TRANSITION_TO_FAULT_MASKED ) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (u8Val & TRANSITION_TO_NORMAL_MASKED ) ? true : false ); + /* encode bitstring */ + apdu_len += encode_application_bitstring(&apdu[apdu_len], &bit_string); + break; + + case PROP_RECIPIENT_LIST: + /* encode all entry of Recipient_List */ + for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) + { + BACNET_DESTINATION *RecipientEntry; + int i = 0; + + /* get pointer of current element for Recipient_List - easier for use */ + RecipientEntry = &CurrentNotify->Recipient_List[idx]; + if (RecipientEntry->Recipient.RecipientType != RECIPIENT_TYPE_NOTINITIALIZED) + { + /* Valid Days - BACnetDaysOfWeek - [bitstring] monday-sunday */ + u8Val = 0x01; + bitstring_init(&bit_string); + + for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) + { + if (RecipientEntry->ValidDays & u8Val) + bitstring_set_bit(&bit_string, i, true); + else + bitstring_set_bit(&bit_string, i, false); + u8Val <<= 1; /* next day */ + } + apdu_len += encode_application_bitstring(&apdu[apdu_len],&bit_string); + + /* From Time */ + apdu_len += encode_application_time(&apdu[apdu_len], + &RecipientEntry->FromTime); + + /* To Time */ + apdu_len += encode_application_time(&apdu[apdu_len], + &RecipientEntry->ToTime); + + /* + BACnetRecipient ::= CHOICE { + device [0] BACnetObjectIdentifier, + address [1] BACnetAddress + } */ + + /* CHOICE - device [0] BACnetObjectIdentifier */ + if(RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) + { + apdu_len += encode_context_object_id(&apdu[apdu_len], 0, OBJECT_DEVICE, + RecipientEntry->Recipient._.DeviceIdentifier); + } + /* CHOICE - address [1] BACnetAddress */ + else if (RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) + { + /* opening tag 1 */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + /* network-number Unsigned16, */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + RecipientEntry->Recipient._.Address.net); + + /* mac-address OCTET STRING */ + if (RecipientEntry->Recipient._.Address.net) + { + octetstring_init(&octet_string, + RecipientEntry->Recipient._.Address.adr, + RecipientEntry->Recipient._.Address.len); + } + else + { + octetstring_init(&octet_string, + RecipientEntry->Recipient._.Address.mac, + RecipientEntry->Recipient._.Address.mac_len); + } + apdu_len += encode_application_octet_string(&apdu[apdu_len], &octet_string); + + /* closing tag 1 */ + apdu_len += encode_closing_tag(&apdu[apdu_len], 1); + + } + else {;} /* shouldn't happen */ + + /* Process Identifier - Unsigned32 */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + RecipientEntry->ProcessIdentifier); + + /* Issue Confirmed Notifications - boolean */ + apdu_len += encode_application_boolean(&apdu[apdu_len], + RecipientEntry->ConfirmedNotify); + + /* Transitions - BACnet Event Transition Bits [bitstring] */ + u8Val = RecipientEntry->Transitions; + + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (u8Val & TRANSITION_TO_FAULT_MASKED ) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (u8Val & TRANSITION_TO_NORMAL_MASKED ) ? true : false ); + + apdu_len += encode_application_bitstring(&apdu[apdu_len], &bit_string); + } + } + break; + + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) + { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + + +bool Notification_Class_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + NOTIFICATION_CLASS_INFO *CurrentNotify; + NOTIFICATION_CLASS_INFO TmpNotify; + BACNET_APPLICATION_DATA_VALUE value; + bool status = false; + int iOffset = 0; + uint8_t idx = 0; + int len = 0; + + + + CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(wp_data->object_instance)]; + /* decode the some of the request */ - len = bacapp_decode_application_data(wp_data->application_data, - wp_data->application_data_len, &value); - - switch (wp_data->object_property) - { - case PROP_PRIORITY: - status = - WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, - &wp_data->error_class, &wp_data->error_code); - - if (status) { - if (wp_data->array_index == 0) { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - } - else if (wp_data->array_index == BACNET_ARRAY_ALL) { + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + + switch (wp_data->object_property) + { + case PROP_PRIORITY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if (wp_data->array_index == 0) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + else if (wp_data->array_index == BACNET_ARRAY_ALL) { /*/ FIXME: wite all array */ - } - else if (wp_data->array_index <= 3) { - CurrentNotify->Priority[wp_data->array_index - 1] = - value.type.Unsigned_Int; - } - else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - } - } - break; - - case PROP_ACK_REQUIRED: - status = - WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, - &wp_data->error_class, &wp_data->error_code); - - if (status) { - if(value.type.Bit_String.bits_used == 3) { - CurrentNotify->Ack_Required = - value.type.Bit_String.value[0]; - } - else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } - break; - - case PROP_RECIPIENT_LIST: - - memset(&TmpNotify, 0x00, sizeof(NOTIFICATION_CLASS_INFO)); - - /* decode all packed */ - while (iOffset < wp_data->application_data_len) - { - /* Decode Valid Days */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - - if (value.type.Bit_String.bits_used == MAX_BACNET_DAYS_OF_WEEK) - /* store value */ - TmpNotify.Recipient_List[idx].ValidDays = value.type.Bit_String.value[0]; - else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_OTHER; - return false; - } - - iOffset += len; - /* Decode From Time */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].FromTime = value.type.Time; - - iOffset += len; - /* Decode To Time */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].ToTime = value.type.Time; - - iOffset += len; - /* context tag [0] - Device */ - if (decode_is_context_tag(&wp_data->application_data[iOffset], 0)) - { - TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_DEVICE; - /* Decode Network Number */ - len = bacapp_decode_context_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value, - PROP_RECIPIENT_LIST); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_OBJECT_ID)) { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].Recipient._.DeviceIdentifier = - value.type.Object_Id.instance; - - iOffset += len; - } - /* opening tag [1] - Recipient */ - else if (decode_is_opening_tag_number(&wp_data->application_data[iOffset], 1)) - { - iOffset++; - TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS; - /* Decode Network Number */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].Recipient._.Address.net = value.type.Unsigned_Int; - - iOffset += len; - /* Decode Address */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_OCTET_STRING)) { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - if (TmpNotify.Recipient_List[idx].Recipient._.Address.net == 0) { - memcpy(TmpNotify.Recipient_List[idx].Recipient._.Address.mac, - value.type.Octet_String.value, - value.type.Octet_String.length); - TmpNotify.Recipient_List[idx].Recipient._.Address.mac_len = - value.type.Octet_String.length; - } - else { - memcpy(TmpNotify.Recipient_List[idx].Recipient._.Address.adr, - value.type.Octet_String.value, - value.type.Octet_String.length); - TmpNotify.Recipient_List[idx].Recipient._.Address.len = - value.type.Octet_String.length; - } - - iOffset += len; - /* closing tag [1] - Recipient */ - if (decode_is_closing_tag_number(&wp_data->application_data[iOffset], 1)) - iOffset++; - else { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - } - else { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - - /* Process Identifier */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].ProcessIdentifier = value.type.Unsigned_Int; - - iOffset += len; - /* Issue Confirmed Notifications */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BOOLEAN)) { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].ConfirmedNotify = value.type.Boolean; - - iOffset += len; - /* Transitions */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { - /* Bad decode, wrong tag or following required parameter missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - - if (value.type.Bit_String.bits_used == MAX_BACNET_EVENT_TRANSITION) - /* store value */ - TmpNotify.Recipient_List[idx].Transitions = value.type.Bit_String.value[0]; - else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_OTHER; - return false; - } - iOffset += len; - - /* Increasing element of list */ - if (++idx >= NC_MAX_RECIPIENTS) { - wp_data->error_class = ERROR_CLASS_RESOURCES; - wp_data->error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; - return false; - } - } - - /* Decoded all recipient list */ - /* copy elements from temporary object */ - for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) - { - BACNET_ADDRESS src = {0}; - unsigned max_apdu = 0; - int32_t DeviceID; - - CurrentNotify->Recipient_List[idx] = TmpNotify.Recipient_List[idx]; - - if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { - /* copy Device_ID */ - DeviceID = CurrentNotify->Recipient_List[idx].Recipient._.DeviceIdentifier; - address_bind_request(DeviceID, &max_apdu, &src); - - } - else if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { - /* copy Address */ + } + else if (wp_data->array_index <= 3) { + CurrentNotify->Priority[wp_data->array_index - 1] = + value.type.Unsigned_Int; + } + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + } + break; + + case PROP_ACK_REQUIRED: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if(value.type.Bit_String.bits_used == 3) { + CurrentNotify->Ack_Required = + value.type.Bit_String.value[0]; + } + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + + case PROP_RECIPIENT_LIST: + + memset(&TmpNotify, 0x00, sizeof(NOTIFICATION_CLASS_INFO)); + + /* decode all packed */ + while (iOffset < wp_data->application_data_len) + { + /* Decode Valid Days */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + + if (value.type.Bit_String.bits_used == MAX_BACNET_DAYS_OF_WEEK) + /* store value */ + TmpNotify.Recipient_List[idx].ValidDays = value.type.Bit_String.value[0]; + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OTHER; + return false; + } + + iOffset += len; + /* Decode From Time */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].FromTime = value.type.Time; + + iOffset += len; + /* Decode To Time */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].ToTime = value.type.Time; + + iOffset += len; + /* context tag [0] - Device */ + if (decode_is_context_tag(&wp_data->application_data[iOffset], 0)) + { + TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_DEVICE; + /* Decode Network Number */ + len = bacapp_decode_context_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value, + PROP_RECIPIENT_LIST); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_OBJECT_ID)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].Recipient._.DeviceIdentifier = + value.type.Object_Id.instance; + + iOffset += len; + } + /* opening tag [1] - Recipient */ + else if (decode_is_opening_tag_number(&wp_data->application_data[iOffset], 1)) + { + iOffset++; + TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS; + /* Decode Network Number */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].Recipient._.Address.net = value.type.Unsigned_Int; + + iOffset += len; + /* Decode Address */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_OCTET_STRING)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + if (TmpNotify.Recipient_List[idx].Recipient._.Address.net == 0) { + memcpy(TmpNotify.Recipient_List[idx].Recipient._.Address.mac, + value.type.Octet_String.value, + value.type.Octet_String.length); + TmpNotify.Recipient_List[idx].Recipient._.Address.mac_len = + value.type.Octet_String.length; + } + else { + memcpy(TmpNotify.Recipient_List[idx].Recipient._.Address.adr, + value.type.Octet_String.value, + value.type.Octet_String.length); + TmpNotify.Recipient_List[idx].Recipient._.Address.len = + value.type.Octet_String.length; + } + + iOffset += len; + /* closing tag [1] - Recipient */ + if (decode_is_closing_tag_number(&wp_data->application_data[iOffset], 1)) + iOffset++; + else { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + } + else { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + + /* Process Identifier */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].ProcessIdentifier = value.type.Unsigned_Int; + + iOffset += len; + /* Issue Confirmed Notifications */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BOOLEAN)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].ConfirmedNotify = value.type.Boolean; + + iOffset += len; + /* Transitions */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + + if (value.type.Bit_String.bits_used == MAX_BACNET_EVENT_TRANSITION) + /* store value */ + TmpNotify.Recipient_List[idx].Transitions = value.type.Bit_String.value[0]; + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OTHER; + return false; + } + iOffset += len; + + /* Increasing element of list */ + if (++idx >= NC_MAX_RECIPIENTS) { + wp_data->error_class = ERROR_CLASS_RESOURCES; + wp_data->error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + return false; + } + } + + /* Decoded all recipient list */ + /* copy elements from temporary object */ + for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) + { + BACNET_ADDRESS src = {0}; + unsigned max_apdu = 0; + int32_t DeviceID; + + CurrentNotify->Recipient_List[idx] = TmpNotify.Recipient_List[idx]; + + if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { + /* copy Device_ID */ + DeviceID = CurrentNotify->Recipient_List[idx].Recipient._.DeviceIdentifier; + address_bind_request(DeviceID, &max_apdu, &src); + + } + else if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { + /* copy Address */ /*/src = CurrentNotify->Recipient_List[idx].Recipient._.Address; */ /*/address_bind_request(BACNET_MAX_INSTANCE, &max_apdu, &src); */ - } - } - - status = true; - - break; - - default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; - break; - } - - return status; -} - - -static bool IsRecipientActive(BACNET_DESTINATION * pBacDest, uint8_t EventToState) -{ - BACNET_DATE_TIME DateTime; - - /* valid Transitions */ - switch (EventToState) - { - case EVENT_STATE_OFFNORMAL: - case EVENT_STATE_HIGH_LIMIT: - case EVENT_STATE_LOW_LIMIT: - if (!(pBacDest->Transitions & TRANSITION_TO_OFFNORMAL_MASKED)) - return false; - break; - - case EVENT_STATE_FAULT: - if (!(pBacDest->Transitions & TRANSITION_TO_FAULT_MASKED)) - return false; - break; - - case EVENT_STATE_NORMAL: - if (!(pBacDest->Transitions & TRANSITION_TO_NORMAL_MASKED)) - return false; - break; - - default: - return false; /* shouldn't happen */ - } - - /* get actual date and time */ - Device_getCurrentDateTime(&DateTime); - - /* valid Days */ - if (!((0x01 << (DateTime.date.wday - 1)) & pBacDest->ValidDays)) - return false; - - /* valid FromTime */ - if (datetime_compare_time(&DateTime.time, &pBacDest->FromTime) < 0) - return false; - - /* valid ToTime */ - if (datetime_compare_time(&pBacDest->ToTime, &DateTime.time) < 0) - return false; - - return true; -} - - -void Notification_Class_common_reporting_function( - BACNET_EVENT_NOTIFICATION_DATA * event_data) -{ - /* Fill the parameters common for all types of events. */ - - NOTIFICATION_CLASS_INFO *CurrentNotify; - BACNET_DESTINATION *pBacDest; - uint32_t notify_index; - uint8_t index; - - - notify_index = Notification_Class_Instance_To_Index( - event_data->notificationClass); - - if (notify_index < MAX_NOTIFICATION_CLASSES) - CurrentNotify = &NC_Info[notify_index]; - else - return; - - - /* Initiating Device Identifier */ - event_data->initiatingObjectIdentifier.type = OBJECT_DEVICE; - event_data->initiatingObjectIdentifier.instance = - Device_Object_Instance_Number(); - - /* Priority and AckRequired*/ - switch (event_data->toState) - { - case EVENT_STATE_OFFNORMAL: - event_data->priority = - CurrentNotify->Priority[EVENT_STATE_OFFNORMAL]; - event_data->ackRequired = (CurrentNotify->Ack_Required & - TRANSITION_TO_OFFNORMAL_MASKED) ? true : false; - break; - - case EVENT_STATE_FAULT: - event_data->priority = - CurrentNotify->Priority[EVENT_STATE_FAULT]; - event_data->ackRequired = (CurrentNotify->Ack_Required & - TRANSITION_TO_FAULT_MASKED) ? true : false; - break; - - case EVENT_STATE_NORMAL: - case EVENT_STATE_HIGH_LIMIT: - case EVENT_STATE_LOW_LIMIT: - event_data->priority = - CurrentNotify->Priority[EVENT_STATE_NORMAL]; - event_data->ackRequired = (CurrentNotify->Ack_Required & - TRANSITION_TO_NORMAL) ? true : false; - break; - + } + } + + status = true; + + break; + + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +static bool IsRecipientActive(BACNET_DESTINATION * pBacDest, uint8_t EventToState) +{ + BACNET_DATE_TIME DateTime; + + /* valid Transitions */ + switch (EventToState) + { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + if (!(pBacDest->Transitions & TRANSITION_TO_OFFNORMAL_MASKED)) + return false; + break; + + case EVENT_STATE_FAULT: + if (!(pBacDest->Transitions & TRANSITION_TO_FAULT_MASKED)) + return false; + break; + + case EVENT_STATE_NORMAL: + if (!(pBacDest->Transitions & TRANSITION_TO_NORMAL_MASKED)) + return false; + break; + + default: + return false; /* shouldn't happen */ + } + + /* get actual date and time */ + Device_getCurrentDateTime(&DateTime); + + /* valid Days */ + if (!((0x01 << (DateTime.date.wday - 1)) & pBacDest->ValidDays)) + return false; + + /* valid FromTime */ + if (datetime_compare_time(&DateTime.time, &pBacDest->FromTime) < 0) + return false; + + /* valid ToTime */ + if (datetime_compare_time(&pBacDest->ToTime, &DateTime.time) < 0) + return false; + + return true; +} + + +void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA * event_data) +{ + /* Fill the parameters common for all types of events. */ + + NOTIFICATION_CLASS_INFO *CurrentNotify; + BACNET_DESTINATION *pBacDest; + uint32_t notify_index; + uint8_t index; + + + notify_index = Notification_Class_Instance_To_Index( + event_data->notificationClass); + + if (notify_index < MAX_NOTIFICATION_CLASSES) + CurrentNotify = &NC_Info[notify_index]; + else + return; + + + /* Initiating Device Identifier */ + event_data->initiatingObjectIdentifier.type = OBJECT_DEVICE; + event_data->initiatingObjectIdentifier.instance = + Device_Object_Instance_Number(); + + /* Priority and AckRequired*/ + switch (event_data->toState) + { + case EVENT_STATE_OFFNORMAL: + event_data->priority = + CurrentNotify->Priority[EVENT_STATE_OFFNORMAL]; + event_data->ackRequired = (CurrentNotify->Ack_Required & + TRANSITION_TO_OFFNORMAL_MASKED) ? true : false; + break; + + case EVENT_STATE_FAULT: + event_data->priority = + CurrentNotify->Priority[EVENT_STATE_FAULT]; + event_data->ackRequired = (CurrentNotify->Ack_Required & + TRANSITION_TO_FAULT_MASKED) ? true : false; + break; + + case EVENT_STATE_NORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + event_data->priority = + CurrentNotify->Priority[EVENT_STATE_NORMAL]; + event_data->ackRequired = (CurrentNotify->Ack_Required & + TRANSITION_TO_NORMAL) ? true : false; + break; + default: /* shouldn't happen */ - break; - } - - /* send notifications for active recipients */ - /* pointer to first recipient */ - pBacDest = &CurrentNotify->Recipient_List[0]; - for (index = 0; index < NC_MAX_RECIPIENTS; index++, pBacDest++) - { - /* check if recipient is defined */ - if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_NOTINITIALIZED) - break; /* recipient doesn't defined - end of list */ - - if (IsRecipientActive(pBacDest, event_data->toState) == true) - { - BACNET_ADDRESS dest; - uint32_t device_id; - unsigned max_apdu; - - /* Process Identifier */ - event_data->processIdentifier = pBacDest->ProcessIdentifier; - - /* send notification */ - if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { - /* send notification to the specified device */ - device_id = pBacDest->Recipient._.DeviceIdentifier; - - if (pBacDest->ConfirmedNotify == true) - Send_CEvent_Notify(device_id, event_data); - else if (address_get_by_device(device_id, &max_apdu, &dest)) - Send_UEvent_Notify(Handler_Transmit_Buffer, event_data, &dest); - } - else if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { - /* send notification to the address indicated */ - if (pBacDest->ConfirmedNotify == true) { - if (address_get_device_id(&dest, &device_id)) - Send_CEvent_Notify(device_id, event_data); - } - else { - dest = pBacDest->Recipient._.Address; - Send_UEvent_Notify(Handler_Transmit_Buffer, event_data, &dest); - } - } - } - } -} - -/* This function tries to find the addresses of the defined devices. */ -/* It should be called periodically (example once per minute). */ -void Notification_Class_find_recipient(void) -{ - NOTIFICATION_CLASS_INFO *CurrentNotify; - BACNET_DESTINATION *pBacDest; - BACNET_ADDRESS src = {0}; - unsigned max_apdu = 0; - uint32_t notify_index; - uint32_t DeviceID; - uint8_t idx; - - - for (notify_index = 0; notify_index < MAX_NOTIFICATION_CLASSES; notify_index++) - { - /* pointer to current notification */ - CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(notify_index)]; - /* pointer to first recipient */ - pBacDest = &CurrentNotify->Recipient_List[0]; - for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++, pBacDest++) - { - if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { - /* Device ID */ - DeviceID = CurrentNotify->Recipient_List[idx].Recipient._.DeviceIdentifier; - /* Send who_ is request only when address of device is unknown. */ - if(!address_bind_request(DeviceID, &max_apdu, &src)) - Send_WhoIs(DeviceID, DeviceID); - } - else if(CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { - - } - } - } -} -#endif /* defined(INTRINSIC_REPORTING) */ + break; + } + + /* send notifications for active recipients */ + /* pointer to first recipient */ + pBacDest = &CurrentNotify->Recipient_List[0]; + for (index = 0; index < NC_MAX_RECIPIENTS; index++, pBacDest++) + { + /* check if recipient is defined */ + if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_NOTINITIALIZED) + break; /* recipient doesn't defined - end of list */ + + if (IsRecipientActive(pBacDest, event_data->toState) == true) + { + BACNET_ADDRESS dest; + uint32_t device_id; + unsigned max_apdu; + + /* Process Identifier */ + event_data->processIdentifier = pBacDest->ProcessIdentifier; + + /* send notification */ + if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { + /* send notification to the specified device */ + device_id = pBacDest->Recipient._.DeviceIdentifier; + + if (pBacDest->ConfirmedNotify == true) + Send_CEvent_Notify(device_id, event_data); + else if (address_get_by_device(device_id, &max_apdu, &dest)) + Send_UEvent_Notify(Handler_Transmit_Buffer, event_data, &dest); + } + else if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { + /* send notification to the address indicated */ + if (pBacDest->ConfirmedNotify == true) { + if (address_get_device_id(&dest, &device_id)) + Send_CEvent_Notify(device_id, event_data); + } + else { + dest = pBacDest->Recipient._.Address; + Send_UEvent_Notify(Handler_Transmit_Buffer, event_data, &dest); + } + } + } + } +} + +/* This function tries to find the addresses of the defined devices. */ +/* It should be called periodically (example once per minute). */ +void Notification_Class_find_recipient(void) +{ + NOTIFICATION_CLASS_INFO *CurrentNotify; + BACNET_DESTINATION *pBacDest; + BACNET_ADDRESS src = {0}; + unsigned max_apdu = 0; + uint32_t notify_index; + uint32_t DeviceID; + uint8_t idx; + + + for (notify_index = 0; notify_index < MAX_NOTIFICATION_CLASSES; notify_index++) + { + /* pointer to current notification */ + CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(notify_index)]; + /* pointer to first recipient */ + pBacDest = &CurrentNotify->Recipient_List[0]; + for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++, pBacDest++) + { + if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { + /* Device ID */ + DeviceID = CurrentNotify->Recipient_List[idx].Recipient._.DeviceIdentifier; + /* Send who_ is request only when address of device is unknown. */ + if(!address_bind_request(DeviceID, &max_apdu, &src)) + Send_WhoIs(DeviceID, DeviceID); + } + else if(CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { + + } + } + } +} +#endif /* defined(INTRINSIC_REPORTING) */ diff --git a/bacnet-stack/demo/object/nc.h b/bacnet-stack/demo/object/nc.h index b2898fe8..0dcc97d3 100644 --- a/bacnet-stack/demo/object/nc.h +++ b/bacnet-stack/demo/object/nc.h @@ -1,124 +1,124 @@ -/************************************************************************** -* -* Copyright (C) 2011 Krzysztof Malorny -* -* 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 NC_H -#define NC_H - -#include "event.h" - - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -#define NC_RESCAN_RECIPIENTS_SECS 60 - -/* max "length" of recipient_list */ -#define NC_MAX_RECIPIENTS 10 -/* Recipient types */ -typedef enum { - RECIPIENT_TYPE_NOTINITIALIZED = 0, - RECIPIENT_TYPE_DEVICE = 1, - RECIPIENT_TYPE_ADDRESS = 2 -} NC_RECIPIENT_TYPE; - - -/* BACnetRecipient sructuer*/ -/* -BACnetRecipient ::= CHOICE { - device [0] BACnetObjectIdentifier, - address [1] BACnetAddress -} -*/ -typedef struct BACnet_Recipient { - uint8_t RecipientType; /* Type of Recipient */ - union { - uint32_t DeviceIdentifier; - BACNET_ADDRESS Address; - } _; -} BACNET_RECIPIENT; - - - -/* BACnetDestination structure */ -typedef struct BACnet_Destination { - uint8_t ValidDays; - BACNET_TIME FromTime; - BACNET_TIME ToTime; - BACNET_RECIPIENT Recipient; - uint32_t ProcessIdentifier; - uint8_t Transitions; - bool ConfirmedNotify; -} BACNET_DESTINATION; - -/* Structure containing configuration for a Notification Class */ -typedef struct Notification_Class_info { - uint8_t Priority[3]; /* BACnetARRAY[3] of Unsigned */ - uint8_t Ack_Required; /* BACnetEventTransitionBits */ - BACNET_DESTINATION Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */ -} NOTIFICATION_CLASS_INFO; - - - void Notification_Class_Property_Lists( - const int **pRequired, - const int **pOptional, - const int **pProprietary); - - void Notification_Class_Init(void); - - bool Notification_Class_Valid_Instance( - uint32_t object_instance); - unsigned Notification_Class_Count(void); - uint32_t Notification_Class_Index_To_Instance(unsigned index); - unsigned Notification_Class_Instance_To_Index( - uint32_t object_instance); - bool Notification_Class_Object_Name( - uint32_t object_instance, - BACNET_CHARACTER_STRING *object_name); - - int Notification_Class_Read_Property( - BACNET_READ_PROPERTY_DATA * rpdata); - - bool Notification_Class_Write_Property( - BACNET_WRITE_PROPERTY_DATA * wp_data); - - void Notification_Class_common_reporting_function( - BACNET_EVENT_NOTIFICATION_DATA * event_data); - - void Notification_Class_find_recipient(void); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#define NOTIFICATION_CLASS_OBJ_FUNCTIONS \ - OBJECT_NOTIFICATION_CLASS, Notification_Class_Init, Notification_Class_Count, \ - Notification_Class_Index_To_Instance, Notification_Class_Valid_Instance, \ - Notification_Class_Object_Name, Notification_Class_Read_Property, \ - Notification_Class_Write_Property, Notification_Class_Property_Lists, \ - NULL, NULL, NULL - +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 NC_H +#define NC_H + +#include "event.h" + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define NC_RESCAN_RECIPIENTS_SECS 60 + +/* max "length" of recipient_list */ +#define NC_MAX_RECIPIENTS 10 +/* Recipient types */ +typedef enum { + RECIPIENT_TYPE_NOTINITIALIZED = 0, + RECIPIENT_TYPE_DEVICE = 1, + RECIPIENT_TYPE_ADDRESS = 2 +} NC_RECIPIENT_TYPE; + + +/* BACnetRecipient sructuer*/ +/* +BACnetRecipient ::= CHOICE { + device [0] BACnetObjectIdentifier, + address [1] BACnetAddress +} +*/ +typedef struct BACnet_Recipient { + uint8_t RecipientType; /* Type of Recipient */ + union { + uint32_t DeviceIdentifier; + BACNET_ADDRESS Address; + } _; +} BACNET_RECIPIENT; + + + +/* BACnetDestination structure */ +typedef struct BACnet_Destination { + uint8_t ValidDays; + BACNET_TIME FromTime; + BACNET_TIME ToTime; + BACNET_RECIPIENT Recipient; + uint32_t ProcessIdentifier; + uint8_t Transitions; + bool ConfirmedNotify; +} BACNET_DESTINATION; + +/* Structure containing configuration for a Notification Class */ +typedef struct Notification_Class_info { + uint8_t Priority[3]; /* BACnetARRAY[3] of Unsigned */ + uint8_t Ack_Required; /* BACnetEventTransitionBits */ + BACNET_DESTINATION Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */ +} NOTIFICATION_CLASS_INFO; + + + void Notification_Class_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + void Notification_Class_Init(void); + + bool Notification_Class_Valid_Instance( + uint32_t object_instance); + unsigned Notification_Class_Count(void); + uint32_t Notification_Class_Index_To_Instance(unsigned index); + unsigned Notification_Class_Instance_To_Index( + uint32_t object_instance); + bool Notification_Class_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING *object_name); + + int Notification_Class_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Notification_Class_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA * event_data); + + void Notification_Class_find_recipient(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define NOTIFICATION_CLASS_OBJ_FUNCTIONS \ + OBJECT_NOTIFICATION_CLASS, Notification_Class_Init, Notification_Class_Count, \ + Notification_Class_Index_To_Instance, Notification_Class_Valid_Instance, \ + Notification_Class_Object_Name, Notification_Class_Read_Property, \ + Notification_Class_Write_Property, Notification_Class_Property_Lists, \ + NULL, NULL, NULL + #endif /* NC_H */ diff --git a/bacnet-stack/include/lso.h b/bacnet-stack/include/lso.h index 0a935f81..80d97dd1 100644 --- a/bacnet-stack/include/lso.h +++ b/bacnet-stack/include/lso.h @@ -1,77 +1,77 @@ -/*####COPYRIGHTBEGIN#### - ------------------------------------------- - Copyright (C) 2006 John Minack - - 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 LSO_H -#define LSO_H - -#include -#include -#include "bacenum.h" -#include "bacdef.h" -#include "bacstr.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Life Safety Operation Service */ - - typedef struct { - uint32_t processId; - BACNET_CHARACTER_STRING requestingSrc; - BACNET_LIFE_SAFETY_OPERATION operation; - BACNET_OBJECT_ID targetObject; - } BACNET_LSO_DATA; - - - int lso_encode_adpu( - uint8_t * apdu, - uint8_t invoke_id, - BACNET_LSO_DATA * data); -/* decode the service request only */ - int lso_decode_service_request( - uint8_t * apdu, - unsigned apdu_len, - BACNET_LSO_DATA * data); - - -#ifdef TEST -#include "ctest.h" - void testLSO( - Test * pTest); -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2006 John Minack + + 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 LSO_H +#define LSO_H + +#include +#include +#include "bacenum.h" +#include "bacdef.h" +#include "bacstr.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Life Safety Operation Service */ + + typedef struct { + uint32_t processId; + BACNET_CHARACTER_STRING requestingSrc; + BACNET_LIFE_SAFETY_OPERATION operation; + BACNET_OBJECT_ID targetObject; + } BACNET_LSO_DATA; + + + int lso_encode_adpu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_LSO_DATA * data); +/* decode the service request only */ + int lso_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_LSO_DATA * data); + + +#ifdef TEST +#include "ctest.h" + void testLSO( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/include/wpm.h b/bacnet-stack/include/wpm.h index 6b70b496..7d75ee00 100644 --- a/bacnet-stack/include/wpm.h +++ b/bacnet-stack/include/wpm.h @@ -1,78 +1,78 @@ -/************************************************************************** -* -* Copyright (C) 2011 Krzysztof Malorny -* -* 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 WRITEPROPERTYMULTIPLE_H -#define WRITEPROPERTYMULTIPLE_H - -#include -#include -#include "bacdcode.h" -#include "bacapp.h" - - - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - - /* decode the service request only */ - int wpm_decode_object_id(uint8_t * apdu, uint16_t apdu_len, - BACNET_WRITE_PROPERTY_DATA * data); - - int wpm_decode_object_property(uint8_t * apdu, - uint16_t apdu_len, - BACNET_WRITE_PROPERTY_DATA * wpm_data); - - - /* encode service */ - int wpm_ack_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id); - - int wpm_error_ack_encode_apdu(uint8_t * apdu, uint8_t invoke_id, - BACNET_WRITE_PROPERTY_DATA * wp_data); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -/** @defgroup DSWP Data Sharing - Write Property Multiple Service (DS-WPM) - * @ingroup DataShare - * 15.10 WriteProperty Multiple Service
- * The WritePropertyMultiple service is used by a client BACnet-user - * to modify the value of one or more specified properties of a BACnet object. - * This service potentially allows write access to any property of any object, - * whether a BACnet-defined object or not. - * Properties shall be modified by the WritePropertyMultiple service - * in the order specified in the 'List of Write Access Specifications' parameter, - * and execution of the service shall continue until all of the specified - * properties have been written to or a property is encountered that - * for some reason cannot be modified as requested. - * Some implementors may wish to restrict write access to certain properties - * of certain objects. In such cases, an attempt to modify a restricted property - * shall result in the return of an error of 'Error Class' PROPERTY and 'Error Code' - * WRITE_ACCESS_DENIED. Note that these restricted properties may be accessible - * through the use of Virtual Terminal services or other means at the discretion - * of the implementor. -*/ -#endif +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 WRITEPROPERTYMULTIPLE_H +#define WRITEPROPERTYMULTIPLE_H + +#include +#include +#include "bacdcode.h" +#include "bacapp.h" + + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + /* decode the service request only */ + int wpm_decode_object_id(uint8_t * apdu, uint16_t apdu_len, + BACNET_WRITE_PROPERTY_DATA * data); + + int wpm_decode_object_property(uint8_t * apdu, + uint16_t apdu_len, + BACNET_WRITE_PROPERTY_DATA * wpm_data); + + + /* encode service */ + int wpm_ack_encode_apdu_init(uint8_t *apdu, uint8_t invoke_id); + + int wpm_error_ack_encode_apdu(uint8_t * apdu, uint8_t invoke_id, + BACNET_WRITE_PROPERTY_DATA * wp_data); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DSWP Data Sharing - Write Property Multiple Service (DS-WPM) + * @ingroup DataShare + * 15.10 WriteProperty Multiple Service
+ * The WritePropertyMultiple service is used by a client BACnet-user + * to modify the value of one or more specified properties of a BACnet object. + * This service potentially allows write access to any property of any object, + * whether a BACnet-defined object or not. + * Properties shall be modified by the WritePropertyMultiple service + * in the order specified in the 'List of Write Access Specifications' parameter, + * and execution of the service shall continue until all of the specified + * properties have been written to or a property is encountered that + * for some reason cannot be modified as requested. + * Some implementors may wish to restrict write access to certain properties + * of certain objects. In such cases, an attempt to modify a restricted property + * shall result in the return of an error of 'Error Class' PROPERTY and 'Error Code' + * WRITE_ACCESS_DENIED. Note that these restricted properties may be accessible + * through the use of Virtual Terminal services or other means at the discretion + * of the implementor. +*/ +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/bname.c b/bacnet-stack/ports/bdk-atxx4-mstp/bname.c index fcadc71b..09d913ee 100644 --- a/bacnet-stack/ports/bdk-atxx4-mstp/bname.c +++ b/bacnet-stack/ports/bdk-atxx4-mstp/bname.c @@ -1,275 +1,275 @@ -/************************************************************************** -* -* Copyright (C) 2011 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#include -#include -#include -#include -#include "hardware.h" -#include "bacdef.h" -#include "bacdcode.h" -#include "bacstr.h" -#include "nvdata.h" -#include "seeprom.h" -#include "device.h" -#include "bname.h" - -/* Basic UTF-8 manipulation routines - by Jeff Bezanson - placed in the public domain Fall 2005 */ -static const char trailingBytesForUTF8[256] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 -}; - -/* based on the valid_utf8 routine from the PCRE library by Philip Hazel - length is in bytes, since without knowing whether the string is valid - it's hard to know how many characters there are! */ -static int utf8_isvalid(const char *str, int length) -{ - const unsigned char *p, *pend = (unsigned char*)str + length; - unsigned char c; - int ab; - - for (p = (unsigned char*)str; p < pend; p++) { - c = *p; - /* null in middle of string */ - if (c == 0) { - return 0; - } - /* ASCII character */ - if (c < 128) { - continue; - } - if ((c & 0xc0) != 0xc0) { - return 0; - } - ab = trailingBytesForUTF8[c]; - if (length < ab) { - return 0; - } - length -= ab; - - p++; - /* Check top bits in the second byte */ - if ((*p & 0xc0) != 0x80) { - return 0; - } - /* Check for overlong sequences for each different length */ - switch (ab) { - /* Check for xx00 000x */ - case 1: - if ((c & 0x3e) == 0) return 0; - continue; /* We know there aren't any more bytes to check */ - - /* Check for 1110 0000, xx0x xxxx */ - case 2: - if (c == 0xe0 && (*p & 0x20) == 0) return 0; - break; - - /* Check for 1111 0000, xx00 xxxx */ - case 3: - if (c == 0xf0 && (*p & 0x30) == 0) return 0; - break; - - /* Check for 1111 1000, xx00 0xxx */ - case 4: - if (c == 0xf8 && (*p & 0x38) == 0) return 0; - break; - - /* Check for leading 0xfe or 0xff, - and then for 1111 1100, xx00 00xx */ - case 5: - if (c == 0xfe || c == 0xff || - (c == 0xfc && (*p & 0x3c) == 0)) return 0; - break; - } - - /* Check for valid bytes after the 2nd, if any; all must start 10 */ - while (--ab > 0) { - if ((*(++p) & 0xc0) != 0x80) return 0; - } - } - - return 1; -} - -static bool bacnet_name_isvalid( - uint8_t encoding, - uint8_t length, - char *str) -{ - bool valid = false; - - if ((encoding < MAX_CHARACTER_STRING_ENCODING) && - (length <= NV_EEPROM_NAME_SIZE)) { - if (encoding == CHARACTER_ANSI_X34) { - if (utf8_isvalid(str, length)) { - valid = true; - } - } else { - valid = true; - } - } - - return valid; -} - -bool bacnet_name_set( - uint16_t offset, - BACNET_CHARACTER_STRING *char_string) -{ - uint8_t encoding = 0; - uint8_t length = 0; - char *str = NULL; - - length = characterstring_length(char_string); - encoding = characterstring_encoding(char_string); - str = characterstring_value(char_string); - if (bacnet_name_isvalid(encoding, length, str)) { - seeprom_bytes_write( - NV_EEPROM_NAME_LENGTH(offset), - &length, 1); - seeprom_bytes_write( - NV_EEPROM_NAME_ENCODING(offset), - &encoding, 1); - seeprom_bytes_write( - NV_EEPROM_NAME_STRING(offset), - (uint8_t *)str, - length); - return true; - } - - return false; -} - -bool bacnet_name_write( - uint16_t offset, - BACNET_CHARACTER_STRING *char_string, - BACNET_ERROR_CLASS *error_class, - BACNET_ERROR_CODE *error_code) -{ - bool status = false; - size_t length = 0; - uint8_t encoding = 0; - - length = characterstring_length(char_string); - - if (length < 1) { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } else if (length <= NV_EEPROM_NAME_SIZE) { - encoding = characterstring_encoding(char_string); - if (encoding < MAX_CHARACTER_STRING_ENCODING) { - if (Device_Valid_Object_Name(char_string, NULL, NULL)) { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_DUPLICATE_NAME; - } else { - status = bacnet_name_set(offset, char_string); - if (status) { - Device_Inc_Database_Revision(); - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; - } - - return status; -} - -/* no required minumum length or duplicate checking */ -bool bacnet_name_write_other( - uint16_t offset, - BACNET_CHARACTER_STRING *char_string, - BACNET_ERROR_CLASS *error_class, - BACNET_ERROR_CODE *error_code) -{ - bool status = false; - size_t length = 0; - uint8_t encoding = 0; - - length = characterstring_length(char_string); - - if (length <= NV_EEPROM_NAME_SIZE) { - encoding = characterstring_encoding(char_string); - if (encoding < MAX_CHARACTER_STRING_ENCODING) { - status = bacnet_name_set(offset, char_string); - if (!status) { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; - } - - return status; -} - -void bacnet_name_init( - uint16_t offset, - BACNET_CHARACTER_STRING *char_string, - char *default_string) -{ - characterstring_init_ansi(char_string, default_string); - (void)bacnet_name_set(offset, char_string); -} - -void bacnet_name( - uint16_t offset, - BACNET_CHARACTER_STRING *char_string, - char *default_string) -{ - uint8_t encoding = 0; - uint8_t length = 0; - char name[NV_EEPROM_NAME_SIZE + 1] = ""; - - seeprom_bytes_read(NV_EEPROM_NAME_ENCODING(offset), &encoding, 1); - seeprom_bytes_read(NV_EEPROM_NAME_LENGTH(offset), &length, 1); - seeprom_bytes_read(NV_EEPROM_NAME_STRING(offset), (uint8_t *) & name, - NV_EEPROM_NAME_SIZE); - if (bacnet_name_isvalid(length, encoding, name)) { - characterstring_init(char_string, encoding, &name[0], length); - } else if (default_string) { - bacnet_name_init(offset, char_string, default_string); - } -} +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include "hardware.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "nvdata.h" +#include "seeprom.h" +#include "device.h" +#include "bname.h" + +/* Basic UTF-8 manipulation routines + by Jeff Bezanson + placed in the public domain Fall 2005 */ +static const char trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* based on the valid_utf8 routine from the PCRE library by Philip Hazel + length is in bytes, since without knowing whether the string is valid + it's hard to know how many characters there are! */ +static int utf8_isvalid(const char *str, int length) +{ + const unsigned char *p, *pend = (unsigned char*)str + length; + unsigned char c; + int ab; + + for (p = (unsigned char*)str; p < pend; p++) { + c = *p; + /* null in middle of string */ + if (c == 0) { + return 0; + } + /* ASCII character */ + if (c < 128) { + continue; + } + if ((c & 0xc0) != 0xc0) { + return 0; + } + ab = trailingBytesForUTF8[c]; + if (length < ab) { + return 0; + } + length -= ab; + + p++; + /* Check top bits in the second byte */ + if ((*p & 0xc0) != 0x80) { + return 0; + } + /* Check for overlong sequences for each different length */ + switch (ab) { + /* Check for xx00 000x */ + case 1: + if ((c & 0x3e) == 0) return 0; + continue; /* We know there aren't any more bytes to check */ + + /* Check for 1110 0000, xx0x xxxx */ + case 2: + if (c == 0xe0 && (*p & 0x20) == 0) return 0; + break; + + /* Check for 1111 0000, xx00 xxxx */ + case 3: + if (c == 0xf0 && (*p & 0x30) == 0) return 0; + break; + + /* Check for 1111 1000, xx00 0xxx */ + case 4: + if (c == 0xf8 && (*p & 0x38) == 0) return 0; + break; + + /* Check for leading 0xfe or 0xff, + and then for 1111 1100, xx00 00xx */ + case 5: + if (c == 0xfe || c == 0xff || + (c == 0xfc && (*p & 0x3c) == 0)) return 0; + break; + } + + /* Check for valid bytes after the 2nd, if any; all must start 10 */ + while (--ab > 0) { + if ((*(++p) & 0xc0) != 0x80) return 0; + } + } + + return 1; +} + +static bool bacnet_name_isvalid( + uint8_t encoding, + uint8_t length, + char *str) +{ + bool valid = false; + + if ((encoding < MAX_CHARACTER_STRING_ENCODING) && + (length <= NV_EEPROM_NAME_SIZE)) { + if (encoding == CHARACTER_ANSI_X34) { + if (utf8_isvalid(str, length)) { + valid = true; + } + } else { + valid = true; + } + } + + return valid; +} + +bool bacnet_name_set( + uint16_t offset, + BACNET_CHARACTER_STRING *char_string) +{ + uint8_t encoding = 0; + uint8_t length = 0; + char *str = NULL; + + length = characterstring_length(char_string); + encoding = characterstring_encoding(char_string); + str = characterstring_value(char_string); + if (bacnet_name_isvalid(encoding, length, str)) { + seeprom_bytes_write( + NV_EEPROM_NAME_LENGTH(offset), + &length, 1); + seeprom_bytes_write( + NV_EEPROM_NAME_ENCODING(offset), + &encoding, 1); + seeprom_bytes_write( + NV_EEPROM_NAME_STRING(offset), + (uint8_t *)str, + length); + return true; + } + + return false; +} + +bool bacnet_name_write( + uint16_t offset, + BACNET_CHARACTER_STRING *char_string, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + bool status = false; + size_t length = 0; + uint8_t encoding = 0; + + length = characterstring_length(char_string); + + if (length < 1) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else if (length <= NV_EEPROM_NAME_SIZE) { + encoding = characterstring_encoding(char_string); + if (encoding < MAX_CHARACTER_STRING_ENCODING) { + if (Device_Valid_Object_Name(char_string, NULL, NULL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_DUPLICATE_NAME; + } else { + status = bacnet_name_set(offset, char_string); + if (status) { + Device_Inc_Database_Revision(); + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + + return status; +} + +/* no required minumum length or duplicate checking */ +bool bacnet_name_write_other( + uint16_t offset, + BACNET_CHARACTER_STRING *char_string, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + bool status = false; + size_t length = 0; + uint8_t encoding = 0; + + length = characterstring_length(char_string); + + if (length <= NV_EEPROM_NAME_SIZE) { + encoding = characterstring_encoding(char_string); + if (encoding < MAX_CHARACTER_STRING_ENCODING) { + status = bacnet_name_set(offset, char_string); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + + return status; +} + +void bacnet_name_init( + uint16_t offset, + BACNET_CHARACTER_STRING *char_string, + char *default_string) +{ + characterstring_init_ansi(char_string, default_string); + (void)bacnet_name_set(offset, char_string); +} + +void bacnet_name( + uint16_t offset, + BACNET_CHARACTER_STRING *char_string, + char *default_string) +{ + uint8_t encoding = 0; + uint8_t length = 0; + char name[NV_EEPROM_NAME_SIZE + 1] = ""; + + seeprom_bytes_read(NV_EEPROM_NAME_ENCODING(offset), &encoding, 1); + seeprom_bytes_read(NV_EEPROM_NAME_LENGTH(offset), &length, 1); + seeprom_bytes_read(NV_EEPROM_NAME_STRING(offset), (uint8_t *) & name, + NV_EEPROM_NAME_SIZE); + if (bacnet_name_isvalid(length, encoding, name)) { + characterstring_init(char_string, encoding, &name[0], length); + } else if (default_string) { + bacnet_name_init(offset, char_string, default_string); + } +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/bname.h b/bacnet-stack/ports/bdk-atxx4-mstp/bname.h index af070223..fc89fed6 100644 --- a/bacnet-stack/ports/bdk-atxx4-mstp/bname.h +++ b/bacnet-stack/ports/bdk-atxx4-mstp/bname.h @@ -1,60 +1,60 @@ -/************************************************************************** -* -* Copyright (C) 2010 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 BACNET_NAME_H -#define BACNET_NAME_H - -#include -#include "bacstr.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - bool bacnet_name_set( - uint16_t eeprom_offset, - BACNET_CHARACTER_STRING *char_string); - void bacnet_name_init( - uint16_t eeprom_offset, - BACNET_CHARACTER_STRING *char_string, - char *default_string); - void bacnet_name( - uint16_t eeprom_offset, - BACNET_CHARACTER_STRING *char_string, - char *default_string); - bool bacnet_name_write( - uint16_t offset, - BACNET_CHARACTER_STRING *char_string, - BACNET_ERROR_CLASS *error_class, - BACNET_ERROR_CODE *error_code); - /* no required minumum length or duplicate checking */ - bool bacnet_name_write_other( - uint16_t offset, - BACNET_CHARACTER_STRING *char_string, - BACNET_ERROR_CLASS *error_class, - BACNET_ERROR_CODE *error_code); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/************************************************************************** +* +* Copyright (C) 2010 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 BACNET_NAME_H +#define BACNET_NAME_H + +#include +#include "bacstr.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool bacnet_name_set( + uint16_t eeprom_offset, + BACNET_CHARACTER_STRING *char_string); + void bacnet_name_init( + uint16_t eeprom_offset, + BACNET_CHARACTER_STRING *char_string, + char *default_string); + void bacnet_name( + uint16_t eeprom_offset, + BACNET_CHARACTER_STRING *char_string, + char *default_string); + bool bacnet_name_write( + uint16_t offset, + BACNET_CHARACTER_STRING *char_string, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code); + /* no required minumum length or duplicate checking */ + bool bacnet_name_write_other( + uint16_t offset, + BACNET_CHARACTER_STRING *char_string, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/stm32f10x/automac.c b/bacnet-stack/ports/stm32f10x/automac.c index 71abe645..9a389623 100644 --- a/bacnet-stack/ports/stm32f10x/automac.c +++ b/bacnet-stack/ports/stm32f10x/automac.c @@ -1,426 +1,426 @@ -/************************************************************************** -* -* Copyright (C) 2010 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 "bacdef.h" -#include "bacdcode.h" -#include "bacenum.h" -#include "config.h" -#include "mstpdef.h" -#include "automac.h" - -/* MS/TP Auto MAC address functionality */ - -/* table to track tokens and poll-for-master frames */ -typedef struct { - /* Poll For Master indicates empty slot */ - bool pfm:1; - /* a device that emits a frame indicates a used slot */ - bool emitter:1; - /* token - indicates a token was passed from this slot */ - /* important to know who the Next Station is */ - bool token:1; - /* reserve some slots for fixed addresses */ - bool reserved:1; -} AUTO_MAC_DATA; -/* starting number available for AutoMAC */ -#define MAC_SLOTS_OFFSET 32 -/* total number of slots */ -#define MAC_SLOTS_MAX 128 -static AUTO_MAC_DATA Auto_MAC_Data[MAC_SLOTS_MAX]; -/* my automatic MAC address */ -static uint8_t My_MAC_Address; -/* my no-token silence timer time slot in milliseconds */ -static uint16_t My_Time_Slot; -/* indication that PFM has happened for a full cycle */ -static bool PFM_Cycle_Complete; -/* indicate that we are an auto-mode node */ -static bool Auto_Mode_Enabled; - -/**************************************************************************** -* DESCRIPTION: Indication that we are an automode node -* RETURN: true if automode enabled -* NOTES: none -*****************************************************************************/ -bool automac_enabled(void) -{ - return Auto_Mode_Enabled; -} - -/**************************************************************************** -* DESCRIPTION: Sets the automode status -* RETURN: nothing -* NOTES: none -*****************************************************************************/ -void automac_enabled_set(bool status) -{ - Auto_Mode_Enabled = status; -} - -/**************************************************************************** -* DESCRIPTION: Indication that PFM has happened for a full cycle -* RETURN: true if full -* NOTES: none -*****************************************************************************/ -bool automac_pfm_cycle_complete(void) -{ - return PFM_Cycle_Complete; -} - -/**************************************************************************** -* DESCRIPTION: Indicates that an address is used or taken -* RETURN: true if used -* NOTES: none -*****************************************************************************/ -static bool automac_address_used(uint8_t mac) -{ - bool status = false; - - if (mac < MAC_SLOTS_MAX) { - if ((Auto_MAC_Data[mac].emitter) || - (Auto_MAC_Data[mac].reserved) || - (Auto_MAC_Data[mac].token)) { - status = true; - } - } - - return status; -} - -/**************************************************************************** -* DESCRIPTION: Validates an address as available, not taken, and within bounds -* RETURN: true if valid -* NOTES: none -*****************************************************************************/ -bool automac_free_address_valid(uint8_t mac) -{ - bool status = false; - - if (mac < MAC_SLOTS_MAX) { - if ((Auto_MAC_Data[mac].pfm) && - (!automac_address_used(mac))) { - status = true; - } - } - - return status; -} - -/**************************************************************************** -* DESCRIPTION: Determines the next station address to send the token -* RETURN: Next_Station, or 255 if there are no next stations -* NOTES: none -*****************************************************************************/ -uint8_t automac_next_station(uint8_t mac) -{ - uint8_t i = 0; /* loop counter */ - uint8_t next_station = 255; /* return value */ - uint8_t test_station = 0; /* station number to test for token */ - - test_station = (mac + 1) % 128; - for (i = 0; i < MAC_SLOTS_MAX; i++) { - if (Auto_MAC_Data[test_station].token) { - next_station = test_station; - break; - } - test_station = (test_station + 1) % MAC_SLOTS_MAX; - } - - return next_station; -} - -/**************************************************************************** -* DESCRIPTION: Determines the number of free MAC addresses -* RETURN: Number of free MAC addresses -* NOTES: none -*****************************************************************************/ -uint8_t automac_free_address_count(void) -{ - uint8_t i = 0; - uint8_t slots = 0; - - for (i = 0; i < MAC_SLOTS_MAX; i++) { - if (automac_free_address_valid(i)) { - slots++; - } - } - - return slots; -} - -/**************************************************************************** -* DESCRIPTION: Determines the number of free MAC addresses -* RETURN: Number of free MAC addresses -* NOTES: none -*****************************************************************************/ -uint8_t automac_free_address_mac(uint8_t count) -{ - uint8_t i = 0; - uint8_t slots = 0; - uint8_t mac = 255; - - for (i = 0; i < MAC_SLOTS_MAX; i++) { - if (automac_free_address_valid(i)) { - if (slots == count) { - mac = i; - break; - } - slots++; - } - } - - return mac; -} - -/**************************************************************************** -* DESCRIPTION: Gets a free random address to use -* RETURN: free MAC addresses -* NOTES: none -*****************************************************************************/ -uint8_t automac_free_address_random(void) -{ - uint8_t count = 0; - uint8_t random_count = 0; - uint8_t mac = 255; - - count = automac_free_address_count(); - if (count) { - random_count = rand()%count; - mac = automac_free_address_mac(random_count); - } - - return mac; -} - -/**************************************************************************** -* DESCRIPTION: Gets the address stored. -* RETURN: MAC addresses -* NOTES: none -*****************************************************************************/ -uint8_t automac_address(void) -{ - return My_MAC_Address; -} - -/**************************************************************************** -* DESCRIPTION: Sets the MAC address -* RETURN: MAC addresses -* NOTES: none -*****************************************************************************/ -void automac_address_set(uint8_t mac) -{ - My_MAC_Address = mac; -} - -/**************************************************************************** -* DESCRIPTION: Gets the address stored. -* RETURN: MAC addresses -* NOTES: none -*****************************************************************************/ -uint16_t automac_time_slot(void) -{ - return My_Time_Slot; -} - -/**************************************************************************** -* DESCRIPTION: Sets the MAC address -* RETURN: MAC addresses -* NOTES: none -*****************************************************************************/ -void automac_address_init(void) -{ - My_MAC_Address = MAC_SLOTS_OFFSET + rand()%(MAC_SLOTS_MAX-MAC_SLOTS_OFFSET); - /* at least as long as a dropped token - worst case */ - My_Time_Slot = Tno_token + (MAC_SLOTS_MAX * Tslot); - My_Time_Slot += (uint16_t)My_MAC_Address * Tslot; -} - -/**************************************************************************** -* DESCRIPTION: Sets an open address slot -* RETURN: nothing -* NOTES: none -*****************************************************************************/ -void automac_pfm_set(uint8_t mac) -{ - if (mac < MAC_SLOTS_MAX) { - if (Auto_MAC_Data[mac].pfm) { - /* indicate that we have completed enough PFM to continue */ - if (automac_free_address_count() > 0) { - PFM_Cycle_Complete = true; - } - } - Auto_MAC_Data[mac].pfm = true; - } -} - -/**************************************************************************** -* DESCRIPTION: Sets a used address slot -* RETURN: nothing -* NOTES: none -*****************************************************************************/ -void automac_token_set(uint8_t mac) -{ - if (mac < MAC_SLOTS_MAX) { - Auto_MAC_Data[mac].token = true; - } -} - -/**************************************************************************** -* DESCRIPTION: Sets a used address slot -* RETURN: nothing -* NOTES: none -*****************************************************************************/ -void automac_emitter_set(uint8_t mac) -{ - if (mac < MAC_SLOTS_MAX) { - Auto_MAC_Data[mac].emitter = true; - } -} - -/**************************************************************************** -* DESCRIPTION: Initializes the auto MAC module -* RETURN: nothing -* NOTES: none -*****************************************************************************/ -void automac_init(void) -{ - uint8_t i = 0; - - for (i = 0; i < MAC_SLOTS_MAX; i++) { - Auto_MAC_Data[i].token = false; - Auto_MAC_Data[i].emitter = false; - Auto_MAC_Data[i].pfm = false; - if (i < MAC_SLOTS_OFFSET) { - Auto_MAC_Data[i].reserved = true; - } else { - Auto_MAC_Data[i].reserved = false; - } - } - automac_address_init(); - PFM_Cycle_Complete = false; -} - -#ifdef TEST -#include -#include -#include -#include "ctest.h" - -/* test the ring buffer */ -void test_Auto_MAC( - Test * pTest) -{ - uint8_t mac = 255; - - automac_init(); - ct_test(pTest, automac_free_address_count() == 0); - mac = automac_free_address_mac(0); - ct_test(pTest, mac == 255); - automac_pfm_set(MAC_SLOTS_OFFSET); - mac = automac_free_address_mac(0); - ct_test(pTest, mac == MAC_SLOTS_OFFSET); - ct_test(pTest, automac_free_address_count() == 1); - automac_token_set(MAC_SLOTS_OFFSET); - mac = automac_free_address_mac(0); - ct_test(pTest, mac == 255); - ct_test(pTest, automac_free_address_count() == 0); - automac_pfm_set(127); - mac = automac_free_address_mac(0); - ct_test(pTest, mac == 127); - ct_test(pTest, automac_free_address_count() == 1); - automac_token_set(127); - mac = automac_free_address_mac(0); - ct_test(pTest, mac == 255); - ct_test(pTest, automac_free_address_count() == 0); - /* the ANSI rand() uses consistent sequence based on seed */ - srand(42); - mac = automac_free_address_random(); - ct_test(pTest, mac == 255); - automac_pfm_set(MAC_SLOTS_OFFSET+1); - mac = automac_free_address_mac(0); - ct_test(pTest, mac == (MAC_SLOTS_OFFSET+1)); - mac = automac_free_address_random(); - ct_test(pTest, mac == (MAC_SLOTS_OFFSET+1)); - /* test 2 free addresses */ - automac_pfm_set(MAC_SLOTS_OFFSET+2); - mac = automac_free_address_mac(0); - ct_test(pTest, mac == (MAC_SLOTS_OFFSET+1)); - mac = automac_free_address_mac(1); - ct_test(pTest, mac == (MAC_SLOTS_OFFSET+2)); - mac = automac_free_address_random(); - ct_test(pTest, - (mac == (MAC_SLOTS_OFFSET+1)) || - (mac == (MAC_SLOTS_OFFSET+2))); - /* test 3 free addresses */ - automac_pfm_set(126); - mac = automac_free_address_mac(0); - ct_test(pTest, mac == (MAC_SLOTS_OFFSET+1)); - mac = automac_free_address_mac(1); - ct_test(pTest, mac == (MAC_SLOTS_OFFSET+2)); - mac = automac_free_address_mac(2); - ct_test(pTest, mac == 126); - mac = automac_free_address_random(); - ct_test(pTest, - (mac == (MAC_SLOTS_OFFSET+1))|| - (mac == (MAC_SLOTS_OFFSET+2))|| - (mac == 126)); - /* test the stored address */ - mac = automac_address(); - ct_test(pTest, mac < MAC_SLOTS_MAX); - automac_address_set(MAC_SLOTS_OFFSET); - mac = automac_address(); - ct_test(pTest, mac == MAC_SLOTS_OFFSET); - - automac_init(); - automac_token_set(0x6B); - mac = automac_next_station(0x25); - ct_test(pTest, mac == 0x6B); -} - -#ifdef TEST_AUTOMAC -int main( - void) -{ - Test *pTest; - bool rc; - - pTest = ct_create("Auto MAC", NULL); - - /* individual tests */ - rc = ct_addTestFunction(pTest, test_Auto_MAC); - assert(rc); - - ct_setStream(pTest, stdout); - ct_run(pTest); - (void) ct_report(pTest); - - ct_destroy(pTest); - - return 0; -} -#endif -#endif - +/************************************************************************** +* +* Copyright (C) 2010 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 "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "mstpdef.h" +#include "automac.h" + +/* MS/TP Auto MAC address functionality */ + +/* table to track tokens and poll-for-master frames */ +typedef struct { + /* Poll For Master indicates empty slot */ + bool pfm:1; + /* a device that emits a frame indicates a used slot */ + bool emitter:1; + /* token - indicates a token was passed from this slot */ + /* important to know who the Next Station is */ + bool token:1; + /* reserve some slots for fixed addresses */ + bool reserved:1; +} AUTO_MAC_DATA; +/* starting number available for AutoMAC */ +#define MAC_SLOTS_OFFSET 32 +/* total number of slots */ +#define MAC_SLOTS_MAX 128 +static AUTO_MAC_DATA Auto_MAC_Data[MAC_SLOTS_MAX]; +/* my automatic MAC address */ +static uint8_t My_MAC_Address; +/* my no-token silence timer time slot in milliseconds */ +static uint16_t My_Time_Slot; +/* indication that PFM has happened for a full cycle */ +static bool PFM_Cycle_Complete; +/* indicate that we are an auto-mode node */ +static bool Auto_Mode_Enabled; + +/**************************************************************************** +* DESCRIPTION: Indication that we are an automode node +* RETURN: true if automode enabled +* NOTES: none +*****************************************************************************/ +bool automac_enabled(void) +{ + return Auto_Mode_Enabled; +} + +/**************************************************************************** +* DESCRIPTION: Sets the automode status +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_enabled_set(bool status) +{ + Auto_Mode_Enabled = status; +} + +/**************************************************************************** +* DESCRIPTION: Indication that PFM has happened for a full cycle +* RETURN: true if full +* NOTES: none +*****************************************************************************/ +bool automac_pfm_cycle_complete(void) +{ + return PFM_Cycle_Complete; +} + +/**************************************************************************** +* DESCRIPTION: Indicates that an address is used or taken +* RETURN: true if used +* NOTES: none +*****************************************************************************/ +static bool automac_address_used(uint8_t mac) +{ + bool status = false; + + if (mac < MAC_SLOTS_MAX) { + if ((Auto_MAC_Data[mac].emitter) || + (Auto_MAC_Data[mac].reserved) || + (Auto_MAC_Data[mac].token)) { + status = true; + } + } + + return status; +} + +/**************************************************************************** +* DESCRIPTION: Validates an address as available, not taken, and within bounds +* RETURN: true if valid +* NOTES: none +*****************************************************************************/ +bool automac_free_address_valid(uint8_t mac) +{ + bool status = false; + + if (mac < MAC_SLOTS_MAX) { + if ((Auto_MAC_Data[mac].pfm) && + (!automac_address_used(mac))) { + status = true; + } + } + + return status; +} + +/**************************************************************************** +* DESCRIPTION: Determines the next station address to send the token +* RETURN: Next_Station, or 255 if there are no next stations +* NOTES: none +*****************************************************************************/ +uint8_t automac_next_station(uint8_t mac) +{ + uint8_t i = 0; /* loop counter */ + uint8_t next_station = 255; /* return value */ + uint8_t test_station = 0; /* station number to test for token */ + + test_station = (mac + 1) % 128; + for (i = 0; i < MAC_SLOTS_MAX; i++) { + if (Auto_MAC_Data[test_station].token) { + next_station = test_station; + break; + } + test_station = (test_station + 1) % MAC_SLOTS_MAX; + } + + return next_station; +} + +/**************************************************************************** +* DESCRIPTION: Determines the number of free MAC addresses +* RETURN: Number of free MAC addresses +* NOTES: none +*****************************************************************************/ +uint8_t automac_free_address_count(void) +{ + uint8_t i = 0; + uint8_t slots = 0; + + for (i = 0; i < MAC_SLOTS_MAX; i++) { + if (automac_free_address_valid(i)) { + slots++; + } + } + + return slots; +} + +/**************************************************************************** +* DESCRIPTION: Determines the number of free MAC addresses +* RETURN: Number of free MAC addresses +* NOTES: none +*****************************************************************************/ +uint8_t automac_free_address_mac(uint8_t count) +{ + uint8_t i = 0; + uint8_t slots = 0; + uint8_t mac = 255; + + for (i = 0; i < MAC_SLOTS_MAX; i++) { + if (automac_free_address_valid(i)) { + if (slots == count) { + mac = i; + break; + } + slots++; + } + } + + return mac; +} + +/**************************************************************************** +* DESCRIPTION: Gets a free random address to use +* RETURN: free MAC addresses +* NOTES: none +*****************************************************************************/ +uint8_t automac_free_address_random(void) +{ + uint8_t count = 0; + uint8_t random_count = 0; + uint8_t mac = 255; + + count = automac_free_address_count(); + if (count) { + random_count = rand()%count; + mac = automac_free_address_mac(random_count); + } + + return mac; +} + +/**************************************************************************** +* DESCRIPTION: Gets the address stored. +* RETURN: MAC addresses +* NOTES: none +*****************************************************************************/ +uint8_t automac_address(void) +{ + return My_MAC_Address; +} + +/**************************************************************************** +* DESCRIPTION: Sets the MAC address +* RETURN: MAC addresses +* NOTES: none +*****************************************************************************/ +void automac_address_set(uint8_t mac) +{ + My_MAC_Address = mac; +} + +/**************************************************************************** +* DESCRIPTION: Gets the address stored. +* RETURN: MAC addresses +* NOTES: none +*****************************************************************************/ +uint16_t automac_time_slot(void) +{ + return My_Time_Slot; +} + +/**************************************************************************** +* DESCRIPTION: Sets the MAC address +* RETURN: MAC addresses +* NOTES: none +*****************************************************************************/ +void automac_address_init(void) +{ + My_MAC_Address = MAC_SLOTS_OFFSET + rand()%(MAC_SLOTS_MAX-MAC_SLOTS_OFFSET); + /* at least as long as a dropped token - worst case */ + My_Time_Slot = Tno_token + (MAC_SLOTS_MAX * Tslot); + My_Time_Slot += (uint16_t)My_MAC_Address * Tslot; +} + +/**************************************************************************** +* DESCRIPTION: Sets an open address slot +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_pfm_set(uint8_t mac) +{ + if (mac < MAC_SLOTS_MAX) { + if (Auto_MAC_Data[mac].pfm) { + /* indicate that we have completed enough PFM to continue */ + if (automac_free_address_count() > 0) { + PFM_Cycle_Complete = true; + } + } + Auto_MAC_Data[mac].pfm = true; + } +} + +/**************************************************************************** +* DESCRIPTION: Sets a used address slot +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_token_set(uint8_t mac) +{ + if (mac < MAC_SLOTS_MAX) { + Auto_MAC_Data[mac].token = true; + } +} + +/**************************************************************************** +* DESCRIPTION: Sets a used address slot +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_emitter_set(uint8_t mac) +{ + if (mac < MAC_SLOTS_MAX) { + Auto_MAC_Data[mac].emitter = true; + } +} + +/**************************************************************************** +* DESCRIPTION: Initializes the auto MAC module +* RETURN: nothing +* NOTES: none +*****************************************************************************/ +void automac_init(void) +{ + uint8_t i = 0; + + for (i = 0; i < MAC_SLOTS_MAX; i++) { + Auto_MAC_Data[i].token = false; + Auto_MAC_Data[i].emitter = false; + Auto_MAC_Data[i].pfm = false; + if (i < MAC_SLOTS_OFFSET) { + Auto_MAC_Data[i].reserved = true; + } else { + Auto_MAC_Data[i].reserved = false; + } + } + automac_address_init(); + PFM_Cycle_Complete = false; +} + +#ifdef TEST +#include +#include +#include +#include "ctest.h" + +/* test the ring buffer */ +void test_Auto_MAC( + Test * pTest) +{ + uint8_t mac = 255; + + automac_init(); + ct_test(pTest, automac_free_address_count() == 0); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == 255); + automac_pfm_set(MAC_SLOTS_OFFSET); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == MAC_SLOTS_OFFSET); + ct_test(pTest, automac_free_address_count() == 1); + automac_token_set(MAC_SLOTS_OFFSET); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == 255); + ct_test(pTest, automac_free_address_count() == 0); + automac_pfm_set(127); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == 127); + ct_test(pTest, automac_free_address_count() == 1); + automac_token_set(127); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == 255); + ct_test(pTest, automac_free_address_count() == 0); + /* the ANSI rand() uses consistent sequence based on seed */ + srand(42); + mac = automac_free_address_random(); + ct_test(pTest, mac == 255); + automac_pfm_set(MAC_SLOTS_OFFSET+1); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET+1)); + mac = automac_free_address_random(); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET+1)); + /* test 2 free addresses */ + automac_pfm_set(MAC_SLOTS_OFFSET+2); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET+1)); + mac = automac_free_address_mac(1); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET+2)); + mac = automac_free_address_random(); + ct_test(pTest, + (mac == (MAC_SLOTS_OFFSET+1)) || + (mac == (MAC_SLOTS_OFFSET+2))); + /* test 3 free addresses */ + automac_pfm_set(126); + mac = automac_free_address_mac(0); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET+1)); + mac = automac_free_address_mac(1); + ct_test(pTest, mac == (MAC_SLOTS_OFFSET+2)); + mac = automac_free_address_mac(2); + ct_test(pTest, mac == 126); + mac = automac_free_address_random(); + ct_test(pTest, + (mac == (MAC_SLOTS_OFFSET+1))|| + (mac == (MAC_SLOTS_OFFSET+2))|| + (mac == 126)); + /* test the stored address */ + mac = automac_address(); + ct_test(pTest, mac < MAC_SLOTS_MAX); + automac_address_set(MAC_SLOTS_OFFSET); + mac = automac_address(); + ct_test(pTest, mac == MAC_SLOTS_OFFSET); + + automac_init(); + automac_token_set(0x6B); + mac = automac_next_station(0x25); + ct_test(pTest, mac == 0x6B); +} + +#ifdef TEST_AUTOMAC +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("Auto MAC", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, test_Auto_MAC); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif + diff --git a/bacnet-stack/ports/stm32f10x/automac.h b/bacnet-stack/ports/stm32f10x/automac.h index bf3b9294..73b5f76d 100644 --- a/bacnet-stack/ports/stm32f10x/automac.h +++ b/bacnet-stack/ports/stm32f10x/automac.h @@ -1,59 +1,59 @@ -/************************************************************************** -* -* Copyright (C) 2010 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 AUTOMAC_H -#define AUTOMAC_H - -#include -#include - -/* MS/TP Auto MAC address functionality */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - void automac_init(void); - - bool automac_free_address_valid(uint8_t mac); - uint8_t automac_free_address_count(void); - uint8_t automac_free_address_mac(uint8_t count); - uint8_t automac_free_address_random(void); - void automac_pfm_set(uint8_t mac); - void automac_token_set(uint8_t mac); - void automac_emitter_set(uint8_t mac); - uint8_t automac_next_station(uint8_t mac); - uint8_t automac_address(void); - void automac_address_set(uint8_t mac); - void automac_address_init(void); - uint16_t automac_time_slot(void); - bool automac_pfm_cycle_complete(void); - bool automac_enabled(void); - void automac_enabled_set(bool status); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif +/************************************************************************** +* +* Copyright (C) 2010 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 AUTOMAC_H +#define AUTOMAC_H + +#include +#include + +/* MS/TP Auto MAC address functionality */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void automac_init(void); + + bool automac_free_address_valid(uint8_t mac); + uint8_t automac_free_address_count(void); + uint8_t automac_free_address_mac(uint8_t count); + uint8_t automac_free_address_random(void); + void automac_pfm_set(uint8_t mac); + void automac_token_set(uint8_t mac); + void automac_emitter_set(uint8_t mac); + uint8_t automac_next_station(uint8_t mac); + uint8_t automac_address(void); + void automac_address_set(uint8_t mac); + void automac_address_init(void); + uint16_t automac_time_slot(void); + bool automac_pfm_cycle_complete(void); + bool automac_enabled(void); + void automac_enabled_set(bool status); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bacnet-stack/ports/stm32f10x/bacnet.c b/bacnet-stack/ports/stm32f10x/bacnet.c index 4fb1c0a4..0c922bba 100644 --- a/bacnet-stack/ports/stm32f10x/bacnet.c +++ b/bacnet-stack/ports/stm32f10x/bacnet.c @@ -1,133 +1,133 @@ -/************************************************************************** -* -* Copyright (C) 2011 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#include -#include -/* hardware layer includes */ -#include "hardware.h" -#include "timer.h" -#include "rs485.h" -/* BACnet Stack includes */ -#include "datalink.h" -#include "npdu.h" -#include "handlers.h" -#include "client.h" -#include "txbuf.h" -#include "dcc.h" -#include "iam.h" -/* BACnet objects */ -#include "device.h" -#include "bo.h" -/* me */ -#include "bacnet.h" - -/* timer for device communications control */ -static struct itimer DCC_Timer; -#define DCC_CYCLE_SECONDS 1 - -void bacnet_init( - void) -{ - dlmstp_set_mac_address(255); - dlmstp_set_max_master(127); - /* initialize datalink layer */ - dlmstp_init(NULL); - /* initialize objects */ - Device_Init(NULL); - - /* set up our confirmed service unrecognized service handler - required! */ - apdu_set_unrecognized_service_handler_handler - (handler_unrecognized_service); - /* we need to handle who-is to support dynamic device binding */ - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); - /* 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_READ_PROP_MULTIPLE, - handler_read_property_multiple); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, - handler_reinitialize_device); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, - handler_write_property); - /* handle communication so we can shutup when asked */ - apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, - handler_device_communication_control); - /* start the cyclic 1 second timer for DCC */ - timer_interval_start_seconds(&DCC_Timer, DCC_CYCLE_SECONDS); - /* Hello World! */ - Send_I_Am(&Handler_Transmit_Buffer[0]); -} - -static uint8_t PDUBuffer[MAX_MPDU]; -void bacnet_task( - void) -{ - uint16_t pdu_len; - BACNET_ADDRESS src; /* source address */ - uint8_t i; - BACNET_BINARY_PV binary_value = BINARY_INACTIVE; - BACNET_POLARITY polarity; - bool out_of_service; - - /* Binary Output */ - for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { - out_of_service = Binary_Output_Out_Of_Service(i); - if (!out_of_service) { - binary_value = Binary_Output_Present_Value(i); - polarity = Binary_Output_Polarity(i); - if (polarity != POLARITY_NORMAL) { - if (binary_value == BINARY_ACTIVE) { - binary_value = BINARY_INACTIVE; - } else { - binary_value = BINARY_ACTIVE; - } - } - if (binary_value == BINARY_ACTIVE) { - if (i == 0) { - /* led_on(LED_2); */ - } else { - /* led_on(LED_3); */ - } - } else { - if (i == 0) { - /* led_off(LED_2); */ - } else { - /* led_off(LED_3); */ - } - } - } - } - /* handle the communication timer */ - if (timer_interval_expired(&DCC_Timer)) { - timer_interval_reset(&DCC_Timer); - dcc_timer_seconds(DCC_CYCLE_SECONDS); - } - /* handle the messaging */ - pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); - if (pdu_len) { - npdu_handler(&src, &PDUBuffer[0], pdu_len); - } -} +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +/* hardware layer includes */ +#include "hardware.h" +#include "timer.h" +#include "rs485.h" +/* BACnet Stack includes */ +#include "datalink.h" +#include "npdu.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dcc.h" +#include "iam.h" +/* BACnet objects */ +#include "device.h" +#include "bo.h" +/* me */ +#include "bacnet.h" + +/* timer for device communications control */ +static struct itimer DCC_Timer; +#define DCC_CYCLE_SECONDS 1 + +void bacnet_init( + void) +{ + dlmstp_set_mac_address(255); + dlmstp_set_max_master(127); + /* initialize datalink layer */ + dlmstp_init(NULL); + /* initialize objects */ + Device_Init(NULL); + + /* set up our confirmed service unrecognized service handler - required! */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* 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_READ_PROP_MULTIPLE, + handler_read_property_multiple); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + /* start the cyclic 1 second timer for DCC */ + timer_interval_start_seconds(&DCC_Timer, DCC_CYCLE_SECONDS); + /* Hello World! */ + Send_I_Am(&Handler_Transmit_Buffer[0]); +} + +static uint8_t PDUBuffer[MAX_MPDU]; +void bacnet_task( + void) +{ + uint16_t pdu_len; + BACNET_ADDRESS src; /* source address */ + uint8_t i; + BACNET_BINARY_PV binary_value = BINARY_INACTIVE; + BACNET_POLARITY polarity; + bool out_of_service; + + /* Binary Output */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + out_of_service = Binary_Output_Out_Of_Service(i); + if (!out_of_service) { + binary_value = Binary_Output_Present_Value(i); + polarity = Binary_Output_Polarity(i); + if (polarity != POLARITY_NORMAL) { + if (binary_value == BINARY_ACTIVE) { + binary_value = BINARY_INACTIVE; + } else { + binary_value = BINARY_ACTIVE; + } + } + if (binary_value == BINARY_ACTIVE) { + if (i == 0) { + /* led_on(LED_2); */ + } else { + /* led_on(LED_3); */ + } + } else { + if (i == 0) { + /* led_off(LED_2); */ + } else { + /* led_off(LED_3); */ + } + } + } + } + /* handle the communication timer */ + if (timer_interval_expired(&DCC_Timer)) { + timer_interval_reset(&DCC_Timer); + dcc_timer_seconds(DCC_CYCLE_SECONDS); + } + /* handle the messaging */ + pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); + if (pdu_len) { + npdu_handler(&src, &PDUBuffer[0], pdu_len); + } +} diff --git a/bacnet-stack/ports/stm32f10x/bacnet.h b/bacnet-stack/ports/stm32f10x/bacnet.h index 5bd3f191..ee352604 100644 --- a/bacnet-stack/ports/stm32f10x/bacnet.h +++ b/bacnet-stack/ports/stm32f10x/bacnet.h @@ -1,41 +1,41 @@ -/************************************************************************** -* -* Copyright (C) 2010 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 BACNET_H -#define BACNET_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - void bacnet_init( - void); - void bacnet_task( - void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/************************************************************************** +* +* Copyright (C) 2010 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 BACNET_H +#define BACNET_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void bacnet_init( + void); + void bacnet_task( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/stm32f10x/bo.c b/bacnet-stack/ports/stm32f10x/bo.c index 4b2489f2..38535212 100644 --- a/bacnet-stack/ports/stm32f10x/bo.c +++ b/bacnet-stack/ports/stm32f10x/bo.c @@ -1,500 +1,500 @@ -/************************************************************************** -* -* Copyright (C) 2011 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ - -/* Binary Output Objects - customize for your use */ - -#include -#include -#include -#include "bacdef.h" -#include "bacdcode.h" -#include "bacenum.h" -#include "config.h" -#include "wp.h" -#include "hardware.h" -#include "handlers.h" -#include "bo.h" - -#ifndef MAX_BINARY_OUTPUTS -#error Please define MAX_BINARY_OUTPUTS -#endif - -/* When all the priorities are level null, the present value returns */ -/* the Relinquish Default value */ -#define RELINQUISH_DEFAULT BINARY_INACTIVE -/* Here is our Priority Array.*/ -static uint8_t Binary_Output_Level[MAX_BINARY_OUTPUTS][BACNET_MAX_PRIORITY]; -/* Writable out-of-service allows others to play with our Present Value */ -/* without changing the physical output */ -static uint8_t Out_Of_Service[MAX_BINARY_OUTPUTS]; -/* polarity - normal or inverse */ -static uint8_t Polarity[MAX_BINARY_OUTPUTS]; - -/* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Binary_Output_Properties_Required[] = { - PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - PROP_OBJECT_TYPE, - PROP_PRESENT_VALUE, - PROP_STATUS_FLAGS, - PROP_EVENT_STATE, - PROP_OUT_OF_SERVICE, - PROP_POLARITY, - PROP_PRIORITY_ARRAY, - PROP_RELINQUISH_DEFAULT, - -1 -}; - -static const int Binary_Output_Properties_Optional[] = { - PROP_ACTIVE_TEXT, - PROP_INACTIVE_TEXT, - -1 -}; - -static const int Binary_Output_Properties_Proprietary[] = { - -1 -}; - -void Binary_Output_Property_Lists( - const int **pRequired, - const int **pOptional, - const int **pProprietary) -{ - if (pRequired) - *pRequired = Binary_Output_Properties_Required; - if (pOptional) - *pOptional = Binary_Output_Properties_Optional; - if (pProprietary) - *pProprietary = Binary_Output_Properties_Proprietary; - - return; -} - -/* we simply have 0-n object instances. */ -bool Binary_Output_Valid_Instance( - uint32_t object_instance) -{ - if (object_instance < MAX_BINARY_OUTPUTS) - return true; - - return false; -} - -/* we simply have 0-n object instances. */ -unsigned Binary_Output_Count( - void) -{ - return MAX_BINARY_OUTPUTS; -} - -/* we simply have 0-n object instances. */ -uint32_t Binary_Output_Index_To_Instance( - unsigned index) -{ - return index; -} - -/* we simply have 0-n object instances. */ -unsigned Binary_Output_Instance_To_Index( - uint32_t object_instance) -{ - unsigned index = MAX_BINARY_OUTPUTS; - - if (object_instance < MAX_BINARY_OUTPUTS) - index = object_instance; - - return index; -} - -static BACNET_BINARY_PV Present_Value( - unsigned int index) -{ - BACNET_BINARY_PV value = RELINQUISH_DEFAULT; - BACNET_BINARY_PV current_value = RELINQUISH_DEFAULT; - unsigned i = 0; - - if (index < MAX_BINARY_OUTPUTS) { - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - current_value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; - if (current_value != BINARY_NULL) { - value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; - break; - } - } - } - - return value; -} - -BACNET_BINARY_PV Binary_Output_Present_Value( - uint32_t object_instance) -{ - unsigned index = 0; - - index = Binary_Output_Instance_To_Index(object_instance); - - return Present_Value(index); -} - -bool Binary_Output_Present_Value_Set( - uint32_t instance, - BACNET_BINARY_PV binary_value, - unsigned priority) -{ /* 0..15 */ - bool status = false; - - if (instance < MAX_BINARY_OUTPUTS) { - if (priority < BACNET_MAX_PRIORITY) { - Binary_Output_Level[instance][priority] = (uint8_t) binary_value; - status = true; - } - } - - return status; -} - -static void Binary_Output_Polarity_Set( - uint32_t instance, - BACNET_POLARITY polarity) -{ - if (instance < MAX_BINARY_OUTPUTS) { - if (polarity < MAX_POLARITY) { - Polarity[instance] = polarity; - } - } -} - -BACNET_POLARITY Binary_Output_Polarity( - uint32_t instance) -{ - BACNET_POLARITY polarity = POLARITY_NORMAL; - - if (instance < MAX_BINARY_OUTPUTS) { - polarity = (BACNET_POLARITY) Polarity[instance]; - } - - return polarity; -} - -static void Binary_Output_Out_Of_Service_Set( - uint32_t instance, - bool flag) -{ - if (instance < MAX_BINARY_OUTPUTS) { - Out_Of_Service[instance] = flag; - } -} - -bool Binary_Output_Out_Of_Service( - uint32_t instance) -{ - bool flag = false; - - if (instance < MAX_BINARY_OUTPUTS) { - flag = Out_Of_Service[instance]; - } - - return flag; -} - -/* note: the object name must be unique within this device */ -bool Binary_Output_Object_Name( - uint32_t object_instance, - BACNET_CHARACTER_STRING *object_name) -{ - static char text_string[16] = "BO-0"; /* okay for single thread */ - bool status = false; - - if (object_instance < MAX_BINARY_OUTPUTS) { - text_string[3] = '0' + (uint8_t) object_instance; - status = characterstring_init_ansi(object_name, text_string); - } - - return status; -} - -/* return apdu len, or -1 on error */ -int Binary_Output_Read_Property( - BACNET_READ_PROPERTY_DATA * rpdata) -{ - int len = 0; - int apdu_len = 0; /* return value */ - BACNET_BIT_STRING bit_string; - BACNET_CHARACTER_STRING char_string; - BACNET_BINARY_PV present_value = BINARY_INACTIVE; - unsigned object_index = 0; - unsigned i = 0; - bool state = false; - uint8_t *apdu = NULL; - - if ((rpdata->application_data == NULL) || - (rpdata->application_data_len == 0)) { - return 0; - } - apdu = rpdata->application_data; - switch (rpdata->object_property) { - /* object id, object name, object type are handled in Device object */ - case PROP_PRESENT_VALUE: - present_value = - Binary_Output_Present_Value(rpdata->object_instance); - apdu_len = encode_application_enumerated(&apdu[0], present_value); - break; - case PROP_STATUS_FLAGS: - /* note: see the details in the standard on how to use these */ - bitstring_init(&bit_string); - bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); - break; - case PROP_EVENT_STATE: - /* note: see the details in the standard on how to use this */ - apdu_len = - encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); - break; - case PROP_OUT_OF_SERVICE: - object_index = - Binary_Output_Instance_To_Index(rpdata->object_instance); - state = Out_Of_Service[object_index]; - apdu_len = encode_application_boolean(&apdu[0], state); - break; - case PROP_POLARITY: - object_index = - Binary_Output_Instance_To_Index(rpdata->object_instance); - apdu_len = - encode_application_enumerated(&apdu[0], - Polarity[object_index]); - break; - case PROP_PRIORITY_ARRAY: - /* Array element zero is the number of elements in the array */ - if (rpdata->array_index == 0) - apdu_len = - encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); - /* if no index was specified, then try to encode the entire list */ - /* into one packet. */ - else if (rpdata->array_index == BACNET_ARRAY_ALL) { - object_index = - Binary_Output_Instance_To_Index(rpdata->object_instance); - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - /* FIXME: check if we have room before adding it to APDU */ - present_value = (BACNET_BINARY_PV) - Binary_Output_Level[object_index][i]; - if (present_value == BINARY_NULL) { - len = encode_application_null(&apdu[apdu_len]); - } else { - len = - encode_application_enumerated(&apdu[apdu_len], - present_value); - } - /* add it if we have room */ - if ((apdu_len + len) < MAX_APDU) - apdu_len += len; - else { - rpdata->error_class = ERROR_CLASS_SERVICES; - rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; - apdu_len = BACNET_STATUS_ERROR; - break; - } - } - } else { - object_index = - Binary_Output_Instance_To_Index(rpdata->object_instance); - if (rpdata->array_index <= BACNET_MAX_PRIORITY) { - present_value = (BACNET_BINARY_PV) - Binary_Output_Level[object_index][rpdata->array_index - - 1]; - if (present_value == BINARY_NULL) { - apdu_len = encode_application_null(&apdu[apdu_len]); - } else { - apdu_len = - encode_application_enumerated(&apdu[apdu_len], - present_value); - } - } else { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = BACNET_STATUS_ERROR; - } - } - break; - case PROP_RELINQUISH_DEFAULT: - present_value = RELINQUISH_DEFAULT; - apdu_len = encode_application_enumerated(&apdu[0], present_value); - break; - case PROP_ACTIVE_TEXT: - characterstring_init_ansi(&char_string, "on"); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_INACTIVE_TEXT: - characterstring_init_ansi(&char_string, "off"); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - default: - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; - apdu_len = BACNET_STATUS_ERROR; - break; - } - /* only array properties can have array options */ - if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && - (rpdata->array_index != BACNET_ARRAY_ALL)) { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; - apdu_len = BACNET_STATUS_ERROR; - } - - return apdu_len; -} - -/* returns true if successful */ -bool Binary_Output_Write_Property( - BACNET_WRITE_PROPERTY_DATA * wp_data) -{ - bool status = false; /* return value */ - unsigned int priority = 0; - BACNET_BINARY_PV level = BINARY_NULL; - int len = 0; - BACNET_APPLICATION_DATA_VALUE value; - - /* 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? */ - if (len < 0) { - /* error while decoding - a value larger than we can handle */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - return false; - } - switch (wp_data->object_property) { - case PROP_PRESENT_VALUE: - status = - WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, - &wp_data->error_class, &wp_data->error_code); - if (status) { - priority = wp_data->priority; - /* Command priority 6 is reserved for use by Minimum On/Off - algorithm and may not be used for other purposes in any - object. */ - if (priority && (priority <= BACNET_MAX_PRIORITY) && - (priority != 6 /* reserved */ ) && - (value.type.Enumerated <= MAX_BINARY_PV)) { - level = (BACNET_BINARY_PV) value.type.Enumerated; - priority--; - Binary_Output_Present_Value_Set(wp_data->object_instance, - level, priority); - } else if (priority == 6) { - /* Command priority 6 is reserved for use by Minimum On/Off - algorithm and may not be used for other purposes in any - object. */ - status = false; - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - } else { - status = false; - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - status = - WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, - &wp_data->error_class, &wp_data->error_code); - if (status) { - level = BINARY_NULL; - priority = wp_data->priority; - if (priority && (priority <= BACNET_MAX_PRIORITY)) { - priority--; - Binary_Output_Present_Value_Set(wp_data-> - object_instance, level, priority); - } else if (priority == 6) { - status = false; - /* Command priority 6 is reserved for use by Minimum On/Off - algorithm and may not be used for other purposes in any - object. */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - } else { - status = false; - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } - } - break; - case PROP_OUT_OF_SERVICE: - status = - WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, - &wp_data->error_class, &wp_data->error_code); - if (status) { - Binary_Output_Out_Of_Service_Set(wp_data->object_instance, - value.type.Boolean); - } - break; - case PROP_POLARITY: - status = - WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, - &wp_data->error_class, &wp_data->error_code); - if (status) { - if (value.type.Enumerated < MAX_POLARITY) { - Binary_Output_Polarity_Set(wp_data->object_instance, - (BACNET_POLARITY) value.type.Enumerated); - } else { - status = false; - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } - break; - default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; - } - /* not using len at this time */ - len = len; - - return status; -} - -void Binary_Output_Init( - void) -{ - unsigned i, j; - - /* initialize all the analog output priority arrays to NULL */ - for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { - Binary_Output_Polarity_Set(i, POLARITY_NORMAL); - Binary_Output_Out_Of_Service_Set(i, false); - for (j = 0; j < BACNET_MAX_PRIORITY; j++) { - Binary_Output_Level[i][j] = BINARY_NULL; - } - } - - return; -} +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" +#include "wp.h" +#include "hardware.h" +#include "handlers.h" +#include "bo.h" + +#ifndef MAX_BINARY_OUTPUTS +#error Please define MAX_BINARY_OUTPUTS +#endif + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define RELINQUISH_DEFAULT BINARY_INACTIVE +/* Here is our Priority Array.*/ +static uint8_t Binary_Output_Level[MAX_BINARY_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static uint8_t Out_Of_Service[MAX_BINARY_OUTPUTS]; +/* polarity - normal or inverse */ +static uint8_t Polarity[MAX_BINARY_OUTPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Binary_Output_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_POLARITY, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Binary_Output_Properties_Optional[] = { + PROP_ACTIVE_TEXT, + PROP_INACTIVE_TEXT, + -1 +}; + +static const int Binary_Output_Properties_Proprietary[] = { + -1 +}; + +void Binary_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Binary_Output_Properties_Required; + if (pOptional) + *pOptional = Binary_Output_Properties_Optional; + if (pProprietary) + *pProprietary = Binary_Output_Properties_Proprietary; + + return; +} + +/* we simply have 0-n object instances. */ +bool Binary_Output_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Count( + void) +{ + return MAX_BINARY_OUTPUTS; +} + +/* we simply have 0-n object instances. */ +uint32_t Binary_Output_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. */ +unsigned Binary_Output_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_BINARY_OUTPUTS; + + if (object_instance < MAX_BINARY_OUTPUTS) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Present_Value( + unsigned int index) +{ + BACNET_BINARY_PV value = RELINQUISH_DEFAULT; + BACNET_BINARY_PV current_value = RELINQUISH_DEFAULT; + unsigned i = 0; + + if (index < MAX_BINARY_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + current_value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; + if (current_value != BINARY_NULL) { + value = (BACNET_BINARY_PV) Binary_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +BACNET_BINARY_PV Binary_Output_Present_Value( + uint32_t object_instance) +{ + unsigned index = 0; + + index = Binary_Output_Instance_To_Index(object_instance); + + return Present_Value(index); +} + +bool Binary_Output_Present_Value_Set( + uint32_t instance, + BACNET_BINARY_PV binary_value, + unsigned priority) +{ /* 0..15 */ + bool status = false; + + if (instance < MAX_BINARY_OUTPUTS) { + if (priority < BACNET_MAX_PRIORITY) { + Binary_Output_Level[instance][priority] = (uint8_t) binary_value; + status = true; + } + } + + return status; +} + +static void Binary_Output_Polarity_Set( + uint32_t instance, + BACNET_POLARITY polarity) +{ + if (instance < MAX_BINARY_OUTPUTS) { + if (polarity < MAX_POLARITY) { + Polarity[instance] = polarity; + } + } +} + +BACNET_POLARITY Binary_Output_Polarity( + uint32_t instance) +{ + BACNET_POLARITY polarity = POLARITY_NORMAL; + + if (instance < MAX_BINARY_OUTPUTS) { + polarity = (BACNET_POLARITY) Polarity[instance]; + } + + return polarity; +} + +static void Binary_Output_Out_Of_Service_Set( + uint32_t instance, + bool flag) +{ + if (instance < MAX_BINARY_OUTPUTS) { + Out_Of_Service[instance] = flag; + } +} + +bool Binary_Output_Out_Of_Service( + uint32_t instance) +{ + bool flag = false; + + if (instance < MAX_BINARY_OUTPUTS) { + flag = Out_Of_Service[instance]; + } + + return flag; +} + +/* note: the object name must be unique within this device */ +bool Binary_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING *object_name) +{ + static char text_string[16] = "BO-0"; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_BINARY_OUTPUTS) { + text_string[3] = '0' + (uint8_t) object_instance; + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or -1 on error */ +int Binary_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + /* object id, object name, object type are handled in Device object */ + case PROP_PRESENT_VALUE: + present_value = + Binary_Output_Present_Value(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_POLARITY: + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + apdu_len = + encode_application_enumerated(&apdu[0], + Polarity[object_index]); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + present_value = (BACNET_BINARY_PV) + Binary_Output_Level[object_index][i]; + if (present_value == BINARY_NULL) { + len = encode_application_null(&apdu[apdu_len]); + } else { + len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Binary_Output_Instance_To_Index(rpdata->object_instance); + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + present_value = (BACNET_BINARY_PV) + Binary_Output_Level[object_index][rpdata->array_index - + 1]; + if (present_value == BINARY_NULL) { + apdu_len = encode_application_null(&apdu[apdu_len]); + } else { + apdu_len = + encode_application_enumerated(&apdu[apdu_len], + present_value); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_RELINQUISH_DEFAULT: + present_value = RELINQUISH_DEFAULT; + apdu_len = encode_application_enumerated(&apdu[0], present_value); + break; + case PROP_ACTIVE_TEXT: + characterstring_init_ansi(&char_string, "on"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_INACTIVE_TEXT: + characterstring_init_ansi(&char_string, "off"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* 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? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = (BACNET_BINARY_PV) value.type.Enumerated; + priority--; + Binary_Output_Present_Value_Set(wp_data->object_instance, + level, priority); + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + level = BINARY_NULL; + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Output_Present_Value_Set(wp_data-> + object_instance, level, priority); + } else if (priority == 6) { + status = false; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Binary_Output_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_POLARITY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + if (value.type.Enumerated < MAX_POLARITY) { + Binary_Output_Polarity_Set(wp_data->object_instance, + (BACNET_POLARITY) value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + /* not using len at this time */ + len = len; + + return status; +} + +void Binary_Output_Init( + void) +{ + unsigned i, j; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + Binary_Output_Polarity_Set(i, POLARITY_NORMAL); + Binary_Output_Out_Of_Service_Set(i, false); + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Binary_Output_Level[i][j] = BINARY_NULL; + } + } + + return; +} diff --git a/bacnet-stack/ports/stm32f10x/device.c b/bacnet-stack/ports/stm32f10x/device.c index e8d4751d..b9c4dcf6 100644 --- a/bacnet-stack/ports/stm32f10x/device.c +++ b/bacnet-stack/ports/stm32f10x/device.c @@ -1,913 +1,913 @@ -/************************************************************************** -* -* Copyright (C) 2011 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ - -#include -#include -#include -#include -#include "bacdef.h" -#include "bacdcode.h" -#include "bacstr.h" -#include "bacenum.h" -#include "apdu.h" -#include "dcc.h" -#include "datalink.h" -#include "rs485.h" -#include "version.h" -#include "handlers.h" -/* objects */ -#include "device.h" -#include "bo.h" - -/* forward prototype */ -int Device_Read_Property_Local( - BACNET_READ_PROPERTY_DATA * rpdata); -bool Device_Write_Property_Local( - BACNET_WRITE_PROPERTY_DATA * wp_data); - -static struct my_object_functions { - BACNET_OBJECT_TYPE Object_Type; - object_init_function Object_Init; - object_count_function Object_Count; - object_index_to_instance_function Object_Index_To_Instance; - object_valid_instance_function Object_Valid_Instance; - object_name_function Object_Name; - read_property_function Object_Read_Property; - write_property_function Object_Write_Property; - rpm_property_lists_function Object_RPM_List; -} Object_Table[] = { - { - OBJECT_DEVICE, NULL, /* don't init - recursive! */ - Device_Count, Device_Index_To_Instance, - Device_Valid_Object_Instance_Number, Device_Object_Name, - Device_Read_Property_Local, Device_Write_Property_Local, - Device_Property_Lists}, { - OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count, - Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance, - Binary_Output_Object_Name, Binary_Output_Read_Property, - Binary_Output_Write_Property, Binary_Output_Property_Lists}, { - MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL} -}; - -/* 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 = 103; -static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; -static BACNET_CHARACTER_STRING My_Object_Name; - -static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; - -/* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Device_Properties_Required[] = { - PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - PROP_OBJECT_TYPE, - PROP_SYSTEM_STATUS, - PROP_VENDOR_NAME, - PROP_VENDOR_IDENTIFIER, - PROP_MODEL_NAME, - PROP_FIRMWARE_REVISION, - PROP_APPLICATION_SOFTWARE_VERSION, - PROP_PROTOCOL_VERSION, - PROP_PROTOCOL_REVISION, - PROP_PROTOCOL_SERVICES_SUPPORTED, - PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, - PROP_OBJECT_LIST, - PROP_MAX_APDU_LENGTH_ACCEPTED, - PROP_SEGMENTATION_SUPPORTED, - PROP_APDU_TIMEOUT, - PROP_NUMBER_OF_APDU_RETRIES, - PROP_MAX_MASTER, - PROP_MAX_INFO_FRAMES, - PROP_DEVICE_ADDRESS_BINDING, - PROP_DATABASE_REVISION, - -1 -}; - -static const int Device_Properties_Optional[] = { - PROP_DESCRIPTION, - -1 -}; - -static const int Device_Properties_Proprietary[] = { - -1 -}; - -static struct my_object_functions *Device_Objects_Find_Functions( - BACNET_OBJECT_TYPE Object_Type) -{ - struct my_object_functions *pObject = NULL; - - pObject = &Object_Table[0]; - while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { - /* handle each object type */ - if (pObject->Object_Type == Object_Type) { - return (pObject); - } - - pObject++; - } - - return (NULL); -} - -static int Read_Property_Common( - struct my_object_functions *pObject, - BACNET_READ_PROPERTY_DATA * rpdata) -{ - int apdu_len = BACNET_STATUS_ERROR; - BACNET_CHARACTER_STRING char_string; - uint8_t *apdu = NULL; - - if ((rpdata->application_data == NULL) || - (rpdata->application_data_len == 0)) { - return 0; - } - apdu = rpdata->application_data; - switch (rpdata->object_property) { - case PROP_OBJECT_IDENTIFIER: - /* only array properties can have array options */ - if (rpdata->array_index != BACNET_ARRAY_ALL) { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; - apdu_len = BACNET_STATUS_ERROR; - } else { - /* Device Object exception: requested instance - may not match our instance if a wildcard */ - if (rpdata->object_type == OBJECT_DEVICE) { - rpdata->object_instance = Object_Instance_Number; - } - apdu_len = - encode_application_object_id(&apdu[0], rpdata->object_type, - rpdata->object_instance); - } - break; - case PROP_OBJECT_NAME: - /* only array properties can have array options */ - if (rpdata->array_index != BACNET_ARRAY_ALL) { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; - apdu_len = BACNET_STATUS_ERROR; - } else { - characterstring_init_ansi(&char_string, ""); - if (pObject->Object_Name) { - (void)pObject->Object_Name( - rpdata->object_instance, - &char_string); - } - } - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_OBJECT_TYPE: - /* only array properties can have array options */ - if (rpdata->array_index != BACNET_ARRAY_ALL) { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; - apdu_len = BACNET_STATUS_ERROR; - } else { - apdu_len = - encode_application_enumerated(&apdu[0], - rpdata->object_type); - } - break; - default: - if (pObject->Object_Read_Property) { - apdu_len = pObject->Object_Read_Property(rpdata); - } - break; - } - - return apdu_len; -} - -/* Encodes the property APDU and returns the length, - or sets the error, and returns BACNET_STATUS_ERROR */ -int Device_Read_Property( - BACNET_READ_PROPERTY_DATA * rpdata) -{ - int apdu_len = BACNET_STATUS_ERROR; - struct my_object_functions *pObject = NULL; - - /* initialize the default return values */ - pObject = Device_Objects_Find_Functions(rpdata->object_type); - if (pObject) { - if (pObject->Object_Valid_Instance && - pObject->Object_Valid_Instance(rpdata->object_instance)) { - apdu_len = Read_Property_Common(pObject, rpdata); - } else { - rpdata->error_class = ERROR_CLASS_OBJECT; - rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; - } - } else { - rpdata->error_class = ERROR_CLASS_OBJECT; - rpdata->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; - } - - return apdu_len; -} - -bool Device_Write_Property( - BACNET_WRITE_PROPERTY_DATA * wp_data) -{ - bool status = false; - struct my_object_functions *pObject = NULL; - - /* initialize the default return values */ - pObject = Device_Objects_Find_Functions(wp_data->object_type); - if (pObject) { - if (pObject->Object_Valid_Instance && - pObject->Object_Valid_Instance(wp_data->object_instance)) { - if (pObject->Object_Write_Property) { - status = pObject->Object_Write_Property(wp_data); - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - } - } else { - wp_data->error_class = ERROR_CLASS_OBJECT; - wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; - } - } else { - wp_data->error_class = ERROR_CLASS_OBJECT; - wp_data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; - } - - return status; -} - -static unsigned property_list_count( - const int *pList) -{ - unsigned property_count = 0; - - if (pList) { - while (*pList != -1) { - property_count++; - pList++; - } - } - - return property_count; -} - -/* for a given object type, returns the special property list */ -void Device_Objects_Property_List( - BACNET_OBJECT_TYPE object_type, - struct special_property_list_t *pPropertyList) -{ - struct my_object_functions *pObject = NULL; - - pPropertyList->Required.pList = NULL; - pPropertyList->Optional.pList = NULL; - pPropertyList->Proprietary.pList = NULL; - - /* If we can find an entry for the required object type - * and there is an Object_List_RPM fn ptr then call it - * to populate the pointers to the individual list counters. - */ - - pObject = Device_Objects_Find_Functions(object_type); - if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { - pObject->Object_RPM_List(&pPropertyList->Required.pList, - &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); - } - - /* Fetch the counts if available otherwise zero them */ - pPropertyList->Required.count = - pPropertyList->Required.pList == - NULL ? 0 : property_list_count(pPropertyList->Required.pList); - - pPropertyList->Optional.count = - pPropertyList->Optional.pList == - NULL ? 0 : property_list_count(pPropertyList->Optional.pList); - - pPropertyList->Proprietary.count = - pPropertyList->Proprietary.pList == - NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); - - return; -} - -void Device_Property_Lists( - const int **pRequired, - const int **pOptional, - const int **pProprietary) -{ - if (pRequired) - *pRequired = Device_Properties_Required; - if (pOptional) - *pOptional = Device_Properties_Optional; - if (pProprietary) - *pProprietary = Device_Properties_Proprietary; - - return; -} - -unsigned Device_Count( - void) -{ - return 1; -} - -uint32_t Device_Index_To_Instance( - unsigned index) -{ - index = index; - return Object_Instance_Number; -} - -bool Device_Object_Name( - uint32_t object_instance, - BACNET_CHARACTER_STRING *object_name) -{ - bool status = false; - - if (object_instance == Object_Instance_Number) { - status = characterstring_copy(object_name, &My_Object_Name); - } - - return status; -} - -bool Device_Reinitialize( - BACNET_REINITIALIZE_DEVICE_DATA * rd_data) -{ - bool status = false; - - if (characterstring_ansi_same(&rd_data->password, "stm32-challenge")) { - Reinitialize_State = rd_data->state; - dcc_set_status_duration(COMMUNICATION_ENABLE, 0); - /* Note: you could use a mix of state - and password to multiple things */ - /* note: you probably want to restart *after* the - simple ack has been sent from the return handler - so just set a flag from here */ - status = true; - } else { - rd_data->error_class = ERROR_CLASS_SECURITY; - rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; - } - - return status; -} - -BACNET_REINITIALIZED_STATE Device_Reinitialized_State( - void) -{ - return Reinitialize_State; -} - -void Device_Init( - object_functions_t * object_table) -{ - struct my_object_functions *pObject = NULL; - - (void)object_table; - pObject = &Object_Table[0]; - while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { - if (pObject->Object_Init) { - pObject->Object_Init(); - } - pObject++; - } - dcc_set_status_duration(COMMUNICATION_ENABLE, 0); - if (Object_Instance_Number >= BACNET_MAX_INSTANCE) { - Object_Instance_Number = 103; - srand(Object_Instance_Number); - } - characterstring_init_ansi(&My_Object_Name, "stm32-design-challenge-103"); -} - -/* 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; - } 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; -} - -int Device_Set_System_Status( - BACNET_DEVICE_STATUS status, - bool local) -{ - /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ - int result = -1; - - if (status < MAX_DEVICE_STATUS) { - System_Status = status; - result = 0; - } - - return result; -} - -uint16_t Device_Vendor_Identifier( - void) -{ - return BACNET_VENDOR_ID; -} - -BACNET_SEGMENTATION Device_Segmentation_Supported( - void) -{ - return SEGMENTATION_NONE; -} - -uint32_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 = 0; /* number of objects */ - struct my_object_functions *pObject = NULL; - - /* initialize the default return values */ - pObject = &Object_Table[0]; - while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { - if (pObject->Object_Count) { - count += pObject->Object_Count(); - } - pObject++; - } - - return count; -} - -bool Device_Object_List_Identifier( - unsigned array_index, - int *object_type, - uint32_t * instance) -{ - bool status = false; - unsigned count = 0; - unsigned object_index = 0; - struct my_object_functions *pObject = NULL; - - /* array index zero is length - so invalid */ - if (array_index == 0) { - return status; - } - object_index = array_index - 1; - /* initialize the default return values */ - pObject = &Object_Table[0]; - while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { - if (pObject->Object_Count && pObject->Object_Index_To_Instance) { - object_index -= count; - count = pObject->Object_Count(); - if (object_index < count) { - *object_type = pObject->Object_Type; - *instance = pObject->Object_Index_To_Instance(object_index); - status = true; - break; - } - } - pObject++; - } - - return status; -} - -bool Device_Valid_Object_Name( - BACNET_CHARACTER_STRING *object_name1, - int *object_type, - uint32_t * object_instance) -{ - bool found = false; - int type = 0; - uint32_t instance; - unsigned max_objects = 0, i = 0; - bool check_id = false; - BACNET_CHARACTER_STRING object_name2; - struct my_object_functions *pObject = NULL; - - max_objects = Device_Object_List_Count(); - for (i = 0; i < max_objects; i++) { - check_id = Device_Object_List_Identifier(i, &type, &instance); - if (check_id) { - pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE)type); - if ((pObject != NULL) && (pObject->Object_Name != NULL) && - (pObject->Object_Name(instance, &object_name2) && - characterstring_same(object_name1, &object_name2))) { - found = true; - if (object_type) { - *object_type = type; - } - if (object_instance) { - *object_instance = instance; - } - break; - } - } - } - - return found; -} - -bool Device_Valid_Object_Id( - int object_type, - uint32_t object_instance) -{ - bool status = false; /* return value */ - struct my_object_functions *pObject = NULL; - - pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE)object_type); - if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { - status = pObject->Object_Valid_Instance(object_instance); - } - - return status; -} - -bool Device_Object_Name_Copy( - int object_type, - uint32_t object_instance, - BACNET_CHARACTER_STRING *object_name) -{ - struct my_object_functions *pObject = NULL; - bool found = false; - int type = 0; - uint32_t instance; - unsigned max_objects = 0, i = 0; - bool check_id = false; - - max_objects = Device_Object_List_Count(); - for (i = 0; i < max_objects; i++) { - check_id = Device_Object_List_Identifier(i, &type, &instance); - if (check_id) { - pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE)type); - if ((pObject != NULL) && (pObject->Object_Name != NULL)) { - found = pObject->Object_Name(instance, object_name); - break; - } - } - } - - return found; -} - -/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error */ -int Device_Read_Property_Local( - BACNET_READ_PROPERTY_DATA * rpdata) -{ - 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; - uint8_t *apdu = NULL; - struct my_object_functions *pObject = NULL; - - if ((rpdata->application_data == NULL) || - (rpdata->application_data_len == 0)) { - return 0; - } - apdu = rpdata->application_data; - switch (rpdata->object_property) { - case PROP_DESCRIPTION: - characterstring_init_ansi(&char_string, "BACnet Development Kit"); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_SYSTEM_STATUS: - apdu_len = - encode_application_enumerated(&apdu[0], - Device_System_Status()); - break; - case PROP_VENDOR_NAME: - characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_VENDOR_IDENTIFIER: - apdu_len = encode_application_unsigned(&apdu[0], BACNET_VENDOR_ID); - break; - case PROP_MODEL_NAME: - characterstring_init_ansi(&char_string, "bdk-stm32-mstp"); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_FIRMWARE_REVISION: - characterstring_init_ansi(&char_string, BACnet_Version); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_APPLICATION_SOFTWARE_VERSION: - characterstring_init_ansi(&char_string, "1.0"); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_PROTOCOL_VERSION: - apdu_len = - encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); - break; - case PROP_PROTOCOL_REVISION: - apdu_len = - encode_application_unsigned(&apdu[0], - BACNET_PROTOCOL_REVISION); - break; - case PROP_PROTOCOL_SERVICES_SUPPORTED: - /* Note: list of services that are executed, not initiated. */ - bitstring_init(&bit_string); - for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { - /* automatic lookup based on handlers set */ - bitstring_set_bit(&bit_string, (uint8_t) i, - apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); - } - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); - break; - case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: - /* Note: this is the list of objects that can be in this device, - not a list of objects that this device can access */ - bitstring_init(&bit_string); - for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { - /* FIXME: if ReadProperty used an array of Functions... */ - /* initialize all the object types to not-supported */ - bitstring_set_bit(&bit_string, (uint8_t) i, false); - } - /* set the object types with objects to supported */ - i = 0; - pObject = &Object_Table[i]; - while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { - if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { - bitstring_set_bit(&bit_string, pObject->Object_Type, true); - } - pObject++; - } - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); - break; - case PROP_OBJECT_LIST: - count = Device_Object_List_Count(); - /* Array element zero is the number of objects in the list */ - if (rpdata->array_index == 0) - apdu_len = encode_application_unsigned(&apdu[0], count); - /* if no index was specified, then try to encode the entire list */ - /* into one packet. Note that more than likely you will have */ - /* to return an error if the number of encoded objects exceeds */ - /* your maximum APDU size. */ - else if (rpdata->array_index == BACNET_ARRAY_ALL) { - for (i = 1; i <= count; i++) { - if (Device_Object_List_Identifier(i, &object_type, - &instance)) { - len = - encode_application_object_id(&apdu[apdu_len], - object_type, instance); - apdu_len += len; - /* assume next one is the same size as this one */ - /* can we all fit into the APDU? */ - if ((apdu_len + len) >= MAX_APDU) { - /* Abort response */ - rpdata->error_code = - ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; - apdu_len = BACNET_STATUS_ABORT; - break; - } - } else { - /* error: internal error? */ - rpdata->error_class = ERROR_CLASS_SERVICES; - rpdata->error_code = ERROR_CODE_OTHER; - apdu_len = BACNET_STATUS_ERROR; - break; - } - } - } else { - if (Device_Object_List_Identifier(rpdata->array_index, - &object_type, &instance)) - apdu_len = - encode_application_object_id(&apdu[0], object_type, - instance); - else { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = BACNET_STATUS_ERROR; - } - } - break; - case PROP_MAX_APDU_LENGTH_ACCEPTED: - apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); - break; - case PROP_SEGMENTATION_SUPPORTED: - apdu_len = - encode_application_enumerated(&apdu[0], - Device_Segmentation_Supported()); - break; - case PROP_APDU_TIMEOUT: - apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); - break; - case PROP_NUMBER_OF_APDU_RETRIES: - apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); - break; - case PROP_DEVICE_ADDRESS_BINDING: - /* FIXME: encode the list here, if it exists */ - break; - case PROP_DATABASE_REVISION: - apdu_len = - encode_application_unsigned(&apdu[0], - Device_Database_Revision()); - break; - case PROP_MAX_INFO_FRAMES: - apdu_len = - encode_application_unsigned(&apdu[0], - dlmstp_max_info_frames()); - break; - case PROP_MAX_MASTER: - apdu_len = - encode_application_unsigned(&apdu[0], dlmstp_max_master()); - break; - case 9600: - apdu_len = - encode_application_unsigned(&apdu[0], rs485_baud_rate()); - break; - default: - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; - apdu_len = BACNET_STATUS_ERROR; - break; - } - /* only array properties can have array options */ - if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && - (rpdata->array_index != BACNET_ARRAY_ALL)) { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; - apdu_len = BACNET_STATUS_ERROR; - } - - return apdu_len; -} - -bool Device_Write_Property_Local( - BACNET_WRITE_PROPERTY_DATA * wp_data) -{ - bool status = false; /* return value - false=error */ - int len = 0; - BACNET_APPLICATION_DATA_VALUE value; - - /* 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? */ - if (len < 0) { - /* error while decoding - a value larger than we can handle */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - return false; - } - 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 { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->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 { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->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 { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - } - break; - case PROP_OBJECT_NAME: - if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { - size_t length = - characterstring_length(&value.type.Character_String); - if (length < 1) { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } else if (length < MAX_CHARACTER_STRING_BYTES) { - uint8_t encoding = - characterstring_encoding(&value.type.Character_String); - if (encoding < MAX_CHARACTER_STRING_ENCODING) { - characterstring_copy(&My_Object_Name, &value.type. - Character_String); - status = true; - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = - ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; - } - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = - ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; - } - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->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_baud_rate_set(value.type.Unsigned_Int))) { - status = true; - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - } - break; - default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; - } - /* not using len at this time */ - len = len; - - return status; -} +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "apdu.h" +#include "dcc.h" +#include "datalink.h" +#include "rs485.h" +#include "version.h" +#include "handlers.h" +/* objects */ +#include "device.h" +#include "bo.h" + +/* forward prototype */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +static struct my_object_functions { + BACNET_OBJECT_TYPE Object_Type; + object_init_function Object_Init; + object_count_function Object_Count; + object_index_to_instance_function Object_Index_To_Instance; + object_valid_instance_function Object_Valid_Instance; + object_name_function Object_Name; + read_property_function Object_Read_Property; + write_property_function Object_Write_Property; + rpm_property_lists_function Object_RPM_List; +} Object_Table[] = { + { + OBJECT_DEVICE, NULL, /* don't init - recursive! */ + Device_Count, Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, Device_Object_Name, + Device_Read_Property_Local, Device_Write_Property_Local, + Device_Property_Lists}, { + OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count, + Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance, + Binary_Output_Object_Name, Binary_Output_Read_Property, + Binary_Output_Write_Property, Binary_Output_Property_Lists}, { + MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL} +}; + +/* 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 = 103; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static BACNET_CHARACTER_STRING My_Object_Name; + +static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Device_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; + +static const int Device_Properties_Optional[] = { + PROP_DESCRIPTION, + -1 +}; + +static const int Device_Properties_Proprietary[] = { + -1 +}; + +static struct my_object_functions *Device_Objects_Find_Functions( + BACNET_OBJECT_TYPE Object_Type) +{ + struct my_object_functions *pObject = NULL; + + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + /* handle each object type */ + if (pObject->Object_Type == Object_Type) { + return (pObject); + } + + pObject++; + } + + return (NULL); +} + +static int Read_Property_Common( + struct my_object_functions *pObject, + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + BACNET_CHARACTER_STRING char_string; + uint8_t *apdu = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + /* only array properties can have array options */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } else { + /* Device Object exception: requested instance + may not match our instance if a wildcard */ + if (rpdata->object_type == OBJECT_DEVICE) { + rpdata->object_instance = Object_Instance_Number; + } + apdu_len = + encode_application_object_id(&apdu[0], rpdata->object_type, + rpdata->object_instance); + } + break; + case PROP_OBJECT_NAME: + /* only array properties can have array options */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } else { + characterstring_init_ansi(&char_string, ""); + if (pObject->Object_Name) { + (void)pObject->Object_Name( + rpdata->object_instance, + &char_string); + } + } + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + /* only array properties can have array options */ + if (rpdata->array_index != BACNET_ARRAY_ALL) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } else { + apdu_len = + encode_application_enumerated(&apdu[0], + rpdata->object_type); + } + break; + default: + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(rpdata); + } + break; + } + + return apdu_len; +} + +/* Encodes the property APDU and returns the length, + or sets the error, and returns BACNET_STATUS_ERROR */ +int Device_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = BACNET_STATUS_ERROR; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Device_Objects_Find_Functions(rpdata->object_type); + if (pObject) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + apdu_len = Read_Property_Common(pObject, rpdata); + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + } + + return apdu_len; +} + +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = Device_Objects_Find_Functions(wp_data->object_type); + if (pObject) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(wp_data->object_instance)) { + if (pObject->Object_Write_Property) { + status = pObject->Object_Write_Property(wp_data); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + } + + return status; +} + +static unsigned property_list_count( + const int *pList) +{ + unsigned property_count = 0; + + if (pList) { + while (*pList != -1) { + property_count++; + pList++; + } + } + + return property_count; +} + +/* for a given object type, returns the special property list */ +void Device_Objects_Property_List( + BACNET_OBJECT_TYPE object_type, + struct special_property_list_t *pPropertyList) +{ + struct my_object_functions *pObject = NULL; + + pPropertyList->Required.pList = NULL; + pPropertyList->Optional.pList = NULL; + pPropertyList->Proprietary.pList = NULL; + + /* If we can find an entry for the required object type + * and there is an Object_List_RPM fn ptr then call it + * to populate the pointers to the individual list counters. + */ + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { + pObject->Object_RPM_List(&pPropertyList->Required.pList, + &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); + } + + /* Fetch the counts if available otherwise zero them */ + pPropertyList->Required.count = + pPropertyList->Required.pList == + NULL ? 0 : property_list_count(pPropertyList->Required.pList); + + pPropertyList->Optional.count = + pPropertyList->Optional.pList == + NULL ? 0 : property_list_count(pPropertyList->Optional.pList); + + pPropertyList->Proprietary.count = + pPropertyList->Proprietary.pList == + NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); + + return; +} + +void Device_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Device_Properties_Required; + if (pOptional) + *pOptional = Device_Properties_Optional; + if (pProprietary) + *pProprietary = Device_Properties_Proprietary; + + return; +} + +unsigned Device_Count( + void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING *object_name) +{ + bool status = false; + + if (object_instance == Object_Instance_Number) { + status = characterstring_copy(object_name, &My_Object_Name); + } + + return status; +} + +bool Device_Reinitialize( + BACNET_REINITIALIZE_DEVICE_DATA * rd_data) +{ + bool status = false; + + if (characterstring_ansi_same(&rd_data->password, "stm32-challenge")) { + Reinitialize_State = rd_data->state; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* Note: you could use a mix of state + and password to multiple things */ + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + status = true; + } else { + rd_data->error_class = ERROR_CLASS_SECURITY; + rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; + } + + return status; +} + +BACNET_REINITIALIZED_STATE Device_Reinitialized_State( + void) +{ + return Reinitialize_State; +} + +void Device_Init( + object_functions_t * object_table) +{ + struct my_object_functions *pObject = NULL; + + (void)object_table; + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + if (Object_Instance_Number >= BACNET_MAX_INSTANCE) { + Object_Instance_Number = 103; + srand(Object_Instance_Number); + } + characterstring_init_ansi(&My_Object_Name, "stm32-design-challenge-103"); +} + +/* 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; + } 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; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + int result = -1; + + if (status < MAX_DEVICE_STATUS) { + System_Status = status; + result = 0; + } + + return result; +} + +uint16_t Device_Vendor_Identifier( + void) +{ + return BACNET_VENDOR_ID; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_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 = 0; /* number of objects */ + struct my_object_functions *pObject = NULL; + + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count) { + count += pObject->Object_Count(); + } + pObject++; + } + + return count; +} + +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + struct my_object_functions *pObject = NULL; + + /* array index zero is length - so invalid */ + if (array_index == 0) { + return status; + } + object_index = array_index - 1; + /* initialize the default return values */ + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Count && pObject->Object_Index_To_Instance) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + *object_type = pObject->Object_Type; + *instance = pObject->Object_Index_To_Instance(object_index); + status = true; + break; + } + } + pObject++; + } + + return status; +} + +bool Device_Valid_Object_Name( + BACNET_CHARACTER_STRING *object_name1, + int *object_type, + uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + BACNET_CHARACTER_STRING object_name2; + struct my_object_functions *pObject = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 0; i < max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE)type); + if ((pObject != NULL) && (pObject->Object_Name != NULL) && + (pObject->Object_Name(instance, &object_name2) && + characterstring_same(object_name1, &object_name2))) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +bool Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + bool status = false; /* return value */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE)object_type); + if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { + status = pObject->Object_Valid_Instance(object_instance); + } + + return status; +} + +bool Device_Object_Name_Copy( + int object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING *object_name) +{ + struct my_object_functions *pObject = NULL; + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + + max_objects = Device_Object_List_Count(); + for (i = 0; i < max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE)type); + if ((pObject != NULL) && (pObject->Object_Name != NULL)) { + found = pObject->Object_Name(instance, object_name); + break; + } + } + } + + return found; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + 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; + uint8_t *apdu = NULL; + struct my_object_functions *pObject = NULL; + + if ((rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + switch (rpdata->object_property) { + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, "BACnet Development Kit"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_System_Status()); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = encode_application_unsigned(&apdu[0], BACNET_VENDOR_ID); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, "bdk-stm32-mstp"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACnet_Version); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, "1.0"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + BACNET_PROTOCOL_REVISION); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* FIXME: if ReadProperty used an array of Functions... */ + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* set the object types with objects to supported */ + i = 0; + pObject = &Object_Table[i]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { + bitstring_set_bit(&bit_string, pObject->Object_Type, true); + } + pObject++; + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } else { + /* error: internal error? */ + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_OTHER; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (Device_Object_List_Identifier(rpdata->array_index, + &object_type, &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Database_Revision()); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; + case 9600: + apdu_len = + encode_application_unsigned(&apdu[0], rs485_baud_rate()); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value - false=error */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* 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? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + 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 { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->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 { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->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 { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + size_t length = + characterstring_length(&value.type.Character_String); + if (length < 1) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else if (length < MAX_CHARACTER_STRING_BYTES) { + uint8_t encoding = + characterstring_encoding(&value.type.Character_String); + if (encoding < MAX_CHARACTER_STRING_ENCODING) { + characterstring_copy(&My_Object_Name, &value.type. + Character_String); + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = + ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->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_baud_rate_set(value.type.Unsigned_Int))) { + status = true; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + /* not using len at this time */ + len = len; + + return status; +} diff --git a/bacnet-stack/ports/stm32f10x/dlmstp.c b/bacnet-stack/ports/stm32f10x/dlmstp.c index c61a2850..c62fb1be 100644 --- a/bacnet-stack/ports/stm32f10x/dlmstp.c +++ b/bacnet-stack/ports/stm32f10x/dlmstp.c @@ -1,1662 +1,1662 @@ -/*####COPYRIGHTBEGIN#### - ------------------------------------------- - Copyright (C) 2009 Steve Karg - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - The Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307 - USA. - - As a special exception, if other files instantiate templates or - use macros or inline functions from this file, or you compile - this file and link it with other works to produce a work based - on this file, this file does not by itself cause the resulting - work to be covered by the GNU General Public License. However - the source code for this file must still be made available in - accordance with section (3) of the GNU General Public License. - - This exception does not invalidate any other reasons why a work - based on this file might be covered by the GNU General Public - License. - ------------------------------------------- -####COPYRIGHTEND####*/ -#include -#include -#include -#include -#include "bacdef.h" -#include "dlmstp.h" -#include "rs485.h" -#include "crc.h" -#include "npdu.h" -#include "bits.h" -#include "bacaddr.h" -#include "timer.h" -#include "ringbuf.h" -#include "mstpdef.h" -#include "automac.h" -#include "device.h" - -/* This file has been customized for use with small microprocessors */ -/* Assumptions: - Only one MS/TP datalink layer -*/ - -/* The state of the Receive State Machine */ -static MSTP_RECEIVE_STATE Receive_State; -/* When a master node is powered up or reset, */ -/* it shall unconditionally enter the INITIALIZE state. */ -static MSTP_MASTER_STATE Master_State; -/* bit-sized boolean flags */ -static struct mstp_flag_t { - /* A Boolean flag set to TRUE by the Receive State Machine */ - /* if an invalid frame is received. */ - /* Set to FALSE by the main state machine. */ - unsigned ReceivedInvalidFrame:1; - /* A Boolean flag set to TRUE by the Receive State Machine */ - /* if a valid frame is received. */ - /* Set to FALSE by the main state machine. */ - unsigned ReceivedValidFrame:1; - /* set to TRUE when we get a frame not for us */ - unsigned ReceivedValidFrameNotForUs:1; - /* A Boolean flag set to TRUE by the master machine if this node is the */ - /* only known master node. */ - unsigned SoleMaster:1; - /* A Boolean flag set TRUE by the datalink if a - packet has been received, but not processed. */ - unsigned ReceivePacketPending:1; -} MSTP_Flag; - -/* Used to store the data length of a received frame. */ -static uint32_t DataLength; -/* Used to store the destination address of a received frame. */ -static uint8_t DestinationAddress; -/* Used to count the number of received octets or errors. */ -/* This is used in the detection of link activity. */ -/* Compared to Nmin_octets */ -static uint8_t EventCount; -/* Used to store the frame type of a received frame. */ -static uint8_t FrameType; -/* An array of octets, used to store octets as they are received. */ -/* InputBuffer is indexed from 0 to InputBufferSize-1. */ -/* FIXME: assign this to an actual array of bytes! */ -/* Note: the buffer is designed as a pointer since some compilers - and microcontroller architectures have limits as to places to - hold contiguous memory. */ -static uint8_t *InputBuffer; -static uint16_t InputBufferSize; -/* Used to store the Source Address of a received frame. */ -static uint8_t SourceAddress; -/* "This Station," the MAC address of this node. TS is generally read from a */ -/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ -/* 0 to 254. The value 255 is used to denote AutoMAC addressing */ -static uint8_t This_Station = 255; -/* This parameter represents the value of the Max_Master property of the */ -/* node's Device object. The value of Max_Master specifies the highest */ -/* allowable address for master nodes. The value of Max_Master shall be */ -/* less than or equal to 127. If Max_Master is not writable in a node, */ -/* its value shall be 127. */ -static uint8_t Nmax_master = 127; - -/* The time without a DataAvailable or ReceiveError event before declaration */ -/* of loss of token: 500 milliseconds. */ -#define Tno_token 500 - -/* The minimum time without a DataAvailable or ReceiveError event */ -/* that a node must wait for a station to begin replying to a */ -/* confirmed request: 255 milliseconds. (Implementations may use */ -/* larger values for this timeout, not to exceed 300 milliseconds.) */ -#define Treply_timeout 260 - -/* The minimum time without a DataAvailable or ReceiveError event that a */ -/* node must wait for a remote node to begin using a token or replying to */ -/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ -/* larger values for this timeout, not to exceed 100 milliseconds.) */ -#define Tusage_timeout 60 - -/* The number of tokens received or used before a Poll For Master cycle */ -/* is executed: 50. */ -#define Npoll 50 - -/* The number of retries on sending Token: 1. */ -#define Nretry_token 1 - -/* The minimum number of DataAvailable or ReceiveError events that must be */ -/* seen by a receiving node in order to declare the line "active": 4. */ -#define Nmin_octets 4 - -/* The minimum time without a DataAvailable or ReceiveError event within */ -/* a frame before a receiving node may discard the frame: 60 bit times. */ -/* (Implementations may use larger values for this timeout, */ -/* not to exceed 100 milliseconds.) */ -/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ -/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ -#define Tframe_abort 30 - -/* The maximum idle time a sending node may allow to elapse between octets */ -/* of a frame the node is transmitting: 20 bit times. */ -#define Tframe_gap 20 - -/* The maximum time after the end of the stop bit of the final */ -/* octet of a transmitted frame before a node must disable its */ -/* EIA-485 driver: 15 bit times. */ -#define Tpostdrive 15 - -/* The maximum time a node may wait after reception of a frame that expects */ -/* a reply before sending the first octet of a reply or Reply Postponed */ -/* frame: 250 milliseconds. */ -#define Treply_delay 250 - -/* The width of the time slot within which a node may generate a token: */ -/* 10 milliseconds. */ -#define Tslot 10 - -/* The maximum time a node may wait after reception of the token or */ -/* a Poll For Master frame before sending the first octet of a frame: */ -/* 15 milliseconds. */ -#define Tusage_delay 15 - -/* we need to be able to increment without rolling over */ -#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} - -/* data structure for MS/TP transmit packet */ -struct mstp_tx_packet { - uint16_t length; - uint16_t index; - uint8_t buffer[MAX_MPDU]; -}; -/* count must be a power of 2 for ringbuf library */ -#ifndef MSTP_TRANSMIT_PACKET_COUNT -#define MSTP_TRANSMIT_PACKET_COUNT 1 -#endif -static struct mstp_tx_packet Transmit_Buffer[MSTP_TRANSMIT_PACKET_COUNT]; -static RING_BUFFER Transmit_Queue; - -/* data structure for MS/TP PDU Queue */ -struct mstp_pdu_packet { - bool data_expecting_reply; - uint8_t destination_mac; - uint16_t length; - uint8_t buffer[MAX_MPDU]; -}; -/* count must be a power of 2 for ringbuf library */ -#ifndef MSTP_PDU_PACKET_COUNT -#define MSTP_PDU_PACKET_COUNT 2 -#endif -static struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT]; -static RING_BUFFER PDU_Queue; -/* This parameter represents the value of the Max_Info_Frames property of */ -/* the node's Device object. The value of Max_Info_Frames specifies the */ -/* maximum number of information frames the node may send before it must */ -/* pass the token. Max_Info_Frames may have different values on different */ -/* nodes. This may be used to allocate more or less of the available link */ -/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ -/* node, its value shall be 1. */ -static uint8_t Nmax_info_frames = MSTP_PDU_PACKET_COUNT; - -void dlmstp_automac_hander(void); - -bool dlmstp_init( - char *ifname) -{ - ifname = ifname; - Ringbuf_Init( - &Transmit_Queue, - (uint8_t *)&Transmit_Buffer, - sizeof(struct mstp_tx_packet), - MSTP_TRANSMIT_PACKET_COUNT); - Ringbuf_Init( - &PDU_Queue, - (uint8_t *)&PDU_Buffer, - sizeof(struct mstp_pdu_packet), - MSTP_PDU_PACKET_COUNT); - rs485_init(); - automac_init(); - - return true; -} - -void dlmstp_cleanup( - void) -{ - /* nothing to do for static buffers */ -} - -void dlmstp_fill_bacnet_address( - BACNET_ADDRESS * src, - uint8_t mstp_address) -{ - int i = 0; - - if (mstp_address == MSTP_BROADCAST_ADDRESS) { - /* mac_len = 0 if broadcast address */ - src->mac_len = 0; - src->mac[0] = 0; - } else { - src->mac_len = 1; - src->mac[0] = mstp_address; - } - /* fill with 0's starting with index 1; index 0 filled above */ - for (i = 1; i < MAX_MAC_LEN; i++) { - src->mac[i] = 0; - } - src->net = 0; - src->len = 0; - for (i = 0; i < MAX_MAC_LEN; i++) { - src->adr[i] = 0; - } -} - -static bool dlmstp_compare_data_expecting_reply( - uint8_t * request_pdu, - uint16_t request_pdu_len, - uint8_t src_address, - uint8_t * reply_pdu, - uint16_t reply_pdu_len, - uint8_t dest_address) -{ - uint16_t offset; - /* One way to check the message is to compare NPDU - src, dest, along with the APDU type, invoke id. - Seems a bit overkill */ - struct DER_compare_t { - BACNET_NPDU_DATA npdu_data; - BACNET_ADDRESS address; - uint8_t pdu_type; - uint8_t invoke_id; - uint8_t service_choice; - }; - struct DER_compare_t request; - struct DER_compare_t reply; - - /* decode the request data */ - request.address.mac[0] = src_address; - request.address.mac_len = 1; - offset = - npdu_decode(&request_pdu[0], NULL, &request.address, - &request.npdu_data); - if (request.npdu_data.network_layer_message) { - return false; - } - request.pdu_type = request_pdu[offset] & 0xF0; - if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { - return false; - } - request.invoke_id = request_pdu[offset + 2]; - /* segmented message? */ - if (request_pdu[offset] & BIT3) - request.service_choice = request_pdu[offset + 5]; - else - request.service_choice = request_pdu[offset + 3]; - /* decode the reply data */ - reply.address.mac[0] = dest_address; - reply.address.mac_len = 1; - offset = - npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); - if (reply.npdu_data.network_layer_message) { - return false; - } - /* reply could be a lot of things: - confirmed, simple ack, abort, reject, error */ - reply.pdu_type = reply_pdu[offset] & 0xF0; - switch (reply.pdu_type) { - case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: - reply.invoke_id = reply_pdu[offset + 2]; - /* segmented message? */ - if (reply_pdu[offset] & BIT3) - reply.service_choice = reply_pdu[offset + 5]; - else - reply.service_choice = reply_pdu[offset + 3]; - break; - case PDU_TYPE_SIMPLE_ACK: - reply.invoke_id = reply_pdu[offset + 1]; - reply.service_choice = reply_pdu[offset + 2]; - break; - case PDU_TYPE_COMPLEX_ACK: - reply.invoke_id = reply_pdu[offset + 1]; - /* segmented message? */ - if (reply_pdu[offset] & BIT3) - reply.service_choice = reply_pdu[offset + 4]; - else - reply.service_choice = reply_pdu[offset + 2]; - break; - case PDU_TYPE_ERROR: - reply.invoke_id = reply_pdu[offset + 1]; - reply.service_choice = reply_pdu[offset + 2]; - break; - case PDU_TYPE_REJECT: - case PDU_TYPE_ABORT: - reply.invoke_id = reply_pdu[offset + 1]; - break; - default: - return false; - } - if (request.invoke_id != reply.invoke_id) { - return false; - } - /* these services don't have service choice included */ - if ((reply.pdu_type != PDU_TYPE_REJECT) && - (reply.pdu_type != PDU_TYPE_ABORT)) { - if (request.service_choice != reply.service_choice) { - return false; - } - } - if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { - return false; - } - if (request.npdu_data.priority != reply.npdu_data.priority) { - return false; - } - if (!bacnet_address_same(&request.address, &reply.address)) { - return false; - } - - return true; -} - -typedef enum -{ - MSTP_TX_STATE_IDLE, - MSTP_TX_STATE_SILENCE_WAIT, - MSTP_TX_STATE_SEND_WAIT, - MSTP_TX_STATE_STOP -} MSTP_TX_STATE; -static bool MSTP_Transmit_FSM(void) -{ - static MSTP_TX_STATE state = MSTP_TX_STATE_IDLE; - static struct mstp_tx_packet *pkt; - - MSTP_TX_START: - switch (state) { - case MSTP_TX_STATE_IDLE: - if (!Ringbuf_Empty(&Transmit_Queue)) { - /* get the packet - but don't remove it from queue */ - pkt = (struct mstp_tx_packet *)Ringbuf_Get_Front( - &Transmit_Queue); - state = MSTP_TX_STATE_SILENCE_WAIT; - } - break; - case MSTP_TX_STATE_SILENCE_WAIT: - if (rs485_turnaround_elapsed()) { - rs485_rts_enable(true); - pkt->index = 0; - rs485_byte_send(pkt->buffer[pkt->index]); - state = MSTP_TX_STATE_SEND_WAIT; - /* optimize a little - for slower CPUs */ - goto MSTP_TX_START; - } - break; - case MSTP_TX_STATE_SEND_WAIT: - if (rs485_byte_sent()) { - pkt->index++; - if (pkt->index < pkt->length) { - /* send next byte */ - rs485_byte_send(pkt->buffer[pkt->index]); - /* optimize a little - for slower CPUs */ - goto MSTP_TX_START; - } else { - state = MSTP_TX_STATE_STOP; - } - } - break; - case MSTP_TX_STATE_STOP: - if (rs485_byte_sent() && rs485_frame_sent()) { - rs485_rts_enable(false); - /* remove the packet from the queue */ - (void)Ringbuf_Pop_Front(&Transmit_Queue); - state = MSTP_TX_STATE_IDLE; - } - break; - default: - state = MSTP_TX_STATE_IDLE; - break; - } - - return (state != MSTP_TX_STATE_IDLE); -} - -/* MS/TP Frame Format */ -/* All frames are of the following format: */ -/* */ -/* Preamble: two octet preamble: X`55', X`FF' */ -/* Frame Type: one octet */ -/* Destination Address: one octet address */ -/* Source Address: one octet address */ -/* Length: two octets, most significant octet first, of the Data field */ -/* Header CRC: one octet */ -/* Data: (present only if Length is non-zero) */ -/* Data CRC: (present only if Length is non-zero) two octets, */ -/* least significant octet first */ -/* (pad): (optional) at most one octet of padding: X'FF' */ -static void MSTP_Send_Frame( - uint8_t frame_type, /* type of frame to send - see defines */ - uint8_t destination, /* destination address */ - uint8_t source, /* source address */ - uint8_t * data, /* any data to be sent - may be null */ - uint16_t data_len) -{ /* number of bytes of data (up to 501) */ - uint8_t crc8 = 0xFF; /* used to calculate the crc value */ - uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ - static struct mstp_tx_packet *pkt; - uint16_t i = 0; /* used to calculate CRC for data */ - - pkt = (struct mstp_tx_packet *)Ringbuf_Alloc( - &Transmit_Queue); - if (pkt) { - /* create the MS/TP header */ - pkt->buffer[0] = 0x55; - pkt->buffer[1] = 0xFF; - pkt->buffer[2] = frame_type; - crc8 = CRC_Calc_Header(pkt->buffer[2], crc8); - pkt->buffer[3] = destination; - crc8 = CRC_Calc_Header(pkt->buffer[3], crc8); - pkt->buffer[4] = source; - crc8 = CRC_Calc_Header(pkt->buffer[4], crc8); - pkt->buffer[5] = data_len / 256; - crc8 = CRC_Calc_Header(pkt->buffer[5], crc8); - pkt->buffer[6] = data_len % 256; - crc8 = CRC_Calc_Header(pkt->buffer[6], crc8); - pkt->buffer[7] = ~crc8; - pkt->length = 8; - if (data_len) { - /* calculate CRC for any data */ - for (i = 0; i < data_len; i++) { - crc16 = CRC_Calc_Data(data[i], crc16); - pkt->buffer[8+i] = data[i]; - } - crc16 = ~crc16; - pkt->buffer[8+data_len] = (crc16 & 0x00FF); - pkt->buffer[8+data_len+1] = ((crc16 & 0xFF00) >> 8); - pkt->length += data_len; - pkt->length += 2; - } - } else { - pkt = NULL; - } -} - -static void MSTP_Receive_Frame_FSM( - void) -{ - /* stores the latest received data octet */ - uint8_t DataRegister = 0; - /* Used to accumulate the CRC on the data field of a frame. */ - static uint16_t DataCRC = 0; - /* Used to accumulate the CRC on the header of a frame. */ - static uint8_t HeaderCRC = 0; - /* Used as an index by the Receive State Machine, - up to a maximum value of the MPDU */ - static uint16_t Index = 0; - - switch (Receive_State) { - case MSTP_RECEIVE_STATE_IDLE: - /* In the IDLE state, the node waits - for the beginning of a frame. */ - if (rs485_receive_error()) { - /* EatAnError */ - rs485_silence_reset(); - INCREMENT_AND_LIMIT_UINT8(EventCount); - } else if (rs485_byte_available(&DataRegister)) { - rs485_silence_reset(); - INCREMENT_AND_LIMIT_UINT8(EventCount); - if (DataRegister == 0x55) { - /* Preamble1 */ - /* receive the remainder of the frame. */ - Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; - } - } - break; - case MSTP_RECEIVE_STATE_PREAMBLE: - /* In the PREAMBLE state, the node waits for the - second octet of the preamble. */ - if (rs485_silence_elapsed(Tframe_abort)) { - /* Timeout */ - /* a correct preamble has not been received */ - /* wait for the start of a frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } else if (rs485_receive_error()) { - /* Error */ - rs485_silence_reset(); - INCREMENT_AND_LIMIT_UINT8(EventCount); - /* wait for the start of a frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } else if (rs485_byte_available(&DataRegister)) { - rs485_silence_reset(); - INCREMENT_AND_LIMIT_UINT8(EventCount); - if (DataRegister == 0xFF) { - /* Preamble2 */ - Index = 0; - HeaderCRC = 0xFF; - /* receive the remainder of the frame. */ - Receive_State = MSTP_RECEIVE_STATE_HEADER; - } else if (DataRegister == 0x55) { - /* ignore RepeatedPreamble1 */ - /* wait for the second preamble octet. */ - Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; - } else { - /* NotPreamble */ - /* wait for the start of a frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } - } - break; - case MSTP_RECEIVE_STATE_HEADER: - /* In the HEADER state, the node waits - for the fixed message header. */ - if (rs485_silence_elapsed(Tframe_abort)) { - /* Timeout */ - /* indicate that an error has occurred - during the reception of a frame */ - MSTP_Flag.ReceivedInvalidFrame = true; - /* wait for the start of a frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } else if (rs485_receive_error()) { - /* Error */ - rs485_silence_reset(); - INCREMENT_AND_LIMIT_UINT8(EventCount); - /* indicate that an error has occurred - during the reception of a frame */ - MSTP_Flag.ReceivedInvalidFrame = true; - /* wait for the start of a frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } else if (rs485_byte_available(&DataRegister)) { - rs485_silence_reset(); - INCREMENT_AND_LIMIT_UINT8(EventCount); - if (Index == 0) { - /* FrameType */ - HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); - FrameType = DataRegister; - Index = 1; - } else if (Index == 1) { - /* Destination */ - HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); - DestinationAddress = DataRegister; - Index = 2; - } else if (Index == 2) { - /* Source */ - HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); - SourceAddress = DataRegister; - Index = 3; - } else if (Index == 3) { - /* Length1 */ - HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); - DataLength = DataRegister * 256; - Index = 4; - } else if (Index == 4) { - /* Length2 */ - HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); - DataLength += DataRegister; - Index = 5; - } else if (Index == 5) { - /* HeaderCRC */ - HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); - /* In the HEADER_CRC state, the node validates the CRC - on the fixed message header. */ - if (HeaderCRC != 0x55) { - /* BadCRC */ - /* indicate that an error has occurred during - the reception of a frame */ - MSTP_Flag.ReceivedInvalidFrame = true; - /* wait for the start of the next frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } else { - if (DataLength == 0) { - /* NoData */ - if ((DestinationAddress == This_Station) || - (DestinationAddress == - MSTP_BROADCAST_ADDRESS)) { - /* ForUs */ - /* indicate that a frame with - no data has been received */ - MSTP_Flag.ReceivedValidFrame = true; - } else { - /* NotForUs */ - MSTP_Flag.ReceivedValidFrameNotForUs = true; - } - /* wait for the start of the next frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } else { - /* receive the data portion of the frame. */ - if ((DestinationAddress == This_Station) || - (DestinationAddress == MSTP_BROADCAST_ADDRESS)) { - if (DataLength <= InputBufferSize) { - /* Data */ - Receive_State = MSTP_RECEIVE_STATE_DATA; - } else { - /* FrameTooLong */ - Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; - } - } else { - /* NotForUs */ - Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; - } - Index = 0; - DataCRC = 0xFFFF; - } - } - } else { - /* indicate that an error has occurred during */ - /* the reception of a frame */ - MSTP_Flag.ReceivedInvalidFrame = true; - /* wait for the start of a frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } - } - break; - case MSTP_RECEIVE_STATE_DATA: - case MSTP_RECEIVE_STATE_SKIP_DATA: - /* In the DATA state, the node waits - for the data portion of a frame. */ - if (rs485_silence_elapsed(Tframe_abort)) { - /* Timeout */ - /* indicate that an error has occurred - during the reception of a frame */ - MSTP_Flag.ReceivedInvalidFrame = true; - /* wait for the start of the next frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } else if (rs485_receive_error()) { - /* Error */ - rs485_silence_reset(); - INCREMENT_AND_LIMIT_UINT8(EventCount); - /* indicate that an error has occurred during - the reception of a frame */ - MSTP_Flag.ReceivedInvalidFrame = true; - /* wait for the start of the next frame. */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } else if (rs485_byte_available(&DataRegister)) { - rs485_silence_reset(); - INCREMENT_AND_LIMIT_UINT8(EventCount); - if (Index < DataLength) { - /* DataOctet */ - DataCRC = CRC_Calc_Data(DataRegister, DataCRC); - if (Index < InputBufferSize) { - InputBuffer[Index] = DataRegister; - } - Index++; - } else if (Index == DataLength) { - /* CRC1 */ - DataCRC = CRC_Calc_Data(DataRegister, DataCRC); - Index++; - } else if (Index == (DataLength + 1)) { - /* CRC2 */ - DataCRC = CRC_Calc_Data(DataRegister, DataCRC); - /* STATE DATA CRC - no need for new state */ - /* indicate the complete reception of a valid frame */ - if (DataCRC == 0xF0B8) { - if (Receive_State == MSTP_RECEIVE_STATE_DATA) { - /* ForUs */ - MSTP_Flag.ReceivedValidFrame = true; - } else { - /* NotForUs */ - MSTP_Flag.ReceivedValidFrameNotForUs = true; - } - } else { - MSTP_Flag.ReceivedInvalidFrame = true; - } - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } else { - MSTP_Flag.ReceivedInvalidFrame = true; - Receive_State = MSTP_RECEIVE_STATE_IDLE; - } - } - break; - default: - /* shouldn't get here - but if we do... */ - Receive_State = MSTP_RECEIVE_STATE_IDLE; - break; - } - - return; -} - -/* returns true if we need to transition immediately */ -static bool MSTP_Master_Node_FSM( - void) -{ - /* The number of frames sent by this node during a single token hold. */ - /* When this counter reaches the value Nmax_info_frames, the node must */ - /* pass the token. */ - static uint8_t FrameCount; - /* "Next Station," the MAC address of the node to which This Station - passes the token. If the Next_Station is unknown, Next_Station shall - be equal to This_Station. */ - static uint8_t Next_Station; - /* "Poll Station," the MAC address of the node to which This Station last */ - /* sent a Poll For Master. This is used during token maintenance. */ - static uint8_t Poll_Station; - /* A counter of transmission retries used for Token and Poll For Master */ - /* transmission. */ - static unsigned RetryCount; - /* The number of tokens received by this node. When this counter reaches */ - /* the value Npoll, the node polls the address range between TS and NS */ - /* for additional master nodes. TokenCount is set to zero at the end of */ - /* the polling process. */ - static unsigned TokenCount; - /* next-x-station calculations */ - uint8_t next_poll_station = 0; - uint8_t next_this_station = 0; - uint8_t next_next_station = 0; - /* timeout values */ - uint16_t my_timeout = 10, ns_timeout = 0; - bool matched = false; - /* transition immediately to the next state */ - bool transition_now = false; - /* packet from the PDU Queue */ - struct mstp_pdu_packet *pkt; - - /* auto mode is active */ - if (This_Station == 255) { - Master_State = MSTP_MASTER_STATE_INITIALIZE; - } - /* some calculations that several states need */ - next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); - next_this_station = (This_Station + 1) % (Nmax_master + 1); - next_next_station = (Next_Station + 1) % (Nmax_master + 1); - switch (Master_State) { - case MSTP_MASTER_STATE_INITIALIZE: - if (This_Station == 255) { - dlmstp_automac_hander(); - if (This_Station != 255) { - Next_Station = automac_next_station(This_Station); - if (Next_Station == 255) { - /* indicate that the next station is unknown */ - Next_Station = This_Station; - Poll_Station = This_Station; - TokenCount = Npoll; - EventCount = 0; - MSTP_Flag.SoleMaster = true; - Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; - } else { - Poll_Station = This_Station; - TokenCount = 1; - EventCount = 0; - RetryCount = 0; - MSTP_Flag.SoleMaster = false; - Master_State = MSTP_MASTER_STATE_PASS_TOKEN; - } - } - } else { - /* DoneInitializing */ - /* indicate that the next station is unknown */ - Next_Station = This_Station; - Poll_Station = This_Station; - /* cause a Poll For Master to be sent when this node first */ - /* receives the token */ - TokenCount = Npoll; - MSTP_Flag.SoleMaster = false; - Master_State = MSTP_MASTER_STATE_IDLE; - transition_now = true; - } - break; - case MSTP_MASTER_STATE_IDLE: - /* In the IDLE state, the node waits for a frame. */ - if (rs485_silence_elapsed(Tno_token)) { - /* LostToken */ - /* assume that the token has been lost */ - EventCount = 0; /* Addendum 135-2004d-8 */ - /* set the receive frame flags to false in case we received - some bytes and had a timeout for some reason */ - MSTP_Flag.ReceivedValidFrame = false; - MSTP_Flag.ReceivedInvalidFrame = false; - MSTP_Flag.ReceivedValidFrameNotForUs = false; - Master_State = MSTP_MASTER_STATE_NO_TOKEN; - transition_now = true; - } else if (MSTP_Flag.ReceivedInvalidFrame == true) { - /* ReceivedInvalidFrame */ - /* invalid frame was received */ - MSTP_Flag.ReceivedInvalidFrame = false; - /* wait for the next frame - remain in IDLE */ - } else if (MSTP_Flag.ReceivedValidFrame == true) { - switch (FrameType) { - case FRAME_TYPE_TOKEN: - /* ReceivedToken */ - /* tokens can't be broadcast */ - if (DestinationAddress == MSTP_BROADCAST_ADDRESS) - break; - MSTP_Flag.ReceivedValidFrame = false; - FrameCount = 0; - MSTP_Flag.SoleMaster = false; - Master_State = MSTP_MASTER_STATE_USE_TOKEN; - transition_now = true; - break; - case FRAME_TYPE_POLL_FOR_MASTER: - /* ReceivedPFM */ - MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, - SourceAddress, This_Station, NULL, 0); - break; - case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: - /* indicate successful reception to the higher layers */ - MSTP_Flag.ReceivePacketPending = true; - break; - case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: - /* indicate successful reception to higher layers */ - MSTP_Flag.ReceivePacketPending = true; - /* broadcast DER just remains IDLE */ - if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { - Master_State = - MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; - } - break; - case FRAME_TYPE_TEST_REQUEST: - MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, - SourceAddress, This_Station, &InputBuffer[0], - DataLength); - break; - case FRAME_TYPE_TEST_RESPONSE: - default: - break; - } - /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for - reference, and the flag will be cleared in the next state */ - if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { - MSTP_Flag.ReceivedValidFrame = false; - } - } - break; - /* In the USE_TOKEN state, the node is allowed to send one or */ - /* more data frames. These may be BACnet Data frames or */ - /* proprietary frames. */ - case MSTP_MASTER_STATE_USE_TOKEN: - /* Note: We could wait for up to Tusage_delay */ - if (Ringbuf_Empty(&PDU_Queue)) { - /* NothingToSend */ - FrameCount = Nmax_info_frames; - Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; - transition_now = true; - } else { - uint8_t frame_type; - pkt = (struct mstp_pdu_packet *)Ringbuf_Pop_Front( - &PDU_Queue); - if (pkt->data_expecting_reply) { - frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; - } else { - frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; - } - MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, - (uint8_t *) & pkt->buffer[0], pkt->length); - FrameCount++; - switch (frame_type) { - case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: - /* SendAndWait */ - if (pkt->destination_mac == MSTP_BROADCAST_ADDRESS) - Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; - else - Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; - break; - case FRAME_TYPE_TEST_REQUEST: - Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; - break; - case FRAME_TYPE_TEST_RESPONSE: - case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: - default: - /* SendNoWait */ - Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; - break; - } - } - break; - case MSTP_MASTER_STATE_WAIT_FOR_REPLY: - /* In the WAIT_FOR_REPLY state, the node waits for */ - /* a reply from another node. */ - if (rs485_silence_elapsed(Treply_timeout)) { - /* ReplyTimeout */ - /* assume that the request has failed */ - FrameCount = Nmax_info_frames; - Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; - /* Any retry of the data frame shall await the next entry */ - /* to the USE_TOKEN state. */ - /* (Because of the length of the timeout, */ - /* this transition will cause the token to be */ - /* passed regardless */ - /* of the initial value of FrameCount.) */ - transition_now = true; - } else { - if (MSTP_Flag.ReceivedInvalidFrame == true) { - /* InvalidFrame */ - /* error in frame reception */ - MSTP_Flag.ReceivedInvalidFrame = false; - Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; - transition_now = true; - } else if (MSTP_Flag.ReceivedValidFrame == true) { - if (DestinationAddress == This_Station) { - /* What did we receive? */ - switch (FrameType) { - case FRAME_TYPE_REPLY_POSTPONED: - /* ReceivedReplyPostponed */ - Master_State = - MSTP_MASTER_STATE_DONE_WITH_TOKEN; - break; - case FRAME_TYPE_TEST_RESPONSE: - Master_State = MSTP_MASTER_STATE_IDLE; - break; - case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: - /* ReceivedReply */ - /* or a proprietary type that indicates - a reply */ - /* indicate successful reception to - the higher layers */ - MSTP_Flag.ReceivePacketPending = true; - Master_State = - MSTP_MASTER_STATE_DONE_WITH_TOKEN; - break; - default: - /* if proprietary frame was expected, you might - need to transition to DONE WITH TOKEN */ - Master_State = MSTP_MASTER_STATE_IDLE; - break; - } - } else { - /* ReceivedUnexpectedFrame */ - /* an unexpected frame was received */ - /* This may indicate the presence of multiple tokens */ - /* or a device that didn't see activity after passing */ - /* a token (how lame!). */ - /* Synchronize with the network. */ - /* This action drops the token. */ - Master_State = MSTP_MASTER_STATE_IDLE; - } - MSTP_Flag.ReceivedValidFrame = false; - transition_now = true; - } - } - break; - /* The DONE_WITH_TOKEN state either sends another data frame, */ - /* passes the token, or initiates a Poll For Master cycle. */ - case MSTP_MASTER_STATE_DONE_WITH_TOKEN: - /* SendAnotherFrame */ - if (FrameCount < Nmax_info_frames) { - /* then this node may send another information frame */ - /* before passing the token. */ - Master_State = MSTP_MASTER_STATE_USE_TOKEN; - transition_now = true; - } else if ((MSTP_Flag.SoleMaster == false) && - (Next_Station == This_Station)) { - /* NextStationUnknown - added in Addendum 135-2008v-1 */ - /* then the next station to which the token - should be sent is unknown - so PollForMaster */ - Poll_Station = next_this_station; - MSTP_Send_Frame( - FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, - This_Station, NULL, 0); - RetryCount = 0; - Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; - } - /* Npoll changed in Errata SSPC-135-2004 */ - else if (TokenCount < (Npoll - 1)) { - if ((MSTP_Flag.SoleMaster == true) && - (Next_Station != next_this_station)) { - /* SoleMaster */ - /* there are no other known master nodes to */ - /* which the token may be sent - (true master-slave operation). */ - FrameCount = 0; - TokenCount++; - Master_State = MSTP_MASTER_STATE_USE_TOKEN; - transition_now = true; - } else { - /* SendToken */ - /* Npoll changed in Errata SSPC-135-2004 */ - /* The comparison of NS and TS+1 - eliminates the Poll For Master - if there are no addresses between - TS and NS, since there is no - address at which a new master node - may be found in that case. */ - TokenCount++; - /* transmit a Token frame to NS */ - MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, - This_Station, NULL, 0); - RetryCount = 0; - EventCount = 0; - Master_State = MSTP_MASTER_STATE_PASS_TOKEN; - } - } else if (next_poll_station == Next_Station) { - if (MSTP_Flag.SoleMaster == true) { - /* SoleMasterRestartMaintenancePFM */ - Poll_Station = next_next_station; - MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, - This_Station, NULL, 0); - /* no known successor node */ - Next_Station = This_Station; - RetryCount = 0; - TokenCount = 1; /* changed in Errata SSPC-135-2004 */ - /* EventCount = 0; removed in Addendum 135-2004d-8 */ - /* find a new successor to TS */ - Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; - } else { - /* ResetMaintenancePFM */ - Poll_Station = This_Station; - /* transmit a Token frame to NS */ - MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, - This_Station, NULL, 0); - RetryCount = 0; - TokenCount = 1; /* changed in Errata SSPC-135-2004 */ - EventCount = 0; - Master_State = MSTP_MASTER_STATE_PASS_TOKEN; - } - } else { - /* SendMaintenancePFM */ - Poll_Station = next_poll_station; - MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, - This_Station, NULL, 0); - RetryCount = 0; - Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; - } - break; - /* The PASS_TOKEN state listens for a successor to begin using */ - /* the token that this node has just attempted to pass. */ - case MSTP_MASTER_STATE_PASS_TOKEN: - if (rs485_silence_elapsed(Tusage_timeout)) { - if (RetryCount < Nretry_token) { - /* RetrySendToken */ - RetryCount++; - /* Transmit a Token frame to NS */ - MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, - This_Station, NULL, 0); - EventCount = 0; - /* re-enter the current state to listen for NS */ - /* to begin using the token. */ - } else { - /* FindNewSuccessor */ - /* Assume that NS has failed. */ - Poll_Station = next_next_station; - /* Transmit a Poll For Master frame to PS. */ - MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, - This_Station, NULL, 0); - /* no known successor node */ - Next_Station = This_Station; - RetryCount = 0; - TokenCount = 0; - /* EventCount = 0; removed in Addendum 135-2004d-8 */ - /* find a new successor to TS */ - Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; - } - } else { - if (EventCount > Nmin_octets) { - /* SawTokenUser */ - /* Assume that a frame has been sent by - the new token user. */ - /* Enter the IDLE state to process the frame. */ - Master_State = MSTP_MASTER_STATE_IDLE; - transition_now = true; - } - } - break; - /* The NO_TOKEN state is entered if Silence Timer - becomes greater than Tno_token, indicating that - there has been no network activity for that period - of time. The timeout is continued to determine - whether or not this node may create a token. */ - case MSTP_MASTER_STATE_NO_TOKEN: - my_timeout = Tno_token + (Tslot * This_Station); - if (rs485_silence_elapsed(my_timeout)) { - ns_timeout = Tno_token + (Tslot * (This_Station + 1)); - if (rs485_silence_elapsed(ns_timeout)) { - /* should never get here unless timer resolution is bad */ - rs485_silence_reset(); - Master_State = MSTP_MASTER_STATE_IDLE; - } else { - /* GenerateToken */ - /* Assume that this node is the lowest numerical address */ - /* on the network and is empowered to create a token. */ - Poll_Station = next_this_station; - /* Transmit a Poll For Master frame to PS. */ - MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, - This_Station, NULL, 0); - /* indicate that the next station is unknown */ - Next_Station = This_Station; - RetryCount = 0; - TokenCount = 0; - /* EventCount = 0; removed Addendum 135-2004d-8 */ - /* enter the POLL_FOR_MASTER state - to find a new successor to TS. */ - Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; - } - } else { - if (EventCount > Nmin_octets) { - /* SawFrame */ - /* Some other node exists at a lower address. */ - /* Enter the IDLE state to receive and - process the incoming frame. */ - Master_State = MSTP_MASTER_STATE_IDLE; - transition_now = true; - } - } - break; - /* In the POLL_FOR_MASTER state, the node listens for a reply to */ - /* a previously sent Poll For Master frame in order to find */ - /* a successor node. */ - case MSTP_MASTER_STATE_POLL_FOR_MASTER: - if (MSTP_Flag.ReceivedValidFrame == true) { - if ((DestinationAddress == This_Station) - && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { - /* ReceivedReplyToPFM */ - MSTP_Flag.SoleMaster = false; - Next_Station = SourceAddress; - EventCount = 0; - /* Transmit a Token frame to NS */ - MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, - This_Station, NULL, 0); - Poll_Station = This_Station; - TokenCount = 0; - RetryCount = 0; - Master_State = MSTP_MASTER_STATE_PASS_TOKEN; - } else { - /* ReceivedUnexpectedFrame */ - /* An unexpected frame was received. */ - /* This may indicate the presence of multiple tokens. */ - /* enter the IDLE state to synchronize with the network. */ - /* This action drops the token. */ - Master_State = MSTP_MASTER_STATE_IDLE; - transition_now = true; - } - MSTP_Flag.ReceivedValidFrame = false; - } else if ((rs485_silence_elapsed(Tusage_timeout)) || - (MSTP_Flag.ReceivedInvalidFrame == true)) { - if (MSTP_Flag.SoleMaster == true) { - /* SoleMaster */ - /* There was no valid reply to the periodic poll */ - /* by the sole known master for other masters. */ - FrameCount = 0; - /* TokenCount++; removed in 2004 */ - Master_State = MSTP_MASTER_STATE_USE_TOKEN; - transition_now = true; - } else { - if (Next_Station != This_Station) { - /* DoneWithPFM */ - /* There was no valid reply to the maintenance */ - /* poll for a master at address PS. */ - EventCount = 0; - /* transmit a Token frame to NS */ - MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, - This_Station, NULL, 0); - RetryCount = 0; - Master_State = MSTP_MASTER_STATE_PASS_TOKEN; - } else { - if (next_poll_station != This_Station) { - /* SendNextPFM */ - Poll_Station = next_poll_station; - /* Transmit a Poll For Master frame to PS. */ - MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, - Poll_Station, This_Station, NULL, 0); - RetryCount = 0; - /* Re-enter the current state. */ - } else { - /* DeclareSoleMaster */ - /* to indicate that this station - is the only master */ - MSTP_Flag.SoleMaster = true; - FrameCount = 0; - Master_State = MSTP_MASTER_STATE_USE_TOKEN; - transition_now = true; - } - } - } - MSTP_Flag.ReceivedInvalidFrame = false; - } - break; - /* The ANSWER_DATA_REQUEST state is entered when a */ - /* BACnet Data Expecting Reply, a Test_Request, or */ - /* a proprietary frame that expects a reply is received. */ - case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: - /* Note: we could wait for up to Treply_delay */ - matched = false; - if (!Ringbuf_Empty(&PDU_Queue)) { - pkt = (struct mstp_pdu_packet *)Ringbuf_Get_Front( - &PDU_Queue); - matched = - dlmstp_compare_data_expecting_reply(&InputBuffer[0], - DataLength, SourceAddress, &pkt->buffer[0], - pkt->length, pkt->destination_mac); - } - if (matched) { - /* Reply */ - /* If a reply is available from the higher layers */ - /* within Treply_delay after the reception of the */ - /* final octet of the requesting frame */ - /* (the mechanism used to determine this is a local matter), */ - /* then call MSTP_Send_Frame to transmit the reply frame */ - /* and enter the IDLE state to wait for the next frame. */ - uint8_t frame_type; - pkt = (struct mstp_pdu_packet *)Ringbuf_Pop_Front( - &PDU_Queue); - if (pkt->data_expecting_reply) { - frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; - } else { - frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; - } - MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, - (uint8_t *) & pkt->buffer[0], pkt->length); - Master_State = MSTP_MASTER_STATE_IDLE; - } else { - /* DeferredReply */ - /* If no reply will be available from the higher layers */ - /* within Treply_delay after the reception of the */ - /* final octet of the requesting frame (the mechanism */ - /* used to determine this is a local matter), */ - /* then an immediate reply is not possible. */ - /* Any reply shall wait until this node receives the token. */ - /* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */ - /* and enter the IDLE state. */ - MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, - This_Station, NULL, 0); - Master_State = MSTP_MASTER_STATE_IDLE; - } - /* clear our flag we were holding for comparison */ - MSTP_Flag.ReceivedValidFrame = false; - break; - default: - Master_State = MSTP_MASTER_STATE_IDLE; - break; - } - - return transition_now; -} - -/* returns number of bytes sent on success, zero on failure */ -int dlmstp_send_pdu( - BACNET_ADDRESS * dest, /* destination address */ - BACNET_NPDU_DATA * npdu_data, /* network information */ - uint8_t * pdu, /* any data to be sent - may be null */ - unsigned pdu_len) -{ /* number of bytes of data */ - int bytes_sent = 0; - struct mstp_pdu_packet *pkt; - uint16_t i = 0; - - pkt = (struct mstp_pdu_packet *)Ringbuf_Alloc(&PDU_Queue); - if (pkt) { - pkt->data_expecting_reply = npdu_data->data_expecting_reply; - for (i = 0; i < pdu_len; i++) { - pkt->buffer[i] = pdu[i]; - } - pkt->length = pdu_len; - pkt->destination_mac = dest->mac[0]; - bytes_sent = pdu_len; - } - - return bytes_sent; -} - -/* master node FSM states */ -typedef enum { - AUTOMAC_STATE_IDLE = 0, - AUTOMAC_STATE_PFM = 1, - AUTOMAC_STATE_TOKEN = 2, - AUTOMAC_STATE_TESTING = 3, - AUTOMAC_STATE_CONFIRM = 4 -} AUTOMAC_STATE; -/* buffer used to send and validate a response - size is min APDU size */ -static uint8_t AutoMAC_Test_Buffer[50]; -void dlmstp_automac_hander(void) -{ - static AUTOMAC_STATE state = AUTOMAC_STATE_IDLE; - uint8_t mac = 0; - uint32_t serial_number = 0; - uint16_t vendor_id = 0; - bool take_address = false; - bool start_over = false; - - switch (state) { - case AUTOMAC_STATE_IDLE: - if ((MSTP_Flag.ReceivedValidFrame) || - (MSTP_Flag.ReceivedValidFrameNotForUs)) { - MSTP_Flag.ReceivedValidFrame = false; - MSTP_Flag.ReceivedValidFrameNotForUs = false; - /* store stats until we get a MAC */ - automac_emitter_set(SourceAddress); - switch (FrameType) { - case FRAME_TYPE_TOKEN: - automac_token_set(SourceAddress); - break; - case FRAME_TYPE_POLL_FOR_MASTER: - automac_pfm_set(DestinationAddress); - break; - default: - break; - } - } else if (MSTP_Flag.ReceivedInvalidFrame) { - MSTP_Flag.ReceivedInvalidFrame = false; - } else if (automac_pfm_cycle_complete()) { - mac = automac_free_address_random(); - if (automac_free_address_valid(mac)) { - automac_address_set(mac); - state = AUTOMAC_STATE_PFM; - } else { - /* start over again */ - automac_init(); - state = AUTOMAC_STATE_IDLE; - } - } else if (rs485_silence_elapsed(automac_time_slot())) { - /* long silence indicates we are alone or - with other silent devices */ - SourceAddress = automac_address(); - state = AUTOMAC_STATE_TESTING; - } - break; - case AUTOMAC_STATE_PFM: - if ((MSTP_Flag.ReceivedValidFrame) || - (MSTP_Flag.ReceivedValidFrameNotForUs)) { - MSTP_Flag.ReceivedValidFrame = false; - MSTP_Flag.ReceivedValidFrameNotForUs = false; - /* store stats until we get a MAC */ - switch (FrameType) { - case FRAME_TYPE_POLL_FOR_MASTER: - mac = automac_address(); - if (mac == SourceAddress) { - /* start over again */ - automac_init(); - state = AUTOMAC_STATE_IDLE; - } else if (mac == DestinationAddress) { - MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, - SourceAddress, mac, NULL, 0); - state = AUTOMAC_STATE_TOKEN; - } - break; - case FRAME_TYPE_TEST_REQUEST: - mac = automac_address(); - if ((mac == DestinationAddress) || - (mac == SourceAddress)) { - /* start over again */ - automac_init(); - state = AUTOMAC_STATE_IDLE; - } - break; - case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: - case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: - case FRAME_TYPE_TEST_RESPONSE: - case FRAME_TYPE_TOKEN: - mac = automac_address(); - if (mac == SourceAddress) { - /* start over again */ - automac_init(); - state = AUTOMAC_STATE_IDLE; - } - break; - default: - break; - } - } else if (MSTP_Flag.ReceivedInvalidFrame) { - MSTP_Flag.ReceivedInvalidFrame = false; - } else if (rs485_silence_elapsed(automac_time_slot())) { - /* start over again */ - automac_init(); - state = AUTOMAC_STATE_IDLE; - } - break; - case AUTOMAC_STATE_TOKEN: - if ((MSTP_Flag.ReceivedValidFrame) || - (MSTP_Flag.ReceivedValidFrameNotForUs)) { - MSTP_Flag.ReceivedValidFrame = false; - MSTP_Flag.ReceivedValidFrameNotForUs = false; - switch (FrameType) { - case FRAME_TYPE_TOKEN: - mac = automac_address(); - if (mac == SourceAddress) { - /* start over again */ - automac_init(); - state = AUTOMAC_STATE_IDLE; - } else if (mac == DestinationAddress) { - state = AUTOMAC_STATE_TESTING; - } - break; - default: - /* start over again */ - automac_init(); - state = AUTOMAC_STATE_IDLE; - break; - } - } else if (MSTP_Flag.ReceivedInvalidFrame) { - MSTP_Flag.ReceivedInvalidFrame = false; - } else if (rs485_silence_elapsed(automac_time_slot())) { - /* start over again */ - automac_init(); - state = AUTOMAC_STATE_IDLE; - } - break; - case AUTOMAC_STATE_TESTING: - /* I have the token - confirm my MAC with a quick test */ - mac = automac_address(); - vendor_id = Device_Vendor_Identifier(); - encode_unsigned16(&AutoMAC_Test_Buffer[0], vendor_id); - serial_number = Device_Object_Instance_Number(); - encode_unsigned32(&AutoMAC_Test_Buffer[2], serial_number); - MSTP_Send_Frame(FRAME_TYPE_TEST_REQUEST, - SourceAddress, mac, &AutoMAC_Test_Buffer[0], - 6); - state = AUTOMAC_STATE_CONFIRM; - break; - case AUTOMAC_STATE_CONFIRM: - /* we may timeout if our chosen MAC is unique */ - if (MSTP_Flag.ReceivedInvalidFrame) { - MSTP_Flag.ReceivedInvalidFrame = false; - start_over = true; - } else if ((MSTP_Flag.ReceivedValidFrame) || - (MSTP_Flag.ReceivedValidFrameNotForUs)) { - MSTP_Flag.ReceivedValidFrame = false; - MSTP_Flag.ReceivedValidFrameNotForUs = false; - mac = automac_address(); - if ((mac == DestinationAddress) && - (DataLength >= 6)) { - decode_unsigned16(&InputBuffer[0], &vendor_id); - decode_unsigned32(&InputBuffer[2], &serial_number); - if ((vendor_id == Device_Vendor_Identifier()) && - (serial_number == Device_Object_Instance_Number())) { - take_address = true; - } else { - start_over = true; - } - } else { - start_over = true; - } - } else if (rs485_silence_elapsed(300)) { - /* use maximum possible value for Treply_timeout */ - /* in case validating device doesn't support Test Request */ - /* no response and no collission */ - take_address = true; - } - if (take_address) { - /* take the address */ - This_Station = automac_address(); - DestinationAddress = automac_next_station(This_Station); - if (DestinationAddress < 128) { - MSTP_Send_Frame(FRAME_TYPE_TOKEN, DestinationAddress, - This_Station, NULL, 0); - } - state = AUTOMAC_STATE_IDLE; - } else if (start_over) { - /* start over again */ - automac_init(); - state = AUTOMAC_STATE_IDLE; - } - break; - default: - break; - } -} - -/* Return the length of the packet */ -uint16_t dlmstp_receive( - BACNET_ADDRESS * src, /* source address */ - uint8_t * pdu, /* PDU data */ - uint16_t max_pdu, /* amount of space available in the PDU */ - unsigned timeout) -{ /* milliseconds to wait for a packet */ - uint16_t pdu_len = 0; /* return value */ - bool transmitting = false; - - /* set the input buffer to the same data storage for zero copy */ - if (!InputBuffer) { - InputBuffer = pdu; - InputBufferSize = max_pdu; - } - if (This_Station == 255) { - automac_enabled_set(true); - } - if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { - transmitting = MSTP_Transmit_FSM(); - } - /* only do receive state machine while we don't have a frame */ - if ((MSTP_Flag.ReceivedValidFrame == false) && - (MSTP_Flag.ReceivedValidFrameNotForUs == false) && - (MSTP_Flag.ReceivedInvalidFrame == false) && - (transmitting == false)) { - MSTP_Receive_Frame_FSM(); - } - /* only do master state machine while rx is idle */ - if ((Receive_State == MSTP_RECEIVE_STATE_IDLE) && - (transmitting == false)) { - if ((This_Station != 255) && - (MSTP_Flag.ReceivedValidFrameNotForUs)) { - MSTP_Flag.ReceivedValidFrameNotForUs = false; - if ((SourceAddress == This_Station) && - automac_enabled()) { - /* duplicate MAC on the wire */ - automac_init(); - This_Station = 255; - } - } else { - while (MSTP_Master_Node_FSM()) { - /* do nothing while some states fast transition */ - }; - } - } - if (This_Station != 255) { - /* if there is a packet that needs processed, do it now. */ - if (MSTP_Flag.ReceivePacketPending) { - MSTP_Flag.ReceivePacketPending = false; - pdu_len = DataLength; - src->mac_len = 1; - src->mac[0] = SourceAddress; - /* data is already in the pdu pointer */ - } - } - - return pdu_len; -} - -void dlmstp_set_mac_address( - uint8_t mac_address) -{ - /* Master Nodes can only have address 0-127 */ - if (mac_address <= 127) { - This_Station = mac_address; - if (mac_address > Nmax_master) { - dlmstp_set_max_master(127); - } - } else if (mac_address == 255) { - /* auto-MAC provision */ - This_Station = mac_address; - } - - return; -} - -uint8_t dlmstp_mac_address( - void) -{ - return This_Station; -} - -/* This parameter represents the value of the Max_Info_Frames property of */ -/* the node's Device object. The value of Max_Info_Frames specifies the */ -/* maximum number of information frames the node may send before it must */ -/* pass the token. Max_Info_Frames may have different values on different */ -/* nodes. This may be used to allocate more or less of the available link */ -/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ -/* node, its value shall be 1. */ -void dlmstp_set_max_info_frames( - uint8_t max_info_frames) -{ - if (max_info_frames >= 1) { - Nmax_info_frames = max_info_frames; - } - - return; -} - -uint8_t dlmstp_max_info_frames( - void) -{ - return Nmax_info_frames; -} - -/* This parameter represents the value of the Max_Master property of the */ -/* node's Device object. The value of Max_Master specifies the highest */ -/* allowable address for master nodes. The value of Max_Master shall be */ -/* less than or equal to 127. If Max_Master is not writable in a node, */ -/* its value shall be 127. */ -void dlmstp_set_max_master( - uint8_t max_master) -{ - if (max_master <= 127) { - if ((This_Station == 255) || - (This_Station <= max_master)) { - Nmax_master = max_master; - } - } - - return; -} - -uint8_t dlmstp_max_master( - void) -{ - return Nmax_master; -} - -void dlmstp_get_my_address( - BACNET_ADDRESS * my_address) -{ - int i = 0; /* counter */ - - my_address->mac_len = 1; - my_address->mac[0] = This_Station; - my_address->net = 0; /* local only, no routing */ - my_address->len = 0; - for (i = 0; i < MAX_MAC_LEN; i++) { - my_address->adr[i] = 0; - } - - return; -} - -void dlmstp_get_broadcast_address( - BACNET_ADDRESS * dest) -{ /* destination address */ - int i = 0; /* counter */ - - if (dest) { - dest->mac_len = 1; - dest->mac[0] = MSTP_BROADCAST_ADDRESS; - dest->net = BACNET_BROADCAST_NETWORK; - dest->len = 0; /* always zero when DNET is broadcast */ - for (i = 0; i < MAX_MAC_LEN; i++) { - dest->adr[i] = 0; - } - } - - return; -} - -bool dlmstp_sole_master(void) -{ - if (MSTP_Flag.SoleMaster) { - return true; - } - - return false; -} - +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 Steve Karg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bacaddr.h" +#include "timer.h" +#include "ringbuf.h" +#include "mstpdef.h" +#include "automac.h" +#include "device.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one MS/TP datalink layer +*/ + +/* The state of the Receive State Machine */ +static MSTP_RECEIVE_STATE Receive_State; +/* When a master node is powered up or reset, */ +/* it shall unconditionally enter the INITIALIZE state. */ +static MSTP_MASTER_STATE Master_State; +/* bit-sized boolean flags */ +static struct mstp_flag_t { + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* set to TRUE when we get a frame not for us */ + unsigned ReceivedValidFrameNotForUs:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* A Boolean flag set TRUE by the datalink if a + packet has been received, but not processed. */ + unsigned ReceivePacketPending:1; +} MSTP_Flag; + +/* Used to store the data length of a received frame. */ +static uint32_t DataLength; +/* Used to store the destination address of a received frame. */ +static uint8_t DestinationAddress; +/* Used to count the number of received octets or errors. */ +/* This is used in the detection of link activity. */ +/* Compared to Nmin_octets */ +static uint8_t EventCount; +/* Used to store the frame type of a received frame. */ +static uint8_t FrameType; +/* An array of octets, used to store octets as they are received. */ +/* InputBuffer is indexed from 0 to InputBufferSize-1. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *InputBuffer; +static uint16_t InputBufferSize; +/* Used to store the Source Address of a received frame. */ +static uint8_t SourceAddress; +/* "This Station," the MAC address of this node. TS is generally read from a */ +/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ +/* 0 to 254. The value 255 is used to denote AutoMAC addressing */ +static uint8_t This_Station = 255; +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +static uint8_t Nmax_master = 127; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 260 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 60 + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +#define Treply_delay 250 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +/* data structure for MS/TP transmit packet */ +struct mstp_tx_packet { + uint16_t length; + uint16_t index; + uint8_t buffer[MAX_MPDU]; +}; +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_TRANSMIT_PACKET_COUNT +#define MSTP_TRANSMIT_PACKET_COUNT 1 +#endif +static struct mstp_tx_packet Transmit_Buffer[MSTP_TRANSMIT_PACKET_COUNT]; +static RING_BUFFER Transmit_Queue; + +/* data structure for MS/TP PDU Queue */ +struct mstp_pdu_packet { + bool data_expecting_reply; + uint8_t destination_mac; + uint16_t length; + uint8_t buffer[MAX_MPDU]; +}; +/* count must be a power of 2 for ringbuf library */ +#ifndef MSTP_PDU_PACKET_COUNT +#define MSTP_PDU_PACKET_COUNT 2 +#endif +static struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT]; +static RING_BUFFER PDU_Queue; +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +static uint8_t Nmax_info_frames = MSTP_PDU_PACKET_COUNT; + +void dlmstp_automac_hander(void); + +bool dlmstp_init( + char *ifname) +{ + ifname = ifname; + Ringbuf_Init( + &Transmit_Queue, + (uint8_t *)&Transmit_Buffer, + sizeof(struct mstp_tx_packet), + MSTP_TRANSMIT_PACKET_COUNT); + Ringbuf_Init( + &PDU_Queue, + (uint8_t *)&PDU_Buffer, + sizeof(struct mstp_pdu_packet), + MSTP_PDU_PACKET_COUNT); + rs485_init(); + automac_init(); + + return true; +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +static bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + uint8_t dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + if (request.invoke_id != reply.invoke_id) { + return false; + } + /* these services don't have service choice included */ + if ((reply.pdu_type != PDU_TYPE_REJECT) && + (reply.pdu_type != PDU_TYPE_ABORT)) { + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +typedef enum +{ + MSTP_TX_STATE_IDLE, + MSTP_TX_STATE_SILENCE_WAIT, + MSTP_TX_STATE_SEND_WAIT, + MSTP_TX_STATE_STOP +} MSTP_TX_STATE; +static bool MSTP_Transmit_FSM(void) +{ + static MSTP_TX_STATE state = MSTP_TX_STATE_IDLE; + static struct mstp_tx_packet *pkt; + + MSTP_TX_START: + switch (state) { + case MSTP_TX_STATE_IDLE: + if (!Ringbuf_Empty(&Transmit_Queue)) { + /* get the packet - but don't remove it from queue */ + pkt = (struct mstp_tx_packet *)Ringbuf_Get_Front( + &Transmit_Queue); + state = MSTP_TX_STATE_SILENCE_WAIT; + } + break; + case MSTP_TX_STATE_SILENCE_WAIT: + if (rs485_turnaround_elapsed()) { + rs485_rts_enable(true); + pkt->index = 0; + rs485_byte_send(pkt->buffer[pkt->index]); + state = MSTP_TX_STATE_SEND_WAIT; + /* optimize a little - for slower CPUs */ + goto MSTP_TX_START; + } + break; + case MSTP_TX_STATE_SEND_WAIT: + if (rs485_byte_sent()) { + pkt->index++; + if (pkt->index < pkt->length) { + /* send next byte */ + rs485_byte_send(pkt->buffer[pkt->index]); + /* optimize a little - for slower CPUs */ + goto MSTP_TX_START; + } else { + state = MSTP_TX_STATE_STOP; + } + } + break; + case MSTP_TX_STATE_STOP: + if (rs485_byte_sent() && rs485_frame_sent()) { + rs485_rts_enable(false); + /* remove the packet from the queue */ + (void)Ringbuf_Pop_Front(&Transmit_Queue); + state = MSTP_TX_STATE_IDLE; + } + break; + default: + state = MSTP_TX_STATE_IDLE; + break; + } + + return (state != MSTP_TX_STATE_IDLE); +} + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ +static void MSTP_Send_Frame( + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + static struct mstp_tx_packet *pkt; + uint16_t i = 0; /* used to calculate CRC for data */ + + pkt = (struct mstp_tx_packet *)Ringbuf_Alloc( + &Transmit_Queue); + if (pkt) { + /* create the MS/TP header */ + pkt->buffer[0] = 0x55; + pkt->buffer[1] = 0xFF; + pkt->buffer[2] = frame_type; + crc8 = CRC_Calc_Header(pkt->buffer[2], crc8); + pkt->buffer[3] = destination; + crc8 = CRC_Calc_Header(pkt->buffer[3], crc8); + pkt->buffer[4] = source; + crc8 = CRC_Calc_Header(pkt->buffer[4], crc8); + pkt->buffer[5] = data_len / 256; + crc8 = CRC_Calc_Header(pkt->buffer[5], crc8); + pkt->buffer[6] = data_len % 256; + crc8 = CRC_Calc_Header(pkt->buffer[6], crc8); + pkt->buffer[7] = ~crc8; + pkt->length = 8; + if (data_len) { + /* calculate CRC for any data */ + for (i = 0; i < data_len; i++) { + crc16 = CRC_Calc_Data(data[i], crc16); + pkt->buffer[8+i] = data[i]; + } + crc16 = ~crc16; + pkt->buffer[8+data_len] = (crc16 & 0x00FF); + pkt->buffer[8+data_len+1] = ((crc16 & 0xFF00) >> 8); + pkt->length += data_len; + pkt->length += 2; + } + } else { + pkt = NULL; + } +} + +static void MSTP_Receive_Frame_FSM( + void) +{ + /* stores the latest received data octet */ + uint8_t DataRegister = 0; + /* Used to accumulate the CRC on the data field of a frame. */ + static uint16_t DataCRC = 0; + /* Used to accumulate the CRC on the header of a frame. */ + static uint8_t HeaderCRC = 0; + /* Used as an index by the Receive State Machine, + up to a maximum value of the MPDU */ + static uint16_t Index = 0; + + switch (Receive_State) { + case MSTP_RECEIVE_STATE_IDLE: + /* In the IDLE state, the node waits + for the beginning of a frame. */ + if (rs485_receive_error()) { + /* EatAnError */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0x55) { + /* Preamble1 */ + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } + } + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + /* In the PREAMBLE state, the node waits for the + second octet of the preamble. */ + if (rs485_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0xFF) { + /* Preamble2 */ + Index = 0; + HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_HEADER; + } else if (DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ + /* wait for the second preamble octet. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } else { + /* NotPreamble */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_HEADER: + /* In the HEADER state, the node waits + for the fixed message header. */ + if (rs485_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index == 0) { + /* FrameType */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + FrameType = DataRegister; + Index = 1; + } else if (Index == 1) { + /* Destination */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DestinationAddress = DataRegister; + Index = 2; + } else if (Index == 2) { + /* Source */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + SourceAddress = DataRegister; + Index = 3; + } else if (Index == 3) { + /* Length1 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength = DataRegister * 256; + Index = 4; + } else if (Index == 4) { + /* Length2 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength += DataRegister; + Index = 5; + } else if (Index == 5) { + /* HeaderCRC */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + /* In the HEADER_CRC state, the node validates the CRC + on the fixed message header. */ + if (HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + if (DataLength == 0) { + /* NoData */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with + no data has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + /* receive the data portion of the frame. */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == MSTP_BROADCAST_ADDRESS)) { + if (DataLength <= InputBufferSize) { + /* Data */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } else { + /* FrameTooLong */ + Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; + } + } else { + /* NotForUs */ + Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; + } + Index = 0; + DataCRC = 0xFFFF; + } + } + } else { + /* indicate that an error has occurred during */ + /* the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_DATA: + case MSTP_RECEIVE_STATE_SKIP_DATA: + /* In the DATA state, the node waits + for the data portion of a frame. */ + if (rs485_silence_elapsed(Tframe_abort)) { + /* Timeout */ + /* indicate that an error has occurred + during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_receive_error()) { + /* Error */ + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (rs485_byte_available(&DataRegister)) { + rs485_silence_reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + if (Index < InputBufferSize) { + InputBuffer[Index] = DataRegister; + } + Index++; + } else if (Index == DataLength) { + /* CRC1 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + Index++; + } else if (Index == (DataLength + 1)) { + /* CRC2 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (DataCRC == 0xF0B8) { + if (Receive_State == MSTP_RECEIVE_STATE_DATA) { + /* ForUs */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + default: + /* shouldn't get here - but if we do... */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + break; + } + + return; +} + +/* returns true if we need to transition immediately */ +static bool MSTP_Master_Node_FSM( + void) +{ + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + static uint8_t FrameCount; + /* "Next Station," the MAC address of the node to which This Station + passes the token. If the Next_Station is unknown, Next_Station shall + be equal to This_Station. */ + static uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + static uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + static unsigned RetryCount; + /* The number of tokens received by this node. When this counter reaches */ + /* the value Npoll, the node polls the address range between TS and NS */ + /* for additional master nodes. TokenCount is set to zero at the end of */ + /* the polling process. */ + static unsigned TokenCount; + /* next-x-station calculations */ + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + /* timeout values */ + uint16_t my_timeout = 10, ns_timeout = 0; + bool matched = false; + /* transition immediately to the next state */ + bool transition_now = false; + /* packet from the PDU Queue */ + struct mstp_pdu_packet *pkt; + + /* auto mode is active */ + if (This_Station == 255) { + Master_State = MSTP_MASTER_STATE_INITIALIZE; + } + /* some calculations that several states need */ + next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); + next_this_station = (This_Station + 1) % (Nmax_master + 1); + next_next_station = (Next_Station + 1) % (Nmax_master + 1); + switch (Master_State) { + case MSTP_MASTER_STATE_INITIALIZE: + if (This_Station == 255) { + dlmstp_automac_hander(); + if (This_Station != 255) { + Next_Station = automac_next_station(This_Station); + if (Next_Station == 255) { + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + TokenCount = Npoll; + EventCount = 0; + MSTP_Flag.SoleMaster = true; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + Poll_Station = This_Station; + TokenCount = 1; + EventCount = 0; + RetryCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } + } else { + /* DoneInitializing */ + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + TokenCount = Npoll; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + break; + case MSTP_MASTER_STATE_IDLE: + /* In the IDLE state, the node waits for a frame. */ + if (rs485_silence_elapsed(Tno_token)) { + /* LostToken */ + /* assume that the token has been lost */ + EventCount = 0; /* Addendum 135-2004d-8 */ + /* set the receive frame flags to false in case we received + some bytes and had a timeout for some reason */ + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedInvalidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + Master_State = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (MSTP_Flag.ReceivedValidFrame == true) { + switch (FrameType) { + case FRAME_TYPE_TOKEN: + /* ReceivedToken */ + /* tokens can't be broadcast */ + if (DestinationAddress == MSTP_BROADCAST_ADDRESS) + break; + MSTP_Flag.ReceivedValidFrame = false; + FrameCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + /* ReceivedPFM */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to higher layers */ + MSTP_Flag.ReceivePacketPending = true; + /* broadcast DER just remains IDLE */ + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + Master_State = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, + SourceAddress, This_Station, &InputBuffer[0], + DataLength); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + MSTP_Flag.ReceivedValidFrame = false; + } + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + /* Note: We could wait for up to Tusage_delay */ + if (Ringbuf_Empty(&PDU_Queue)) { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t frame_type; + pkt = (struct mstp_pdu_packet *)Ringbuf_Pop_Front( + &PDU_Queue); + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, + (uint8_t *) & pkt->buffer[0], pkt->length); + FrameCount++; + switch (frame_type) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (pkt->destination_mac == MSTP_BROADCAST_ADDRESS) + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + } + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (rs485_silence_elapsed(Treply_timeout)) { + /* ReplyTimeout */ + /* assume that the request has failed */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. */ + /* (Because of the length of the timeout, */ + /* this transition will cause the token to be */ + /* passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + MSTP_Flag.ReceivedInvalidFrame = false; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedValidFrame == true) { + if (DestinationAddress == This_Station) { + /* What did we receive? */ + switch (FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates + a reply */ + /* indicate successful reception to + the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens */ + /* or a device that didn't see activity after passing */ + /* a token (how lame!). */ + /* Synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + } + MSTP_Flag.ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (FrameCount < Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else if ((MSTP_Flag.SoleMaster == false) && + (Next_Station == This_Station)) { + /* NextStationUnknown - added in Addendum 135-2008v-1 */ + /* then the next station to which the token + should be sent is unknown - so PollForMaster */ + Poll_Station = next_this_station; + MSTP_Send_Frame( + FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (TokenCount < (Npoll - 1)) { + if ((MSTP_Flag.SoleMaster == true) && + (Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent + (true master-slave operation). */ + FrameCount = 0; + TokenCount++; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 + eliminates the Poll For Master + if there are no addresses between + TS and NS, since there is no + address at which a new master node + may be found in that case. */ + TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == Next_Station) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + Poll_Station = next_next_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + Poll_Station = This_Station; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + Poll_Station = next_poll_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (rs485_silence_elapsed(Tusage_timeout)) { + if (RetryCount < Nretry_token) { + /* RetrySendToken */ + RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } else { + if (EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by + the new token user. */ + /* Enter the IDLE state to process the frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + break; + /* The NO_TOKEN state is entered if Silence Timer + becomes greater than Tno_token, indicating that + there has been no network activity for that period + of time. The timeout is continued to determine + whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * This_Station); + if (rs485_silence_elapsed(my_timeout)) { + ns_timeout = Tno_token + (Tslot * (This_Station + 1)); + if (rs485_silence_elapsed(ns_timeout)) { + /* should never get here unless timer resolution is bad */ + rs485_silence_reset(); + Master_State = MSTP_MASTER_STATE_IDLE; + } else { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* indicate that the next station is unknown */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state + to find a new successor to TS. */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } else { + if (EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and + process the incoming frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (MSTP_Flag.ReceivedValidFrame == true) { + if ((DestinationAddress == This_Station) + && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + MSTP_Flag.SoleMaster = false; + Next_Station = SourceAddress; + EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + Poll_Station = This_Station; + TokenCount = 0; + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + MSTP_Flag.ReceivedValidFrame = false; + } else if ((rs485_silence_elapsed(Tusage_timeout)) || + (MSTP_Flag.ReceivedInvalidFrame == true)) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + FrameCount = 0; + /* TokenCount++; removed in 2004 */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (Next_Station != This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != This_Station) { + /* SendNextPFM */ + Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, + Poll_Station, This_Station, NULL, 0); + RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station + is the only master */ + MSTP_Flag.SoleMaster = true; + FrameCount = 0; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + MSTP_Flag.ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + /* Note: we could wait for up to Treply_delay */ + matched = false; + if (!Ringbuf_Empty(&PDU_Queue)) { + pkt = (struct mstp_pdu_packet *)Ringbuf_Get_Front( + &PDU_Queue); + matched = + dlmstp_compare_data_expecting_reply(&InputBuffer[0], + DataLength, SourceAddress, &pkt->buffer[0], + pkt->length, pkt->destination_mac); + } + if (matched) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + uint8_t frame_type; + pkt = (struct mstp_pdu_packet *)Ringbuf_Pop_Front( + &PDU_Queue); + if (pkt->data_expecting_reply) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, + (uint8_t *) & pkt->buffer[0], pkt->length); + Master_State = MSTP_MASTER_STATE_IDLE; + } else { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, + This_Station, NULL, 0); + Master_State = MSTP_MASTER_STATE_IDLE; + } + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + break; + default: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + struct mstp_pdu_packet *pkt; + uint16_t i = 0; + + pkt = (struct mstp_pdu_packet *)Ringbuf_Alloc(&PDU_Queue); + if (pkt) { + pkt->data_expecting_reply = npdu_data->data_expecting_reply; + for (i = 0; i < pdu_len; i++) { + pkt->buffer[i] = pdu[i]; + } + pkt->length = pdu_len; + pkt->destination_mac = dest->mac[0]; + bytes_sent = pdu_len; + } + + return bytes_sent; +} + +/* master node FSM states */ +typedef enum { + AUTOMAC_STATE_IDLE = 0, + AUTOMAC_STATE_PFM = 1, + AUTOMAC_STATE_TOKEN = 2, + AUTOMAC_STATE_TESTING = 3, + AUTOMAC_STATE_CONFIRM = 4 +} AUTOMAC_STATE; +/* buffer used to send and validate a response - size is min APDU size */ +static uint8_t AutoMAC_Test_Buffer[50]; +void dlmstp_automac_hander(void) +{ + static AUTOMAC_STATE state = AUTOMAC_STATE_IDLE; + uint8_t mac = 0; + uint32_t serial_number = 0; + uint16_t vendor_id = 0; + bool take_address = false; + bool start_over = false; + + switch (state) { + case AUTOMAC_STATE_IDLE: + if ((MSTP_Flag.ReceivedValidFrame) || + (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + /* store stats until we get a MAC */ + automac_emitter_set(SourceAddress); + switch (FrameType) { + case FRAME_TYPE_TOKEN: + automac_token_set(SourceAddress); + break; + case FRAME_TYPE_POLL_FOR_MASTER: + automac_pfm_set(DestinationAddress); + break; + default: + break; + } + } else if (MSTP_Flag.ReceivedInvalidFrame) { + MSTP_Flag.ReceivedInvalidFrame = false; + } else if (automac_pfm_cycle_complete()) { + mac = automac_free_address_random(); + if (automac_free_address_valid(mac)) { + automac_address_set(mac); + state = AUTOMAC_STATE_PFM; + } else { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + } else if (rs485_silence_elapsed(automac_time_slot())) { + /* long silence indicates we are alone or + with other silent devices */ + SourceAddress = automac_address(); + state = AUTOMAC_STATE_TESTING; + } + break; + case AUTOMAC_STATE_PFM: + if ((MSTP_Flag.ReceivedValidFrame) || + (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + /* store stats until we get a MAC */ + switch (FrameType) { + case FRAME_TYPE_POLL_FOR_MASTER: + mac = automac_address(); + if (mac == SourceAddress) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } else if (mac == DestinationAddress) { + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, mac, NULL, 0); + state = AUTOMAC_STATE_TOKEN; + } + break; + case FRAME_TYPE_TEST_REQUEST: + mac = automac_address(); + if ((mac == DestinationAddress) || + (mac == SourceAddress)) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_TOKEN: + mac = automac_address(); + if (mac == SourceAddress) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + default: + break; + } + } else if (MSTP_Flag.ReceivedInvalidFrame) { + MSTP_Flag.ReceivedInvalidFrame = false; + } else if (rs485_silence_elapsed(automac_time_slot())) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + case AUTOMAC_STATE_TOKEN: + if ((MSTP_Flag.ReceivedValidFrame) || + (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + switch (FrameType) { + case FRAME_TYPE_TOKEN: + mac = automac_address(); + if (mac == SourceAddress) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } else if (mac == DestinationAddress) { + state = AUTOMAC_STATE_TESTING; + } + break; + default: + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + break; + } + } else if (MSTP_Flag.ReceivedInvalidFrame) { + MSTP_Flag.ReceivedInvalidFrame = false; + } else if (rs485_silence_elapsed(automac_time_slot())) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + case AUTOMAC_STATE_TESTING: + /* I have the token - confirm my MAC with a quick test */ + mac = automac_address(); + vendor_id = Device_Vendor_Identifier(); + encode_unsigned16(&AutoMAC_Test_Buffer[0], vendor_id); + serial_number = Device_Object_Instance_Number(); + encode_unsigned32(&AutoMAC_Test_Buffer[2], serial_number); + MSTP_Send_Frame(FRAME_TYPE_TEST_REQUEST, + SourceAddress, mac, &AutoMAC_Test_Buffer[0], + 6); + state = AUTOMAC_STATE_CONFIRM; + break; + case AUTOMAC_STATE_CONFIRM: + /* we may timeout if our chosen MAC is unique */ + if (MSTP_Flag.ReceivedInvalidFrame) { + MSTP_Flag.ReceivedInvalidFrame = false; + start_over = true; + } else if ((MSTP_Flag.ReceivedValidFrame) || + (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrame = false; + MSTP_Flag.ReceivedValidFrameNotForUs = false; + mac = automac_address(); + if ((mac == DestinationAddress) && + (DataLength >= 6)) { + decode_unsigned16(&InputBuffer[0], &vendor_id); + decode_unsigned32(&InputBuffer[2], &serial_number); + if ((vendor_id == Device_Vendor_Identifier()) && + (serial_number == Device_Object_Instance_Number())) { + take_address = true; + } else { + start_over = true; + } + } else { + start_over = true; + } + } else if (rs485_silence_elapsed(300)) { + /* use maximum possible value for Treply_timeout */ + /* in case validating device doesn't support Test Request */ + /* no response and no collission */ + take_address = true; + } + if (take_address) { + /* take the address */ + This_Station = automac_address(); + DestinationAddress = automac_next_station(This_Station); + if (DestinationAddress < 128) { + MSTP_Send_Frame(FRAME_TYPE_TOKEN, DestinationAddress, + This_Station, NULL, 0); + } + state = AUTOMAC_STATE_IDLE; + } else if (start_over) { + /* start over again */ + automac_init(); + state = AUTOMAC_STATE_IDLE; + } + break; + default: + break; + } +} + +/* Return the length of the packet */ +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + bool transmitting = false; + + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + if (This_Station == 255) { + automac_enabled_set(true); + } + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { + transmitting = MSTP_Transmit_FSM(); + } + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedValidFrameNotForUs == false) && + (MSTP_Flag.ReceivedInvalidFrame == false) && + (transmitting == false)) { + MSTP_Receive_Frame_FSM(); + } + /* only do master state machine while rx is idle */ + if ((Receive_State == MSTP_RECEIVE_STATE_IDLE) && + (transmitting == false)) { + if ((This_Station != 255) && + (MSTP_Flag.ReceivedValidFrameNotForUs)) { + MSTP_Flag.ReceivedValidFrameNotForUs = false; + if ((SourceAddress == This_Station) && + automac_enabled()) { + /* duplicate MAC on the wire */ + automac_init(); + This_Station = 255; + } + } else { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } + } + if (This_Station != 255) { + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + MSTP_Flag.ReceivePacketPending = false; + pdu_len = DataLength; + src->mac_len = 1; + src->mac[0] = SourceAddress; + /* data is already in the pdu pointer */ + } + } + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + This_Station = mac_address; + if (mac_address > Nmax_master) { + dlmstp_set_max_master(127); + } + } else if (mac_address == 255) { + /* auto-MAC provision */ + This_Station = mac_address; + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + Nmax_info_frames = max_info_frames; + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if ((This_Station == 255) || + (This_Station <= max_master)) { + Nmax_master = max_master; + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return Nmax_master; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +bool dlmstp_sole_master(void) +{ + if (MSTP_Flag.SoleMaster) { + return true; + } + + return false; +} + diff --git a/bacnet-stack/ports/stm32f10x/hardware.h b/bacnet-stack/ports/stm32f10x/hardware.h index 79b1e684..b2732dc8 100644 --- a/bacnet-stack/ports/stm32f10x/hardware.h +++ b/bacnet-stack/ports/stm32f10x/hardware.h @@ -1,33 +1,33 @@ -/************************************************************************** -* -* Copyright (C) 2011 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 "stm32f10x_conf.h" -#include "stm32f10x_it.h" - -#define MAX_BINARY_OUTPUTS 2 - -#endif +/************************************************************************** +* +* Copyright (C) 2011 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 "stm32f10x_conf.h" +#include "stm32f10x_it.h" + +#define MAX_BINARY_OUTPUTS 2 + +#endif diff --git a/bacnet-stack/ports/stm32f10x/led.c b/bacnet-stack/ports/stm32f10x/led.c index 03babd4a..090f1aa1 100644 --- a/bacnet-stack/ports/stm32f10x/led.c +++ b/bacnet-stack/ports/stm32f10x/led.c @@ -1,308 +1,308 @@ -/************************************************************************** -* -* Copyright (C) 2011 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*********************************************************************/ -#include -#include "hardware.h" -#include "timer.h" -#include "led.h" - -static struct itimer Off_Delay_Timer_Rx; -static struct itimer Off_Delay_Timer_Tx; -static bool Rx_State; -static bool Tx_State; -static bool LD3_State; - -/************************************************************************* -* Description: Activate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -void led_tx_on( - void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_15, Bit_SET); - timer_interval_no_expire(&Off_Delay_Timer_Tx); - Tx_State = true; -} - -/************************************************************************* -* Description: Activate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -void led_rx_on( - void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_14, Bit_SET); - timer_interval_no_expire(&Off_Delay_Timer_Rx); - Rx_State = true; -} - -/************************************************************************* -* Description: Deactivate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -void led_tx_off( - void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_15, Bit_RESET); - timer_interval_no_expire(&Off_Delay_Timer_Tx); - Tx_State = false; -} - -/************************************************************************* -* Description: Deactivate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -void led_rx_off( - void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_14, Bit_RESET); - timer_interval_no_expire(&Off_Delay_Timer_Rx); - Rx_State = false; -} - -/************************************************************************* -* Description: Get the state of the LED -* Returns: true if on, false if off. -* Notes: none -*************************************************************************/ -bool led_rx_state(void) -{ - return Rx_State; -} - -/************************************************************************* -* Description: Get the state of the LED -* Returns: true if on, false if off. -* Notes: none -*************************************************************************/ -bool led_tx_state(void) -{ - return Tx_State; -} - -/************************************************************************* -* Description: Toggle the state of the LED -* Returns: none -* Notes: none -*************************************************************************/ -void led_tx_toggle(void) -{ - if (led_tx_state()) { - led_tx_off(); - } else { - led_tx_on(); - } -} - -/************************************************************************* -* Description: Toggle the state of the LED -* Returns: none -* Notes: none -*************************************************************************/ -void led_rx_toggle(void) -{ - if (led_rx_state()) { - led_rx_off(); - } else { - led_rx_on(); - } -} - -/************************************************************************* -* Description: Delay before going off to give minimum brightness. -* Returns: none -* Notes: none -*************************************************************************/ -void led_rx_off_delay( - uint32_t delay_ms) -{ - timer_interval_start(&Off_Delay_Timer_Rx, delay_ms); -} - -/************************************************************************* -* Description: Delay before going off to give minimum brightness. -* Returns: none -* Notes: none -*************************************************************************/ -void led_tx_off_delay( - uint32_t delay_ms) -{ - timer_interval_start(&Off_Delay_Timer_Tx, delay_ms); -} - -/************************************************************************* -* Description: Turn on, and delay before going off. -* Returns: none -* Notes: none -*************************************************************************/ -void led_rx_on_interval( - uint16_t interval_ms) -{ - led_rx_on(); - timer_interval_start(&Off_Delay_Timer_Rx, interval_ms); -} - -/************************************************************************* -* Description: Turn on, and delay before going off. -* Returns: none -* Notes: none -*************************************************************************/ -void led_tx_on_interval( - uint16_t interval_ms) -{ - led_tx_on(); - timer_interval_start(&Off_Delay_Timer_Tx, interval_ms); -} - -/************************************************************************* -* Description: Task for blinking LED -* Returns: none -* Notes: none -*************************************************************************/ -void led_task( - void) -{ - if (timer_interval_expired(&Off_Delay_Timer_Rx)) { - timer_interval_no_expire(&Off_Delay_Timer_Rx); - led_rx_off(); - } - if (timer_interval_expired(&Off_Delay_Timer_Tx)) { - timer_interval_no_expire(&Off_Delay_Timer_Tx); - led_tx_off(); - } -} - -/************************************************************************* -* Description: Activate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -void led_ld4_on( - void) -{ - GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); -} - -/************************************************************************* -* Description: Deactivate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -void led_ld4_off( - void) -{ - GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); -} - -/************************************************************************* -* Description: Activate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -void led_ld3_on( - void) -{ - GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_SET); - LD3_State = true; -} - -/************************************************************************* -* Description: Deactivate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -void led_ld3_off( - void) -{ - GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_RESET); - LD3_State = false; -} - -/************************************************************************* -* Description: Get the state of the LED -* Returns: true if on, false if off. -* Notes: none -*************************************************************************/ -bool led_ld3_state(void) -{ - return LD3_State; -} - -/************************************************************************* -* Description: Toggle the state of the LED -* Returns: none -* Notes: none -*************************************************************************/ -void led_ld3_toggle(void) -{ - if (led_ld3_state()) { - led_ld3_off(); - } else { - led_ld3_on(); - } -} - -/************************************************************************* -* Description: Initialize the LED hardware -* Returns: none -* Notes: none -*************************************************************************/ -void led_init( - void) -{ - GPIO_InitTypeDef GPIO_InitStructure; - - GPIO_StructInit(&GPIO_InitStructure); - /* Configure the Receive LED on MS/TP board */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; - GPIO_Init(GPIOB, &GPIO_InitStructure); - /* Configure the Transmit LED on MS/TP board */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; - GPIO_Init(GPIOB, &GPIO_InitStructure); - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); - /* Configure the LD4 on Discovery board */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; - GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Configure the LD3 on Discovery board */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; - GPIO_Init(GPIOC, &GPIO_InitStructure); - /* Enable the GPIO_LED Clock */ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); - - led_tx_on(); - led_rx_on(); - led_ld3_on(); - led_ld4_on(); -} +/************************************************************************** +* +* Copyright (C) 2011 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include "hardware.h" +#include "timer.h" +#include "led.h" + +static struct itimer Off_Delay_Timer_Rx; +static struct itimer Off_Delay_Timer_Tx; +static bool Rx_State; +static bool Tx_State; +static bool LD3_State; + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_tx_on( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_15, Bit_SET); + timer_interval_no_expire(&Off_Delay_Timer_Tx); + Tx_State = true; +} + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_rx_on( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_14, Bit_SET); + timer_interval_no_expire(&Off_Delay_Timer_Rx); + Rx_State = true; +} + +/************************************************************************* +* Description: Deactivate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_tx_off( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_15, Bit_RESET); + timer_interval_no_expire(&Off_Delay_Timer_Tx); + Tx_State = false; +} + +/************************************************************************* +* Description: Deactivate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_rx_off( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_14, Bit_RESET); + timer_interval_no_expire(&Off_Delay_Timer_Rx); + Rx_State = false; +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_rx_state(void) +{ + return Rx_State; +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_tx_state(void) +{ + return Tx_State; +} + +/************************************************************************* +* Description: Toggle the state of the LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_tx_toggle(void) +{ + if (led_tx_state()) { + led_tx_off(); + } else { + led_tx_on(); + } +} + +/************************************************************************* +* Description: Toggle the state of the LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_rx_toggle(void) +{ + if (led_rx_state()) { + led_rx_off(); + } else { + led_rx_on(); + } +} + +/************************************************************************* +* Description: Delay before going off to give minimum brightness. +* Returns: none +* Notes: none +*************************************************************************/ +void led_rx_off_delay( + uint32_t delay_ms) +{ + timer_interval_start(&Off_Delay_Timer_Rx, delay_ms); +} + +/************************************************************************* +* Description: Delay before going off to give minimum brightness. +* Returns: none +* Notes: none +*************************************************************************/ +void led_tx_off_delay( + uint32_t delay_ms) +{ + timer_interval_start(&Off_Delay_Timer_Tx, delay_ms); +} + +/************************************************************************* +* Description: Turn on, and delay before going off. +* Returns: none +* Notes: none +*************************************************************************/ +void led_rx_on_interval( + uint16_t interval_ms) +{ + led_rx_on(); + timer_interval_start(&Off_Delay_Timer_Rx, interval_ms); +} + +/************************************************************************* +* Description: Turn on, and delay before going off. +* Returns: none +* Notes: none +*************************************************************************/ +void led_tx_on_interval( + uint16_t interval_ms) +{ + led_tx_on(); + timer_interval_start(&Off_Delay_Timer_Tx, interval_ms); +} + +/************************************************************************* +* Description: Task for blinking LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_task( + void) +{ + if (timer_interval_expired(&Off_Delay_Timer_Rx)) { + timer_interval_no_expire(&Off_Delay_Timer_Rx); + led_rx_off(); + } + if (timer_interval_expired(&Off_Delay_Timer_Tx)) { + timer_interval_no_expire(&Off_Delay_Timer_Tx); + led_tx_off(); + } +} + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_ld4_on( + void) +{ + GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); +} + +/************************************************************************* +* Description: Deactivate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_ld4_off( + void) +{ + GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); +} + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_ld3_on( + void) +{ + GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_SET); + LD3_State = true; +} + +/************************************************************************* +* Description: Deactivate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +void led_ld3_off( + void) +{ + GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_RESET); + LD3_State = false; +} + +/************************************************************************* +* Description: Get the state of the LED +* Returns: true if on, false if off. +* Notes: none +*************************************************************************/ +bool led_ld3_state(void) +{ + return LD3_State; +} + +/************************************************************************* +* Description: Toggle the state of the LED +* Returns: none +* Notes: none +*************************************************************************/ +void led_ld3_toggle(void) +{ + if (led_ld3_state()) { + led_ld3_off(); + } else { + led_ld3_on(); + } +} + +/************************************************************************* +* Description: Initialize the LED hardware +* Returns: none +* Notes: none +*************************************************************************/ +void led_init( + void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_StructInit(&GPIO_InitStructure); + /* Configure the Receive LED on MS/TP board */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOB, &GPIO_InitStructure); + /* Configure the Transmit LED on MS/TP board */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOB, &GPIO_InitStructure); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); + /* Configure the LD4 on Discovery board */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Configure the LD3 on Discovery board */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOC, &GPIO_InitStructure); + /* Enable the GPIO_LED Clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); + + led_tx_on(); + led_rx_on(); + led_ld3_on(); + led_ld4_on(); +} diff --git a/bacnet-stack/ports/stm32f10x/led.h b/bacnet-stack/ports/stm32f10x/led.h index 51e4fb22..113c5eb0 100644 --- a/bacnet-stack/ports/stm32f10x/led.h +++ b/bacnet-stack/ports/stm32f10x/led.h @@ -1,75 +1,75 @@ -/************************************************************************** -* -* Copyright (C) 2009 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*********************************************************************/ -#ifndef LED_H -#define LED_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - void led_ld3_on( - void); - void led_ld4_on( - void); - void led_ld3_off( - void); - void led_ld4_off( - void); - bool led_ld3_state(void); - void led_ld3_toggle(void); - - void led_tx_on(void); - void led_rx_on(void); - - void led_tx_on_interval( - uint16_t interval_ms); - void led_rx_on_interval( - uint16_t interval_ms); - - void led_tx_off(void); - void led_rx_off(void); - - void led_tx_off_delay( - uint32_t delay_ms); - void led_rx_off_delay( - uint32_t delay_ms); - - void led_tx_toggle(void); - void led_rx_toggle(void); - - bool led_tx_state(void); - bool led_rx_state(void); - - void led_task( - void); - void led_init( - void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef LED_H +#define LED_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void led_ld3_on( + void); + void led_ld4_on( + void); + void led_ld3_off( + void); + void led_ld4_off( + void); + bool led_ld3_state(void); + void led_ld3_toggle(void); + + void led_tx_on(void); + void led_rx_on(void); + + void led_tx_on_interval( + uint16_t interval_ms); + void led_rx_on_interval( + uint16_t interval_ms); + + void led_tx_off(void); + void led_rx_off(void); + + void led_tx_off_delay( + uint32_t delay_ms); + void led_rx_off_delay( + uint32_t delay_ms); + + void led_tx_toggle(void); + void led_rx_toggle(void); + + bool led_tx_state(void); + bool led_rx_state(void); + + void led_task( + void); + void led_init( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/stm32f10x/main.c b/bacnet-stack/ports/stm32f10x/main.c index e0b3d5c9..70b3a6ba 100644 --- a/bacnet-stack/ports/stm32f10x/main.c +++ b/bacnet-stack/ports/stm32f10x/main.c @@ -1,142 +1,142 @@ -/************************************************************************ -* -* Copyright (C) 2011 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*************************************************************************/ - -#include -#include -#include "hardware.h" -#include "timer.h" -#include "timer.h" -#include "rs485.h" -#include "led.h" -#include "bacnet.h" - -/* local version override */ -char *BACnet_Version = "1.0"; - -#ifdef USE_FULL_ASSERT - -/** - * @brief Reports the name of the source file and the source line number - * where the assert_param error has occurred. - * @param file: pointer to the source file name - * @param line: assert_param error line source number - * @retval None - */ -void assert_failed(uint8_t* file, uint32_t line) -{ - /* User can add his own implementation to report the file name and line number, - ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ - - /* Infinite loop */ - while (1) - { - } -} -#endif - -/* Private define ------------------------------------------------------------*/ -#define LSE_FAIL_FLAG 0x80 -#define LSE_PASS_FLAG 0x100 - -void lse_init(void) -{ - uint32_t LSE_Delay = 0; - struct etimer Delay_Timer; - - /* Enable access to the backup register => LSE can be enabled */ - PWR_BackupAccessCmd(ENABLE); - /* Enable LSE (Low Speed External Oscillation) */ - RCC_LSEConfig(RCC_LSE_ON); - - /* Check the LSE Status */ - while(1) - { - if(LSE_Delay < LSE_FAIL_FLAG) - { - timer_elapsed_start(&Delay_Timer); - while (!timer_elapsed_milliseconds(&Delay_Timer,500)) { - /* do nothing */ - } - /* check whether LSE is ready, with 4 seconds timeout */ - LSE_Delay += 0x10; - if(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != RESET) - { - /* Set flag: LSE PASS */ - LSE_Delay |= LSE_PASS_FLAG; - led_ld4_off(); - /* Disable LSE */ - RCC_LSEConfig(RCC_LSE_OFF); - break; - } - } - - /* LSE_FAIL_FLAG = 0x80 */ - else if(LSE_Delay >= LSE_FAIL_FLAG) - { - if(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) - { - /* Set flag: LSE FAIL */ - LSE_Delay |= LSE_FAIL_FLAG; - led_ld4_on(); - } - /* Disable LSE */ - RCC_LSEConfig(RCC_LSE_OFF); - break; - } - } -} - -int main( - void) -{ - struct itimer Blink_Timer; - - /*At this stage the microcontroller clock setting is already configured, - this is done through SystemInit() function which is called from startup - file (startup_stm32f10x_xx.s) before to branch to application main. - To reconfigure the default setting of SystemInit() function, refer to - system_stm32f10x.c file */ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); - RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); - led_init(); - RCC_APB2PeriphClockCmd( - RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | - RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | - RCC_APB2Periph_GPIOE, ENABLE); - timer_init(); - lse_init(); - led_init(); - rs485_init(); - bacnet_init(); - timer_interval_start(&Blink_Timer, 125); - for (;;) { - if (timer_interval_expired(&Blink_Timer)) { - timer_interval_reset(&Blink_Timer); - led_ld3_toggle(); - } - led_task(); - bacnet_task(); - } -} +/************************************************************************ +* +* Copyright (C) 2011 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*************************************************************************/ + +#include +#include +#include "hardware.h" +#include "timer.h" +#include "timer.h" +#include "rs485.h" +#include "led.h" +#include "bacnet.h" + +/* local version override */ +char *BACnet_Version = "1.0"; + +#ifdef USE_FULL_ASSERT + +/** + * @brief Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * @param file: pointer to the source file name + * @param line: assert_param error line source number + * @retval None + */ +void assert_failed(uint8_t* file, uint32_t line) +{ + /* User can add his own implementation to report the file name and line number, + ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ + + /* Infinite loop */ + while (1) + { + } +} +#endif + +/* Private define ------------------------------------------------------------*/ +#define LSE_FAIL_FLAG 0x80 +#define LSE_PASS_FLAG 0x100 + +void lse_init(void) +{ + uint32_t LSE_Delay = 0; + struct etimer Delay_Timer; + + /* Enable access to the backup register => LSE can be enabled */ + PWR_BackupAccessCmd(ENABLE); + /* Enable LSE (Low Speed External Oscillation) */ + RCC_LSEConfig(RCC_LSE_ON); + + /* Check the LSE Status */ + while(1) + { + if(LSE_Delay < LSE_FAIL_FLAG) + { + timer_elapsed_start(&Delay_Timer); + while (!timer_elapsed_milliseconds(&Delay_Timer,500)) { + /* do nothing */ + } + /* check whether LSE is ready, with 4 seconds timeout */ + LSE_Delay += 0x10; + if(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != RESET) + { + /* Set flag: LSE PASS */ + LSE_Delay |= LSE_PASS_FLAG; + led_ld4_off(); + /* Disable LSE */ + RCC_LSEConfig(RCC_LSE_OFF); + break; + } + } + + /* LSE_FAIL_FLAG = 0x80 */ + else if(LSE_Delay >= LSE_FAIL_FLAG) + { + if(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) + { + /* Set flag: LSE FAIL */ + LSE_Delay |= LSE_FAIL_FLAG; + led_ld4_on(); + } + /* Disable LSE */ + RCC_LSEConfig(RCC_LSE_OFF); + break; + } + } +} + +int main( + void) +{ + struct itimer Blink_Timer; + + /*At this stage the microcontroller clock setting is already configured, + this is done through SystemInit() function which is called from startup + file (startup_stm32f10x_xx.s) before to branch to application main. + To reconfigure the default setting of SystemInit() function, refer to + system_stm32f10x.c file */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); + led_init(); + RCC_APB2PeriphClockCmd( + RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | + RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | + RCC_APB2Periph_GPIOE, ENABLE); + timer_init(); + lse_init(); + led_init(); + rs485_init(); + bacnet_init(); + timer_interval_start(&Blink_Timer, 125); + for (;;) { + if (timer_interval_expired(&Blink_Timer)) { + timer_interval_reset(&Blink_Timer); + led_ld3_toggle(); + } + led_task(); + bacnet_task(); + } +} diff --git a/bacnet-stack/ports/stm32f10x/rs485.c b/bacnet-stack/ports/stm32f10x/rs485.c index 025466e2..0d95327d 100644 --- a/bacnet-stack/ports/stm32f10x/rs485.c +++ b/bacnet-stack/ports/stm32f10x/rs485.c @@ -1,340 +1,340 @@ -/************************************************************************** -* -* Copyright (C) 2011 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. -* -* Module Description: -* Handle the configuration and operation of the RS485 bus. -**************************************************************************/ -#include -#include -#include -#include "hardware.h" -#include "timer.h" -#include "bits.h" -#include "fifo.h" -#include "led.h" -#include "rs485.h" - -/* buffer for storing received bytes - size must be power of two */ -static uint8_t Receive_Buffer_Data[512]; -static FIFO_BUFFER Receive_Buffer; -/* amount of silence on the wire */ -static struct etimer Silence_Timer; -/* baud rate */ -static uint32_t Baud_Rate = 38400; - -/* The minimum time after the end of the stop bit of the final octet of a */ -/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ -/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ -/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ -/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ -/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ -/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ -/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ -/* 40 bits is 4 octets including a start and stop bit with each octet */ -#define Tturnaround (40UL) - -/************************************************************************* -* Description: Reset the silence on the wire timer. -* Returns: nothing -* Notes: none -**************************************************************************/ -void rs485_silence_reset(void) -{ - timer_elapsed_start(&Silence_Timer); -} - -/************************************************************************* -* Description: Determine the amount of silence on the wire from the timer. -* Returns: true if the amount of time has elapsed -* Notes: none -**************************************************************************/ -bool rs485_silence_elapsed(uint32_t interval) -{ - return timer_elapsed_milliseconds(&Silence_Timer, interval); -} - -/************************************************************************* -* Description: Baud rate determines turnaround time. -* Returns: amount of milliseconds -* Notes: none -**************************************************************************/ -static uint16_t rs485_turnaround_time( - void) -{ - /* delay after reception before transmitting - per MS/TP spec */ - /* wait a minimum 40 bit times since reception */ - /* at least 2 ms for errors: rounding, clock tick */ - return (2 + ((Tturnaround * 1000UL) / Baud_Rate)); -} - -/************************************************************************* -* Description: Use the silence timer to determine turnaround time. -* Returns: true if turnaround time has expired. -* Notes: none -**************************************************************************/ -bool rs485_turnaround_elapsed( - void) -{ - return timer_elapsed_milliseconds( - &Silence_Timer, - rs485_turnaround_time()); -} - - -/************************************************************************* -* Description: Determines if an error occured while receiving -* Returns: true an error occurred. -* Notes: none -**************************************************************************/ -bool rs485_receive_error( - void) -{ - return false; -} - -/*********************************************************************//** - * @brief USARTx interrupt handler sub-routine - * @param[in] None - * @return None - **********************************************************************/ -void USART2_IRQHandler(void) -{ - uint8_t data_byte; - - - if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) { - /* Read one byte from the receive data register */ - data_byte = USART_ReceiveData(USART2); - (void)FIFO_Put(&Receive_Buffer, data_byte); - } -} - -/************************************************************************* -* DESCRIPTION: Return true if a byte is available -* RETURN: true if a byte is available, with the byte in the parameter -* NOTES: none -**************************************************************************/ -bool rs485_byte_available( - uint8_t * data_register) -{ - bool data_available = false; /* return value */ - - if (!FIFO_Empty(&Receive_Buffer)) { - *data_register = FIFO_Get(&Receive_Buffer); - timer_elapsed_start(&Silence_Timer); - data_available = true; - led_rx_on_interval(10); - } - - return data_available; -} - -/************************************************************************* -* DESCRIPTION: Sends a byte of data -* RETURN: nothing -* NOTES: none -**************************************************************************/ -void rs485_byte_send(uint8_t tx_byte) -{ - led_tx_on_interval(10); - USART_SendData(USART2, tx_byte); - timer_elapsed_start(&Silence_Timer); -} - -/************************************************************************* -* Description: Determines if a byte in the USART has been shifted from -* register -* Returns: true if the USART register is empty -* Notes: none -**************************************************************************/ -bool rs485_byte_sent(void) -{ - return USART_GetFlagStatus(USART2, USART_FLAG_TXE); -} - -/************************************************************************* -* Description: Determines if the entire frame is sent from USART FIFO -* Returns: true if the USART FIFO is empty -* Notes: none -**************************************************************************/ -bool rs485_frame_sent(void) -{ - return USART_GetFlagStatus(USART2, USART_FLAG_TC); -} - -/************************************************************************* -* DESCRIPTION: Send some data and wait until it is sent -* RETURN: true if a collision or timeout occurred -* NOTES: none -**************************************************************************/ -void rs485_bytes_send( - uint8_t * buffer, /* data to send */ - uint16_t nbytes) /* number of bytes of data */ -{ - uint8_t tx_byte; - - while (nbytes) { - /* Send the data byte */ - tx_byte = *buffer; - /* Send one byte */ - USART_SendData(USART2, tx_byte); - while (!rs485_byte_sent()) { - /* do nothing - wait until Tx buffer is empty */ - } - buffer++; - nbytes--; - } - /* was the frame sent? */ - while (!rs485_frame_sent()) { - /* do nothing - wait until the entire frame in the - Transmit Shift Register has been shifted out */ - } - timer_elapsed_start(&Silence_Timer); - - return; -} - -/************************************************************************* -* Description: Configures the baud rate of the USART -* Returns: nothing -* Notes: none -**************************************************************************/ -static void rs485_baud_rate_configure( - void) -{ - USART_InitTypeDef USART_InitStructure; - - USART_InitStructure.USART_BaudRate = Baud_Rate; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; - USART_InitStructure.USART_HardwareFlowControl = - USART_HardwareFlowControl_None; - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; - - /* Configure USARTx */ - USART_Init(USART2, &USART_InitStructure); -} - -/************************************************************************* -* Description: Sets the baud rate to non-volatile storeage and configures USART -* Returns: true if a value baud rate was saved -* Notes: none -**************************************************************************/ -bool rs485_baud_rate_set( - uint32_t baud) -{ - bool valid = true; - - switch (baud) { - case 9600: - case 19200: - case 38400: - case 57600: - case 76800: - case 115200: - Baud_Rate = baud; - rs485_baud_rate_configure(); - break; - default: - valid = false; - break; - } - - return valid; -} - -/************************************************************************* -* Description: Determines the baud rate in bps -* Returns: baud rate in bps -* Notes: none -**************************************************************************/ -uint32_t rs485_baud_rate( - void) -{ - return Baud_Rate; -} - -/************************************************************************* -* Description: Enable the Request To Send (RTS) aka Transmit Enable pin -* Returns: nothing -* Notes: none -**************************************************************************/ -void rs485_rts_enable( - bool enable) -{ - if (enable) { - GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET); - } else { - GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET); - } -} - -/************************************************************************* -* Description: Initialize the room network USART -* Returns: nothing -* Notes: none -**************************************************************************/ -void rs485_init(void) -{ - GPIO_InitTypeDef GPIO_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; - - GPIO_StructInit(&GPIO_InitStructure); - /* Configure USARTx Rx as input floating */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; - GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure USARTx Tx as alternate function push-pull */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; - GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Configure the Request To Send (RTS) aka Transmit Enable pin */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; - GPIO_Init(GPIOA, &GPIO_InitStructure); - /* Enable USARTx Clock */ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); - /* Enable GPIO Clock */ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); - /* Configure the NVIC Preemption Priority Bits */ - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); - /* Enable the USARTx Interrupt */ - NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - /* enable the USART to generate interrupts */ - USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); - - rs485_baud_rate_set(Baud_Rate); - - USART_Cmd(USART2, ENABLE); - - FIFO_Init(&Receive_Buffer, &Receive_Buffer_Data[0], - (unsigned)sizeof(Receive_Buffer_Data)); - timer_elapsed_start(&Silence_Timer); -} +/************************************************************************** +* +* Copyright (C) 2011 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. +* +* Module Description: +* Handle the configuration and operation of the RS485 bus. +**************************************************************************/ +#include +#include +#include +#include "hardware.h" +#include "timer.h" +#include "bits.h" +#include "fifo.h" +#include "led.h" +#include "rs485.h" + +/* buffer for storing received bytes - size must be power of two */ +static uint8_t Receive_Buffer_Data[512]; +static FIFO_BUFFER Receive_Buffer; +/* amount of silence on the wire */ +static struct etimer Silence_Timer; +/* baud rate */ +static uint32_t Baud_Rate = 38400; + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround (40UL) + +/************************************************************************* +* Description: Reset the silence on the wire timer. +* Returns: nothing +* Notes: none +**************************************************************************/ +void rs485_silence_reset(void) +{ + timer_elapsed_start(&Silence_Timer); +} + +/************************************************************************* +* Description: Determine the amount of silence on the wire from the timer. +* Returns: true if the amount of time has elapsed +* Notes: none +**************************************************************************/ +bool rs485_silence_elapsed(uint32_t interval) +{ + return timer_elapsed_milliseconds(&Silence_Timer, interval); +} + +/************************************************************************* +* Description: Baud rate determines turnaround time. +* Returns: amount of milliseconds +* Notes: none +**************************************************************************/ +static uint16_t rs485_turnaround_time( + void) +{ + /* delay after reception before transmitting - per MS/TP spec */ + /* wait a minimum 40 bit times since reception */ + /* at least 2 ms for errors: rounding, clock tick */ + return (2 + ((Tturnaround * 1000UL) / Baud_Rate)); +} + +/************************************************************************* +* Description: Use the silence timer to determine turnaround time. +* Returns: true if turnaround time has expired. +* Notes: none +**************************************************************************/ +bool rs485_turnaround_elapsed( + void) +{ + return timer_elapsed_milliseconds( + &Silence_Timer, + rs485_turnaround_time()); +} + + +/************************************************************************* +* Description: Determines if an error occured while receiving +* Returns: true an error occurred. +* Notes: none +**************************************************************************/ +bool rs485_receive_error( + void) +{ + return false; +} + +/*********************************************************************//** + * @brief USARTx interrupt handler sub-routine + * @param[in] None + * @return None + **********************************************************************/ +void USART2_IRQHandler(void) +{ + uint8_t data_byte; + + + if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) { + /* Read one byte from the receive data register */ + data_byte = USART_ReceiveData(USART2); + (void)FIFO_Put(&Receive_Buffer, data_byte); + } +} + +/************************************************************************* +* DESCRIPTION: Return true if a byte is available +* RETURN: true if a byte is available, with the byte in the parameter +* NOTES: none +**************************************************************************/ +bool rs485_byte_available( + uint8_t * data_register) +{ + bool data_available = false; /* return value */ + + if (!FIFO_Empty(&Receive_Buffer)) { + *data_register = FIFO_Get(&Receive_Buffer); + timer_elapsed_start(&Silence_Timer); + data_available = true; + led_rx_on_interval(10); + } + + return data_available; +} + +/************************************************************************* +* DESCRIPTION: Sends a byte of data +* RETURN: nothing +* NOTES: none +**************************************************************************/ +void rs485_byte_send(uint8_t tx_byte) +{ + led_tx_on_interval(10); + USART_SendData(USART2, tx_byte); + timer_elapsed_start(&Silence_Timer); +} + +/************************************************************************* +* Description: Determines if a byte in the USART has been shifted from +* register +* Returns: true if the USART register is empty +* Notes: none +**************************************************************************/ +bool rs485_byte_sent(void) +{ + return USART_GetFlagStatus(USART2, USART_FLAG_TXE); +} + +/************************************************************************* +* Description: Determines if the entire frame is sent from USART FIFO +* Returns: true if the USART FIFO is empty +* Notes: none +**************************************************************************/ +bool rs485_frame_sent(void) +{ + return USART_GetFlagStatus(USART2, USART_FLAG_TC); +} + +/************************************************************************* +* DESCRIPTION: Send some data and wait until it is sent +* RETURN: true if a collision or timeout occurred +* NOTES: none +**************************************************************************/ +void rs485_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) /* number of bytes of data */ +{ + uint8_t tx_byte; + + while (nbytes) { + /* Send the data byte */ + tx_byte = *buffer; + /* Send one byte */ + USART_SendData(USART2, tx_byte); + while (!rs485_byte_sent()) { + /* do nothing - wait until Tx buffer is empty */ + } + buffer++; + nbytes--; + } + /* was the frame sent? */ + while (!rs485_frame_sent()) { + /* do nothing - wait until the entire frame in the + Transmit Shift Register has been shifted out */ + } + timer_elapsed_start(&Silence_Timer); + + return; +} + +/************************************************************************* +* Description: Configures the baud rate of the USART +* Returns: nothing +* Notes: none +**************************************************************************/ +static void rs485_baud_rate_configure( + void) +{ + USART_InitTypeDef USART_InitStructure; + + USART_InitStructure.USART_BaudRate = Baud_Rate; + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_HardwareFlowControl = + USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + + /* Configure USARTx */ + USART_Init(USART2, &USART_InitStructure); +} + +/************************************************************************* +* Description: Sets the baud rate to non-volatile storeage and configures USART +* Returns: true if a value baud rate was saved +* Notes: none +**************************************************************************/ +bool rs485_baud_rate_set( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + Baud_Rate = baud; + rs485_baud_rate_configure(); + break; + default: + valid = false; + break; + } + + return valid; +} + +/************************************************************************* +* Description: Determines the baud rate in bps +* Returns: baud rate in bps +* Notes: none +**************************************************************************/ +uint32_t rs485_baud_rate( + void) +{ + return Baud_Rate; +} + +/************************************************************************* +* Description: Enable the Request To Send (RTS) aka Transmit Enable pin +* Returns: nothing +* Notes: none +**************************************************************************/ +void rs485_rts_enable( + bool enable) +{ + if (enable) { + GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET); + } else { + GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET); + } +} + +/************************************************************************* +* Description: Initialize the room network USART +* Returns: nothing +* Notes: none +**************************************************************************/ +void rs485_init(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + GPIO_StructInit(&GPIO_InitStructure); + /* Configure USARTx Rx as input floating */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOA, &GPIO_InitStructure); + /* Configure USARTx Tx as alternate function push-pull */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; + GPIO_Init(GPIOA, &GPIO_InitStructure); + /* Configure the Request To Send (RTS) aka Transmit Enable pin */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOA, &GPIO_InitStructure); + /* Enable USARTx Clock */ + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); + /* Enable GPIO Clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + /* Configure the NVIC Preemption Priority Bits */ + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); + /* Enable the USARTx Interrupt */ + NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + /* enable the USART to generate interrupts */ + USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); + + rs485_baud_rate_set(Baud_Rate); + + USART_Cmd(USART2, ENABLE); + + FIFO_Init(&Receive_Buffer, &Receive_Buffer_Data[0], + (unsigned)sizeof(Receive_Buffer_Data)); + timer_elapsed_start(&Silence_Timer); +} diff --git a/bacnet-stack/ports/stm32f10x/rs485.h b/bacnet-stack/ports/stm32f10x/rs485.h index 2fb43fab..0103a94c 100644 --- a/bacnet-stack/ports/stm32f10x/rs485.h +++ b/bacnet-stack/ports/stm32f10x/rs485.h @@ -1,66 +1,66 @@ -/************************************************************************** -* -* Copyright (C) 2011 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. -* -* Module Description: -* Handle the configuration and operation of the RS485 bus. -**************************************************************************/ -#ifndef RS485_H -#define RS485_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - void rs485_init( - void); - void rs485_rts_enable( - bool enable); - bool rs485_byte_available( - uint8_t * data_register); - bool rs485_receive_error( - void); - void rs485_bytes_send( - uint8_t * buffer, /* data to send */ - uint16_t nbytes); /* number of bytes of data */ - uint32_t rs485_baud_rate( - void); - bool rs485_baud_rate_set( - uint32_t baud); - /* a granular approach */ - void rs485_byte_send( - uint8_t data_register); - bool rs485_byte_sent(void); - bool rs485_frame_sent(void); - bool rs485_turnaround_elapsed( - void); - - void rs485_silence_reset(void); - bool rs485_silence_elapsed(uint32_t interval); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/************************************************************************** +* +* Copyright (C) 2011 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. +* +* Module Description: +* Handle the configuration and operation of the RS485 bus. +**************************************************************************/ +#ifndef RS485_H +#define RS485_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void rs485_init( + void); + void rs485_rts_enable( + bool enable); + bool rs485_byte_available( + uint8_t * data_register); + bool rs485_receive_error( + void); + void rs485_bytes_send( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + uint32_t rs485_baud_rate( + void); + bool rs485_baud_rate_set( + uint32_t baud); + /* a granular approach */ + void rs485_byte_send( + uint8_t data_register); + bool rs485_byte_sent(void); + bool rs485_frame_sent(void); + bool rs485_turnaround_elapsed( + void); + + void rs485_silence_reset(void); + bool rs485_silence_elapsed(uint32_t interval); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/stm32f10x/stm32f10x_conf.h b/bacnet-stack/ports/stm32f10x/stm32f10x_conf.h index d4c9e812..1ec60d00 100644 --- a/bacnet-stack/ports/stm32f10x/stm32f10x_conf.h +++ b/bacnet-stack/ports/stm32f10x/stm32f10x_conf.h @@ -1,74 +1,74 @@ -/** - ****************************************************************************** - * @file I2C/EEPROM/stm32f10x_conf.h - * @author MCD Application Team - * @version V3.4.0 - * @date 10/15/2010 - * @brief Library configuration file. - ****************************************************************************** - * @copy - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2010 STMicroelectronics

- */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __STM32F10x_CONF_H -#define __STM32F10x_CONF_H - -/* Includes ------------------------------------------------------------------*/ -/* Uncomment the line below to enable peripheral header file inclusion */ -#include "stm32f10x_adc.h" -#include "stm32f10x_bkp.h" -#include "stm32f10x_can.h" -#include "stm32f10x_cec.h" -#include "stm32f10x_crc.h" -#include "stm32f10x_dac.h" -#include "stm32f10x_dbgmcu.h" -#include "stm32f10x_dma.h" -#include "stm32f10x_exti.h" -#include "stm32f10x_flash.h" -#include "stm32f10x_fsmc.h" -#include "stm32f10x_gpio.h" -#include "stm32f10x_i2c.h" -#include "stm32f10x_iwdg.h" -#include "stm32f10x_pwr.h" -#include "stm32f10x_rcc.h" -#include "stm32f10x_rtc.h" -#include "stm32f10x_sdio.h" -#include "stm32f10x_spi.h" -#include "stm32f10x_tim.h" -#include "stm32f10x_usart.h" -#include "stm32f10x_wwdg.h" -#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ - -/* Exported types ------------------------------------------------------------*/ -/* Exported constants --------------------------------------------------------*/ -/* Uncomment the line below to expanse the "assert_param" macro in the - Standard Peripheral Library drivers code */ -#ifdef USE_FULL_ASSERT - -/** - * @brief The assert_param macro is used for function's parameters check. - * @param expr: If expr is false, it calls assert_failed function - * which reports the name of the source file and the source - * line number of the call that failed. - * If expr is true, it returns no value. - * @retval None - */ - #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) -/* Exported functions ------------------------------------------------------- */ - void assert_failed(uint8_t* file, uint32_t line); -#else - #define assert_param(expr) ((void)0) -#endif /* USE_FULL_ASSERT */ - -#endif /* __STM32F10x_CONF_H */ - -/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file I2C/EEPROM/stm32f10x_conf.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief Library configuration file. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_CONF_H +#define __STM32F10x_CONF_H + +/* Includes ------------------------------------------------------------------*/ +/* Uncomment the line below to enable peripheral header file inclusion */ +#include "stm32f10x_adc.h" +#include "stm32f10x_bkp.h" +#include "stm32f10x_can.h" +#include "stm32f10x_cec.h" +#include "stm32f10x_crc.h" +#include "stm32f10x_dac.h" +#include "stm32f10x_dbgmcu.h" +#include "stm32f10x_dma.h" +#include "stm32f10x_exti.h" +#include "stm32f10x_flash.h" +#include "stm32f10x_fsmc.h" +#include "stm32f10x_gpio.h" +#include "stm32f10x_i2c.h" +#include "stm32f10x_iwdg.h" +#include "stm32f10x_pwr.h" +#include "stm32f10x_rcc.h" +#include "stm32f10x_rtc.h" +#include "stm32f10x_sdio.h" +#include "stm32f10x_spi.h" +#include "stm32f10x_tim.h" +#include "stm32f10x_usart.h" +#include "stm32f10x_wwdg.h" +#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Uncomment the line below to expanse the "assert_param" macro in the + Standard Peripheral Library drivers code */ +#ifdef USE_FULL_ASSERT + +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ + #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ + void assert_failed(uint8_t* file, uint32_t line); +#else + #define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + +#endif /* __STM32F10x_CONF_H */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/bacnet-stack/ports/stm32f10x/stm32f10x_it.c b/bacnet-stack/ports/stm32f10x/stm32f10x_it.c index 2368252c..1787ae80 100644 --- a/bacnet-stack/ports/stm32f10x/stm32f10x_it.c +++ b/bacnet-stack/ports/stm32f10x/stm32f10x_it.c @@ -1,133 +1,133 @@ -/** - ****************************************************************************** - * @file I2C/EEPROM/stm32f10x_it.c - * @author MCD Application Team - * @version V3.4.0 - * @date 10/15/2010 - * @brief Main Interrupt Service Routines. - * This file provides template for all exceptions handler and - * peripherals interrupt service routine. - ****************************************************************************** - * @copy - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2010 STMicroelectronics

- */ - -/* Includes ------------------------------------------------------------------*/ -#include "stm32f10x_it.h" - -/** @addtogroup STM32F10x_StdPeriph_Examples - * @{ - */ - -/** @addtogroup I2C_EEPROM - * @{ - */ - -/* Private typedef -----------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ -/* Private macro -------------------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -/* Private function prototypes -----------------------------------------------*/ -/* Private functions ---------------------------------------------------------*/ - -/******************************************************************************/ -/* Cortex-M3 Processor Exceptions Handlers */ -/******************************************************************************/ - -/** - * @brief This function handles NMI exception. - * @param None - * @retval None - */ -void NMI_Handler(void) -{ -} - -/** - * @brief This function handles Hard Fault exception. - * @param None - * @retval None - */ -void HardFault_Handler(void) -{ - /* Go to infinite loop when Hard Fault exception occurs */ - while (1) - { - } -} - -/** - * @brief This function handles Memory Manage exception. - * @param None - * @retval None - */ -void MemManage_Handler(void) -{ - /* Go to infinite loop when Memory Manage exception occurs */ - while (1) - { - } -} - -/** - * @brief This function handles Bus Fault exception. - * @param None - * @retval None - */ -void BusFault_Handler(void) -{ - /* Go to infinite loop when Bus Fault exception occurs */ - while (1) - { - } -} - -/** - * @brief This function handles Usage Fault exception. - * @param None - * @retval None - */ -void UsageFault_Handler(void) -{ - /* Go to infinite loop when Usage Fault exception occurs */ - while (1) - { - } -} - -/** - * @brief This function handles SVCall exception. - * @param None - * @retval None - */ -void SVC_Handler(void) -{ -} - -/** - * @brief This function handles Debug Monitor exception. - * @param None - * @retval None - */ -void DebugMon_Handler(void) -{ -} - -/** - * @brief This function handles PendSV_Handler exception. - * @param None - * @retval None - */ -void PendSV_Handler(void) -{ -} - -/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file I2C/EEPROM/stm32f10x_it.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief Main Interrupt Service Routines. + * This file provides template for all exceptions handler and + * peripherals interrupt service routine. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x_it.h" + +/** @addtogroup STM32F10x_StdPeriph_Examples + * @{ + */ + +/** @addtogroup I2C_EEPROM + * @{ + */ + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ + +/******************************************************************************/ +/* Cortex-M3 Processor Exceptions Handlers */ +/******************************************************************************/ + +/** + * @brief This function handles NMI exception. + * @param None + * @retval None + */ +void NMI_Handler(void) +{ +} + +/** + * @brief This function handles Hard Fault exception. + * @param None + * @retval None + */ +void HardFault_Handler(void) +{ + /* Go to infinite loop when Hard Fault exception occurs */ + while (1) + { + } +} + +/** + * @brief This function handles Memory Manage exception. + * @param None + * @retval None + */ +void MemManage_Handler(void) +{ + /* Go to infinite loop when Memory Manage exception occurs */ + while (1) + { + } +} + +/** + * @brief This function handles Bus Fault exception. + * @param None + * @retval None + */ +void BusFault_Handler(void) +{ + /* Go to infinite loop when Bus Fault exception occurs */ + while (1) + { + } +} + +/** + * @brief This function handles Usage Fault exception. + * @param None + * @retval None + */ +void UsageFault_Handler(void) +{ + /* Go to infinite loop when Usage Fault exception occurs */ + while (1) + { + } +} + +/** + * @brief This function handles SVCall exception. + * @param None + * @retval None + */ +void SVC_Handler(void) +{ +} + +/** + * @brief This function handles Debug Monitor exception. + * @param None + * @retval None + */ +void DebugMon_Handler(void) +{ +} + +/** + * @brief This function handles PendSV_Handler exception. + * @param None + * @retval None + */ +void PendSV_Handler(void) +{ +} + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/bacnet-stack/ports/stm32f10x/stm32f10x_it.h b/bacnet-stack/ports/stm32f10x/stm32f10x_it.h index 50b8dca3..0187ac00 100644 --- a/bacnet-stack/ports/stm32f10x/stm32f10x_it.h +++ b/bacnet-stack/ports/stm32f10x/stm32f10x_it.h @@ -1,44 +1,44 @@ -/** - ****************************************************************************** - * @file I2C/EEPROM/stm32f10x_it.h - * @author MCD Application Team - * @version V3.4.0 - * @date 10/15/2010 - * @brief This file contains the headers of the interrupt handlers. - ****************************************************************************** - * @copy - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2010 STMicroelectronics

- */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __STM32F10x_IT_H -#define __STM32F10x_IT_H - -/* Includes ------------------------------------------------------------------*/ -#include "stm32f10x.h" - -/* Exported types ------------------------------------------------------------*/ -/* Exported constants --------------------------------------------------------*/ -/* Exported macro ------------------------------------------------------------*/ -/* Exported functions ------------------------------------------------------- */ - -void NMI_Handler(void); -void HardFault_Handler(void); -void MemManage_Handler(void); -void BusFault_Handler(void); -void UsageFault_Handler(void); -void SVC_Handler(void); -void DebugMon_Handler(void); -void PendSV_Handler(void); - -#endif /* __STM32F10x_IT_H */ - -/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file I2C/EEPROM/stm32f10x_it.h + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief This file contains the headers of the interrupt handlers. + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F10x_IT_H +#define __STM32F10x_IT_H + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f10x.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Exported macro ------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ + +void NMI_Handler(void); +void HardFault_Handler(void); +void MemManage_Handler(void); +void BusFault_Handler(void); +void UsageFault_Handler(void); +void SVC_Handler(void); +void DebugMon_Handler(void); +void PendSV_Handler(void); + +#endif /* __STM32F10x_IT_H */ + +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/bacnet-stack/ports/stm32f10x/system_stm32f10x.c b/bacnet-stack/ports/stm32f10x/system_stm32f10x.c index a1526908..fd7728c9 100644 --- a/bacnet-stack/ports/stm32f10x/system_stm32f10x.c +++ b/bacnet-stack/ports/stm32f10x/system_stm32f10x.c @@ -1,1033 +1,1033 @@ -/** - ****************************************************************************** - * @file SysTick/system_stm32f10x.c - * @author MCD Application Team - * @version V3.4.0 - * @date 10/15/2010 - * @brief CMSIS Cortex-M3 Device Peripheral Access Layer System Source File. - ****************************************************************************** - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2010 STMicroelectronics

- ****************************************************************************** - */ - -/** @addtogroup CMSIS - * @{ - */ - -/** @addtogroup stm32f10x_system - * @{ - */ - -/** @addtogroup STM32F10x_System_Private_Includes - * @{ - */ - -#include "stm32f10x.h" - -/** - * @} - */ - -/** @addtogroup STM32F10x_System_Private_TypesDefinitions - * @{ - */ - -/** - * @} - */ - -/** @addtogroup STM32F10x_System_Private_Defines - * @{ - */ - -/*!< Uncomment the line corresponding to the desired System clock (SYSCLK) - frequency (after reset the HSI is used as SYSCLK source) - - IMPORTANT NOTE: - ============== - 1. After each device reset the HSI is used as System clock source. - - 2. Please make sure that the selected System clock doesn't exceed your device's - maximum frequency. - - 3. If none of the define below is enabled, the HSI is used as System clock - source. - - 4. The System clock configuration functions provided within this file assume that: - - For Low, Medium and High density Value line devices an external 8MHz - crystal is used to drive the System clock. - - For Low, Medium and High density devices an external 8MHz crystal is - used to drive the System clock. - - For Connectivity line devices an external 25MHz crystal is used to drive - the System clock. - If you are using different crystal you have to adapt those functions accordingly. - */ - -#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) -/* #define SYSCLK_FREQ_HSE HSE_VALUE */ - #define SYSCLK_FREQ_24MHz 24000000 -#else -/* #define SYSCLK_FREQ_HSE HSE_VALUE */ -/* #define SYSCLK_FREQ_24MHz 24000000 */ -/* #define SYSCLK_FREQ_36MHz 36000000 */ -/* #define SYSCLK_FREQ_48MHz 48000000 */ -/* #define SYSCLK_FREQ_56MHz 56000000 */ -#define SYSCLK_FREQ_72MHz 72000000 -#endif - -/*!< Uncomment the following line if you need to use external SRAM mounted - on STM3210E-EVAL board (STM32 High density and XL-density devices) or on - STM32100E-EVAL board (STM32 High-density value line devices) as data memory */ -#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) -/* #define DATA_IN_ExtSRAM */ -#endif - -/*!< Uncomment the following line if you need to relocate your vector Table in - Internal SRAM. */ -/* #define VECT_TAB_SRAM */ -#define VECT_TAB_OFFSET 0x0 /*!< Vector Table base offset field. - This value must be a multiple of 0x100. */ - - -/** - * @} - */ - -/** @addtogroup STM32F10x_System_Private_Macros - * @{ - */ - -/** - * @} - */ - -/** @addtogroup STM32F10x_System_Private_Variables - * @{ - */ - -/******************************************************************************* -* Clock Definitions -*******************************************************************************/ -#ifdef SYSCLK_FREQ_HSE - uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /*!< System Clock Frequency (Core Clock) */ -#elif defined SYSCLK_FREQ_24MHz - uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz; /*!< System Clock Frequency (Core Clock) */ -#elif defined SYSCLK_FREQ_36MHz - uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz; /*!< System Clock Frequency (Core Clock) */ -#elif defined SYSCLK_FREQ_48MHz - uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz; /*!< System Clock Frequency (Core Clock) */ -#elif defined SYSCLK_FREQ_56MHz - uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz; /*!< System Clock Frequency (Core Clock) */ -#elif defined SYSCLK_FREQ_72MHz - uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; /*!< System Clock Frequency (Core Clock) */ -#else /*!< HSI Selected as System Clock source */ - uint32_t SystemCoreClock = HSI_VALUE; /*!< System Clock Frequency (Core Clock) */ -#endif - -__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; -/** - * @} - */ - -/** @addtogroup STM32F10x_System_Private_FunctionPrototypes - * @{ - */ - -static void SetSysClock(void); - -#ifdef SYSCLK_FREQ_HSE - static void SetSysClockToHSE(void); -#elif defined SYSCLK_FREQ_24MHz - static void SetSysClockTo24(void); -#elif defined SYSCLK_FREQ_36MHz - static void SetSysClockTo36(void); -#elif defined SYSCLK_FREQ_48MHz - static void SetSysClockTo48(void); -#elif defined SYSCLK_FREQ_56MHz - static void SetSysClockTo56(void); -#elif defined SYSCLK_FREQ_72MHz - static void SetSysClockTo72(void); -#endif - -#ifdef DATA_IN_ExtSRAM - static void SystemInit_ExtMemCtl(void); -#endif /* DATA_IN_ExtSRAM */ - -/** - * @} - */ - -/** @addtogroup STM32F10x_System_Private_Functions - * @{ - */ - -/** - * @brief Setup the microcontroller system - * Initialize the Embedded Flash Interface, the PLL and update the - * SystemCoreClock variable. - * @note This function should be used only after reset. - * @param None - * @retval None - */ -void SystemInit (void) -{ - /* Reset the RCC clock configuration to the default reset state(for debug purpose) */ - /* Set HSION bit */ - RCC->CR |= (uint32_t)0x00000001; - - /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ -#ifndef STM32F10X_CL - RCC->CFGR &= (uint32_t)0xF8FF0000; -#else - RCC->CFGR &= (uint32_t)0xF0FF0000; -#endif /* STM32F10X_CL */ - - /* Reset HSEON, CSSON and PLLON bits */ - RCC->CR &= (uint32_t)0xFEF6FFFF; - - /* Reset HSEBYP bit */ - RCC->CR &= (uint32_t)0xFFFBFFFF; - - /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ - RCC->CFGR &= (uint32_t)0xFF80FFFF; - -#ifdef STM32F10X_CL - /* Reset PLL2ON and PLL3ON bits */ - RCC->CR &= (uint32_t)0xEBFFFFFF; - - /* Disable all interrupts and clear pending bits */ - RCC->CIR = 0x00FF0000; - - /* Reset CFGR2 register */ - RCC->CFGR2 = 0x00000000; -#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) - /* Disable all interrupts and clear pending bits */ - RCC->CIR = 0x009F0000; - - /* Reset CFGR2 register */ - RCC->CFGR2 = 0x00000000; -#else - /* Disable all interrupts and clear pending bits */ - RCC->CIR = 0x009F0000; -#endif /* STM32F10X_CL */ - -#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) - #ifdef DATA_IN_ExtSRAM - SystemInit_ExtMemCtl(); - #endif /* DATA_IN_ExtSRAM */ -#endif - - /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */ - /* Configure the Flash Latency cycles and enable prefetch buffer */ - SetSysClock(); - -#ifdef VECT_TAB_SRAM - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */ -#else - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */ -#endif -} - -/** - * @brief Update SystemCoreClock according to Clock Register Values - * @note None - * @param None - * @retval None - */ -void SystemCoreClockUpdate (void) -{ - uint32_t tmp = 0, pllmull = 0, pllsource = 0; - -#ifdef STM32F10X_CL - uint32_t prediv1source = 0, prediv1factor = 0, prediv2factor = 0, pll2mull = 0; -#endif /* STM32F10X_CL */ - -#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) - uint32_t prediv1factor = 0; -#endif /* STM32F10X_LD_VL or STM32F10X_MD_VL or STM32F10X_HD_VL */ - - /* Get SYSCLK source -------------------------------------------------------*/ - tmp = RCC->CFGR & RCC_CFGR_SWS; - - switch (tmp) - { - case 0x00: /* HSI used as system clock */ - SystemCoreClock = HSI_VALUE; - break; - case 0x04: /* HSE used as system clock */ - SystemCoreClock = HSE_VALUE; - break; - case 0x08: /* PLL used as system clock */ - - /* Get PLL clock source and multiplication factor ----------------------*/ - pllmull = RCC->CFGR & RCC_CFGR_PLLMULL; - pllsource = RCC->CFGR & RCC_CFGR_PLLSRC; - -#ifndef STM32F10X_CL - pllmull = ( pllmull >> 18) + 2; - - if (pllsource == 0x00) - { - /* HSI oscillator clock divided by 2 selected as PLL clock entry */ - SystemCoreClock = (HSI_VALUE >> 1) * pllmull; - } - else - { - #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) - prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1; - /* HSE oscillator clock selected as PREDIV1 clock entry */ - SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; - #else - /* HSE selected as PLL clock entry */ - if ((RCC->CFGR & RCC_CFGR_PLLXTPRE) != (uint32_t)RESET) - {/* HSE oscillator clock divided by 2 */ - SystemCoreClock = (HSE_VALUE >> 1) * pllmull; - } - else - { - SystemCoreClock = HSE_VALUE * pllmull; - } - #endif - } -#else - pllmull = pllmull >> 18; - - if (pllmull != 0x0D) - { - pllmull += 2; - } - else - { /* PLL multiplication factor = PLL input clock * 6.5 */ - pllmull = 13 / 2; - } - - if (pllsource == 0x00) - { - /* HSI oscillator clock divided by 2 selected as PLL clock entry */ - SystemCoreClock = (HSI_VALUE >> 1) * pllmull; - } - else - {/* PREDIV1 selected as PLL clock entry */ - - /* Get PREDIV1 clock source and division factor */ - prediv1source = RCC->CFGR2 & RCC_CFGR2_PREDIV1SRC; - prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1; - - if (prediv1source == 0) - { - /* HSE oscillator clock selected as PREDIV1 clock entry */ - SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; - } - else - {/* PLL2 clock selected as PREDIV1 clock entry */ - - /* Get PREDIV2 division factor and PLL2 multiplication factor */ - prediv2factor = ((RCC->CFGR2 & RCC_CFGR2_PREDIV2) >> 4) + 1; - pll2mull = ((RCC->CFGR2 & RCC_CFGR2_PLL2MUL) >> 8 ) + 2; - SystemCoreClock = (((HSE_VALUE / prediv2factor) * pll2mull) / prediv1factor) * pllmull; - } - } -#endif /* STM32F10X_CL */ - break; - - default: - SystemCoreClock = HSI_VALUE; - break; - } - - /* Compute HCLK clock frequency ----------------*/ - /* Get HCLK prescaler */ - tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; - /* HCLK clock frequency */ - SystemCoreClock >>= tmp; -} - -/** - * @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers. - * @param None - * @retval None - */ -static void SetSysClock(void) -{ -#ifdef SYSCLK_FREQ_HSE - SetSysClockToHSE(); -#elif defined SYSCLK_FREQ_24MHz - SetSysClockTo24(); -#elif defined SYSCLK_FREQ_36MHz - SetSysClockTo36(); -#elif defined SYSCLK_FREQ_48MHz - SetSysClockTo48(); -#elif defined SYSCLK_FREQ_56MHz - SetSysClockTo56(); -#elif defined SYSCLK_FREQ_72MHz - SetSysClockTo72(); -#endif - - /* If none of the define above is enabled, the HSI is used as System clock - source (default after reset) */ -} - -/** - * @brief Setup the external memory controller. Called in startup_stm32f10x.s - * before jump to __main - * @param None - * @retval None - */ -#ifdef DATA_IN_ExtSRAM -/** - * @brief Setup the external memory controller. - * Called in startup_stm32f10x_xx.s/.c before jump to main. - * This function configures the external SRAM mounted on STM3210E-EVAL - * board (STM32 High density devices). This SRAM will be used as program - * data memory (including heap and stack). - * @param None - * @retval None - */ -void SystemInit_ExtMemCtl(void) -{ -/*!< FSMC Bank1 NOR/SRAM3 is used for the STM3210E-EVAL, if another Bank is - required, then adjust the Register Addresses */ - - /* Enable FSMC clock */ - RCC->AHBENR = 0x00000114; - - /* Enable GPIOD, GPIOE, GPIOF and GPIOG clocks */ - RCC->APB2ENR = 0x000001E0; - -/* --------------- SRAM Data lines, NOE and NWE configuration ---------------*/ -/*---------------- SRAM Address lines configuration -------------------------*/ -/*---------------- NOE and NWE configuration --------------------------------*/ -/*---------------- NE3 configuration ----------------------------------------*/ -/*---------------- NBL0, NBL1 configuration ---------------------------------*/ - - GPIOD->CRL = 0x44BB44BB; - GPIOD->CRH = 0xBBBBBBBB; - - GPIOE->CRL = 0xB44444BB; - GPIOE->CRH = 0xBBBBBBBB; - - GPIOF->CRL = 0x44BBBBBB; - GPIOF->CRH = 0xBBBB4444; - - GPIOG->CRL = 0x44BBBBBB; - GPIOG->CRH = 0x44444B44; - -/*---------------- FSMC Configuration ---------------------------------------*/ -/*---------------- Enable FSMC Bank1_SRAM Bank ------------------------------*/ - - FSMC_Bank1->BTCR[4] = 0x00001011; - FSMC_Bank1->BTCR[5] = 0x00000200; -} -#endif /* DATA_IN_ExtSRAM */ - -#ifdef SYSCLK_FREQ_HSE -/** - * @brief Selects HSE as System clock source and configure HCLK, PCLK2 - * and PCLK1 prescalers. - * @note This function should be used only after reset. - * @param None - * @retval None - */ -static void SetSysClockToHSE(void) -{ - __IO uint32_t StartUpCounter = 0, HSEStatus = 0; - - /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ - /* Enable HSE */ - RCC->CR |= ((uint32_t)RCC_CR_HSEON); - - /* Wait till HSE is ready and if Time out is reached exit */ - do - { - HSEStatus = RCC->CR & RCC_CR_HSERDY; - StartUpCounter++; - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); - - if ((RCC->CR & RCC_CR_HSERDY) != RESET) - { - HSEStatus = (uint32_t)0x01; - } - else - { - HSEStatus = (uint32_t)0x00; - } - - if (HSEStatus == (uint32_t)0x01) - { - -#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL - /* Enable Prefetch Buffer */ - FLASH->ACR |= FLASH_ACR_PRFTBE; - - /* Flash 0 wait state */ - FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); - -#ifndef STM32F10X_CL - FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0; -#else - if (HSE_VALUE <= 24000000) - { - FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0; - } - else - { - FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1; - } -#endif /* STM32F10X_CL */ -#endif - - /* HCLK = SYSCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; - - /* PCLK2 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; - - /* PCLK1 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1; - - /* Select HSE as system clock source */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); - RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSE; - - /* Wait till HSE is used as system clock source */ - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04) - { - } - } - else - { /* If HSE fails to start-up, the application will have wrong clock - configuration. User can add here some code to deal with this error */ - } -} -#elif defined SYSCLK_FREQ_24MHz -/** - * @brief Sets System clock frequency to 24MHz and configure HCLK, PCLK2 - * and PCLK1 prescalers. - * @note This function should be used only after reset. - * @param None - * @retval None - */ -static void SetSysClockTo24(void) -{ - __IO uint32_t StartUpCounter = 0, HSEStatus = 0; - - /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ - /* Enable HSE */ - RCC->CR |= ((uint32_t)RCC_CR_HSEON); - - /* Wait till HSE is ready and if Time out is reached exit */ - do - { - HSEStatus = RCC->CR & RCC_CR_HSERDY; - StartUpCounter++; - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); - - if ((RCC->CR & RCC_CR_HSERDY) != RESET) - { - HSEStatus = (uint32_t)0x01; - } - else - { - HSEStatus = (uint32_t)0x00; - } - - if (HSEStatus == (uint32_t)0x01) - { -#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL - /* Enable Prefetch Buffer */ - FLASH->ACR |= FLASH_ACR_PRFTBE; - - /* Flash 0 wait state */ - FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); - FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0; -#endif - - /* HCLK = SYSCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; - - /* PCLK2 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; - - /* PCLK1 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1; - -#ifdef STM32F10X_CL - /* Configure PLLs ------------------------------------------------------*/ - /* PLL configuration: PLLCLK = PREDIV1 * 6 = 24 MHz */ - RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | - RCC_CFGR_PLLMULL6); - - /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ - /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 10 = 4 MHz */ - RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | - RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); - RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | - RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV10); - - /* Enable PLL2 */ - RCC->CR |= RCC_CR_PLL2ON; - /* Wait till PLL2 is ready */ - while((RCC->CR & RCC_CR_PLL2RDY) == 0) - { - } -#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) - /* PLL configuration: = (HSE / 2) * 6 = 24 MHz */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1_Div2 | RCC_CFGR_PLLMULL6); -#else - /* PLL configuration: = (HSE / 2) * 6 = 24 MHz */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL6); -#endif /* STM32F10X_CL */ - - /* Enable PLL */ - RCC->CR |= RCC_CR_PLLON; - - /* Wait till PLL is ready */ - while((RCC->CR & RCC_CR_PLLRDY) == 0) - { - } - - /* Select PLL as system clock source */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); - RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; - - /* Wait till PLL is used as system clock source */ - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) - { - } - } - else - { /* If HSE fails to start-up, the application will have wrong clock - configuration. User can add here some code to deal with this error */ - } -} -#elif defined SYSCLK_FREQ_36MHz -/** - * @brief Sets System clock frequency to 36MHz and configure HCLK, PCLK2 - * and PCLK1 prescalers. - * @note This function should be used only after reset. - * @param None - * @retval None - */ -static void SetSysClockTo36(void) -{ - __IO uint32_t StartUpCounter = 0, HSEStatus = 0; - - /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ - /* Enable HSE */ - RCC->CR |= ((uint32_t)RCC_CR_HSEON); - - /* Wait till HSE is ready and if Time out is reached exit */ - do - { - HSEStatus = RCC->CR & RCC_CR_HSERDY; - StartUpCounter++; - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); - - if ((RCC->CR & RCC_CR_HSERDY) != RESET) - { - HSEStatus = (uint32_t)0x01; - } - else - { - HSEStatus = (uint32_t)0x00; - } - - if (HSEStatus == (uint32_t)0x01) - { - /* Enable Prefetch Buffer */ - FLASH->ACR |= FLASH_ACR_PRFTBE; - - /* Flash 1 wait state */ - FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); - FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1; - - /* HCLK = SYSCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; - - /* PCLK2 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; - - /* PCLK1 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1; - -#ifdef STM32F10X_CL - /* Configure PLLs ------------------------------------------------------*/ - - /* PLL configuration: PLLCLK = PREDIV1 * 9 = 36 MHz */ - RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | - RCC_CFGR_PLLMULL9); - - /*!< PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ - /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 10 = 4 MHz */ - - RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | - RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); - RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | - RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV10); - - /* Enable PLL2 */ - RCC->CR |= RCC_CR_PLL2ON; - /* Wait till PLL2 is ready */ - while((RCC->CR & RCC_CR_PLL2RDY) == 0) - { - } - -#else - /* PLL configuration: PLLCLK = (HSE / 2) * 9 = 36 MHz */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL9); -#endif /* STM32F10X_CL */ - - /* Enable PLL */ - RCC->CR |= RCC_CR_PLLON; - - /* Wait till PLL is ready */ - while((RCC->CR & RCC_CR_PLLRDY) == 0) - { - } - - /* Select PLL as system clock source */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); - RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; - - /* Wait till PLL is used as system clock source */ - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) - { - } - } - else - { /* If HSE fails to start-up, the application will have wrong clock - configuration. User can add here some code to deal with this error */ - } -} -#elif defined SYSCLK_FREQ_48MHz -/** - * @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 - * and PCLK1 prescalers. - * @note This function should be used only after reset. - * @param None - * @retval None - */ -static void SetSysClockTo48(void) -{ - __IO uint32_t StartUpCounter = 0, HSEStatus = 0; - - /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ - /* Enable HSE */ - RCC->CR |= ((uint32_t)RCC_CR_HSEON); - - /* Wait till HSE is ready and if Time out is reached exit */ - do - { - HSEStatus = RCC->CR & RCC_CR_HSERDY; - StartUpCounter++; - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); - - if ((RCC->CR & RCC_CR_HSERDY) != RESET) - { - HSEStatus = (uint32_t)0x01; - } - else - { - HSEStatus = (uint32_t)0x00; - } - - if (HSEStatus == (uint32_t)0x01) - { - /* Enable Prefetch Buffer */ - FLASH->ACR |= FLASH_ACR_PRFTBE; - - /* Flash 1 wait state */ - FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); - FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1; - - /* HCLK = SYSCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; - - /* PCLK2 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; - - /* PCLK1 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; - -#ifdef STM32F10X_CL - /* Configure PLLs ------------------------------------------------------*/ - /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ - /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ - - RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | - RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); - RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | - RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); - - /* Enable PLL2 */ - RCC->CR |= RCC_CR_PLL2ON; - /* Wait till PLL2 is ready */ - while((RCC->CR & RCC_CR_PLL2RDY) == 0) - { - } - - - /* PLL configuration: PLLCLK = PREDIV1 * 6 = 48 MHz */ - RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | - RCC_CFGR_PLLMULL6); -#else - /* PLL configuration: PLLCLK = HSE * 6 = 48 MHz */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL6); -#endif /* STM32F10X_CL */ - - /* Enable PLL */ - RCC->CR |= RCC_CR_PLLON; - - /* Wait till PLL is ready */ - while((RCC->CR & RCC_CR_PLLRDY) == 0) - { - } - - /* Select PLL as system clock source */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); - RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; - - /* Wait till PLL is used as system clock source */ - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) - { - } - } - else - { /* If HSE fails to start-up, the application will have wrong clock - configuration. User can add here some code to deal with this error */ - } -} - -#elif defined SYSCLK_FREQ_56MHz -/** - * @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 - * and PCLK1 prescalers. - * @note This function should be used only after reset. - * @param None - * @retval None - */ -static void SetSysClockTo56(void) -{ - __IO uint32_t StartUpCounter = 0, HSEStatus = 0; - - /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ - /* Enable HSE */ - RCC->CR |= ((uint32_t)RCC_CR_HSEON); - - /* Wait till HSE is ready and if Time out is reached exit */ - do - { - HSEStatus = RCC->CR & RCC_CR_HSERDY; - StartUpCounter++; - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); - - if ((RCC->CR & RCC_CR_HSERDY) != RESET) - { - HSEStatus = (uint32_t)0x01; - } - else - { - HSEStatus = (uint32_t)0x00; - } - - if (HSEStatus == (uint32_t)0x01) - { - /* Enable Prefetch Buffer */ - FLASH->ACR |= FLASH_ACR_PRFTBE; - - /* Flash 2 wait state */ - FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); - FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; - - /* HCLK = SYSCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; - - /* PCLK2 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; - - /* PCLK1 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; - -#ifdef STM32F10X_CL - /* Configure PLLs ------------------------------------------------------*/ - /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ - /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ - - RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | - RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); - RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | - RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); - - /* Enable PLL2 */ - RCC->CR |= RCC_CR_PLL2ON; - /* Wait till PLL2 is ready */ - while((RCC->CR & RCC_CR_PLL2RDY) == 0) - { - } - - - /* PLL configuration: PLLCLK = PREDIV1 * 7 = 56 MHz */ - RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | - RCC_CFGR_PLLMULL7); -#else - /* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL7); - -#endif /* STM32F10X_CL */ - - /* Enable PLL */ - RCC->CR |= RCC_CR_PLLON; - - /* Wait till PLL is ready */ - while((RCC->CR & RCC_CR_PLLRDY) == 0) - { - } - - /* Select PLL as system clock source */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); - RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; - - /* Wait till PLL is used as system clock source */ - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) - { - } - } - else - { /* If HSE fails to start-up, the application will have wrong clock - configuration. User can add here some code to deal with this error */ - } -} - -#elif defined SYSCLK_FREQ_72MHz -/** - * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 - * and PCLK1 prescalers. - * @note This function should be used only after reset. - * @param None - * @retval None - */ -static void SetSysClockTo72(void) -{ - __IO uint32_t StartUpCounter = 0, HSEStatus = 0; - - /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ - /* Enable HSE */ - RCC->CR |= ((uint32_t)RCC_CR_HSEON); - - /* Wait till HSE is ready and if Time out is reached exit */ - do - { - HSEStatus = RCC->CR & RCC_CR_HSERDY; - StartUpCounter++; - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); - - if ((RCC->CR & RCC_CR_HSERDY) != RESET) - { - HSEStatus = (uint32_t)0x01; - } - else - { - HSEStatus = (uint32_t)0x00; - } - - if (HSEStatus == (uint32_t)0x01) - { - /* Enable Prefetch Buffer */ - FLASH->ACR |= FLASH_ACR_PRFTBE; - - /* Flash 2 wait state */ - FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); - FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; - - - /* HCLK = SYSCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; - - /* PCLK2 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; - - /* PCLK1 = HCLK */ - RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; - -#ifdef STM32F10X_CL - /* Configure PLLs ------------------------------------------------------*/ - /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ - /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ - - RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | - RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); - RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | - RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); - - /* Enable PLL2 */ - RCC->CR |= RCC_CR_PLL2ON; - /* Wait till PLL2 is ready */ - while((RCC->CR & RCC_CR_PLL2RDY) == 0) - { - } - - - /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ - RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | - RCC_CFGR_PLLMULL9); -#else - /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | - RCC_CFGR_PLLMULL)); - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); -#endif /* STM32F10X_CL */ - - /* Enable PLL */ - RCC->CR |= RCC_CR_PLLON; - - /* Wait till PLL is ready */ - while((RCC->CR & RCC_CR_PLLRDY) == 0) - { - } - - /* Select PLL as system clock source */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); - RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; - - /* Wait till PLL is used as system clock source */ - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) - { - } - } - else - { /* If HSE fails to start-up, the application will have wrong clock - configuration. User can add here some code to deal with this error */ - } -} -#endif - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ -/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ +/** + ****************************************************************************** + * @file SysTick/system_stm32f10x.c + * @author MCD Application Team + * @version V3.4.0 + * @date 10/15/2010 + * @brief CMSIS Cortex-M3 Device Peripheral Access Layer System Source File. + ****************************************************************************** + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2010 STMicroelectronics

+ ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f10x_system + * @{ + */ + +/** @addtogroup STM32F10x_System_Private_Includes + * @{ + */ + +#include "stm32f10x.h" + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_Defines + * @{ + */ + +/*!< Uncomment the line corresponding to the desired System clock (SYSCLK) + frequency (after reset the HSI is used as SYSCLK source) + + IMPORTANT NOTE: + ============== + 1. After each device reset the HSI is used as System clock source. + + 2. Please make sure that the selected System clock doesn't exceed your device's + maximum frequency. + + 3. If none of the define below is enabled, the HSI is used as System clock + source. + + 4. The System clock configuration functions provided within this file assume that: + - For Low, Medium and High density Value line devices an external 8MHz + crystal is used to drive the System clock. + - For Low, Medium and High density devices an external 8MHz crystal is + used to drive the System clock. + - For Connectivity line devices an external 25MHz crystal is used to drive + the System clock. + If you are using different crystal you have to adapt those functions accordingly. + */ + +#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) +/* #define SYSCLK_FREQ_HSE HSE_VALUE */ + #define SYSCLK_FREQ_24MHz 24000000 +#else +/* #define SYSCLK_FREQ_HSE HSE_VALUE */ +/* #define SYSCLK_FREQ_24MHz 24000000 */ +/* #define SYSCLK_FREQ_36MHz 36000000 */ +/* #define SYSCLK_FREQ_48MHz 48000000 */ +/* #define SYSCLK_FREQ_56MHz 56000000 */ +#define SYSCLK_FREQ_72MHz 72000000 +#endif + +/*!< Uncomment the following line if you need to use external SRAM mounted + on STM3210E-EVAL board (STM32 High density and XL-density devices) or on + STM32100E-EVAL board (STM32 High-density value line devices) as data memory */ +#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) +/* #define DATA_IN_ExtSRAM */ +#endif + +/*!< Uncomment the following line if you need to relocate your vector Table in + Internal SRAM. */ +/* #define VECT_TAB_SRAM */ +#define VECT_TAB_OFFSET 0x0 /*!< Vector Table base offset field. + This value must be a multiple of 0x100. */ + + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_Variables + * @{ + */ + +/******************************************************************************* +* Clock Definitions +*******************************************************************************/ +#ifdef SYSCLK_FREQ_HSE + uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_24MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_36MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_48MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_56MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz; /*!< System Clock Frequency (Core Clock) */ +#elif defined SYSCLK_FREQ_72MHz + uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; /*!< System Clock Frequency (Core Clock) */ +#else /*!< HSI Selected as System Clock source */ + uint32_t SystemCoreClock = HSI_VALUE; /*!< System Clock Frequency (Core Clock) */ +#endif + +__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_FunctionPrototypes + * @{ + */ + +static void SetSysClock(void); + +#ifdef SYSCLK_FREQ_HSE + static void SetSysClockToHSE(void); +#elif defined SYSCLK_FREQ_24MHz + static void SetSysClockTo24(void); +#elif defined SYSCLK_FREQ_36MHz + static void SetSysClockTo36(void); +#elif defined SYSCLK_FREQ_48MHz + static void SetSysClockTo48(void); +#elif defined SYSCLK_FREQ_56MHz + static void SetSysClockTo56(void); +#elif defined SYSCLK_FREQ_72MHz + static void SetSysClockTo72(void); +#endif + +#ifdef DATA_IN_ExtSRAM + static void SystemInit_ExtMemCtl(void); +#endif /* DATA_IN_ExtSRAM */ + +/** + * @} + */ + +/** @addtogroup STM32F10x_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system + * Initialize the Embedded Flash Interface, the PLL and update the + * SystemCoreClock variable. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +void SystemInit (void) +{ + /* Reset the RCC clock configuration to the default reset state(for debug purpose) */ + /* Set HSION bit */ + RCC->CR |= (uint32_t)0x00000001; + + /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ +#ifndef STM32F10X_CL + RCC->CFGR &= (uint32_t)0xF8FF0000; +#else + RCC->CFGR &= (uint32_t)0xF0FF0000; +#endif /* STM32F10X_CL */ + + /* Reset HSEON, CSSON and PLLON bits */ + RCC->CR &= (uint32_t)0xFEF6FFFF; + + /* Reset HSEBYP bit */ + RCC->CR &= (uint32_t)0xFFFBFFFF; + + /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ + RCC->CFGR &= (uint32_t)0xFF80FFFF; + +#ifdef STM32F10X_CL + /* Reset PLL2ON and PLL3ON bits */ + RCC->CR &= (uint32_t)0xEBFFFFFF; + + /* Disable all interrupts and clear pending bits */ + RCC->CIR = 0x00FF0000; + + /* Reset CFGR2 register */ + RCC->CFGR2 = 0x00000000; +#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) + /* Disable all interrupts and clear pending bits */ + RCC->CIR = 0x009F0000; + + /* Reset CFGR2 register */ + RCC->CFGR2 = 0x00000000; +#else + /* Disable all interrupts and clear pending bits */ + RCC->CIR = 0x009F0000; +#endif /* STM32F10X_CL */ + +#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) + #ifdef DATA_IN_ExtSRAM + SystemInit_ExtMemCtl(); + #endif /* DATA_IN_ExtSRAM */ +#endif + + /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */ + /* Configure the Flash Latency cycles and enable prefetch buffer */ + SetSysClock(); + +#ifdef VECT_TAB_SRAM + SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */ +#else + SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */ +#endif +} + +/** + * @brief Update SystemCoreClock according to Clock Register Values + * @note None + * @param None + * @retval None + */ +void SystemCoreClockUpdate (void) +{ + uint32_t tmp = 0, pllmull = 0, pllsource = 0; + +#ifdef STM32F10X_CL + uint32_t prediv1source = 0, prediv1factor = 0, prediv2factor = 0, pll2mull = 0; +#endif /* STM32F10X_CL */ + +#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) + uint32_t prediv1factor = 0; +#endif /* STM32F10X_LD_VL or STM32F10X_MD_VL or STM32F10X_HD_VL */ + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) + { + case 0x00: /* HSI used as system clock */ + SystemCoreClock = HSI_VALUE; + break; + case 0x04: /* HSE used as system clock */ + SystemCoreClock = HSE_VALUE; + break; + case 0x08: /* PLL used as system clock */ + + /* Get PLL clock source and multiplication factor ----------------------*/ + pllmull = RCC->CFGR & RCC_CFGR_PLLMULL; + pllsource = RCC->CFGR & RCC_CFGR_PLLSRC; + +#ifndef STM32F10X_CL + pllmull = ( pllmull >> 18) + 2; + + if (pllsource == 0x00) + { + /* HSI oscillator clock divided by 2 selected as PLL clock entry */ + SystemCoreClock = (HSI_VALUE >> 1) * pllmull; + } + else + { + #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) + prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1; + /* HSE oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; + #else + /* HSE selected as PLL clock entry */ + if ((RCC->CFGR & RCC_CFGR_PLLXTPRE) != (uint32_t)RESET) + {/* HSE oscillator clock divided by 2 */ + SystemCoreClock = (HSE_VALUE >> 1) * pllmull; + } + else + { + SystemCoreClock = HSE_VALUE * pllmull; + } + #endif + } +#else + pllmull = pllmull >> 18; + + if (pllmull != 0x0D) + { + pllmull += 2; + } + else + { /* PLL multiplication factor = PLL input clock * 6.5 */ + pllmull = 13 / 2; + } + + if (pllsource == 0x00) + { + /* HSI oscillator clock divided by 2 selected as PLL clock entry */ + SystemCoreClock = (HSI_VALUE >> 1) * pllmull; + } + else + {/* PREDIV1 selected as PLL clock entry */ + + /* Get PREDIV1 clock source and division factor */ + prediv1source = RCC->CFGR2 & RCC_CFGR2_PREDIV1SRC; + prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1; + + if (prediv1source == 0) + { + /* HSE oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; + } + else + {/* PLL2 clock selected as PREDIV1 clock entry */ + + /* Get PREDIV2 division factor and PLL2 multiplication factor */ + prediv2factor = ((RCC->CFGR2 & RCC_CFGR2_PREDIV2) >> 4) + 1; + pll2mull = ((RCC->CFGR2 & RCC_CFGR2_PLL2MUL) >> 8 ) + 2; + SystemCoreClock = (((HSE_VALUE / prediv2factor) * pll2mull) / prediv1factor) * pllmull; + } + } +#endif /* STM32F10X_CL */ + break; + + default: + SystemCoreClock = HSI_VALUE; + break; + } + + /* Compute HCLK clock frequency ----------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK clock frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers. + * @param None + * @retval None + */ +static void SetSysClock(void) +{ +#ifdef SYSCLK_FREQ_HSE + SetSysClockToHSE(); +#elif defined SYSCLK_FREQ_24MHz + SetSysClockTo24(); +#elif defined SYSCLK_FREQ_36MHz + SetSysClockTo36(); +#elif defined SYSCLK_FREQ_48MHz + SetSysClockTo48(); +#elif defined SYSCLK_FREQ_56MHz + SetSysClockTo56(); +#elif defined SYSCLK_FREQ_72MHz + SetSysClockTo72(); +#endif + + /* If none of the define above is enabled, the HSI is used as System clock + source (default after reset) */ +} + +/** + * @brief Setup the external memory controller. Called in startup_stm32f10x.s + * before jump to __main + * @param None + * @retval None + */ +#ifdef DATA_IN_ExtSRAM +/** + * @brief Setup the external memory controller. + * Called in startup_stm32f10x_xx.s/.c before jump to main. + * This function configures the external SRAM mounted on STM3210E-EVAL + * board (STM32 High density devices). This SRAM will be used as program + * data memory (including heap and stack). + * @param None + * @retval None + */ +void SystemInit_ExtMemCtl(void) +{ +/*!< FSMC Bank1 NOR/SRAM3 is used for the STM3210E-EVAL, if another Bank is + required, then adjust the Register Addresses */ + + /* Enable FSMC clock */ + RCC->AHBENR = 0x00000114; + + /* Enable GPIOD, GPIOE, GPIOF and GPIOG clocks */ + RCC->APB2ENR = 0x000001E0; + +/* --------------- SRAM Data lines, NOE and NWE configuration ---------------*/ +/*---------------- SRAM Address lines configuration -------------------------*/ +/*---------------- NOE and NWE configuration --------------------------------*/ +/*---------------- NE3 configuration ----------------------------------------*/ +/*---------------- NBL0, NBL1 configuration ---------------------------------*/ + + GPIOD->CRL = 0x44BB44BB; + GPIOD->CRH = 0xBBBBBBBB; + + GPIOE->CRL = 0xB44444BB; + GPIOE->CRH = 0xBBBBBBBB; + + GPIOF->CRL = 0x44BBBBBB; + GPIOF->CRH = 0xBBBB4444; + + GPIOG->CRL = 0x44BBBBBB; + GPIOG->CRH = 0x44444B44; + +/*---------------- FSMC Configuration ---------------------------------------*/ +/*---------------- Enable FSMC Bank1_SRAM Bank ------------------------------*/ + + FSMC_Bank1->BTCR[4] = 0x00001011; + FSMC_Bank1->BTCR[5] = 0x00000200; +} +#endif /* DATA_IN_ExtSRAM */ + +#ifdef SYSCLK_FREQ_HSE +/** + * @brief Selects HSE as System clock source and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockToHSE(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + +#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 0 wait state */ + FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); + +#ifndef STM32F10X_CL + FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0; +#else + if (HSE_VALUE <= 24000000) + { + FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0; + } + else + { + FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1; + } +#endif /* STM32F10X_CL */ +#endif + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1; + + /* Select HSE as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSE; + + /* Wait till HSE is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04) + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} +#elif defined SYSCLK_FREQ_24MHz +/** + * @brief Sets System clock frequency to 24MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo24(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { +#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 0 wait state */ + FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0; +#endif + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------*/ + /* PLL configuration: PLLCLK = PREDIV1 * 6 = 24 MHz */ + RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL6); + + /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 10 = 4 MHz */ + RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV10); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while((RCC->CR & RCC_CR_PLL2RDY) == 0) + { + } +#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) + /* PLL configuration: = (HSE / 2) * 6 = 24 MHz */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1_Div2 | RCC_CFGR_PLLMULL6); +#else + /* PLL configuration: = (HSE / 2) * 6 = 24 MHz */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL6); +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} +#elif defined SYSCLK_FREQ_36MHz +/** + * @brief Sets System clock frequency to 36MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo36(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 1 wait state */ + FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1; + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------*/ + + /* PLL configuration: PLLCLK = PREDIV1 * 9 = 36 MHz */ + RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL9); + + /*!< PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 10 = 4 MHz */ + + RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV10); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while((RCC->CR & RCC_CR_PLL2RDY) == 0) + { + } + +#else + /* PLL configuration: PLLCLK = (HSE / 2) * 9 = 36 MHz */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL9); +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} +#elif defined SYSCLK_FREQ_48MHz +/** + * @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo48(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 1 wait state */ + FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1; + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------*/ + /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ + + RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while((RCC->CR & RCC_CR_PLL2RDY) == 0) + { + } + + + /* PLL configuration: PLLCLK = PREDIV1 * 6 = 48 MHz */ + RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL6); +#else + /* PLL configuration: PLLCLK = HSE * 6 = 48 MHz */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL6); +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} + +#elif defined SYSCLK_FREQ_56MHz +/** + * @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo56(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 2 wait state */ + FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------*/ + /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ + + RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while((RCC->CR & RCC_CR_PLL2RDY) == 0) + { + } + + + /* PLL configuration: PLLCLK = PREDIV1 * 7 = 56 MHz */ + RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL7); +#else + /* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL7); + +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} + +#elif defined SYSCLK_FREQ_72MHz +/** + * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 + * and PCLK1 prescalers. + * @note This function should be used only after reset. + * @param None + * @retval None + */ +static void SetSysClockTo72(void) +{ + __IO uint32_t StartUpCounter = 0, HSEStatus = 0; + + /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ + /* Enable HSE */ + RCC->CR |= ((uint32_t)RCC_CR_HSEON); + + /* Wait till HSE is ready and if Time out is reached exit */ + do + { + HSEStatus = RCC->CR & RCC_CR_HSERDY; + StartUpCounter++; + } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); + + if ((RCC->CR & RCC_CR_HSERDY) != RESET) + { + HSEStatus = (uint32_t)0x01; + } + else + { + HSEStatus = (uint32_t)0x00; + } + + if (HSEStatus == (uint32_t)0x01) + { + /* Enable Prefetch Buffer */ + FLASH->ACR |= FLASH_ACR_PRFTBE; + + /* Flash 2 wait state */ + FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); + FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; + + + /* HCLK = SYSCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; + + /* PCLK2 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; + + /* PCLK1 = HCLK */ + RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; + +#ifdef STM32F10X_CL + /* Configure PLLs ------------------------------------------------------*/ + /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ + /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ + + RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | + RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); + RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | + RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); + + /* Enable PLL2 */ + RCC->CR |= RCC_CR_PLL2ON; + /* Wait till PLL2 is ready */ + while((RCC->CR & RCC_CR_PLL2RDY) == 0) + { + } + + + /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ + RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | + RCC_CFGR_PLLMULL9); +#else + /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | + RCC_CFGR_PLLMULL)); + RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); +#endif /* STM32F10X_CL */ + + /* Enable PLL */ + RCC->CR |= RCC_CR_PLLON; + + /* Wait till PLL is ready */ + while((RCC->CR & RCC_CR_PLLRDY) == 0) + { + } + + /* Select PLL as system clock source */ + RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); + RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; + + /* Wait till PLL is used as system clock source */ + while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) + { + } + } + else + { /* If HSE fails to start-up, the application will have wrong clock + configuration. User can add here some code to deal with this error */ + } +} +#endif + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ diff --git a/bacnet-stack/ports/stm32f10x/timer.c b/bacnet-stack/ports/stm32f10x/timer.c index ed8f4433..679529e0 100644 --- a/bacnet-stack/ports/stm32f10x/timer.c +++ b/bacnet-stack/ports/stm32f10x/timer.c @@ -1,431 +1,431 @@ -/************************************************************************** -* -* Copyright (C) 2009 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*********************************************************************/ -#include -#include -#include "timer.h" - -/* generic elapsed timer handling */ -/* interval not to exceed 49.7 days */ -/* interval of 1ms may be 0 to 1ms */ - -/************************************************************************* -* Description: Sets the start time for an elapsed timer -* Returns: the value of the start timer -* Notes: none -*************************************************************************/ -void timer_elapsed_start( - struct etimer *t) -{ - uint32_t now = timer_milliseconds(); - - if (t) { - t->start = now; - } -} - -/************************************************************************* -* Description: Gets the amount of elapsed time in milliseconds -* Returns: elapsed time in milliseconds -* Notes: none -*************************************************************************/ -uint32_t timer_elapsed_time( - struct etimer *t) -{ - uint32_t now = timer_milliseconds(); - uint32_t delta = 0; - - if (t) { - delta = now - t->start; - } - - return delta; -} - -/************************************************************************* -* Description: Sets the start time with an offset -* Returns: elapsed time in milliseconds -* Notes: none -*************************************************************************/ -void timer_elapsed_start_offset( - struct etimer *t, - uint32_t offset) -{ - uint32_t now = timer_milliseconds(); - - if (t) { - t->start = now + offset; - } -} - -/************************************************************************* -* Description: Tests to see if time has elapsed -* Returns: true if time has elapsed -* Notes: none -*************************************************************************/ -bool timer_elapsed_milliseconds( - struct etimer *t, - uint32_t milliseconds) -{ - return (timer_elapsed_time(t) >= milliseconds); -} - -/************************************************************************* -* Description: Tests to see if time has elapsed -* Returns: true if time has elapsed -* Notes: none -*************************************************************************/ -bool timer_elapsed_seconds( - struct etimer * t, - uint32_t seconds) -{ - uint32_t milliseconds = seconds; - - milliseconds *= 1000L; - - return timer_elapsed_milliseconds(t, milliseconds); -} - -/************************************************************************* -* Description: Tests to see if time has elapsed -* Returns: true if time has elapsed -* Notes: none -*************************************************************************/ -bool timer_elapsed_minutes( - struct etimer * t, - uint32_t minutes) -{ - uint32_t milliseconds = minutes; - - milliseconds *= 1000L; - milliseconds *= 60L; - - return timer_elapsed_milliseconds(t, milliseconds); -} - -/************************************************************************* -* Description: Tests to see if time has elapsed -* Returns: true if time has elapsed -* Notes: none -*************************************************************************/ -bool timer_elapsed_milliseconds_short( - struct etimer * t, - uint16_t value) -{ - uint32_t milliseconds; - - milliseconds = value; - - return (timer_elapsed_time(t) >= milliseconds); -} - -/************************************************************************* -* Description: Tests to see if time has elapsed -* Returns: true if time has elapsed -* Notes: none -*************************************************************************/ -bool timer_elapsed_seconds_short( - struct etimer * t, - uint16_t value) -{ - return timer_elapsed_seconds(t, value); -} - -/************************************************************************* -* Description: Tests to see if time has elapsed -* Returns: true if time has elapsed -* Notes: none -*************************************************************************/ -bool timer_elapsed_minutes_short( - struct etimer * t, - uint16_t value) -{ - return timer_elapsed_minutes(t, value); -} - -/************************************************************************* -* Description: Starts an interval timer -* Returns: nothing -* Notes: none -*************************************************************************/ -void timer_interval_start( - struct itimer *t, - uint32_t interval) -{ - if (t) { - t->start = timer_milliseconds(); - t->interval = interval; - } -} - -/************************************************************************* -* Description: Starts an interval timer -* Returns: nothing -* Notes: none -*************************************************************************/ -void timer_interval_start_seconds( - struct itimer *t, - uint32_t seconds) -{ - uint32_t interval = seconds; - - interval *= 1000L; - timer_interval_start(t, interval); -} - -/************************************************************************* -* Description: Starts an interval timer -* Returns: nothing -* Notes: none -*************************************************************************/ -void timer_interval_start_minutes( - struct itimer *t, - uint32_t minutes) -{ - uint32_t interval = minutes; - - interval *= 1000L; - interval *= 60L; - timer_interval_start(t, interval); -} - -/************************************************************************* -* Description: Determines the amount of time that has elapsed -* Returns: elapsed milliseconds -* Notes: none -*************************************************************************/ -uint32_t timer_interval_elapsed( - struct itimer *t) -{ - uint32_t now = timer_milliseconds(); - uint32_t delta = 0; - - if (t) { - delta = now - t->start; - } - - return delta; -} - -/************************************************************************* -* Description: Determines the amount of time that has elapsed -* Returns: elapsed milliseconds -* Notes: none -*************************************************************************/ -uint32_t timer_interval( - struct itimer * t) -{ - uint32_t interval = 0; - - if (t) { - interval = t->interval; - } - - return interval; -} - -/************************************************************************* -* Description: Tests to see if time has elapsed -* Returns: true if time has elapsed -* Notes: none -*************************************************************************/ -bool timer_interval_expired( - struct itimer * t) -{ - bool expired = false; - - if (t) { - if (t->interval) { - expired = timer_interval_elapsed(t) >= t->interval; - } - } - - return expired; -} - -/************************************************************************* -* Description: Sets the interval value to zero so it never expires -* Returns: nothing -* Notes: none -*************************************************************************/ -void timer_interval_no_expire( - struct itimer *t) -{ - if (t) { - t->interval = 0; - } -} - -/************************************************************************* -* Description: Adds another interval to the start time. Used for cyclic -* timers that won't lose ticks. -* Returns: nothing -* Notes: none -*************************************************************************/ -void timer_interval_reset( - struct itimer *t) -{ - if (t) { - t->start += t->interval; - } -} - -/************************************************************************* -* Description: Restarts the timer with the same interval -* Returns: nothing -* Notes: none -*************************************************************************/ -void timer_interval_restart( - struct itimer *t) -{ - if (t) { - t->start = timer_milliseconds(); - } -} - -/************************************************************************* -* Description: Return the elapsed time -* Returns: number of milliseconds elapsed -* Notes: only up to 255ms elapsed -**************************************************************************/ -uint8_t timer_milliseconds_delta( - uint8_t start) -{ - return (timer_milliseconds_byte() - start); -} - -/************************************************************************* -* Description: Mark the start of a delta timer -* Returns: mark timer starting tick -* Notes: only up to 255ms elapsed -**************************************************************************/ -uint8_t timer_milliseconds_mark( - void) -{ - return timer_milliseconds_byte(); -} - -#ifdef TEST -#include -#include - -#include "ctest.h" - -static uint32_t Milliseconds; - -uint32_t timer_milliseconds( - void) -{ - return Milliseconds; -} - -uint32_t timer_milliseconds_set( - uint32_t value) -{ - uint32_t old_value = Milliseconds; - - Milliseconds = value; - - return old_value; -} - -void testElapsedTimer( - Test * pTest) -{ - struct etimer t; - uint32_t test_time = 0; - - timer_milliseconds_set(test_time); - timer_elapsed_start(&t); - ct_test(pTest, timer_elapsed_time(&t) == test_time); - test_time = 0xffff; - timer_milliseconds_set(test_time); - ct_test(pTest, timer_elapsed_time(&t) == test_time); - test_time = 0xffffffff; - timer_milliseconds_set(test_time); - ct_test(pTest, timer_elapsed_time(&t) == test_time); -} - -void testIntervalTimer( - Test * pTest) -{ - struct itimer t; - uint32_t interval = 0; - uint32_t test_time = 0; - - timer_milliseconds_set(test_time); - timer_interval_start(&t, interval); - test_time = 0xffff; - timer_milliseconds_set(test_time); - ct_test(pTest, timer_interval(&t) == interval); - ct_test(pTest, timer_interval_elapsed(&t) == test_time); - test_time = 0xffffffff; - timer_milliseconds_set(test_time); - ct_test(pTest, timer_interval(&t) == interval); - ct_test(pTest, timer_interval_elapsed(&t) == test_time); - test_time = 0; - timer_milliseconds_set(test_time); - interval = 0xffff; - timer_interval_start(&t, interval); - ct_test(pTest, timer_interval(&t) == interval); - interval = 0xffffffff; - timer_interval_start(&t, interval); - ct_test(pTest, timer_interval(&t) == interval); - - interval = 0; - timer_interval_start_seconds(&t, interval); - ct_test(pTest, timer_interval(&t) == interval); - interval = 60L; - timer_interval_start_seconds(&t, interval); - interval *= 1000L; - ct_test(pTest, timer_interval(&t) == interval); - -} - - -#ifdef TEST_TIMER -int main( - void) -{ - Test *pTest; - bool rc; - - pTest = ct_create("Timer", NULL); - - /* individual tests */ - rc = ct_addTestFunction(pTest, testElapsedTimer); - assert(rc); - rc = ct_addTestFunction(pTest, testIntervalTimer); - assert(rc); - - - ct_setStream(pTest, stdout); - ct_run(pTest); - (void) ct_report(pTest); - - ct_destroy(pTest); - - return 0; -} -#endif -#endif +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#include +#include +#include "timer.h" + +/* generic elapsed timer handling */ +/* interval not to exceed 49.7 days */ +/* interval of 1ms may be 0 to 1ms */ + +/************************************************************************* +* Description: Sets the start time for an elapsed timer +* Returns: the value of the start timer +* Notes: none +*************************************************************************/ +void timer_elapsed_start( + struct etimer *t) +{ + uint32_t now = timer_milliseconds(); + + if (t) { + t->start = now; + } +} + +/************************************************************************* +* Description: Gets the amount of elapsed time in milliseconds +* Returns: elapsed time in milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_elapsed_time( + struct etimer *t) +{ + uint32_t now = timer_milliseconds(); + uint32_t delta = 0; + + if (t) { + delta = now - t->start; + } + + return delta; +} + +/************************************************************************* +* Description: Sets the start time with an offset +* Returns: elapsed time in milliseconds +* Notes: none +*************************************************************************/ +void timer_elapsed_start_offset( + struct etimer *t, + uint32_t offset) +{ + uint32_t now = timer_milliseconds(); + + if (t) { + t->start = now + offset; + } +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds( + struct etimer *t, + uint32_t milliseconds) +{ + return (timer_elapsed_time(t) >= milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds( + struct etimer * t, + uint32_t seconds) +{ + uint32_t milliseconds = seconds; + + milliseconds *= 1000L; + + return timer_elapsed_milliseconds(t, milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes( + struct etimer * t, + uint32_t minutes) +{ + uint32_t milliseconds = minutes; + + milliseconds *= 1000L; + milliseconds *= 60L; + + return timer_elapsed_milliseconds(t, milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds_short( + struct etimer * t, + uint16_t value) +{ + uint32_t milliseconds; + + milliseconds = value; + + return (timer_elapsed_time(t) >= milliseconds); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds_short( + struct etimer * t, + uint16_t value) +{ + return timer_elapsed_seconds(t, value); +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes_short( + struct etimer * t, + uint16_t value) +{ + return timer_elapsed_minutes(t, value); +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start( + struct itimer *t, + uint32_t interval) +{ + if (t) { + t->start = timer_milliseconds(); + t->interval = interval; + } +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start_seconds( + struct itimer *t, + uint32_t seconds) +{ + uint32_t interval = seconds; + + interval *= 1000L; + timer_interval_start(t, interval); +} + +/************************************************************************* +* Description: Starts an interval timer +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_start_minutes( + struct itimer *t, + uint32_t minutes) +{ + uint32_t interval = minutes; + + interval *= 1000L; + interval *= 60L; + timer_interval_start(t, interval); +} + +/************************************************************************* +* Description: Determines the amount of time that has elapsed +* Returns: elapsed milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_interval_elapsed( + struct itimer *t) +{ + uint32_t now = timer_milliseconds(); + uint32_t delta = 0; + + if (t) { + delta = now - t->start; + } + + return delta; +} + +/************************************************************************* +* Description: Determines the amount of time that has elapsed +* Returns: elapsed milliseconds +* Notes: none +*************************************************************************/ +uint32_t timer_interval( + struct itimer * t) +{ + uint32_t interval = 0; + + if (t) { + interval = t->interval; + } + + return interval; +} + +/************************************************************************* +* Description: Tests to see if time has elapsed +* Returns: true if time has elapsed +* Notes: none +*************************************************************************/ +bool timer_interval_expired( + struct itimer * t) +{ + bool expired = false; + + if (t) { + if (t->interval) { + expired = timer_interval_elapsed(t) >= t->interval; + } + } + + return expired; +} + +/************************************************************************* +* Description: Sets the interval value to zero so it never expires +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_no_expire( + struct itimer *t) +{ + if (t) { + t->interval = 0; + } +} + +/************************************************************************* +* Description: Adds another interval to the start time. Used for cyclic +* timers that won't lose ticks. +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_reset( + struct itimer *t) +{ + if (t) { + t->start += t->interval; + } +} + +/************************************************************************* +* Description: Restarts the timer with the same interval +* Returns: nothing +* Notes: none +*************************************************************************/ +void timer_interval_restart( + struct itimer *t) +{ + if (t) { + t->start = timer_milliseconds(); + } +} + +/************************************************************************* +* Description: Return the elapsed time +* Returns: number of milliseconds elapsed +* Notes: only up to 255ms elapsed +**************************************************************************/ +uint8_t timer_milliseconds_delta( + uint8_t start) +{ + return (timer_milliseconds_byte() - start); +} + +/************************************************************************* +* Description: Mark the start of a delta timer +* Returns: mark timer starting tick +* Notes: only up to 255ms elapsed +**************************************************************************/ +uint8_t timer_milliseconds_mark( + void) +{ + return timer_milliseconds_byte(); +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +static uint32_t Milliseconds; + +uint32_t timer_milliseconds( + void) +{ + return Milliseconds; +} + +uint32_t timer_milliseconds_set( + uint32_t value) +{ + uint32_t old_value = Milliseconds; + + Milliseconds = value; + + return old_value; +} + +void testElapsedTimer( + Test * pTest) +{ + struct etimer t; + uint32_t test_time = 0; + + timer_milliseconds_set(test_time); + timer_elapsed_start(&t); + ct_test(pTest, timer_elapsed_time(&t) == test_time); + test_time = 0xffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_elapsed_time(&t) == test_time); + test_time = 0xffffffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_elapsed_time(&t) == test_time); +} + +void testIntervalTimer( + Test * pTest) +{ + struct itimer t; + uint32_t interval = 0; + uint32_t test_time = 0; + + timer_milliseconds_set(test_time); + timer_interval_start(&t, interval); + test_time = 0xffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_interval(&t) == interval); + ct_test(pTest, timer_interval_elapsed(&t) == test_time); + test_time = 0xffffffff; + timer_milliseconds_set(test_time); + ct_test(pTest, timer_interval(&t) == interval); + ct_test(pTest, timer_interval_elapsed(&t) == test_time); + test_time = 0; + timer_milliseconds_set(test_time); + interval = 0xffff; + timer_interval_start(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + interval = 0xffffffff; + timer_interval_start(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + + interval = 0; + timer_interval_start_seconds(&t, interval); + ct_test(pTest, timer_interval(&t) == interval); + interval = 60L; + timer_interval_start_seconds(&t, interval); + interval *= 1000L; + ct_test(pTest, timer_interval(&t) == interval); + +} + + +#ifdef TEST_TIMER +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("Timer", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testElapsedTimer); + assert(rc); + rc = ct_addTestFunction(pTest, testIntervalTimer); + assert(rc); + + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif diff --git a/bacnet-stack/ports/stm32f10x/timer.h b/bacnet-stack/ports/stm32f10x/timer.h index 9f212f16..dac54d47 100644 --- a/bacnet-stack/ports/stm32f10x/timer.h +++ b/bacnet-stack/ports/stm32f10x/timer.h @@ -1,115 +1,115 @@ -/************************************************************************** -* -* Copyright (C) 2009 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*********************************************************************/ -#ifndef TIMER_H -#define TIMER_H - -#include -#include - -/* Timer Module */ - -/* elapsed timer structure */ -struct etimer { - uint32_t start; -}; -/* interval timer structure */ -struct itimer { - uint32_t start; - uint32_t interval; -}; - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - /* these 3 functions are created in the hardware specific module */ - void timer_init( - void); - uint32_t timer_milliseconds( - void); - uint8_t timer_milliseconds_byte( - void); - - /* these functions are in the generic timer.c module */ - - /* elapsed timer */ - void timer_elapsed_start( - struct etimer *t); - void timer_elapsed_start_offset( - struct etimer *t, - uint32_t offset); - uint32_t timer_elapsed_time( - struct etimer *t); - bool timer_elapsed_milliseconds( - struct etimer *t, - uint32_t value); - bool timer_elapsed_seconds( - struct etimer *t, - uint32_t value); - bool timer_elapsed_minutes( - struct etimer *t, - uint32_t value); - bool timer_elapsed_milliseconds_short( - struct etimer *t, - uint16_t value); - bool timer_elapsed_seconds_short( - struct etimer *t, - uint16_t value); - bool timer_elapsed_minutes_short( - struct etimer *t, - uint16_t value); - - /* interval timer */ - void timer_interval_start( - struct itimer *t, - uint32_t interval); - void timer_interval_start_seconds( - struct itimer *t, - uint32_t interval); - void timer_interval_start_minutes( - struct itimer *t, - uint32_t interval); - bool timer_interval_expired( - struct itimer *t); - uint32_t timer_interval( - struct itimer *t); - uint32_t timer_interval_elapsed( - struct itimer *t); - void timer_interval_no_expire( - struct itimer *t); - void timer_interval_reset( - struct itimer *t); - void timer_interval_restart( - struct itimer *t); - - /* special for 8-bit microcontrollers - limited to 255ms */ - uint8_t timer_milliseconds_delta( - uint8_t start); - uint8_t timer_milliseconds_mark( - void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/************************************************************************** +* +* Copyright (C) 2009 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef TIMER_H +#define TIMER_H + +#include +#include + +/* Timer Module */ + +/* elapsed timer structure */ +struct etimer { + uint32_t start; +}; +/* interval timer structure */ +struct itimer { + uint32_t start; + uint32_t interval; +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* these 3 functions are created in the hardware specific module */ + void timer_init( + void); + uint32_t timer_milliseconds( + void); + uint8_t timer_milliseconds_byte( + void); + + /* these functions are in the generic timer.c module */ + + /* elapsed timer */ + void timer_elapsed_start( + struct etimer *t); + void timer_elapsed_start_offset( + struct etimer *t, + uint32_t offset); + uint32_t timer_elapsed_time( + struct etimer *t); + bool timer_elapsed_milliseconds( + struct etimer *t, + uint32_t value); + bool timer_elapsed_seconds( + struct etimer *t, + uint32_t value); + bool timer_elapsed_minutes( + struct etimer *t, + uint32_t value); + bool timer_elapsed_milliseconds_short( + struct etimer *t, + uint16_t value); + bool timer_elapsed_seconds_short( + struct etimer *t, + uint16_t value); + bool timer_elapsed_minutes_short( + struct etimer *t, + uint16_t value); + + /* interval timer */ + void timer_interval_start( + struct itimer *t, + uint32_t interval); + void timer_interval_start_seconds( + struct itimer *t, + uint32_t interval); + void timer_interval_start_minutes( + struct itimer *t, + uint32_t interval); + bool timer_interval_expired( + struct itimer *t); + uint32_t timer_interval( + struct itimer *t); + uint32_t timer_interval_elapsed( + struct itimer *t); + void timer_interval_no_expire( + struct itimer *t); + void timer_interval_reset( + struct itimer *t); + void timer_interval_restart( + struct itimer *t); + + /* special for 8-bit microcontrollers - limited to 255ms */ + uint8_t timer_milliseconds_delta( + uint8_t start); + uint8_t timer_milliseconds_mark( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/stm32f10x/timer_sys.c b/bacnet-stack/ports/stm32f10x/timer_sys.c index a0605d7e..26c8b4d3 100644 --- a/bacnet-stack/ports/stm32f10x/timer_sys.c +++ b/bacnet-stack/ports/stm32f10x/timer_sys.c @@ -1,121 +1,121 @@ -/************************************************************************** -* -* Copyright (C) 2011 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. -* -* Module Description: -* Generate a periodic timer tick for use by generic timers in the code. -* -*************************************************************************/ -#include -#include -#include "hardware.h" -#include "timer.h" -#include "debug.h" - -/* counter for the various timers */ -static volatile uint32_t Millisecond_Counter; - -/************************************************************************* -* Description: Activate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -static void timer_debug_on( - void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_13, Bit_SET); -} - -/************************************************************************* -* Description: Activate the LED -* Returns: nothing -* Notes: none -**************************************************************************/ -static void timer_debug_off( - void) -{ - GPIO_WriteBit(GPIOB, GPIO_Pin_13, Bit_RESET); -} - -/************************************************************************* -* Description: Toggle the state of the setup LED -* Returns: none -* Notes: none -*************************************************************************/ -void timer_debug_toggle(void) -{ - static bool state = false; - - if (state) { - timer_debug_off(); - state = false; - } else { - timer_debug_on(); - state = true; - } -} - -/************************************************************************* -* Description: Interrupt Service Routine -* Returns: nothing -* Notes: reserved name for ISR handlers -*************************************************************************/ -void SysTick_Handler(void) -{ - /* increment the tick count */ - Millisecond_Counter++; - timer_debug_toggle(); -} - -/************************************************************************* -* Description: returns the current millisecond count -* Returns: none -* Notes: none -*************************************************************************/ -uint32_t timer_milliseconds(void) -{ - return Millisecond_Counter; -} - -/************************************************************************* -* Description: Timer setup for 1 millisecond timer -* Returns: none -* Notes: peripheral frequency defined in hardware.h -*************************************************************************/ -void timer_init(void) -{ - GPIO_InitTypeDef GPIO_InitStructure; - - GPIO_StructInit(&GPIO_InitStructure); - /* Configure the Receive LED */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; - GPIO_Init(GPIOB, &GPIO_InitStructure); - - /* Setup SysTick Timer for 1ms interrupts */ - if (SysTick_Config(SystemCoreClock / 1000)) { - /* Capture error */ - while (1); - } - -} +/************************************************************************** +* +* Copyright (C) 2011 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. +* +* Module Description: +* Generate a periodic timer tick for use by generic timers in the code. +* +*************************************************************************/ +#include +#include +#include "hardware.h" +#include "timer.h" +#include "debug.h" + +/* counter for the various timers */ +static volatile uint32_t Millisecond_Counter; + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +static void timer_debug_on( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_13, Bit_SET); +} + +/************************************************************************* +* Description: Activate the LED +* Returns: nothing +* Notes: none +**************************************************************************/ +static void timer_debug_off( + void) +{ + GPIO_WriteBit(GPIOB, GPIO_Pin_13, Bit_RESET); +} + +/************************************************************************* +* Description: Toggle the state of the setup LED +* Returns: none +* Notes: none +*************************************************************************/ +void timer_debug_toggle(void) +{ + static bool state = false; + + if (state) { + timer_debug_off(); + state = false; + } else { + timer_debug_on(); + state = true; + } +} + +/************************************************************************* +* Description: Interrupt Service Routine +* Returns: nothing +* Notes: reserved name for ISR handlers +*************************************************************************/ +void SysTick_Handler(void) +{ + /* increment the tick count */ + Millisecond_Counter++; + timer_debug_toggle(); +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_milliseconds(void) +{ + return Millisecond_Counter; +} + +/************************************************************************* +* Description: Timer setup for 1 millisecond timer +* Returns: none +* Notes: peripheral frequency defined in hardware.h +*************************************************************************/ +void timer_init(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_StructInit(&GPIO_InitStructure); + /* Configure the Receive LED */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + /* Setup SysTick Timer for 1ms interrupts */ + if (SysTick_Config(SystemCoreClock / 1000)) { + /* Capture error */ + while (1); + } + +} diff --git a/bacnet-stack/src/wpm.c b/bacnet-stack/src/wpm.c index b2a35be6..43f36df4 100644 --- a/bacnet-stack/src/wpm.c +++ b/bacnet-stack/src/wpm.c @@ -1,180 +1,180 @@ -/************************************************************************** -* -* Copyright (C) 2011 Krzysztof Malorny -* -* 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 "bacapp.h" -#include "bacenum.h" -#include "bacdcode.h" -#include "bacdef.h" -#include "wp.h" -#include "wpm.h" - -/** @file wpm.c Encode/Decode BACnet Write Property Multiple APDUs */ - -/* decode service */ -int wpm_decode_object_id(uint8_t * apdu, uint16_t apdu_len, - BACNET_WRITE_PROPERTY_DATA * data) -{ - uint8_t tag_number = 0; - uint32_t len_value = 0; - uint32_t object_instance = 0; - uint16_t object_type = 0; - uint16_t len = 0; - - if((apdu )&& (apdu_len)) - { +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* 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 "bacapp.h" +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "wp.h" +#include "wpm.h" + +/** @file wpm.c Encode/Decode BACnet Write Property Multiple APDUs */ + +/* decode service */ +int wpm_decode_object_id(uint8_t * apdu, uint16_t apdu_len, + BACNET_WRITE_PROPERTY_DATA * data) +{ + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t object_instance = 0; + uint16_t object_type = 0; + uint16_t len = 0; + + if((apdu )&& (apdu_len)) + { /* Context tag 0 - Object ID */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if(tag_number == 0) - { - len += decode_object_id(&apdu[len], &object_type, &object_instance); - data->object_type = object_type; - data->object_instance = object_instance; - } - else - return -1; - } - else - return -1; - - return len; -} - - -int wpm_decode_object_property(uint8_t * apdu, - uint16_t apdu_len, - BACNET_WRITE_PROPERTY_DATA * wp_data) -{ - uint8_t tag_number = 0; - uint32_t len_value = 0; - uint32_t ulVal = 0; - int len = 0, i = 0; - - - if((apdu) && (apdu_len) && (wp_data)) - { - wp_data->array_index = BACNET_ARRAY_ALL; - wp_data->priority = BACNET_NO_PRIORITY; - wp_data->application_data_len = 0; - + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if(tag_number == 0) + { + len += decode_object_id(&apdu[len], &object_type, &object_instance); + data->object_type = object_type; + data->object_instance = object_instance; + } + else + return -1; + } + else + return -1; + + return len; +} + + +int wpm_decode_object_property(uint8_t * apdu, + uint16_t apdu_len, + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t ulVal = 0; + int len = 0, i = 0; + + + if((apdu) && (apdu_len) && (wp_data)) + { + wp_data->array_index = BACNET_ARRAY_ALL; + wp_data->priority = BACNET_NO_PRIORITY; + wp_data->application_data_len = 0; + /* tag 0 - Property Identifier */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if(tag_number == 0) - { - len += decode_enumerated(&apdu[len], len_value, &ulVal); - wp_data->object_property = ulVal; - } - else - return -1; - + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if(tag_number == 0) + { + len += decode_enumerated(&apdu[len], len_value, &ulVal); + wp_data->object_property = ulVal; + } + else + return -1; + /* tag 1 - Property Array Index - optional */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if(tag_number ==1) - { - len += decode_unsigned(&apdu[len],len_value, &ulVal); - wp_data->array_index = ulVal; - - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - } + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if(tag_number ==1) + { + len += decode_unsigned(&apdu[len],len_value, &ulVal); + wp_data->array_index = ulVal; + + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + } /* tag 2 - Property Value */ - if((tag_number == 2) && (decode_is_opening_tag(&apdu[len-1]))) - { - len--; - wp_data->application_data_len = bacapp_data_len(&apdu[len], - apdu_len - len, wp_data->object_property); - len++; - + if((tag_number == 2) && (decode_is_opening_tag(&apdu[len-1]))) + { + len--; + wp_data->application_data_len = bacapp_data_len(&apdu[len], + apdu_len - len, wp_data->object_property); + len++; + /* copy application data */ - for(i = 0; i < wp_data->application_data_len; i++) - wp_data->application_data[i] = apdu[len+i]; - len += wp_data->application_data_len; - - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + for(i = 0; i < wp_data->application_data_len; i++) + wp_data->application_data[i] = apdu[len+i]; + len += wp_data->application_data_len; + + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); /* closing tag 2 */ - if((tag_number != 2) &&(decode_is_closing_tag(&apdu[len-1]))) - return -1; - } - else - return -1; - + if((tag_number != 2) &&(decode_is_closing_tag(&apdu[len-1]))) + return -1; + } + else + return -1; + /* tag 3 - Priority - optional */ - len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); - if(tag_number == 3) - { - uint32_t priority = BACNET_NO_PRIORITY; - - len += decode_unsigned(&apdu[len], len_value, &priority); - wp_data->priority = priority; - } - else - len--; - } - else - return -1; - - return len; -} - -int wpm_ack_encode_apdu_init(uint8_t * apdu, uint8_t invoke_id) -{ - int len = 0; - - if (apdu) { - apdu[len++] = PDU_TYPE_SIMPLE_ACK; - apdu[len++] = invoke_id; - apdu[len++] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; - } - - return len; -} - -int wpm_error_ack_encode_apdu(uint8_t * apdu, uint8_t invoke_id, - BACNET_WRITE_PROPERTY_DATA * wp_data) -{ - int len = 0; - - if (apdu) - { - apdu[len++] = PDU_TYPE_ERROR; - apdu[len++] = invoke_id; - apdu[len++] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; - - len += encode_opening_tag(&apdu[len], 0); - len += encode_application_enumerated(&apdu[len], wp_data->error_class); - len += encode_application_enumerated(&apdu[len], wp_data->error_code); - len += encode_closing_tag(&apdu[len], 0); - - len += encode_opening_tag(&apdu[len], 1); - len += encode_context_object_id(&apdu[len], 0, - wp_data->object_type, wp_data->object_instance); - len += encode_context_enumerated(&apdu[len], 1, - wp_data->object_property); - - if(wp_data->array_index != BACNET_ARRAY_ALL) - len += encode_context_unsigned(&apdu[len], 2, wp_data->array_index); - len += encode_closing_tag(&apdu[len], 1); - } - return len; -} - + len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value); + if(tag_number == 3) + { + uint32_t priority = BACNET_NO_PRIORITY; + + len += decode_unsigned(&apdu[len], len_value, &priority); + wp_data->priority = priority; + } + else + len--; + } + else + return -1; + + return len; +} + +int wpm_ack_encode_apdu_init(uint8_t * apdu, uint8_t invoke_id) +{ + int len = 0; + + if (apdu) { + apdu[len++] = PDU_TYPE_SIMPLE_ACK; + apdu[len++] = invoke_id; + apdu[len++] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; + } + + return len; +} + +int wpm_error_ack_encode_apdu(uint8_t * apdu, uint8_t invoke_id, + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + int len = 0; + + if (apdu) + { + apdu[len++] = PDU_TYPE_ERROR; + apdu[len++] = invoke_id; + apdu[len++] = SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE; + + len += encode_opening_tag(&apdu[len], 0); + len += encode_application_enumerated(&apdu[len], wp_data->error_class); + len += encode_application_enumerated(&apdu[len], wp_data->error_code); + len += encode_closing_tag(&apdu[len], 0); + + len += encode_opening_tag(&apdu[len], 1); + len += encode_context_object_id(&apdu[len], 0, + wp_data->object_type, wp_data->object_instance); + len += encode_context_enumerated(&apdu[len], 1, + wp_data->object_property); + + if(wp_data->array_index != BACNET_ARRAY_ALL) + len += encode_context_unsigned(&apdu[len], 2, wp_data->array_index); + len += encode_closing_tag(&apdu[len], 1); + } + return len; +} +