diff --git a/bacnet-stack/ports/stm32f10x/automac.c b/bacnet-stack/ports/stm32f10x/automac.c new file mode 100644 index 00000000..71abe645 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/automac.c @@ -0,0 +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 + diff --git a/bacnet-stack/ports/stm32f10x/automac.h b/bacnet-stack/ports/stm32f10x/automac.h new file mode 100644 index 00000000..bf3b9294 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/automac.h @@ -0,0 +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 diff --git a/bacnet-stack/ports/stm32f10x/bacnet.c b/bacnet-stack/ports/stm32f10x/bacnet.c new file mode 100644 index 00000000..f62871b3 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/bacnet.c @@ -0,0 +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(103); + dlmstp_set_max_master(127); + /* initialize datalink layer */ + dlmstp_init(NULL); + /* initialize objects */ + Device_Init(); + + /* 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.ewp b/bacnet-stack/ports/stm32f10x/bacnet.ewp new file mode 100644 index 00000000..6b695e34 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/bacnet.ewp @@ -0,0 +1,2003 @@ + + + + 2 + + Debug + + ARM + + 1 + + General + 3 + + 20 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 26 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 8 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 11 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + + Release + + ARM + + 0 + + General + 3 + + 20 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 26 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 8 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 0 + + + + + + + + + CUSTOM + 3 + + + + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 11 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + + BACnet + + $PROJ_DIR$\..\..\src\abort.c + + + $PROJ_DIR$\..\..\src\apdu.c + + + $PROJ_DIR$\..\..\src\bacaddr.c + + + $PROJ_DIR$\..\..\src\bacapp.c + + + $PROJ_DIR$\..\..\src\bacdcode.c + + + $PROJ_DIR$\..\..\src\bacerror.c + + + $PROJ_DIR$\..\..\src\bacint.c + + + $PROJ_DIR$\..\..\src\bacreal.c + + + $PROJ_DIR$\..\..\src\bacstr.c + + + $PROJ_DIR$\..\..\src\crc.c + + + $PROJ_DIR$\..\..\src\dcc.c + + + $PROJ_DIR$\..\..\src\fifo.c + + + $PROJ_DIR$\..\..\src\iam.c + + + $PROJ_DIR$\..\..\src\ihave.c + + + $PROJ_DIR$\..\..\src\memcopy.c + + + $PROJ_DIR$\..\..\src\npdu.c + + + $PROJ_DIR$\..\..\src\rd.c + + + $PROJ_DIR$\..\..\src\reject.c + + + $PROJ_DIR$\..\..\src\ringbuf.c + + + $PROJ_DIR$\..\..\src\rp.c + + + $PROJ_DIR$\..\..\src\rpm.c + + + $PROJ_DIR$\..\..\src\whohas.c + + + $PROJ_DIR$\..\..\src\whois.c + + + $PROJ_DIR$\..\..\src\wp.c + + + + BACnet-Handlers + + $PROJ_DIR$\..\..\demo\handler\h_dcc.c + + + $PROJ_DIR$\..\..\demo\handler\h_npdu.c + + + $PROJ_DIR$\..\..\demo\handler\h_rd.c + + + $PROJ_DIR$\..\..\demo\handler\h_rp.c + + + $PROJ_DIR$\..\..\demo\handler\h_rpm.c + + + $PROJ_DIR$\..\..\demo\handler\h_whohas.c + + + $PROJ_DIR$\..\..\demo\handler\h_whois.c + + + $PROJ_DIR$\..\..\demo\handler\h_wp.c + + + $PROJ_DIR$\..\..\demo\handler\noserv.c + + + $PROJ_DIR$\..\..\demo\handler\s_iam.c + + + $PROJ_DIR$\..\..\demo\handler\s_ihave.c + + + $PROJ_DIR$\..\..\demo\handler\txbuf.c + + + + Cortex-M3 + + $PROJ_DIR$\CMSIS\CM3\CoreSupport\core_cm3.c + + + $PROJ_DIR$\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\iar\startup_stm32f10x_xl.s + + + + STM32-Lib + + $PROJ_DIR$\drivers\src\misc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_adc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_bkp.c + + + $PROJ_DIR$\drivers\src\stm32f10x_can.c + + + $PROJ_DIR$\drivers\src\stm32f10x_cec.c + + + $PROJ_DIR$\drivers\src\stm32f10x_crc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_dac.c + + + $PROJ_DIR$\drivers\src\stm32f10x_dbgmcu.c + + + $PROJ_DIR$\drivers\src\stm32f10x_dma.c + + + $PROJ_DIR$\drivers\src\stm32f10x_exti.c + + + $PROJ_DIR$\drivers\src\stm32f10x_flash.c + + + $PROJ_DIR$\drivers\src\stm32f10x_fsmc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_gpio.c + + + $PROJ_DIR$\drivers\src\stm32f10x_i2c.c + + + $PROJ_DIR$\drivers\src\stm32f10x_iwdg.c + + + $PROJ_DIR$\drivers\src\stm32f10x_pwr.c + + + $PROJ_DIR$\drivers\src\stm32f10x_rcc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_rtc.c + + + $PROJ_DIR$\drivers\src\stm32f10x_sdio.c + + + $PROJ_DIR$\drivers\src\stm32f10x_spi.c + + + $PROJ_DIR$\drivers\src\stm32f10x_tim.c + + + $PROJ_DIR$\drivers\src\stm32f10x_usart.c + + + $PROJ_DIR$\drivers\src\stm32f10x_wwdg.c + + + + $PROJ_DIR$\automac.c + + + $PROJ_DIR$\bacnet.c + + + $PROJ_DIR$\bo.c + + + $PROJ_DIR$\device.c + + + $PROJ_DIR$\dlmstp.c + + + $PROJ_DIR$\led.c + + + $PROJ_DIR$\main.c + + + $PROJ_DIR$\rs485.c + + + $PROJ_DIR$\stm32f10x_it.c + + + $PROJ_DIR$\system_stm32f10x.c + + + $PROJ_DIR$\timer.c + + + $PROJ_DIR$\timer_sys.c + + + + diff --git a/bacnet-stack/ports/stm32f10x/bacnet.eww b/bacnet-stack/ports/stm32f10x/bacnet.eww new file mode 100644 index 00000000..7a4cd894 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/bacnet.eww @@ -0,0 +1,10 @@ + + + + + $WS_DIR$\bacnet.ewp + + + + + diff --git a/bacnet-stack/ports/stm32f10x/bacnet.h b/bacnet-stack/ports/stm32f10x/bacnet.h new file mode 100644 index 00000000..5bd3f191 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/bacnet.h @@ -0,0 +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 diff --git a/bacnet-stack/ports/stm32f10x/bo.c b/bacnet-stack/ports/stm32f10x/bo.c new file mode 100644 index 00000000..e4431569 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/bo.c @@ -0,0 +1,498 @@ +/************************************************************************** +* +* 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 */ +char *Binary_Output_Name( + uint32_t object_instance) +{ + static char text_string[32]; /* okay for single thread */ + + if (object_instance < MAX_BINARY_OUTPUTS) { + sprintf(text_string, "BO-%lu", object_instance); + return text_string; + } + + return NULL; +} + +/* 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 new file mode 100644 index 00000000..83416674 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/device.c @@ -0,0 +1,853 @@ +/************************************************************************** +* +* 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_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_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; + char *pString = ""; + 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: + /* 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: + if (pObject->Object_Name) { + pString = pObject->Object_Name(rpdata->object_instance); + } + characterstring_init_ansi(&char_string, pString); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + 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; +} + +char *Device_Name( + uint32_t object_instance) +{ + return characterstring_value(&My_Object_Name); +} + +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( + void) +{ + struct my_object_functions *pObject = NULL; + + 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( + const char *object_name, + 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; + char *name = 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) { + name = Device_Valid_Object_Id(type, instance); + if (strcmp(name, object_name) == 0) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +/* returns the name or NULL if not found */ +char *Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + char *name = NULL; /* return value */ + struct my_object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) object_type); + if ((pObject) && (pObject->Object_Name)) { + name = pObject->Object_Name(object_instance); + } + + return name; +} + +/* 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 new file mode 100644 index 00000000..c61a2850 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/dlmstp.c @@ -0,0 +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; +} + diff --git a/bacnet-stack/ports/stm32f10x/hardware.h b/bacnet-stack/ports/stm32f10x/hardware.h new file mode 100644 index 00000000..79b1e684 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/hardware.h @@ -0,0 +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 diff --git a/bacnet-stack/ports/stm32f10x/led.c b/bacnet-stack/ports/stm32f10x/led.c new file mode 100644 index 00000000..60bfa31d --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/led.c @@ -0,0 +1,224 @@ +/************************************************************************** +* +* 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; + +/************************************************************************* +* 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 setup 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 setup 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: Initialize the LED hardware +* Returns: none +* Notes: none +*************************************************************************/ +void led_init( + void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_StructInit(&GPIO_InitStructure); + /* Configure the Receive LED */ + 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 */ + 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); + + led_tx_on_interval(500); + led_rx_on_interval(500); +} diff --git a/bacnet-stack/ports/stm32f10x/led.h b/bacnet-stack/ports/stm32f10x/led.h new file mode 100644 index 00000000..fe4d931f --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/led.h @@ -0,0 +1,64 @@ +/************************************************************************** +* +* 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_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 new file mode 100644 index 00000000..8d650be0 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/main.c @@ -0,0 +1,76 @@ +/************************************************************************ +* +* 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 + +int main( + void) +{ + /*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_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); + timer_init(); + led_init(); + rs485_init(); + bacnet_init(); + for (;;) { + led_task(); + bacnet_task(); + } +} diff --git a/bacnet-stack/ports/stm32f10x/readme.txt b/bacnet-stack/ports/stm32f10x/readme.txt new file mode 100644 index 00000000..10b8a2d9 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/readme.txt @@ -0,0 +1,10 @@ +This port was done with the STM32 ARM Cortex-M3 STM32F103RGT6 on +a STM32 Discovery Kit using the STM32 CMSIS library and drivers +and IAR EWARM 6.10 compiler. + +The hardware interface only uses the USART and a peripheral pin +(RTS) for the MS/TP RS-485 interface, and the System Clock for +the millisecond timer. + +It was created for the STM32 Design Challenge on March 20, 2011, +by Steve Karg. \ No newline at end of file diff --git a/bacnet-stack/ports/stm32f10x/rs485.c b/bacnet-stack/ports/stm32f10x/rs485.c new file mode 100644 index 00000000..025466e2 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/rs485.c @@ -0,0 +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); +} diff --git a/bacnet-stack/ports/stm32f10x/rs485.h b/bacnet-stack/ports/stm32f10x/rs485.h new file mode 100644 index 00000000..2fb43fab --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/rs485.h @@ -0,0 +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 diff --git a/bacnet-stack/ports/stm32f10x/stm32f10x_conf.h b/bacnet-stack/ports/stm32f10x/stm32f10x_conf.h new file mode 100644 index 00000000..d4c9e812 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/stm32f10x_conf.h @@ -0,0 +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****/ diff --git a/bacnet-stack/ports/stm32f10x/stm32f10x_flash.icf b/bacnet-stack/ports/stm32f10x/stm32f10x_flash.icf new file mode 100644 index 00000000..e14b28a7 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/stm32f10x_flash.icf @@ -0,0 +1,31 @@ +/*###ICF### Section handled by ICF editor, don't touch! ****/ +/*-Editor annotation file-*/ +/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */ +/*-Specials-*/ +define symbol __ICFEDIT_intvec_start__ = 0x08000000; +/*-Memory Regions-*/ +define symbol __ICFEDIT_region_ROM_start__ = 0x08000000 ; +define symbol __ICFEDIT_region_ROM_end__ = 0x0807FFFF; +define symbol __ICFEDIT_region_RAM_start__ = 0x20000000; +define symbol __ICFEDIT_region_RAM_end__ = 0x2000FFFF; +/*-Sizes-*/ +define symbol __ICFEDIT_size_cstack__ = 0x800; +define symbol __ICFEDIT_size_heap__ = 0x200; +/**** End of ICF editor section. ###ICF###*/ + + +define memory mem with size = 4G; +define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__]; +define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__]; + +define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { }; +define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { }; + +initialize by copy { readwrite }; +do not initialize { section .noinit }; + +place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; + +place in ROM_region { readonly }; +place in RAM_region { readwrite, + block CSTACK, block HEAP }; \ No newline at end of file diff --git a/bacnet-stack/ports/stm32f10x/stm32f10x_it.c b/bacnet-stack/ports/stm32f10x/stm32f10x_it.c new file mode 100644 index 00000000..2368252c --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/stm32f10x_it.c @@ -0,0 +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****/ diff --git a/bacnet-stack/ports/stm32f10x/stm32f10x_it.h b/bacnet-stack/ports/stm32f10x/stm32f10x_it.h new file mode 100644 index 00000000..50b8dca3 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/stm32f10x_it.h @@ -0,0 +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****/ diff --git a/bacnet-stack/ports/stm32f10x/system_stm32f10x.c b/bacnet-stack/ports/stm32f10x/system_stm32f10x.c new file mode 100644 index 00000000..a1526908 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/system_stm32f10x.c @@ -0,0 +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****/ diff --git a/bacnet-stack/ports/stm32f10x/timer.c b/bacnet-stack/ports/stm32f10x/timer.c new file mode 100644 index 00000000..ed8f4433 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/timer.c @@ -0,0 +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 diff --git a/bacnet-stack/ports/stm32f10x/timer.h b/bacnet-stack/ports/stm32f10x/timer.h new file mode 100644 index 00000000..9f212f16 --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/timer.h @@ -0,0 +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 diff --git a/bacnet-stack/ports/stm32f10x/timer_sys.c b/bacnet-stack/ports/stm32f10x/timer_sys.c new file mode 100644 index 00000000..a0605d7e --- /dev/null +++ b/bacnet-stack/ports/stm32f10x/timer_sys.c @@ -0,0 +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); + } + +}