Added an MS/TP port for the stm32f10x that I did for the STM32 Design Challenge. I didn't add the CMSIS or stm32f10x peripheral driver library, which are available for download from st.com website. I used the IAR EWARM 6.10 compiler.

This commit is contained in:
skarg
2011-03-22 20:56:27 +00:00
parent e095715cec
commit b0acccb580
24 changed files with 8480 additions and 0 deletions
+426
View File
@@ -0,0 +1,426 @@
/**************************************************************************
*
* Copyright (C) 2010 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#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 <assert.h>
#include <string.h>
#include <stdio.h>
#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
+59
View File
@@ -0,0 +1,59 @@
/**************************************************************************
*
* Copyright (C) 2010 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef AUTOMAC_H
#define AUTOMAC_H
#include <stdbool.h>
#include <stdint.h>
/* 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
+133
View File
@@ -0,0 +1,133 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stdint.h>
#include <stdbool.h>
/* 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);
}
}
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<workspace>
<project>
<path>$WS_DIR$\bacnet.ewp</path>
</project>
<batchBuild/>
</workspace>
+41
View File
@@ -0,0 +1,41 @@
/**************************************************************************
*
* Copyright (C) 2010 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef BACNET_H
#define BACNET_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void bacnet_init(
void);
void bacnet_task(
void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+498
View File
@@ -0,0 +1,498 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Binary Output Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacdef.h"
#include "bacdcode.h"
#include "bacenum.h"
#include "config.h"
#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;
}
+853
View File
@@ -0,0 +1,853 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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;
}
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef HARDWARE_H
#define HARDWARE_H
#include "stm32f10x_conf.h"
#include "stm32f10x_it.h"
#define MAX_BINARY_OUTPUTS 2
#endif
+224
View File
@@ -0,0 +1,224 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#include <stdint.h>
#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);
}
+64
View File
@@ -0,0 +1,64 @@
/**************************************************************************
*
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef LED_H
#define LED_H
#include <stdint.h>
#include <stdbool.h>
#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
+76
View File
@@ -0,0 +1,76 @@
/************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*************************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include "hardware.h"
#include "timer.h"
#include "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();
}
}
+10
View File
@@ -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.
+340
View File
@@ -0,0 +1,340 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Module Description:
* Handle the configuration and operation of the RS485 bus.
**************************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#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);
}
+66
View File
@@ -0,0 +1,66 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Module Description:
* Handle the configuration and operation of the RS485 bus.
**************************************************************************/
#ifndef RS485_H
#define RS485_H
#include <stdint.h>
#include <stdbool.h>
#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
@@ -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.
*
* <h2><center>&copy; COPYRIGHT 2010 STMicroelectronics</center></h2>
*/
/* 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****/
@@ -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 };
+133
View File
@@ -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.
*
* <h2><center>&copy; COPYRIGHT 2010 STMicroelectronics</center></h2>
*/
/* 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****/
@@ -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.
*
* <h2><center>&copy; COPYRIGHT 2010 STMicroelectronics</center></h2>
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F10x_IT_H
#define __STM32F10x_IT_H
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
#endif /* __STM32F10x_IT_H */
/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/
File diff suppressed because it is too large Load Diff
+431
View File
@@ -0,0 +1,431 @@
/**************************************************************************
*
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include "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 <assert.h>
#include <string.h>
#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
+115
View File
@@ -0,0 +1,115 @@
/**************************************************************************
*
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef TIMER_H
#define TIMER_H
#include <stdbool.h>
#include <stdint.h>
/* 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
+121
View File
@@ -0,0 +1,121 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Module Description:
* Generate a periodic timer tick for use by generic timers in the code.
*
*************************************************************************/
#include <stdbool.h>
#include <stdint.h>
#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);
}
}