Added port for Atmel AT91SAM7S-EK ARM7 board using the YAGARTO GNU ARM tool suite. Still testing.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
# gdb setup for J-Link - start JLinkGDBServer first
|
||||
target remote localhost:2331
|
||||
monitor reset
|
||||
monitor speed 5
|
||||
monitor speed auto
|
||||
monitor long 0xffffff60 0x00320100
|
||||
monitor long 0xfffffd44 0xa0008000
|
||||
monitor long 0xfffffc20 0xa0000601
|
||||
#monitor sleep 100
|
||||
monitor long 0xfffffc2c 0x00480a0e
|
||||
#monitor sleep 200
|
||||
monitor long 0xfffffc30 0x7
|
||||
#monitor sleep 100
|
||||
monitor long 0xfffffd08 0xa5000401
|
||||
#monitor sleep 100
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/* ****************************************************************************************************** */
|
||||
/* demo_at91sam7_blink_flash.cmd LINKER SCRIPT */
|
||||
/* */
|
||||
/* */
|
||||
/* The Linker Script defines how the code and data emitted by the GNU C compiler and assembler are */
|
||||
/* to be loaded into memory (code goes into FLASH, variables go into RAM). */
|
||||
/* */
|
||||
/* Any symbols defined in the Linker Script are automatically global and available to the rest of the */
|
||||
/* program. */
|
||||
/* */
|
||||
/* To force the linker to use this LINKER SCRIPT, just add the -T demo_at91sam7_blink_flash.cmd */
|
||||
/* directive to the linker flags in the makefile. For example, */
|
||||
/* */
|
||||
/* LFLAGS = -Map main.map -nostartfiles -T demo_at91sam7_blink_flash.cmd */
|
||||
/* */
|
||||
/* */
|
||||
/* The order that the object files are listed in the makefile determines what .text section is */
|
||||
/* placed first. */
|
||||
/* */
|
||||
/* For example: $(LD) $(LFLAGS) -o main.out crt.o main.o lowlevelinit.o */
|
||||
/* */
|
||||
/* crt.o is first in the list of objects, so it will be placed at address 0x00000000 */
|
||||
/* */
|
||||
/* */
|
||||
/* The top of the stack (_stack_end) is (last_byte_of_ram +1) - 4 */
|
||||
/* */
|
||||
/* Therefore: _stack_end = (0x00020FFFF + 1) - 4 = 0x00210000 - 4 = 0x0020FFFC */
|
||||
/* Therefore: _stack_end = (0x000203FFF + 1) - 4 = 0x00204000 - 4 = 0x00203FFC */
|
||||
/* */
|
||||
/* Note that this symbol (_stack_end) is automatically GLOBAL and will be used by the crt.s */
|
||||
/* startup assembler routine to specify all stacks for the various ARM modes */
|
||||
/* */
|
||||
/* MEMORY MAP */
|
||||
/* | | */
|
||||
/* .-------->|---------------------------------|0x00210000 */
|
||||
/* . | |0x0020FFFC <---------- _stack_end */
|
||||
/* . | UDF Stack 16 bytes | */
|
||||
/* . | | */
|
||||
/* . |---------------------------------|0x0020FFEC */
|
||||
/* . | | */
|
||||
/* . | ABT Stack 16 bytes | */
|
||||
/* . | | */
|
||||
/* . |---------------------------------|0x0020FFDC */
|
||||
/* . | | */
|
||||
/* . | | */
|
||||
/* . | FIQ Stack 128 bytes | */
|
||||
/* . | | */
|
||||
/* . | | */
|
||||
/* RAM |---------------------------------|0x0020FF5C */
|
||||
/* . | | */
|
||||
/* . | | */
|
||||
/* . | IRQ Stack 128 bytes | */
|
||||
/* . | | */
|
||||
/* . | | */
|
||||
/* . |---------------------------------|0x0020FEDC */
|
||||
/* . | | */
|
||||
/* . | SVC Stack 16 bytes | */
|
||||
/* . | | */
|
||||
/* . |---------------------------------|0x0020FECC */
|
||||
/* . | | */
|
||||
/* . | stack area for user program | */
|
||||
/* . | | */
|
||||
/* . | | */
|
||||
/* . | | */
|
||||
/* . | free ram | */
|
||||
/* . | | */
|
||||
/* . |.................................|0x002006D8 <---------- _bss_end */
|
||||
/* . | | */
|
||||
/* . | .bss uninitialized variables | */
|
||||
/* . |.................................|0x002006D0 <---------- _bss_start, _edata */
|
||||
/* . | | */
|
||||
/* . | .data initialized variables | */
|
||||
/* . | | */
|
||||
/* .-------->|_________________________________|0x00200000 */
|
||||
/* */
|
||||
/* */
|
||||
/* .-------->|---------------------------------|0x00100000 */
|
||||
/* . | | */
|
||||
/* . | | */
|
||||
/* . | free flash | */
|
||||
/* . | | */
|
||||
/* . | | */
|
||||
/* . |.................................|0x000006D0 <---------- _bss_start, _edata */
|
||||
/* . | | */
|
||||
/* . | .data initialized variables | */
|
||||
/* . | | */
|
||||
/* . |---------------------------------|0x000006C4 <----------- _etext */
|
||||
/* . | | */
|
||||
/* . | C code | */
|
||||
/* . | | */
|
||||
/* . | | */
|
||||
/* . |---------------------------------|0x00000118 main() */
|
||||
/* . | | */
|
||||
/* . | Startup Code (crt.s) | */
|
||||
/* . | (assembler) | */
|
||||
/* . | | */
|
||||
/* . |---------------------------------|0x00000020 */
|
||||
/* . | | */
|
||||
/* . | Interrupt Vector Table | */
|
||||
/* . | 32 bytes | */
|
||||
/* .-------->|---------------------------------|0x00000000 _vec_reset */
|
||||
/* */
|
||||
/* */
|
||||
/* Author: James P. Lynch May 12, 2007 */
|
||||
/* */
|
||||
/* ****************************************************************************************************** */
|
||||
|
||||
|
||||
/* identify the Entry Point (_vec_reset is defined in file crt.s) */
|
||||
ENTRY(_vec_reset)
|
||||
|
||||
/* specify the AT91SAM7S64 memory areas */
|
||||
MEMORY
|
||||
{
|
||||
flash : ORIGIN = 0, LENGTH = 64K /* FLASH EPROM */
|
||||
ram : ORIGIN = 0x00200000, LENGTH = 16K /* static RAM area */
|
||||
}
|
||||
|
||||
|
||||
/* define a global symbol _stack_end (see analysis in annotation above) */
|
||||
/*_stack_end = 0x20FFFC;*/
|
||||
_stack_end = 0x203FFC;
|
||||
|
||||
/* now define the output sections */
|
||||
SECTIONS
|
||||
{
|
||||
. = 0; /* set location counter to address zero */
|
||||
|
||||
.text : /* collect all sections that should go into FLASH after startup */
|
||||
{
|
||||
*(.text) /* all .text sections (code) */
|
||||
*(.rodata) /* all .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* all .rodata* sections (constants, strings, etc.) */
|
||||
*(.glue_7) /* all .glue_7 sections (no idea what these are) */
|
||||
*(.glue_7t) /* all .glue_7t sections (no idea what these are) */
|
||||
_etext = .; /* define a global symbol _etext just after the last code byte */
|
||||
} >flash /* put all the above into FLASH */
|
||||
|
||||
.data : /* collect all initialized .data sections that go into RAM */
|
||||
{
|
||||
_data = .; /* create a global symbol marking the start of the .data section */
|
||||
*(.data) /* all .data sections */
|
||||
_edata = .; /* define a global symbol marking the end of the .data section */
|
||||
} >ram AT >flash /* put all the above into RAM (but load the LMA initializer copy into FLASH) */
|
||||
|
||||
.bss : /* collect all uninitialized .bss sections that go into RAM */
|
||||
{
|
||||
_bss_start = .; /* define a global symbol marking the start of the .bss section */
|
||||
*(.bss) /* all .bss sections */
|
||||
} >ram /* put all the above in RAM (it will be cleared in the startup code */
|
||||
|
||||
. = ALIGN(4); /* advance location counter to the next 32-bit boundary */
|
||||
_bss_end = . ; /* define a global symbol marking the end of the .bss section */
|
||||
}
|
||||
_end = .; /* define a global symbol marking the end of application RAM */
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Analog Input Objects customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "config.h"
|
||||
|
||||
/* Analog Input = Photocell */
|
||||
#define MAX_ANALOG_INPUTS 2
|
||||
#if (MAX_ANALOG_INPUTS > 9)
|
||||
#error Modify the Analog_Input_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
static uint8_t Present_Value[MAX_ANALOG_INPUTS];
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Analog_Input_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_ANALOG_INPUTS)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
unsigned Analog_Input_Count(void)
|
||||
{
|
||||
return MAX_ANALOG_INPUTS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
uint32_t Analog_Input_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
char *Analog_Input_Name(uint32_t object_instance)
|
||||
{
|
||||
static char text_string[16] = "AI-0"; /* okay for single thread */
|
||||
|
||||
if (object_instance < MAX_ANALOG_INPUTS) {
|
||||
text_string[3] = '0' + (uint8_t)object_instance;
|
||||
return text_string;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static float Analog_Input_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
float value = 0.0;
|
||||
|
||||
if (object_instance < MAX_ANALOG_INPUTS)
|
||||
value = Present_Value[object_instance];
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* return apdu length, or -1 on error */
|
||||
/* assumption - object has already exists */
|
||||
int Analog_Input_Encode_Property_APDU(uint8_t * apdu,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID property,
|
||||
int32_t array_index,
|
||||
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
|
||||
(void) array_index;
|
||||
switch (property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_ANALOG_INPUT,
|
||||
object_instance);
|
||||
break;
|
||||
/* note: Name and Description don't have to be the same.
|
||||
You could make Description writable and different */
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
characterstring_init_ansi(&char_string,
|
||||
Analog_Input_Name(object_instance));
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0],
|
||||
OBJECT_ANALOG_INPUT);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
apdu_len = encode_tagged_real(&apdu[0],
|
||||
Analog_Input_Present_Value(object_instance));
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
|
||||
apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
apdu_len = encode_tagged_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_UNITS:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], UNITS_PERCENT);
|
||||
break;
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testAnalogInput(Test * pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_OUTPUT;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint32_t instance = 123;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
|
||||
/* FIXME: we should do a lot more testing here... */
|
||||
len = Analog_Input_Encode_Property_APDU(&apdu[0],
|
||||
instance,
|
||||
PROP_OBJECT_IDENTIFIER,
|
||||
BACNET_ARRAY_ALL, &error_class, &error_code);
|
||||
ct_test(pTest, len >= 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len],
|
||||
(int *) &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == OBJECT_ANALOG_INPUT);
|
||||
ct_test(pTest, decoded_instance == instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ANALOG_INPUT
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Analog Input", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAnalogInput);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void) ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ANALOG_INPUT */
|
||||
#endif /* TEST */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,419 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Analog Value Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "bacapp.h"
|
||||
#include "config.h" /* the custom stuff */
|
||||
#include "wp.h"
|
||||
|
||||
#define MAX_ANALOG_VALUES 4
|
||||
#if (MAX_ANALOG_VALUES > 9)
|
||||
#error Modify the Analog_Value_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
/* we choose to have a NULL level in our system represented by */
|
||||
/* a particular value. When the priorities are not in use, they */
|
||||
/* will be relinquished (i.e. set to the NULL level). */
|
||||
#define ANALOG_LEVEL_NULL 255
|
||||
/* When all the priorities are level null, the present value returns */
|
||||
/* the Relinquish Default value */
|
||||
#define ANALOG_RELINQUISH_DEFAULT 0
|
||||
/* Here is our Present_Value. They are supposed to be Real, but */
|
||||
/* we don't have that kind of memory, so we will use a single byte */
|
||||
/* and load a Real for returning the value when asked. */
|
||||
static uint8_t Present_Value[MAX_ANALOG_VALUES];
|
||||
|
||||
/* we need to have our arrays initialized before answering any calls */
|
||||
static bool Analog_Value_Initialized = false;
|
||||
|
||||
void Analog_Value_Init(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (!Analog_Value_Initialized) {
|
||||
Analog_Value_Initialized = true;
|
||||
|
||||
/* initialize all the analog output priority arrays to NULL */
|
||||
for (i = 0; i < MAX_ANALOG_VALUES; i++) {
|
||||
Present_Value[i] = ANALOG_LEVEL_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Analog_Value_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
Analog_Value_Init();
|
||||
if (object_instance < MAX_ANALOG_VALUES)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Analog_Value_Count(void)
|
||||
{
|
||||
Analog_Value_Init();
|
||||
return MAX_ANALOG_VALUES;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Analog_Value_Index_To_Instance(unsigned index)
|
||||
{
|
||||
Analog_Value_Init();
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Analog_Value_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_ANALOG_VALUES;
|
||||
|
||||
Analog_Value_Init();
|
||||
if (object_instance < MAX_ANALOG_VALUES)
|
||||
index = object_instance;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static float Analog_Value_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
float value = ANALOG_RELINQUISH_DEFAULT;
|
||||
unsigned index = 0;
|
||||
|
||||
Analog_Value_Init();
|
||||
index = Analog_Value_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ANALOG_VALUES) {
|
||||
value = Present_Value[index];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
char *Analog_Value_Name(uint32_t object_instance)
|
||||
{
|
||||
static char text_string[16] = "AV-0"; /* okay for single thread */
|
||||
|
||||
if (object_instance < MAX_ANALOG_VALUES) {
|
||||
text_string[3] = '0' + (uint8_t)object_instance;
|
||||
return text_string;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return apdu len, or -1 on error */
|
||||
int Analog_Value_Encode_Property_APDU(uint8_t * apdu,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID property,
|
||||
int32_t array_index,
|
||||
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
float real_value = (float) 1.414;
|
||||
#if 0
|
||||
int len = 0;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
bool state = false;
|
||||
#endif
|
||||
|
||||
Analog_Value_Init();
|
||||
switch (property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_ANALOG_VALUE,
|
||||
object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
characterstring_init_ansi(&char_string,
|
||||
Analog_Value_Name(object_instance));
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_ANALOG_VALUE);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
real_value = Analog_Value_Present_Value(object_instance);
|
||||
apdu_len = encode_tagged_real(&apdu[0], real_value);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
|
||||
apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
#if 0
|
||||
object_index = Analog_Value_Instance_To_Index(object_instance);
|
||||
state = Analog_Value_Out_Of_Service[object_index];
|
||||
#endif
|
||||
apdu_len = encode_tagged_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_UNITS:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], UNITS_PERCENT);
|
||||
break;
|
||||
#if 0
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
/* Array element zero is the number of elements in the array */
|
||||
if (array_index == 0)
|
||||
apdu_len =
|
||||
encode_tagged_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
|
||||
/* if no index was specified, then try to encode the entire list */
|
||||
/* into one packet. */
|
||||
else if (array_index == BACNET_ARRAY_ALL) {
|
||||
object_index = Analog_Value_Instance_To_Index(object_instance);
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
/* FIXME: check if we have room before adding it to APDU */
|
||||
if (Present_Value[object_index][i] ==
|
||||
ANALOG_LEVEL_NULL)
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
else {
|
||||
real_value = Present_Value[object_index][i];
|
||||
len = encode_tagged_real(&apdu[apdu_len], real_value);
|
||||
}
|
||||
/* add it if we have room */
|
||||
if ((apdu_len + len) < MAX_APDU)
|
||||
apdu_len += len;
|
||||
else {
|
||||
*error_class = ERROR_CLASS_SERVICES;
|
||||
*error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
object_index = Analog_Value_Instance_To_Index(object_instance);
|
||||
if (array_index <= BACNET_MAX_PRIORITY) {
|
||||
if (Present_Value[object_index][array_index - 1] ==
|
||||
ANALOG_LEVEL_NULL)
|
||||
apdu_len = encode_tagged_null(&apdu[0]);
|
||||
else {
|
||||
real_value =
|
||||
Present_Value[object_index][array_index - 1];
|
||||
apdu_len = encode_tagged_real(&apdu[0], real_value);
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = -1;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case PROP_RELINQUISH_DEFAULT:
|
||||
real_value = ANALOG_RELINQUISH_DEFAULT;
|
||||
apdu_len = encode_tagged_real(&apdu[0], real_value);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned int object_index = 0;
|
||||
unsigned int priority = 0;
|
||||
uint8_t level = ANALOG_LEVEL_NULL;
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
Analog_Value_Init();
|
||||
if (!Analog_Value_Valid_Instance(wp_data->object_instance)) {
|
||||
*error_class = ERROR_CLASS_OBJECT;
|
||||
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
return false;
|
||||
}
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(wp_data->application_data,
|
||||
wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
/* FIXME: len == 0: unable to decode? */
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_REAL) {
|
||||
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.Real >= 0.0) && (value.type.Real <= 100.0)) {
|
||||
level = (uint8_t) value.type.Real;
|
||||
object_index =
|
||||
Analog_Value_Instance_To_Index(wp_data->
|
||||
object_instance);
|
||||
priority--;
|
||||
Present_Value[object_index] = level;
|
||||
/* Note: you could set the physical output here if we
|
||||
are the highest priority.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. This comment may apply to the
|
||||
main loop (i.e. check out of service before changing output) */
|
||||
status = true;
|
||||
} 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. */
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
#if 0
|
||||
} else if (value.tag == BACNET_APPLICATION_TAG_NULL) {
|
||||
level = ANALOG_LEVEL_NULL;
|
||||
object_index =
|
||||
Analog_Value_Instance_To_Index(wp_data->object_instance);
|
||||
priority = wp_data->priority;
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
|
||||
priority--;
|
||||
Present_Value[object_index][priority] = level;
|
||||
/* Note: you could set the physical output here to the next
|
||||
highest priority, or to the relinquish default if no
|
||||
priorities are set.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. This comment may apply to the
|
||||
main loop (i.e. check out of service before changing output) */
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
#if 0
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) {
|
||||
object_index =
|
||||
Analog_Value_Instance_To_Index(wp_data->object_instance);
|
||||
Analog_Value_Out_Of_Service[object_index] = value.type.Boolean;
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testAnalog_Value(Test * pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_VALUE;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint32_t instance = 123;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
|
||||
len = Analog_Value_Encode_Property_APDU(&apdu[0],
|
||||
instance,
|
||||
PROP_OBJECT_IDENTIFIER,
|
||||
BACNET_ARRAY_ALL, &error_class, &error_code);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len],
|
||||
(int *) &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == OBJECT_ANALOG_VALUE);
|
||||
ct_test(pTest, decoded_instance == instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ANALOG_VALUE
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Analog Value", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAnalog_Value);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void) ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ANALOG_VALUE */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,235 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Binary Input Objects customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "config.h"
|
||||
|
||||
#define MAX_BINARY_INPUTS 8
|
||||
#if (MAX_BINARY_INPUTS > 9)
|
||||
#error Modify the Binary_Input_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS];
|
||||
|
||||
static void Binary_Input_Initialize(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
unsigned i;
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
for (i = 0; i < MAX_BINARY_INPUTS; i++) {
|
||||
Present_Value[i] = BINARY_INACTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
bool Binary_Input_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_BINARY_INPUTS)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
unsigned Binary_Input_Count(void)
|
||||
{
|
||||
return MAX_BINARY_INPUTS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances.*/
|
||||
uint32_t Binary_Input_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Binary_Input_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_BINARY_INPUTS;
|
||||
|
||||
if (object_instance < MAX_BINARY_INPUTS)
|
||||
index = object_instance;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static BACNET_BINARY_PV Binary_Input_Present_Value(uint32_t
|
||||
object_instance)
|
||||
{
|
||||
BACNET_BINARY_PV value = BINARY_INACTIVE;
|
||||
unsigned index = 0;
|
||||
|
||||
Binary_Input_Initialize();
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
value = Present_Value[index];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
char *Binary_Input_Name(uint32_t object_instance)
|
||||
{
|
||||
static char text_string[16] = "BI-0"; /* okay for single thread */
|
||||
|
||||
if (object_instance < MAX_BINARY_INPUTS) {
|
||||
text_string[3] = '0' + (uint8_t)object_instance;
|
||||
return text_string;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return apdu length, or -1 on error */
|
||||
/* assumption - object already exists, and has been bounds checked */
|
||||
int Binary_Input_Encode_Property_APDU(uint8_t * apdu,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID property,
|
||||
int32_t array_index,
|
||||
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
BACNET_POLARITY polarity = POLARITY_NORMAL;
|
||||
BACNET_BINARY_PV value = BINARY_INACTIVE;
|
||||
|
||||
|
||||
(void) array_index;
|
||||
Binary_Input_Initialize();
|
||||
switch (property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_BINARY_INPUT,
|
||||
object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
/* note: object name must be unique in our device */
|
||||
characterstring_init_ansi(&char_string,
|
||||
Binary_Input_Name(object_instance));
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_BINARY_INPUT);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
value = Binary_Input_Present_Value(object_instance);
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0],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_tagged_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_tagged_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
apdu_len = encode_tagged_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_POLARITY:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], polarity);
|
||||
break;
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testBinaryInput(Test * pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
BACNET_OBJECT_TYPE decoded_type = OBJECT_BINARY_OUTPUT;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint32_t instance = 123;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
|
||||
/* FIXME: we should do a lot more testing here... */
|
||||
len = Binary_Input_Encode_Property_APDU(&apdu[0],
|
||||
instance,
|
||||
PROP_OBJECT_IDENTIFIER,
|
||||
BACNET_ARRAY_ALL, &error_class, &error_code);
|
||||
ct_test(pTest, len >= 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len],
|
||||
(int *) &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == OBJECT_BINARY_INPUT);
|
||||
ct_test(pTest, decoded_instance == instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_BINARY_INPUT
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Binary Input", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBinaryInput);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void) ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_BINARY_INPUT */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,41 @@
|
||||
/*****************************************************************************
|
||||
blinker.c
|
||||
|
||||
Endless loop blinks a code for crash analysis
|
||||
|
||||
Inputs: Code - blink code to display
|
||||
1 = undefined instruction (one blinks ........ long pause)
|
||||
2 = prefetch abort (two blinks ........ long pause)
|
||||
3 = data abort (three blinks ...... long pause)
|
||||
|
||||
Author: James P Lynch May 12, 2007
|
||||
*****************************************************************************/
|
||||
#include "AT91SAM7S256.h"
|
||||
#include "board.h"
|
||||
|
||||
/* global variables */
|
||||
unsigned long blinkcount;
|
||||
|
||||
void blinker(unsigned char code) {
|
||||
|
||||
volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; /* pointer to PIO register structure */
|
||||
volatile unsigned int j,k; /* loop counters */
|
||||
|
||||
/* endless loop */
|
||||
while (1) {
|
||||
/* count out the proper number of blinks */
|
||||
for (j = code; j != 0; j--) {
|
||||
/* turn LED1 (DS1) on */
|
||||
pPIO->PIO_CODR = LED1;
|
||||
/* wait 250 msec */
|
||||
for (k = 600000; k != 0; k-- );
|
||||
/* turn LED1 (DS1) off */
|
||||
pPIO->PIO_SODR = LED1;
|
||||
/* wait 250 msec */
|
||||
for (k = 600000; k != 0; k-- );
|
||||
}
|
||||
/* wait 2 seconds */
|
||||
for (k = 5000000; (code != 0) && (k != 0); k-- );
|
||||
blinkcount++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// ATMEL Microcontroller Software Support - ROUSSET -
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// The software is delivered "AS IS" without warranty or condition of any
|
||||
// kind, either express, implied or statutory. This includes without
|
||||
// limitation any warranty or condition with respect to merchantability or
|
||||
// fitness for any particular purpose, or against the infringements of
|
||||
// intellectual property rights of others.
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// File Name: Board.h
|
||||
// Object: AT91SAM7S Evaluation Board Features Definition File.
|
||||
//
|
||||
// Creation: JPP 16/June/2004
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
#ifndef Board_h
|
||||
#define Board_h
|
||||
|
||||
#include "AT91SAM7S256.h"
|
||||
#define __inline inline
|
||||
|
||||
//-----------------------------------------------
|
||||
// SAM7Board Memories Definition
|
||||
//-----------------------------------------------
|
||||
// The AT91SAM7S2564 embeds a 64-Kbyte SRAM bank, and 256 K-Byte Flash
|
||||
|
||||
#define INT_SRAM 0x00200000
|
||||
#define INT_SRAM_REMAP 0x00000000
|
||||
|
||||
#define INT_FLASH 0x00000000
|
||||
#define INT_FLASH_REMAP 0x01000000
|
||||
|
||||
#define FLASH_PAGE_NB 512
|
||||
#define FLASH_PAGE_SIZE 128
|
||||
|
||||
//------------------------
|
||||
// Leds Definition
|
||||
//------------------------
|
||||
#define LED1 (1<<0) // PA0
|
||||
#define LED2 (1<<1) // PA1
|
||||
#define LED3 (1<<2) // PA2
|
||||
#define LED4 (1<<3) // PA3
|
||||
#define NB_LEB 4
|
||||
#define LED_MASK (LED1|LED2|LED3|LED4)
|
||||
|
||||
|
||||
//----------------------------------
|
||||
// Push Buttons Definition
|
||||
//-----------------------------------
|
||||
#define SW1_MASK (1<<19) // PA19
|
||||
#define SW2_MASK (1<<20) // PA20
|
||||
#define SW3_MASK (1<<15) // PA15
|
||||
#define SW4_MASK (1<<14) // PA14
|
||||
#define SW_MASK (SW1_MASK|SW2_MASK|SW3_MASK|SW4_MASK)
|
||||
|
||||
#define SW1 (1<<19) // PA19
|
||||
#define SW2 (1<<20) // PA20
|
||||
#define SW3 (1<<15) // PA15
|
||||
#define SW4 (1<<14) // PA14
|
||||
|
||||
//-------------------------
|
||||
// USART Definition
|
||||
//-------------------------
|
||||
// SUB-D 9 points J3 DBGU
|
||||
#define DBGU_RXD AT91C_PA9_DRXD // JP11 must be close
|
||||
#define DBGU_TXD AT91C_PA10_DTXD // JP12 must be close
|
||||
#define AT91C_DBGU_BAUD 115200 // Baud rate
|
||||
#define US_RXD_PIN AT91C_PA5_RXD0 // JP9 must be close
|
||||
#define US_TXD_PIN AT91C_PA6_TXD0 // JP7 must be close
|
||||
#define US_RTS_PIN AT91C_PA7_RTS0 // JP8 must be close
|
||||
#define US_CTS_PIN AT91C_PA8_CTS0 // JP6 must be close
|
||||
|
||||
//--------------
|
||||
// Master Clock
|
||||
//--------------
|
||||
#define EXT_OC 18432000 // Exetrnal ocilator MAINCK
|
||||
#define MCK 47923200 // MCK (PLLRC div by 2)
|
||||
#define MCKKHz (MCK/1000) //
|
||||
|
||||
#endif // Board_h
|
||||
@@ -0,0 +1,337 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Binary Value Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "config.h" /* the custom stuff */
|
||||
#include "wp.h"
|
||||
|
||||
#define MAX_BINARY_VALUES 8
|
||||
#if (MAX_BINARY_VALUES > 9)
|
||||
#error Modify the Binary_Value_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
static BACNET_BINARY_PV Present_Value[MAX_BINARY_VALUES];
|
||||
|
||||
static void Binary_Value_Initialize(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
unsigned i;
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
for (i = 0; i < MAX_BINARY_VALUES; i++) {
|
||||
Present_Value[i] = BINARY_INACTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
bool Binary_Value_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_BINARY_VALUES)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
unsigned Binary_Value_Count(void)
|
||||
{
|
||||
return MAX_BINARY_VALUES;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
uint32_t Binary_Value_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
unsigned Binary_Value_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_BINARY_VALUES;
|
||||
|
||||
if (object_instance < MAX_BINARY_VALUES)
|
||||
index = object_instance;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t
|
||||
object_instance)
|
||||
{
|
||||
BACNET_BINARY_PV value = BINARY_INACTIVE;
|
||||
|
||||
Binary_Value_Initialize();
|
||||
if (object_instance < MAX_BINARY_VALUES) {
|
||||
value = Present_Value[object_instance];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
char *Binary_Value_Name(uint32_t object_instance)
|
||||
{
|
||||
static char text_string[16] = "BV-0"; /* okay for single thread */
|
||||
|
||||
if (object_instance < MAX_BINARY_VALUES) {
|
||||
text_string[3] = '0' + (uint8_t)object_instance;
|
||||
return text_string;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return apdu len, or -1 on error */
|
||||
int Binary_Value_Encode_Property_APDU(uint8_t * apdu,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID property,
|
||||
int32_t array_index,
|
||||
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
|
||||
BACNET_POLARITY polarity = POLARITY_NORMAL;
|
||||
|
||||
Binary_Value_Initialize();
|
||||
switch (property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_BINARY_VALUE,
|
||||
object_instance);
|
||||
break;
|
||||
/* note: Name and Description don't have to be the same.
|
||||
You could make Description writable and different */
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
characterstring_init_ansi(&char_string,
|
||||
Binary_Value_Name(object_instance));
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_BINARY_VALUE);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
present_value = Binary_Value_Present_Value(object_instance);
|
||||
apdu_len = encode_tagged_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_tagged_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_tagged_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
apdu_len = encode_tagged_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_POLARITY:
|
||||
/* FIXME: figure out the polarity */
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], polarity);
|
||||
break;
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned int object_index = 0;
|
||||
unsigned int priority = 0;
|
||||
BACNET_BINARY_PV level = BINARY_NULL;
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
if (!Binary_Value_Valid_Instance(wp_data->object_instance)) {
|
||||
*error_class = ERROR_CLASS_OBJECT;
|
||||
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
return false;
|
||||
}
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(wp_data->application_data,
|
||||
wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
/* FIXME: len == 0: unable to decode? */
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
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 >= MIN_BINARY_PV) &&
|
||||
(value.type.Enumerated <= MAX_BINARY_PV)) {
|
||||
level = value.type.Enumerated;
|
||||
object_index =
|
||||
Binary_Value_Instance_To_Index(wp_data->
|
||||
object_instance);
|
||||
priority--;
|
||||
/* NOTE: this Binary value has no priority array */
|
||||
Present_Value[object_index] = level;
|
||||
/* Note: you could set the physical output here if we
|
||||
are the highest priority.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. */
|
||||
status = true;
|
||||
} 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. */
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else if (value.tag == BACNET_APPLICATION_TAG_NULL) {
|
||||
#if 0
|
||||
/* NOTE: this Binary Value has no priority array */
|
||||
level = BINARY_NULL;
|
||||
object_index =
|
||||
Binary_Value_Instance_To_Index(wp_data->object_instance);
|
||||
priority = wp_data->priority;
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
|
||||
priority--;
|
||||
Binary_Value_Level[object_index][priority] = level;
|
||||
/* Note: you could set the physical output here to the next
|
||||
highest priority, or to the relinquish default if no
|
||||
priorities are set.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. This comment may apply to the
|
||||
main loop (i.e. check out of service before changing output) */
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
#else
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
#endif
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
#if 0
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) {
|
||||
object_index =
|
||||
Binary_Value_Instance_To_Index(wp_data->object_instance);
|
||||
Binary_Value_Out_Of_Service[object_index] = value.type.Boolean;
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testBinary_Value(Test * pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
BACNET_OBJECT_TYPE decoded_type = OBJECT_BINARY_VALUE;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint32_t instance = 123;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
|
||||
len = Binary_Value_Encode_Property_APDU(&apdu[0],
|
||||
instance,
|
||||
PROP_OBJECT_IDENTIFIER,
|
||||
BACNET_ARRAY_ALL, &error_class, &error_code);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len],
|
||||
(int *) &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == OBJECT_BINARY_VALUE);
|
||||
ct_test(pTest, decoded_instance == instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_BINARY_VALUE
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Binary_Value", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBinary_Value);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void) ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_BINARY_VALUE */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,330 @@
|
||||
/* ****************************************************************************************************** */
|
||||
/* crt.s */
|
||||
/* */
|
||||
/* Assembly Language Startup Code for Atmel AT91SAM7S256 */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* Author: James P Lynch May 12, 2007 */
|
||||
/* ****************************************************************************************************** */
|
||||
|
||||
/* Stack Sizes */
|
||||
.set UND_STACK_SIZE, 0x00000010 /* stack for "undefined instruction" interrupts is 16 bytes */
|
||||
.set ABT_STACK_SIZE, 0x00000010 /* stack for "abort" interrupts is 16 bytes */
|
||||
.set FIQ_STACK_SIZE, 0x00000080 /* stack for "FIQ" interrupts is 128 bytes */
|
||||
.set IRQ_STACK_SIZE, 0X00000080 /* stack for "IRQ" normal interrupts is 128 bytes */
|
||||
.set SVC_STACK_SIZE, 0x00000080 /* stack for "SVC" supervisor mode is 128 bytes */
|
||||
|
||||
/* Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs (program status registers) */
|
||||
.set ARM_MODE_USR, 0x10 /* Normal User Mode */
|
||||
.set ARM_MODE_FIQ, 0x11 /* FIQ Processing Fast Interrupts Mode */
|
||||
.set ARM_MODE_IRQ, 0x12 /* IRQ Processing Standard Interrupts Mode */
|
||||
.set ARM_MODE_SVC, 0x13 /* Supervisor Processing Software Interrupts Mode */
|
||||
.set ARM_MODE_ABT, 0x17 /* Abort Processing memory Faults Mode */
|
||||
.set ARM_MODE_UND, 0x1B /* Undefined Processing Undefined Instructions Mode */
|
||||
.set ARM_MODE_SYS, 0x1F /* System Running Priviledged Operating System Tasks Mode */
|
||||
.set I_BIT, 0x80 /* when I bit is set, IRQ is disabled (program status registers) */
|
||||
.set F_BIT, 0x40 /* when F bit is set, FIQ is disabled (program status registers) */
|
||||
|
||||
/* Addresses and offsets of AIC and PIO */
|
||||
.set AT91C_BASE_AIC, 0xFFFFF000 /* (AIC) Base Address */
|
||||
.set AT91C_PIOA_CODR, 0xFFFFF434 /* (PIO) Clear Output Data Register */
|
||||
.set AT91C_AIC_IVR, 0xFFFFF100 /* (AIC) IRQ Interrupt Vector Register */
|
||||
.set AT91C_AIC_FVR, 0xFFFFF104 /* (AIC) FIQ Interrupt Vector Register */
|
||||
.set AIC_IVR, 256 /* IRQ Vector Register offset from base above */
|
||||
.set AIC_FVR, 260 /* FIQ Vector Register offset from base above */
|
||||
.set AIC_EOICR, 304 /* End of Interrupt Command Register */
|
||||
|
||||
/* identify all GLOBAL symbols */
|
||||
.global _vec_reset
|
||||
.global _vec_undef
|
||||
.global _vec_swi
|
||||
.global _vec_pabt
|
||||
.global _vec_dabt
|
||||
.global _vec_rsv
|
||||
.global _vec_irq
|
||||
.global _vec_fiq
|
||||
.global AT91F_Irq_Handler
|
||||
.global AT91F_Fiq_Handler
|
||||
.global AT91F_Default_FIQ_handler
|
||||
.global AT91F_Default_IRQ_handler
|
||||
.global AT91F_Spurious_handler
|
||||
.global AT91F_Dabt_Handler
|
||||
.global AT91F_Pabt_Handler
|
||||
.global AT91F_Undef_Handler
|
||||
|
||||
|
||||
/* GNU assembler controls */
|
||||
.text /* all assembler code that follows will go into .text section */
|
||||
.arm /* compile for 32-bit ARM instruction set */
|
||||
.align /* align section on 32-bit boundary */
|
||||
|
||||
/* ============================================================ */
|
||||
/* VECTOR TABLE */
|
||||
/* */
|
||||
/* Must be located in FLASH at address 0x00000000 */
|
||||
/* */
|
||||
/* Easy to do if this file crt.s is first in the list */
|
||||
/* for the linker step in the makefile, e.g. */
|
||||
/* */
|
||||
/* $(LD) $(LFLAGS) -o main.out crt.o main.o */
|
||||
/* */
|
||||
/* ============================================================ */
|
||||
|
||||
_vec_reset: b _init_reset /* RESET vector - must be at 0x00000000 */
|
||||
_vec_undef: b AT91F_Undef_Handler /* Undefined Instruction vector */
|
||||
_vec_swi: b _vec_swi /* Software Interrupt vector */
|
||||
_vec_pabt: b AT91F_Pabt_Handler /* Prefetch abort vector */
|
||||
_vec_dabt: b AT91F_Dabt_Handler /* Data abort vector */
|
||||
_vec_rsv: nop /* Reserved vector */
|
||||
_vec_irq: b AT91F_Irq_Handler /* Interrupt Request (IRQ) vector */
|
||||
_vec_fiq: /* Fast interrupt request (FIQ) vector */
|
||||
|
||||
/* ======================================================================== */
|
||||
/* Function: AT91F_Fiq_Handler */
|
||||
/* */
|
||||
/* The FIQ interrupt asserts when switch SW1 is pressed. */
|
||||
/* */
|
||||
/* This simple FIQ handler turns on LED3 (Port PA2). The LED3 will be */
|
||||
/* turned off by the background loop in main() thus giving a visual */
|
||||
/* indication that the interrupt has occurred. */
|
||||
/* */
|
||||
/* This FIQ_Handler supports non-nested FIQ interrupts (a FIQ interrupt */
|
||||
/* cannot itself be interrupted). */
|
||||
/* */
|
||||
/* The Fast Interrupt Vector Register (AIC_FVR) is read to clear the */
|
||||
/* interrupt. */
|
||||
/* */
|
||||
/* A global variable FiqCount is also incremented. */
|
||||
/* */
|
||||
/* Remember that switch SW1 is not debounced, so the FIQ interrupt may */
|
||||
/* occur more than once for a single button push. */
|
||||
/* */
|
||||
/* Programmer: James P Lynch */
|
||||
/* ======================================================================== */
|
||||
AT91F_Fiq_Handler:
|
||||
|
||||
/* Adjust LR_irq */
|
||||
sub lr, lr, #4
|
||||
|
||||
/* Read the AIC Fast Interrupt Vector register to clear the interrupt */
|
||||
ldr r12, =AT91C_AIC_FVR
|
||||
ldr r11, [r12]
|
||||
|
||||
/* Turn on LED3 (write 0x0008 to PIOA_CODR at 0xFFFFF434) */
|
||||
ldr r12, =AT91C_PIOA_CODR
|
||||
mov r11, #0x04
|
||||
str r11, [r12]
|
||||
|
||||
/* Increment the _FiqCount variable */
|
||||
ldr r12, =FiqCount
|
||||
ldr r11, [r12]
|
||||
add r11, r11, #1
|
||||
str r11, [r12]
|
||||
|
||||
/* Return from Fiq interrupt */
|
||||
movs pc, lr
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* _init_reset Handler */
|
||||
/* */
|
||||
/* RESET vector 0x00000000 branches to here. */
|
||||
/* */
|
||||
/* ARM microprocessor begins execution after RESET at address 0x00000000 */
|
||||
/* in Supervisor mode with interrupts disabled! */
|
||||
/* */
|
||||
/* _init_reset handler: creates a stack for each ARM mode. */
|
||||
/* sets up a stack pointer for each ARM mode. */
|
||||
/* turns off interrupts in each mode. */
|
||||
/* leaves CPU in SYS (System) mode. */
|
||||
/* */
|
||||
/* block copies the initializers to .data section */
|
||||
/* clears the .bss section to zero */
|
||||
/* */
|
||||
/* branches to main( ) */
|
||||
/* ======================================================================== */
|
||||
.text /* all assembler code that follows will go into .text section */
|
||||
.align /* align section on 32-bit boundary */
|
||||
_init_reset:
|
||||
/* Setup a stack for each mode with interrupts initially disabled. */
|
||||
ldr r0, =_stack_end /* r0 = top-of-stack */
|
||||
|
||||
msr CPSR_c, #ARM_MODE_UND|I_BIT|F_BIT /* switch to Undefined Instruction Mode */
|
||||
mov sp, r0 /* set stack pointer for UND mode */
|
||||
sub r0, r0, #UND_STACK_SIZE /* adjust r0 past UND stack */
|
||||
|
||||
msr CPSR_c, #ARM_MODE_ABT|I_BIT|F_BIT /* switch to Abort Mode */
|
||||
mov sp, r0 /* set stack pointer for ABT mode */
|
||||
sub r0, r0, #ABT_STACK_SIZE /* adjust r0 past ABT stack */
|
||||
|
||||
msr CPSR_c, #ARM_MODE_FIQ|I_BIT|F_BIT /* switch to FIQ Mode */
|
||||
mov sp, r0 /* set stack pointer for FIQ mode */
|
||||
sub r0, r0, #FIQ_STACK_SIZE /* adjust r0 past FIQ stack */
|
||||
|
||||
msr CPSR_c, #ARM_MODE_IRQ|I_BIT|F_BIT /* switch to IRQ Mode */
|
||||
mov sp, r0 /* set stack pointer for IRQ mode */
|
||||
sub r0, r0, #IRQ_STACK_SIZE /* adjust r0 past IRQ stack */
|
||||
|
||||
msr CPSR_c, #ARM_MODE_SVC|I_BIT|F_BIT /* switch to Supervisor Mode */
|
||||
mov sp, r0 /* set stack pointer for SVC mode */
|
||||
sub r0, r0, #SVC_STACK_SIZE /* adjust r0 past SVC stack */
|
||||
|
||||
msr CPSR_c, #ARM_MODE_SYS|I_BIT|F_BIT /* switch to System Mode */
|
||||
mov sp, r0 /* set stack pointer for SYS mode */
|
||||
/* we now start execution in SYSTEM mode */
|
||||
/* This is exactly like USER mode (same stack) */
|
||||
/* but SYSTEM mode has more privileges */
|
||||
|
||||
/* copy initialized variables .data section (Copy from ROM to RAM) */
|
||||
ldr R1, =_etext
|
||||
ldr R2, =_data
|
||||
ldr R3, =_edata
|
||||
1: cmp R2, R3
|
||||
ldrlo R0, [R1], #4
|
||||
strlo R0, [R2], #4
|
||||
blo 1b
|
||||
|
||||
/* Clear uninitialized variables .bss section (Zero init) */
|
||||
mov R0, #0
|
||||
ldr R1, =_bss_start
|
||||
ldr R2, =_bss_end
|
||||
2: cmp R1, R2
|
||||
strlo R0, [R1], #4
|
||||
blo 2b
|
||||
|
||||
/* Enter the C code */
|
||||
b main
|
||||
|
||||
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* Function: AT91F_Irq_Handler */
|
||||
/* */
|
||||
/* This IRQ_Handler supports nested interrupts (an IRQ interrupt can itself */
|
||||
/* be interrupted). */
|
||||
/* */
|
||||
/* This handler re-enables interrupts and switches to "Supervisor" mode to */
|
||||
/* prevent any corruption to the link and IP registers. */
|
||||
/* */
|
||||
/* The Interrupt Vector Register (AIC_IVR) is read to determine the address */
|
||||
/* of the required interrupt service routine. The ISR routine can be a */
|
||||
/* standard C function since this handler minds all the save/restore */
|
||||
/* protocols. */
|
||||
/* */
|
||||
/* */
|
||||
/* Programmers: */
|
||||
/*--------------------------------------------------------------------------*/
|
||||
/* ATMEL Microcontroller Software Support - ROUSSET - */
|
||||
/*--------------------------------------------------------------------------*/
|
||||
/* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS */
|
||||
/* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
|
||||
/* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
|
||||
/* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR */
|
||||
/* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR */
|
||||
/* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT */
|
||||
/* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR */
|
||||
/* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */
|
||||
/* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE */
|
||||
/* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
|
||||
/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
|
||||
/* File source : Cstartup.s79 */
|
||||
/* Object : Generic CStartup to AT91SAM7S256 */
|
||||
/* 1.0 09/May/06 JPP : Creation */
|
||||
/* */
|
||||
/* */
|
||||
/* Note: taken from Atmel web site (www.at91.com) */
|
||||
/* Keil example project: AT91SAM7S-Interrupt_SAM7S */
|
||||
/* ======================================================================== */
|
||||
AT91F_Irq_Handler:
|
||||
|
||||
/* Manage Exception Entry */
|
||||
/* Adjust and save LR_irq in IRQ stack */
|
||||
sub lr, lr, #4
|
||||
stmfd sp!, {lr}
|
||||
|
||||
/* Save r0 and SPSR (need to be saved for nested interrupt) */
|
||||
mrs r14, SPSR
|
||||
stmfd sp!, {r0,r14}
|
||||
|
||||
/* Write in the IVR to support Protect Mode */
|
||||
/* No effect in Normal Mode */
|
||||
/* De-assert the NIRQ and clear the source in Protect Mode */
|
||||
ldr r14, =AT91C_BASE_AIC
|
||||
ldr r0 , [r14, #AIC_IVR]
|
||||
str r14, [r14, #AIC_IVR]
|
||||
|
||||
/* Enable Interrupt and Switch in Supervisor Mode */
|
||||
msr CPSR_c, #ARM_MODE_SVC
|
||||
|
||||
/* Save scratch/used registers and LR in User Stack */
|
||||
stmfd sp!, { r1-r3, r12, r14}
|
||||
|
||||
/* Branch to the routine pointed by the AIC_IVR */
|
||||
mov r14, pc
|
||||
bx r0
|
||||
|
||||
/* Manage Exception Exit */
|
||||
/* Restore scratch/used registers and LR from User Stack */
|
||||
ldmia sp!, { r1-r3, r12, r14}
|
||||
|
||||
/* Disable Interrupt and switch back in IRQ mode */
|
||||
msr CPSR_c, #I_BIT | ARM_MODE_IRQ
|
||||
|
||||
/* Mark the End of Interrupt on the AIC */
|
||||
ldr r14, =AT91C_BASE_AIC
|
||||
str r14, [r14, #AIC_EOICR]
|
||||
|
||||
/* Restore SPSR_irq and r0 from IRQ stack */
|
||||
ldmia sp!, {r0,r14}
|
||||
msr SPSR_cxsf, r14
|
||||
|
||||
/* Restore adjusted LR_irq from IRQ stack directly in the PC */
|
||||
ldmia sp!, {pc}^
|
||||
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* Function: AT91F_Dabt_Handler */
|
||||
/* */
|
||||
/* Entered on Data Abort exception. */
|
||||
/* Enters blink routine (3 blinks followed by a pause) */
|
||||
/* processor hangs in the blink loop forever */
|
||||
/* */
|
||||
/* ======================================================================== */
|
||||
AT91F_Dabt_Handler: mov R0, #3
|
||||
b blinker
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* Function: AT91F_Pabt_Handler */
|
||||
/* */
|
||||
/* Entered on Prefetch Abort exception. */
|
||||
/* Enters blink routine (2 blinks followed by a pause) */
|
||||
/* processor hangs in the blink loop forever */
|
||||
/* */
|
||||
/* ======================================================================== */
|
||||
AT91F_Pabt_Handler: mov R0, #2
|
||||
b blinker
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* Function: AT91F_Undef_Handler */
|
||||
/* */
|
||||
/* Entered on Undefined Instruction exception. */
|
||||
/* Enters blink routine (1 blinks followed by a pause) */
|
||||
/* processor hangs in the blink loop forever */
|
||||
/* */
|
||||
/* ======================================================================== */
|
||||
AT91F_Undef_Handler: mov R0, #1
|
||||
b blinker
|
||||
|
||||
|
||||
AT91F_Default_FIQ_handler: b AT91F_Default_FIQ_handler
|
||||
|
||||
AT91F_Default_IRQ_handler: b AT91F_Default_IRQ_handler
|
||||
|
||||
AT91F_Spurious_handler: b AT91F_Spurious_handler
|
||||
.end
|
||||
@@ -0,0 +1,567 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacstr.h"
|
||||
#include "bacenum.h"
|
||||
#include "apdu.h"
|
||||
#include "dlmstp.h"
|
||||
#include "rs485.h"
|
||||
/* objects */
|
||||
#include "device.h"
|
||||
#include "ai.h"
|
||||
#include "av.h"
|
||||
#include "bi.h"
|
||||
#include "bv.h"
|
||||
#include "wp.h"
|
||||
#include "dcc.h"
|
||||
|
||||
/* note: you really only need to define variables for
|
||||
properties that are writable or that may change.
|
||||
The properties that are constant can be hard coded
|
||||
into the read-property encoding. */
|
||||
static uint32_t Object_Instance_Number = 12345;
|
||||
static char Object_Name[32] = "ARM7 Device";
|
||||
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
|
||||
|
||||
BACNET_REINITIALIZED_STATE_OF_DEVICE Reinitialize_State =
|
||||
REINITIALIZED_STATE_IDLE;
|
||||
|
||||
void Device_Reinit(void)
|
||||
{
|
||||
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
}
|
||||
|
||||
void Device_Init(void)
|
||||
{
|
||||
Reinitialize_State = REINITIALIZED_STATE_IDLE;
|
||||
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
|
||||
/* FIXME: Get the data from the eeprom */
|
||||
/* I2C_Read_Block(EEPROM_DEVICE_ADDRESS,
|
||||
(char *)&Object_Instance_Number,
|
||||
sizeof(Object_Instance_Number),
|
||||
EEPROM_BACNET_ID_ADDR); */
|
||||
}
|
||||
|
||||
/* methods to manipulate the data */
|
||||
uint32_t Device_Object_Instance_Number(void)
|
||||
{
|
||||
return Object_Instance_Number;
|
||||
}
|
||||
|
||||
bool Device_Set_Object_Instance_Number(uint32_t object_id)
|
||||
{
|
||||
bool status = true; /* return value */
|
||||
|
||||
if (object_id <= BACNET_MAX_INSTANCE) {
|
||||
Object_Instance_Number = object_id;
|
||||
/* FIXME: Write the data to the eeprom */
|
||||
/* I2C_Write_Block(
|
||||
EEPROM_DEVICE_ADDRESS,
|
||||
(char *)&Object_Instance_Number,
|
||||
sizeof(Object_Instance_Number),
|
||||
EEPROM_BACNET_ID_ADDR); */
|
||||
} else
|
||||
status = false;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Device_Valid_Object_Instance_Number(uint32_t object_id)
|
||||
{
|
||||
/* BACnet allows for a wildcard instance number */
|
||||
return ((Object_Instance_Number == object_id) ||
|
||||
(object_id == BACNET_MAX_INSTANCE));
|
||||
}
|
||||
|
||||
BACNET_DEVICE_STATUS Device_System_Status(void)
|
||||
{
|
||||
return System_Status;
|
||||
}
|
||||
|
||||
void Device_Set_System_Status(BACNET_DEVICE_STATUS status)
|
||||
{
|
||||
if (status < MAX_DEVICE_STATUS)
|
||||
System_Status = status;
|
||||
}
|
||||
|
||||
/* FIXME: put your vendor ID here! */
|
||||
uint16_t Device_Vendor_Identifier(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t Device_Protocol_Version(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t Device_Protocol_Revision(void)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
/* FIXME: MAX_APDU is defined in config.ini - set it! */
|
||||
uint16_t Device_Max_APDU_Length_Accepted(void)
|
||||
{
|
||||
return MAX_APDU;
|
||||
}
|
||||
|
||||
BACNET_SEGMENTATION Device_Segmentation_Supported(void)
|
||||
{
|
||||
return SEGMENTATION_NONE;
|
||||
}
|
||||
|
||||
uint16_t Device_APDU_Timeout(void)
|
||||
{
|
||||
return 60000;
|
||||
}
|
||||
|
||||
|
||||
uint8_t Device_Number_Of_APDU_Retries(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t Device_Database_Revision(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned Device_Object_List_Count(void)
|
||||
{
|
||||
unsigned count = 1; /* at least 1 for device object */
|
||||
|
||||
/* FIXME: add objects as needed */
|
||||
count += Binary_Input_Count();
|
||||
count += Binary_Value_Count();
|
||||
count += Analog_Input_Count();
|
||||
count += Analog_Value_Count();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool Device_Object_List_Identifier(unsigned array_index,
|
||||
int *object_type, uint32_t * instance)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned object_index = 0;
|
||||
unsigned object_count = 0;
|
||||
|
||||
/* device object */
|
||||
if (array_index == 1) {
|
||||
*object_type = OBJECT_DEVICE;
|
||||
*instance = Object_Instance_Number;
|
||||
status = true;
|
||||
}
|
||||
/* normalize the index since
|
||||
we know it is not the previous objects */
|
||||
/* array index starts at 1 */
|
||||
object_index = array_index - 1;
|
||||
/* 1 for the device object */
|
||||
object_count = 1;
|
||||
/* FIXME: add objects as needed */
|
||||
/* binary value objects */
|
||||
if (!status) {
|
||||
object_index -= object_count;
|
||||
object_count = Binary_Value_Count();
|
||||
/* is it a valid index for this object? */
|
||||
if (object_index < object_count) {
|
||||
*object_type = OBJECT_BINARY_VALUE;
|
||||
*instance = Binary_Value_Index_To_Instance(object_index);
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
/* analog input objects */
|
||||
if (!status) {
|
||||
/* array index starts at 1, and 1 for the device object */
|
||||
object_index -= object_count;
|
||||
object_count = Analog_Value_Count();
|
||||
if (object_index < object_count) {
|
||||
*object_type = OBJECT_ANALOG_VALUE;
|
||||
*instance = Analog_Value_Index_To_Instance(object_index);
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
/* analog input objects */
|
||||
if (!status) {
|
||||
/* array index starts at 1, and 1 for the device object */
|
||||
object_index -= object_count;
|
||||
object_count = Analog_Input_Count();
|
||||
if (object_index < object_count) {
|
||||
*object_type = OBJECT_ANALOG_INPUT;
|
||||
*instance = Analog_Input_Index_To_Instance(object_index);
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
/* binary input objects */
|
||||
if (!status) {
|
||||
/* normalize the index since
|
||||
we know it is not the previous objects */
|
||||
object_index -= object_count;
|
||||
object_count = Binary_Input_Count();
|
||||
/* is it a valid index for this object? */
|
||||
if (object_index < object_count) {
|
||||
*object_type = OBJECT_BINARY_INPUT;
|
||||
*instance = Binary_Input_Index_To_Instance(object_index);
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* return the length of the apdu encoded or -1 for error */
|
||||
int Device_Encode_Property_APDU(uint8_t * apdu,
|
||||
BACNET_PROPERTY_ID property,
|
||||
int32_t array_index,
|
||||
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
int len = 0; /* apdu len intermediate value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
unsigned i = 0;
|
||||
int object_type = 0;
|
||||
uint32_t instance = 0;
|
||||
unsigned count = 0;
|
||||
BACNET_TIME local_time;
|
||||
BACNET_DATE local_date;
|
||||
|
||||
/* FIXME: change the hardcoded names to suit your application */
|
||||
switch (property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE,
|
||||
Object_Instance_Number);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
characterstring_init_ansi(&char_string, Object_Name);
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_DEVICE);
|
||||
break;
|
||||
case PROP_DESCRIPTION:
|
||||
characterstring_init_ansi(&char_string, "BACnet Demo");
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_SYSTEM_STATUS:
|
||||
apdu_len =
|
||||
encode_tagged_enumerated(&apdu[0], Device_System_Status());
|
||||
break;
|
||||
case PROP_VENDOR_NAME:
|
||||
characterstring_init_ansi(&char_string, "ASHRAE");
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_VENDOR_IDENTIFIER:
|
||||
apdu_len =
|
||||
encode_tagged_unsigned(&apdu[0], Device_Vendor_Identifier());
|
||||
break;
|
||||
case PROP_MODEL_NAME:
|
||||
characterstring_init_ansi(&char_string, "GNU Demo");
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_FIRMWARE_REVISION:
|
||||
characterstring_init_ansi(&char_string, "0.3.3");
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_APPLICATION_SOFTWARE_VERSION:
|
||||
characterstring_init_ansi(&char_string, "1.0");
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_LOCATION:
|
||||
characterstring_init_ansi(&char_string, "USA");
|
||||
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_PROTOCOL_VERSION:
|
||||
apdu_len =
|
||||
encode_tagged_unsigned(&apdu[0], Device_Protocol_Version());
|
||||
break;
|
||||
case PROP_PROTOCOL_REVISION:
|
||||
apdu_len =
|
||||
encode_tagged_unsigned(&apdu[0], Device_Protocol_Revision());
|
||||
break;
|
||||
/* BACnet Legacy Support */
|
||||
case PROP_PROTOCOL_CONFORMANCE_CLASS:
|
||||
apdu_len = encode_tagged_unsigned(&apdu[0], 1);
|
||||
break;
|
||||
case PROP_PROTOCOL_SERVICES_SUPPORTED:
|
||||
/* Note: list of services that are executed, not initiated. */
|
||||
bitstring_init(&bit_string);
|
||||
for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) {
|
||||
/* automatic lookup based on handlers set */
|
||||
bitstring_set_bit(&bit_string, (uint8_t) i,
|
||||
apdu_service_supported(i));
|
||||
}
|
||||
apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
|
||||
/* Note: this is the list of objects that can be in this device,
|
||||
not a list of objects that this device can access */
|
||||
bitstring_init(&bit_string);
|
||||
for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) {
|
||||
/* 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);
|
||||
}
|
||||
/* FIXME: indicate the objects that YOU support */
|
||||
bitstring_set_bit(&bit_string, OBJECT_DEVICE, true);
|
||||
bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true);
|
||||
bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true);
|
||||
bitstring_set_bit(&bit_string, OBJECT_ANALOG_INPUT, true);
|
||||
bitstring_set_bit(&bit_string, OBJECT_BINARY_INPUT, true);
|
||||
apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_OBJECT_LIST:
|
||||
count = Device_Object_List_Count();
|
||||
/* Array element zero is the number of objects in the list */
|
||||
if (array_index == 0)
|
||||
apdu_len = encode_tagged_unsigned(&apdu[0], count);
|
||||
/* if no index was specified, then try to encode the entire list */
|
||||
/* into one packet. Note that more than likely you will have */
|
||||
/* to return an error if the number of encoded objects exceeds */
|
||||
/* your maximum APDU size. */
|
||||
else if (array_index == BACNET_ARRAY_ALL) {
|
||||
for (i = 1; i <= count; i++) {
|
||||
if (Device_Object_List_Identifier(i, &object_type,
|
||||
&instance)) {
|
||||
len =
|
||||
encode_tagged_object_id(&apdu[apdu_len],
|
||||
object_type, instance);
|
||||
apdu_len += len;
|
||||
/* assume next one is the same size as this one */
|
||||
/* can we all fit into the APDU? */
|
||||
if ((apdu_len + len) >= MAX_APDU) {
|
||||
*error_class = ERROR_CLASS_SERVICES;
|
||||
*error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* error: internal error? */
|
||||
*error_class = ERROR_CLASS_SERVICES;
|
||||
*error_code = ERROR_CODE_OTHER;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Device_Object_List_Identifier(array_index, &object_type,
|
||||
&instance))
|
||||
apdu_len =
|
||||
encode_tagged_object_id(&apdu[0], object_type,
|
||||
instance);
|
||||
else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_MAX_APDU_LENGTH_ACCEPTED:
|
||||
apdu_len = encode_tagged_unsigned(&apdu[0],
|
||||
Device_Max_APDU_Length_Accepted());
|
||||
break;
|
||||
case PROP_SEGMENTATION_SUPPORTED:
|
||||
apdu_len = encode_tagged_enumerated(&apdu[0],
|
||||
Device_Segmentation_Supported());
|
||||
break;
|
||||
case PROP_APDU_TIMEOUT:
|
||||
apdu_len = encode_tagged_unsigned(&apdu[0], Device_APDU_Timeout());
|
||||
break;
|
||||
case PROP_NUMBER_OF_APDU_RETRIES:
|
||||
apdu_len =
|
||||
encode_tagged_unsigned(&apdu[0],
|
||||
Device_Number_Of_APDU_Retries());
|
||||
break;
|
||||
case PROP_DEVICE_ADDRESS_BINDING:
|
||||
/* FIXME: encode the list here, if it exists */
|
||||
break;
|
||||
case PROP_DATABASE_REVISION:
|
||||
apdu_len =
|
||||
encode_tagged_unsigned(&apdu[0], Device_Database_Revision());
|
||||
break;
|
||||
case PROP_MAX_INFO_FRAMES:
|
||||
apdu_len =
|
||||
encode_tagged_unsigned(&apdu[0], dlmstp_max_info_frames());
|
||||
break;
|
||||
case PROP_MAX_MASTER:
|
||||
apdu_len = encode_tagged_unsigned(&apdu[0], dlmstp_max_master());
|
||||
break;
|
||||
case PROP_LOCAL_TIME:
|
||||
/* FIXME: if you support time */
|
||||
local_time.hour = 0;
|
||||
local_time.min = 0;
|
||||
local_time.sec = 0;
|
||||
local_time.hundredths = 0;
|
||||
apdu_len = encode_tagged_time(&apdu[0], &local_time);
|
||||
break;
|
||||
case PROP_UTC_OFFSET:
|
||||
/* Note: BACnet Time Zone is inverse of everybody else */
|
||||
apdu_len = encode_tagged_signed(&apdu[0], 5 /* EST */ );
|
||||
break;
|
||||
case PROP_LOCAL_DATE:
|
||||
/* FIXME: if you support date */
|
||||
local_date.year = 2006; /* AD */
|
||||
local_date.month = 4; /* Jan=1..Dec=12 */
|
||||
local_date.day = 11; /* 1..31 */
|
||||
local_date.wday = 0; /* 1=Mon..7=Sun */
|
||||
apdu_len = encode_tagged_date(&apdu[0], &local_date);
|
||||
break;
|
||||
case PROP_DAYLIGHT_SAVINGS_STATUS:
|
||||
/* FIXME: if you support time/date */
|
||||
apdu_len = encode_tagged_boolean(&apdu[0], false);
|
||||
break;
|
||||
case 9600:
|
||||
apdu_len = encode_tagged_unsigned(&apdu[0], RS485_Get_Baud_Rate());
|
||||
break;
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) {
|
||||
*error_class = ERROR_CLASS_OBJECT;
|
||||
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
return false;
|
||||
}
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(wp_data->application_data,
|
||||
wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
/* FIXME: len == 0: unable to decode? */
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) {
|
||||
if ((value.type.Object_Id.type == OBJECT_DEVICE) &&
|
||||
(Device_Set_Object_Instance_Number(value.type.
|
||||
Object_Id.instance))) {
|
||||
/* we could send an I-Am broadcast to let the world know */
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_MAX_INFO_FRAMES:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
if (value.type.Unsigned_Int <= 255) {
|
||||
dlmstp_set_max_info_frames(value.type.Unsigned_Int);
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_MAX_MASTER:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
if ((value.type.Unsigned_Int > 0) &&
|
||||
(value.type.Unsigned_Int <= 127)) {
|
||||
dlmstp_set_max_master(value.type.Unsigned_Int);
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
|
||||
uint8_t encoding;
|
||||
size_t len;
|
||||
|
||||
encoding =
|
||||
characterstring_encoding(&value.type.Character_String);
|
||||
len = characterstring_length(&value.type.Character_String);
|
||||
if (encoding == CHARACTER_ANSI_X34) {
|
||||
if (len <= 20) {
|
||||
/* FIXME: set the name */
|
||||
/* Display_Set_Name(
|
||||
characterstring_value(&value.type.Character_String)); */
|
||||
/* FIXME: All the object names in a device must be unique.
|
||||
Disallow setting the Device Object Name to any objects in
|
||||
the device. */
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case 9600:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
if (value.type.Unsigned_Int > 115200) {
|
||||
RS485_Set_Baud_Rate(value.type.Unsigned_Int);
|
||||
status = true;
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "bacdef.h"
|
||||
#include "mstp.h"
|
||||
#include "dlmstp.h"
|
||||
#include "rs485.h"
|
||||
#include "npdu.h"
|
||||
/* This file has been customized for use with the AT91SAM7S-EK */
|
||||
#include "board.h"
|
||||
|
||||
/* Number of MS/TP Packets Rx/Tx */
|
||||
uint16_t MSTP_Packets = 0;
|
||||
|
||||
/* receive buffer */
|
||||
static DLMSTP_PACKET Receive_Packet;
|
||||
static DLMSTP_PACKET Transmit_Packet;
|
||||
/* temp buffer for NPDU insertion */
|
||||
/* local MS/TP port data - shared with RS-485 */
|
||||
static volatile struct mstp_port_struct_t MSTP_Port;
|
||||
/* buffers needed by mstp port struct */
|
||||
static uint8_t TxBuffer[MAX_MPDU];
|
||||
static uint8_t RxBuffer[MAX_MPDU];
|
||||
|
||||
#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;}
|
||||
|
||||
void dlmstp_millisecond_timer(void)
|
||||
{
|
||||
INCREMENT_AND_LIMIT_UINT16(MSTP_Port.SilenceTimer);
|
||||
}
|
||||
|
||||
void dlmstp_copy_bacnet_address(BACNET_ADDRESS * dest, BACNET_ADDRESS * src)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (src && dest) {
|
||||
src->mac_len = dest->mac_len;
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
src->mac[i] = dest->mac[i];
|
||||
}
|
||||
src->net = dest->net;
|
||||
src->len = dest->len;
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
src->adr[i] = dest->adr[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool dlmstp_init(char *ifname)
|
||||
{
|
||||
(void)ifname;
|
||||
/* initialize packet */
|
||||
Receive_Packet.ready = false;
|
||||
Receive_Packet.pdu_len = 0;
|
||||
/* initialize hardware */
|
||||
RS485_Initialize();
|
||||
/* initialize MS/TP data structures */
|
||||
MSTP_Port.InputBuffer = &RxBuffer[0];
|
||||
MSTP_Port.InputBufferSize = sizeof(RxBuffer);
|
||||
MSTP_Port.OutputBuffer = &TxBuffer[0];
|
||||
MSTP_Port.OutputBufferSize = sizeof(TxBuffer);
|
||||
MSTP_Init(&MSTP_Port);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dlmstp_cleanup(void)
|
||||
{
|
||||
/* nothing to do for static buffers */
|
||||
}
|
||||
|
||||
/* returns number of bytes sent on success, zero on failure */
|
||||
int dlmstp_send_pdu(BACNET_ADDRESS * dest, /* destination address */
|
||||
BACNET_NPDU_DATA * npdu_data, /* network information */
|
||||
uint8_t * pdu, /* any data to be sent - may be null */
|
||||
unsigned pdu_len) /* number of bytes of data */
|
||||
{
|
||||
int bytes_sent = 0;
|
||||
unsigned i = 0; /* loop counter */
|
||||
|
||||
if (Transmit_Packet.ready == false) {
|
||||
if (npdu_data->data_expecting_reply) {
|
||||
Transmit_Packet.frame_type =
|
||||
FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY;
|
||||
} else {
|
||||
Transmit_Packet.frame_type =
|
||||
FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY;
|
||||
}
|
||||
Transmit_Packet.pdu_len = pdu_len;
|
||||
for (i = 0; i < pdu_len; i++) {
|
||||
Transmit_Packet.pdu[i] = pdu[i];
|
||||
}
|
||||
dlmstp_copy_bacnet_address(&Transmit_Packet.address, dest);
|
||||
bytes_sent = sizeof(Transmit_Packet);
|
||||
}
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
static void dlmstp_task(void)
|
||||
{
|
||||
/* only do receive state machine while we don't have a frame */
|
||||
if ((MSTP_Port.ReceivedValidFrame == false) &&
|
||||
(MSTP_Port.ReceivedInvalidFrame == false)) {
|
||||
do {
|
||||
RS485_Check_UART_Data(&MSTP_Port);
|
||||
MSTP_Receive_Frame_FSM(&MSTP_Port);
|
||||
if (MSTP_Port.ReceivedValidFrame ||
|
||||
MSTP_Port.ReceivedInvalidFrame)
|
||||
break;
|
||||
} while (MSTP_Port.DataAvailable);
|
||||
} else {
|
||||
/* toggle LED on Frame received */
|
||||
volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA;
|
||||
if ((pPIO->PIO_ODSR & LED2) == LED2)
|
||||
pPIO->PIO_CODR = LED2;
|
||||
else
|
||||
pPIO->PIO_SODR = LED2;
|
||||
|
||||
}
|
||||
/* only do master state machine while rx is idle */
|
||||
if (MSTP_Port.receive_state == MSTP_RECEIVE_STATE_IDLE) {
|
||||
while (MSTP_Master_Node_FSM(&MSTP_Port)) {
|
||||
/* do nothing */
|
||||
};
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* copy the packet if one is received.
|
||||
Return the length of the packet */
|
||||
uint16_t dlmstp_receive(
|
||||
BACNET_ADDRESS * src, /* source address */
|
||||
uint8_t * pdu, /* PDU data */
|
||||
uint16_t max_pdu, /* amount of space available in the PDU */
|
||||
unsigned timeout) /* milliseconds to wait for a packet */
|
||||
{
|
||||
unsigned i = 0; /* loop counter */
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
|
||||
dlmstp_task();
|
||||
/* see if there is a packet available, and a place
|
||||
to put the reply (if necessary) and process it */
|
||||
if (Receive_Packet.ready) {
|
||||
if (Receive_Packet.pdu_len) {
|
||||
MSTP_Packets++;
|
||||
for (i = 0; i < Receive_Packet.pdu_len; i++) {
|
||||
pdu[i] = Receive_Packet.pdu[i];
|
||||
}
|
||||
dlmstp_copy_bacnet_address(src, &Receive_Packet.address);
|
||||
pdu_len = Receive_Packet.pdu_len;
|
||||
}
|
||||
Receive_Packet.ready = false;
|
||||
}
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
void dlmstp_fill_bacnet_address(BACNET_ADDRESS * src, uint8_t mstp_address)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (mstp_address == MSTP_BROADCAST_ADDRESS) {
|
||||
/* mac_len = 0 if broadcast address */
|
||||
src->mac_len = 0;
|
||||
src->mac[0] = 0;
|
||||
} else {
|
||||
src->mac_len = 1;
|
||||
src->mac[0] = mstp_address;
|
||||
}
|
||||
/* fill with 0's starting with index 1; index 0 filled above */
|
||||
for (i = 1; i < MAX_MAC_LEN; i++) {
|
||||
src->mac[i] = 0;
|
||||
}
|
||||
src->net = 0;
|
||||
src->len = 0;
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
src->adr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* for the MS/TP state machine to use for putting received data */
|
||||
uint16_t MSTP_Put_Receive(
|
||||
volatile struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
DLMSTP_PACKET packet;
|
||||
uint16_t pdu_len = mstp_port->DataLength;
|
||||
unsigned i = 0;
|
||||
|
||||
/* bounds check - maybe this should send an abort? */
|
||||
if (pdu_len > sizeof(packet.pdu))
|
||||
pdu_len = sizeof(packet.pdu);
|
||||
if (pdu_len) {
|
||||
MSTP_Packets++;
|
||||
for (i = 0; i < Receive_Packet.pdu_len; i++) {
|
||||
Receive_Packet.pdu[i] = mstp_port->InputBuffer[i];
|
||||
}
|
||||
dlmstp_fill_bacnet_address(&Receive_Packet.address,
|
||||
mstp_port->SourceAddress);
|
||||
Receive_Packet.pdu_len = pdu_len;
|
||||
Receive_Packet.ready = true;
|
||||
}
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Send(
|
||||
uint8_t src, /* source MS/TP address for creating packet */
|
||||
uint8_t * pdu, /* data to send */
|
||||
uint16_t max_pdu, /* amount of space available */
|
||||
unsigned timeout) /* milliseconds to wait for a packet */
|
||||
{
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
uint8_t destination = 0; /* destination address */
|
||||
|
||||
if (Transmit_Packet.ready) {
|
||||
/* load destination MAC address */
|
||||
if (Transmit_Packet.address.mac_len == 1) {
|
||||
destination = Transmit_Packet.address.mac[0];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
if ((8 /* header len */ + Transmit_Packet.pdu_len) > MAX_MPDU) {
|
||||
return 0;
|
||||
}
|
||||
/* convert the PDU into the MSTP Frame */
|
||||
pdu_len = MSTP_Create_Frame(
|
||||
pdu, /* <-- loading this */
|
||||
max_pdu,
|
||||
Transmit_Packet.frame_type,
|
||||
destination, src,
|
||||
&Transmit_Packet.pdu[0],
|
||||
Transmit_Packet.pdu_len);
|
||||
}
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
void dlmstp_set_mac_address(uint8_t mac_address)
|
||||
{
|
||||
/* Master Nodes can only have address 0-127 */
|
||||
if (mac_address <= 127) {
|
||||
MSTP_Port.This_Station = mac_address;
|
||||
/* FIXME: implement your data storage */
|
||||
/* I2C_Write_Byte(
|
||||
EEPROM_DEVICE_ADDRESS,
|
||||
mac_address,
|
||||
EEPROM_MSTP_MAC_ADDR); */
|
||||
if (mac_address > MSTP_Port.Nmax_master)
|
||||
dlmstp_set_max_master(mac_address);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t dlmstp_mac_address(void)
|
||||
{
|
||||
return MSTP_Port.This_Station;
|
||||
}
|
||||
|
||||
/* This parameter represents the value of the Max_Info_Frames property of */
|
||||
/* the node's Device object. The value of Max_Info_Frames specifies the */
|
||||
/* maximum number of information frames the node may send before it must */
|
||||
/* pass the token. Max_Info_Frames may have different values on different */
|
||||
/* nodes. This may be used to allocate more or less of the available link */
|
||||
/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */
|
||||
/* node, its value shall be 1. */
|
||||
void dlmstp_set_max_info_frames(uint8_t max_info_frames)
|
||||
{
|
||||
if (max_info_frames >= 1) {
|
||||
MSTP_Port.Nmax_info_frames = max_info_frames;
|
||||
/* FIXME: implement your data storage */
|
||||
/* I2C_Write_Byte(
|
||||
EEPROM_DEVICE_ADDRESS,
|
||||
(uint8_t)max_info_frames,
|
||||
EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t dlmstp_max_info_frames(void)
|
||||
{
|
||||
return MSTP_Port.Nmax_info_frames;
|
||||
}
|
||||
|
||||
/* This parameter represents the value of the Max_Master property of the */
|
||||
/* node's Device object. The value of Max_Master specifies the highest */
|
||||
/* allowable address for master nodes. The value of Max_Master shall be */
|
||||
/* less than or equal to 127. If Max_Master is not writable in a node, */
|
||||
/* its value shall be 127. */
|
||||
void dlmstp_set_max_master(uint8_t max_master)
|
||||
{
|
||||
if (max_master <= 127) {
|
||||
if (MSTP_Port.This_Station <= max_master) {
|
||||
MSTP_Port.Nmax_master = max_master;
|
||||
/* FIXME: implement your data storage */
|
||||
/* I2C_Write_Byte(
|
||||
EEPROM_DEVICE_ADDRESS,
|
||||
max_master,
|
||||
EEPROM_MSTP_MAX_MASTER_ADDR); */
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t dlmstp_max_master(void)
|
||||
{
|
||||
return MSTP_Port.Nmax_master;
|
||||
}
|
||||
|
||||
void dlmstp_get_my_address(BACNET_ADDRESS * my_address)
|
||||
{
|
||||
int i = 0; /* counter */
|
||||
|
||||
my_address->mac_len = 1;
|
||||
my_address->mac[0] = MSTP_Port.This_Station;
|
||||
my_address->net = 0; /* local only, no routing */
|
||||
my_address->len = 0;
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
my_address->adr[i] = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest)
|
||||
{ /* destination address */
|
||||
int i = 0; /* counter */
|
||||
|
||||
if (dest) {
|
||||
dest->mac_len = 1;
|
||||
dest->mac[0] = MSTP_BROADCAST_ADDRESS;
|
||||
dest->net = BACNET_BROADCAST_NETWORK;
|
||||
dest->len = 0; /* always zero when DNET is broadcast */
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
dest->adr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "rp.h"
|
||||
/* demo objects */
|
||||
#include "device.h"
|
||||
#include "ai.h"
|
||||
#include "av.h"
|
||||
#include "bi.h"
|
||||
#include "bv.h"
|
||||
|
||||
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
|
||||
|
||||
/* Encodes the property APDU and returns the length,
|
||||
or sets the error, and returns -1 */
|
||||
int Encode_Property_APDU(
|
||||
uint8_t * apdu,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID property,
|
||||
int32_t array_index,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
int apdu_len = -1;
|
||||
|
||||
/* initialize the default return values */
|
||||
*error_class = ERROR_CLASS_OBJECT;
|
||||
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
/* handle each object type */
|
||||
switch(object_type) {
|
||||
case OBJECT_DEVICE:
|
||||
if ((object_instance == Device_Object_Instance_Number()) ||
|
||||
(object_instance == BACNET_MAX_INSTANCE)) {
|
||||
apdu_len = Device_Encode_Property_APDU(
|
||||
&apdu[0],
|
||||
property,
|
||||
array_index,
|
||||
error_class, error_code);
|
||||
}
|
||||
break;
|
||||
case OBJECT_ANALOG_INPUT:
|
||||
if (Analog_Input_Valid_Instance(object_instance)) {
|
||||
apdu_len = Analog_Input_Encode_Property_APDU(
|
||||
&apdu[0],
|
||||
object_instance,
|
||||
property,
|
||||
array_index,
|
||||
error_class, error_code);
|
||||
}
|
||||
break;
|
||||
case OBJECT_ANALOG_VALUE:
|
||||
if (Analog_Value_Valid_Instance(object_instance)) {
|
||||
apdu_len = Analog_Value_Encode_Property_APDU(&Temp_Buf[0],
|
||||
object_instance,
|
||||
property,
|
||||
array_index,
|
||||
error_class, error_code);
|
||||
}
|
||||
break;
|
||||
case OBJECT_BINARY_INPUT:
|
||||
if (Binary_Input_Valid_Instance(object_instance)) {
|
||||
apdu_len = Binary_Input_Encode_Property_APDU(
|
||||
&apdu[0],
|
||||
object_instance,
|
||||
property,
|
||||
array_index,
|
||||
error_class, error_code);
|
||||
}
|
||||
break;
|
||||
case OBJECT_BINARY_VALUE:
|
||||
if (Binary_Value_Valid_Instance(object_instance)) {
|
||||
apdu_len = Binary_Value_Encode_Property_APDU(&Temp_Buf[0],
|
||||
object_instance,
|
||||
property,
|
||||
array_index,
|
||||
error_class, error_code);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*error_class = ERROR_CLASS_OBJECT;
|
||||
*error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
void handler_read_property(uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_READ_PROPERTY_DATA data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
bool error = false;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
len = rp_decode_service_request(service_request, service_len, &data);
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
|
||||
&my_address, &npdu_data);
|
||||
if (len < 0) {
|
||||
/* bad decoding - send an abort */
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
} else if (service_data->segmented_message) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
} else {
|
||||
/* most cases will be error */
|
||||
error = true;
|
||||
len = Encode_Property_APDU(
|
||||
&Temp_Buf[0],
|
||||
data.object_type,
|
||||
data.object_instance,
|
||||
data.object_property,
|
||||
data.array_index,
|
||||
&error_class, &error_code);
|
||||
if (len >= 0) {
|
||||
/* encode the APDU portion of the packet */
|
||||
data.application_data = &Temp_Buf[0];
|
||||
data.application_data_len = len;
|
||||
/* FIXME: probably need a length limitation sent with encode */
|
||||
len =
|
||||
rp_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
error = false;
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
switch (len) {
|
||||
/* BACnet APDU too small to fit data, so proper response is Abort */
|
||||
case -2:
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
break;
|
||||
case -1:
|
||||
default:
|
||||
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, error_class, error_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu(src, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "wp.h"
|
||||
/* demo objects */
|
||||
#include "device.h"
|
||||
#include "ai.h"
|
||||
#include "av.h"
|
||||
#include "bi.h"
|
||||
#include "bv.h"
|
||||
|
||||
/* too big to reside on stack frame for PIC */
|
||||
static BACNET_WRITE_PROPERTY_DATA wp_data;
|
||||
|
||||
void handler_write_property(uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
/* decode the service request only */
|
||||
len = wp_decode_service_request(service_request,
|
||||
service_len, &wp_data);
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
|
||||
&my_address, &npdu_data);
|
||||
/* bad decoding or something we didn't understand - send an abort */
|
||||
if (len <= 0) {
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
} else if (service_data->segmented_message) {
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
} else {
|
||||
switch (wp_data.object_type) {
|
||||
case OBJECT_DEVICE:
|
||||
if (Device_Write_Property(&wp_data, &error_class, &error_code)) {
|
||||
len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY);
|
||||
} else {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
|
||||
error_code);
|
||||
}
|
||||
break;
|
||||
case OBJECT_ANALOG_INPUT:
|
||||
case OBJECT_BINARY_INPUT:
|
||||
error_class = ERROR_CLASS_PROPERTY;
|
||||
error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
error_class, error_code);
|
||||
break;
|
||||
case OBJECT_BINARY_VALUE:
|
||||
if (Binary_Value_Write_Property(&wp_data, &error_class,
|
||||
&error_code)) {
|
||||
len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY);
|
||||
} else {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
|
||||
error_code);
|
||||
}
|
||||
break;
|
||||
case OBJECT_ANALOG_VALUE:
|
||||
if (Analog_Value_Write_Property(&wp_data, &error_class,
|
||||
&error_code)) {
|
||||
len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY);
|
||||
} else {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
|
||||
error_code);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
error_class, error_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu(src, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// ATMEL Microcontroller Software Support - ROUSSET -
|
||||
// ----------------------------------------------------------------------------
|
||||
// The software is delivered "AS IS" without warranty or condition of any
|
||||
// kind, either express, implied or statutory. This includes without
|
||||
// limitation any warranty or condition with respect to merchantability or
|
||||
// fitness for any particular purpose, or against the infringements of
|
||||
// intellectual property rights of others.
|
||||
// ----------------------------------------------------------------------------
|
||||
// File Name : Cstartup_SAM7.c
|
||||
// Object : Low level initializations written in C for IAR tools
|
||||
// 1.0 08/Sep/04 JPP : Creation
|
||||
// 1.10 10/Sep/04 JPP : Update AT91C_CKGR_PLLCOUNT filed
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Include the board file description
|
||||
#include "AT91SAM7S256.h"
|
||||
#include "Board.h"
|
||||
|
||||
// The following functions must be write in ARM mode this function called directly
|
||||
// by exception vector
|
||||
extern void AT91F_Spurious_handler(void);
|
||||
extern void AT91F_Default_IRQ_handler(void);
|
||||
extern void AT91F_Default_FIQ_handler(void);
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_LowLevelInit
|
||||
//* \brief This function performs very low level HW initialization
|
||||
//* this function can be use a Stack, depending the compilation
|
||||
//* optimization mode
|
||||
//*----------------------------------------------------------------------------
|
||||
void LowLevelInit(void)
|
||||
{
|
||||
int i;
|
||||
AT91PS_PMC pPMC = AT91C_BASE_PMC;
|
||||
|
||||
//* Set Flash Wait sate
|
||||
// Single Cycle Access at Up to 30 MHz, or 40
|
||||
// if MCK = 48054841 I have 50 Cycle for 1 usecond ( flied MC_FMR->FMCN
|
||||
// result: AT91C_MC_FMR = 0x00320100 (MC Flash Mode Register)
|
||||
AT91C_BASE_MC->MC_FMR = (((AT91C_MC_FMCN) & (50 <<16)) | AT91C_MC_FWS_1FWS);
|
||||
|
||||
//* Watchdog Disable
|
||||
// result: AT91C_WDTC_WDMR = 0x00008000 (Watchdog Mode Register)
|
||||
AT91C_BASE_WDTC->WDTC_WDMR= AT91C_WDTC_WDDIS;
|
||||
|
||||
//* Set MCK at 48 054 841
|
||||
// 1 Enabling the Main Oscillator:
|
||||
// SCK = 1/32768 = 30.51 uSecond
|
||||
// Start up time = 8 * 6 / SCK = 56 * 30.51 = 1,46484375 ms
|
||||
// result: AT91C_CKGR_MOR = 0x00000601 (Main Oscillator Register)
|
||||
pPMC->PMC_MOR = ((AT91C_CKGR_OSCOUNT & (0x06<<8)) | AT91C_CKGR_MOSCEN);
|
||||
|
||||
// Wait the startup time
|
||||
while(!(pPMC->PMC_SR & AT91C_PMC_MOSCS));
|
||||
|
||||
// PMC Clock Generator PLL Register setup
|
||||
//
|
||||
// The following settings are used: DIV = 14
|
||||
// MUL = 72
|
||||
// PLLCOUNT = 10
|
||||
//
|
||||
// Main Clock (MAINCK from crystal oscillator) = 18432000 hz (see AT91SAM7-EK schematic)
|
||||
// MAINCK / DIV = 18432000/14 = 1316571 hz
|
||||
// PLLCK = 1316571 * (MUL + 1) = 1316571 * (72 + 1) = 1316571 * 73 = 96109683 hz
|
||||
//
|
||||
// PLLCOUNT = number of slow clock cycles before the LOCK bit is set
|
||||
// in PMC_SR after CKGR_PLLR is written.
|
||||
//
|
||||
// PLLCOUNT = 10
|
||||
//
|
||||
// OUT = 0 (not used)
|
||||
// result: AT91C_CKGR_PLLR = 0x00000000480A0E (PLL Register)
|
||||
pPMC->PMC_PLLR = ((AT91C_CKGR_DIV & 14) |
|
||||
(AT91C_CKGR_PLLCOUNT & (10<<8)) |
|
||||
(AT91C_CKGR_MUL & (72<<16)));
|
||||
|
||||
// Wait the startup time (until PMC Status register LOCK bit is set)
|
||||
while(!(pPMC->PMC_SR & AT91C_PMC_LOCK));
|
||||
|
||||
// PMC Master Clock (MCK) Register setup
|
||||
//
|
||||
// CSS = 3 (PLLCK clock selected)
|
||||
//
|
||||
// PRES = 1 (MCK = PLLCK / 2) = 96109683/2 = 48054841 hz
|
||||
//
|
||||
// Note: Master Clock MCK = 48054841 hz (this is the CPU clock speed)
|
||||
// result: AT91C_PMC_MCKR = 0x00000007 (Master Clock Register)
|
||||
pPMC->PMC_MCKR = AT91C_PMC_CSS_PLL_CLK | AT91C_PMC_PRES_CLK_2;
|
||||
|
||||
// Set up the default interrupts handler vectors
|
||||
AT91C_BASE_AIC->AIC_SVR[0] = (int) AT91F_Default_FIQ_handler;
|
||||
for (i = 1; i < 31; i++)
|
||||
{
|
||||
AT91C_BASE_AIC->AIC_SVR[i] = (int) AT91F_Default_IRQ_handler;
|
||||
}
|
||||
AT91C_BASE_AIC->AIC_SPU = (int) AT91F_Spurious_handler;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// **********************************************************************************************
|
||||
//
|
||||
// File Name : isr.c
|
||||
// Title : interrupt enable/disable functions
|
||||
//
|
||||
//
|
||||
// This module provides the interface routines for setting up and
|
||||
// controlling the various interrupt modes present on the ARM processor.
|
||||
// Copyright 2004, R O SoftWare
|
||||
// No guarantees, warrantees, or promises, implied or otherwise.
|
||||
// May be used for hobby or commercial purposes provided copyright
|
||||
// notice remains intact.
|
||||
//
|
||||
// Note from Jim Lynch:
|
||||
// This module was developed by Bill Knight, RO Software and used with his permission.
|
||||
// Taken from the Yahoo LPC2000 User's Group - Files Section 'UT050418A.ZIP'
|
||||
// Specifically, the module armVIC.c with the include file references removed
|
||||
// **********************************************************************************************
|
||||
|
||||
#define IRQ_MASK 0x00000080
|
||||
#define FIQ_MASK 0x00000040
|
||||
#define INT_MASK (IRQ_MASK | FIQ_MASK)
|
||||
|
||||
static inline unsigned __get_cpsr(void)
|
||||
{
|
||||
unsigned long retval;
|
||||
asm volatile (" mrs %0, cpsr" : "=r" (retval) : /* no inputs */ );
|
||||
return retval;
|
||||
}
|
||||
|
||||
static inline void __set_cpsr(unsigned val)
|
||||
{
|
||||
asm volatile (" msr cpsr, %0" : /* no outputs */ : "r" (val) );
|
||||
}
|
||||
|
||||
unsigned disableIRQ(void)
|
||||
{
|
||||
unsigned _cpsr;
|
||||
_cpsr = __get_cpsr();
|
||||
__set_cpsr(_cpsr | IRQ_MASK);
|
||||
return _cpsr;
|
||||
}
|
||||
|
||||
unsigned restoreIRQ(unsigned oldCPSR)
|
||||
{
|
||||
unsigned _cpsr;
|
||||
|
||||
_cpsr = __get_cpsr();
|
||||
__set_cpsr((_cpsr & ~IRQ_MASK) | (oldCPSR & IRQ_MASK));
|
||||
return _cpsr;
|
||||
}
|
||||
|
||||
unsigned enableIRQ(void)
|
||||
{
|
||||
unsigned _cpsr;
|
||||
|
||||
_cpsr = __get_cpsr();
|
||||
__set_cpsr(_cpsr & ~IRQ_MASK);
|
||||
return _cpsr;
|
||||
}
|
||||
|
||||
unsigned disableFIQ(void)
|
||||
{
|
||||
unsigned _cpsr;
|
||||
|
||||
_cpsr = __get_cpsr();
|
||||
__set_cpsr(_cpsr | FIQ_MASK);
|
||||
return _cpsr;
|
||||
}
|
||||
|
||||
unsigned restoreFIQ(unsigned oldCPSR)
|
||||
{
|
||||
unsigned _cpsr;
|
||||
|
||||
_cpsr = __get_cpsr();
|
||||
__set_cpsr((_cpsr & ~FIQ_MASK) | (oldCPSR & FIQ_MASK));
|
||||
return _cpsr;
|
||||
}
|
||||
|
||||
unsigned enableFIQ(void)
|
||||
{
|
||||
unsigned _cpsr;
|
||||
|
||||
_cpsr = __get_cpsr();
|
||||
__set_cpsr(_cpsr & ~FIQ_MASK);
|
||||
return _cpsr;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include "AT91SAM7S256.h"
|
||||
#include "board.h"
|
||||
#include "math.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include "stdbool.h"
|
||||
/* BACnet */
|
||||
#include "rs485.h"
|
||||
#include "datalink.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "dcc.h"
|
||||
#include "iam.h"
|
||||
#include "handlers.h"
|
||||
|
||||
// *******************************************************
|
||||
// FIXME: put in header files External References
|
||||
// *******************************************************
|
||||
extern void LowLevelInit(void);
|
||||
extern unsigned enableIRQ(void);
|
||||
extern unsigned enableFIQ(void);
|
||||
|
||||
extern void TimerInit(void);
|
||||
extern volatile unsigned long Timer_Milliseconds;
|
||||
|
||||
// *******************************************************
|
||||
// Global Variables - ???
|
||||
// *******************************************************
|
||||
unsigned int FiqCount = 0;
|
||||
|
||||
|
||||
static unsigned long LED_Timer_1 = 0;
|
||||
static unsigned long LED_Timer_2 = 0;
|
||||
static unsigned long LED_Timer_3 = 0;
|
||||
static unsigned long LED_Timer_4 = 1000;
|
||||
|
||||
void millisecond_timer(void)
|
||||
{
|
||||
while (Timer_Milliseconds) {
|
||||
Timer_Milliseconds--;
|
||||
if (LED_Timer_1) {
|
||||
LED_Timer_1--;
|
||||
}
|
||||
if (LED_Timer_2) {
|
||||
LED_Timer_2--;
|
||||
}
|
||||
if (LED_Timer_3) {
|
||||
LED_Timer_3--;
|
||||
}
|
||||
if (LED_Timer_4) {
|
||||
LED_Timer_4--;
|
||||
}
|
||||
dlmstp_millisecond_timer();
|
||||
}
|
||||
}
|
||||
|
||||
int main (void) {
|
||||
unsigned long IdleCount = 0; // idle loop blink counter (2x)
|
||||
bool LED3_Off_Enabled = true;
|
||||
uint16_t pdu_len = 0;
|
||||
BACNET_ADDRESS src; /* source address */
|
||||
uint8_t pdu[MAX_MPDU]; /* PDU data */
|
||||
|
||||
// Initialize the Atmel AT91SAM7S256 (watchdog, PLL clock, default interrupts, etc.)
|
||||
LowLevelInit();
|
||||
TimerInit();
|
||||
/* Initialize the Parallel I/O Controller A Peripheral Clock */
|
||||
volatile AT91PS_PMC pPMC = AT91C_BASE_PMC;
|
||||
pPMC->PMC_PCER = pPMC->PMC_PCSR | (1<<AT91C_ID_PIOA);
|
||||
|
||||
// Set up the LEDs (PA0 - PA3)
|
||||
volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; // pointer to PIO data structure
|
||||
pPIO->PIO_PER = LED_MASK | SW1_MASK; // PIO Enable Register - allow PIO to control pins P0 - P3 and pin 19
|
||||
pPIO->PIO_OER = LED_MASK; // PIO Output Enable Register - sets pins P0 - P3 to outputs
|
||||
pPIO->PIO_SODR = LED_MASK; // PIO Set Output Data Register - turns off the four LEDs
|
||||
|
||||
// Select PA19 (pushbutton) to be FIQ function (Peripheral B)
|
||||
pPIO->PIO_BSR = SW1_MASK;
|
||||
|
||||
// Set up the AIC registers for FIQ (pushbutton SW1)
|
||||
volatile AT91PS_AIC pAIC = AT91C_BASE_AIC; // pointer to AIC data structure
|
||||
pAIC->AIC_IDCR = (1<<AT91C_ID_FIQ); // Disable FIQ interrupt in AIC Interrupt Disable Command Register
|
||||
pAIC->AIC_SMR[AT91C_ID_FIQ] = // Set the interrupt source type in AIC Source
|
||||
(AT91C_AIC_SRCTYPE_INT_EDGE_TRIGGERED); // Mode Register[0]
|
||||
pAIC->AIC_ICCR = (1<<AT91C_ID_FIQ); // Clear the FIQ interrupt in AIC Interrupt Clear Command Register
|
||||
pAIC->AIC_IDCR = (0<<AT91C_ID_FIQ); // Remove disable FIQ interrupt in AIC Interrupt Disable Command Register
|
||||
pAIC->AIC_IECR = (1<<AT91C_ID_FIQ); // Enable the FIQ interrupt in AIC Interrupt Enable Command Register
|
||||
|
||||
#if defined(BACDL_MSTP)
|
||||
RS485_Set_Baud_Rate(38400);
|
||||
dlmstp_set_mac_address(55);
|
||||
dlmstp_set_max_master(127);
|
||||
dlmstp_set_max_info_frames(1);
|
||||
dlmstp_init(NULL);
|
||||
#endif
|
||||
|
||||
#ifndef DLMSTP_TEST
|
||||
/* we need to handle who-is to support dynamic device binding */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
|
||||
handler_who_is);
|
||||
/* Set the handlers for any confirmed services that we support. */
|
||||
/* We must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_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);
|
||||
#endif
|
||||
|
||||
// enable interrupts
|
||||
enableIRQ();
|
||||
enableFIQ();
|
||||
|
||||
// endless blink loop
|
||||
while (1) {
|
||||
millisecond_timer();
|
||||
/* interrupt turns on the LED, we turn it off */
|
||||
if (((pPIO->PIO_ODSR & LED3) == LED3) && (LED3_Off_Enabled))
|
||||
{
|
||||
LED3_Off_Enabled = false;
|
||||
/* wait */
|
||||
LED_Timer_3 = 250;
|
||||
}
|
||||
if (!LED_Timer_3) {
|
||||
/* turn LED3 (DS3) off */
|
||||
pPIO->PIO_SODR = LED3;
|
||||
LED3_Off_Enabled = true;
|
||||
}
|
||||
if (!LED_Timer_4) {
|
||||
if ((pPIO->PIO_ODSR & LED4) == LED4)
|
||||
pPIO->PIO_CODR = LED4; // turn LED2 (DS2) on
|
||||
else
|
||||
pPIO->PIO_SODR = LED4; // turn LED2 (DS2) off
|
||||
/* wait */
|
||||
LED_Timer_4 = 1000;
|
||||
}
|
||||
|
||||
IdleCount++; // count # of times through the idle loop
|
||||
|
||||
/* BACnet handling */
|
||||
pdu_len = datalink_receive(&src, &pdu[0], MAX_MPDU, 0);
|
||||
if (pdu_len) {
|
||||
pPIO->PIO_CODR = LED3;
|
||||
#ifndef DLMSTP_TEST
|
||||
npdu_handler(&src, &pdu[0], pdu_len);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Makefile for AT91SAM7S evaluation kit with RS-485 Transceiver on UART0
|
||||
# Written by Steve Karg <skarg@users.sourceforge.net> 06-Aug-2007
|
||||
|
||||
TARGET=bacnet
|
||||
|
||||
# Tools
|
||||
CC=arm-elf-gcc
|
||||
OBJCOPY=arm-elf-objcopy
|
||||
OBJDUMP=arm-elf-objdump
|
||||
|
||||
LDSCRIPT=at91sam7s256.ld
|
||||
|
||||
BACNET_FLAGS = -DBACDL_MSTP=1 -DPRINT_ENABLED=0 -DBIG_ENDIAN=0 -DMAX_APDU=480 -DDLMSTP_TEST
|
||||
INCLUDES = -I. -I../.. -I../../demo/handler -I../../demo/object
|
||||
OPTIMIZATION = -O0
|
||||
CFLAGS = -fno-common $(OPTIMIZATION) $(INCLUDES) $(BACNET_FLAGS) -Wall -g
|
||||
# -Wa,<options> Pass comma-separated <options> on to the assembler
|
||||
AFLAGS = -Wa,-ahls,-mapcs-32,-adhlns=$(<:.s=.lst)
|
||||
# -Wl,<options> Pass comma-separated <options> on to the linker
|
||||
LDFLAGS = -nostartfiles -Wl,-Map=$(TARGET).map,-lc,-lgcc,-lm,-T$(LDSCRIPT)
|
||||
CPFLAGS = --output-target=binary
|
||||
ODFLAGS = -x --syms
|
||||
|
||||
ASRC = crt.s
|
||||
|
||||
PORTSRC = main.c \
|
||||
timer.c \
|
||||
isr.c \
|
||||
init.c \
|
||||
blinker.c \
|
||||
rs485.c \
|
||||
dlmstp.c \
|
||||
../../crc.c \
|
||||
../../mstp.c
|
||||
|
||||
CORESRC = device.c \
|
||||
ai.c \
|
||||
av.c \
|
||||
bi.c \
|
||||
bv.c \
|
||||
h_rp.c \
|
||||
h_wp.c \
|
||||
../../npdu.c \
|
||||
../../bacint.c \
|
||||
../../apdu.c \
|
||||
../../bacdcode.c \
|
||||
../../bacstr.c \
|
||||
../../abort.c \
|
||||
../../bacerror.c \
|
||||
../../reject.c \
|
||||
../../bacapp.c \
|
||||
../../datetime.c \
|
||||
../../rp.c \
|
||||
../../wp.c \
|
||||
../../dcc.c \
|
||||
../../rd.c \
|
||||
../../whois.c \
|
||||
../../iam.c \
|
||||
../../demo\handler\txbuf.c \
|
||||
../../demo\handler\h_whois.c \
|
||||
../../demo\handler\h_rd.c \
|
||||
../../demo\handler\h_dcc.c
|
||||
|
||||
#CSRC = $(PORTSRC) $(CORESRC)
|
||||
CSRC = $(PORTSRC)
|
||||
|
||||
AOBJ = $(ASRC:.s=.o)
|
||||
COBJ = $(CSRC:.c=.o)
|
||||
|
||||
all: $(TARGET).bin
|
||||
$(OBJDUMP) $(ODFLAGS) $(TARGET).elf > $(TARGET).dmp
|
||||
|
||||
$(TARGET).bin: $(TARGET).elf
|
||||
$(OBJCOPY) $(TARGET).elf $(CPFLAGS) $(TARGET).bin
|
||||
|
||||
$(TARGET).elf: $(COBJ) $(AOBJ) Makefile
|
||||
$(CC) $(CFLAGS) $(AOBJ) $(COBJ) $(LDFLAGS) -o $@
|
||||
|
||||
$(COBJ): %.o : %.c Makefile
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(AOBJ): %.o : %.s Makefile
|
||||
$(CC) -c $(AFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
touch Makefile
|
||||
rm $(COBJ) $(AOBJ)
|
||||
rm $(TARGET).elf $(TARGET).bin $(TARGET).dmp $(TARGET).map
|
||||
rm *.lst
|
||||
@@ -0,0 +1,222 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* The module handles sending data out the RS-485 port */
|
||||
/* and handles receiving data from the RS-485 port. */
|
||||
/* Customize this file for your specific hardware */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "mstp.h"
|
||||
|
||||
/* This file has been customized for use with UART0
|
||||
on the AT91SAM7S-EK */
|
||||
#include "board.h"
|
||||
|
||||
/* UART */
|
||||
static AT91S_USART *RS485_Interface = AT91C_BASE_US0;
|
||||
/* baud rate */
|
||||
static int RS485_Baud = 38400;
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Sets the interface - UART0 or UART1 or...
|
||||
* RETURN: none
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
void RS485_Set_Interface(char *ifname)
|
||||
{
|
||||
RS485_Interface = (AT91S_USART *)ifname;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in
|
||||
* receive mode.
|
||||
* RETURN: none
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
void RS485_Initialize(void)
|
||||
{
|
||||
// enable the USART0 peripheral clock
|
||||
volatile AT91PS_PMC pPMC = AT91C_BASE_PMC;
|
||||
pPMC->PMC_PCER = pPMC->PMC_PCSR | (1<<AT91C_ID_US0);
|
||||
|
||||
/* enable the peripheral by disabling the pin in the PIO controller */
|
||||
*AT91C_PIOA_PDR = AT91C_PA5_RXD0 | AT91C_PA6_TXD0 | AT91C_PA7_RTS0;
|
||||
|
||||
RS485_Interface->US_CR =
|
||||
AT91C_US_RSTRX | /* Reset Receiver */
|
||||
AT91C_US_RSTTX | /* Reset Transmitter */
|
||||
AT91C_US_RXDIS | /* Receiver Disable */
|
||||
AT91C_US_TXDIS; /* Transmitter Disable */
|
||||
|
||||
RS485_Interface->US_MR =
|
||||
AT91C_US_USMODE_RS485 | /* RS-485 Mode - RTS auto asserted */
|
||||
AT91C_US_CLKS_CLOCK | /* Clock = MCK */
|
||||
AT91C_US_CHRL_8_BITS | /* 8-bit Data */
|
||||
AT91C_US_PAR_NONE | /* No Parity */
|
||||
AT91C_US_NBSTOP_1_BIT; /* 1 Stop Bit */
|
||||
|
||||
/* set the Time Guard to release RTS after x bit times */
|
||||
RS485_Interface->US_TTGR = 4;
|
||||
|
||||
/* baud rate */
|
||||
RS485_Interface->US_BRGR = MCK/16/RS485_Baud;
|
||||
|
||||
RS485_Interface->US_CR =
|
||||
AT91C_US_RXEN | /* Receiver Enable */
|
||||
AT91C_US_TXEN; /* Transmitter Enable */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void RS485_Cleanup(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Returns the baud rate that we are currently running at
|
||||
* RETURN: none
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
uint32_t RS485_Get_Baud_Rate(void)
|
||||
{
|
||||
return RS485_Baud;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Sets the baud rate for the chip USART
|
||||
* RETURN: none
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
bool RS485_Set_Baud_Rate(uint32_t baud)
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
switch (baud) {
|
||||
case 9600:
|
||||
case 19200:
|
||||
case 38400:
|
||||
case 57600:
|
||||
case 115200:
|
||||
RS485_Baud = baud;
|
||||
RS485_Interface->US_BRGR = MCK/16/baud;
|
||||
/* FIXME: store the baud rate */
|
||||
break;
|
||||
default:
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/* Transmits a Frame on the wire */
|
||||
void RS485_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
uint8_t * buffer, /* frame to send (up to 501 bytes of data) */
|
||||
uint16_t nbytes) /* number of bytes of data (up to 501) */
|
||||
{
|
||||
uint8_t turnaround_time;
|
||||
uint32_t baud;
|
||||
|
||||
/* toggle LED on send */
|
||||
volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA;
|
||||
if ((pPIO->PIO_ODSR & LED1) == LED1)
|
||||
pPIO->PIO_CODR = LED1;
|
||||
else
|
||||
pPIO->PIO_SODR = LED1;
|
||||
/* delay after reception - per MS/TP spec */
|
||||
if (mstp_port) {
|
||||
baud = RS485_Get_Baud_Rate();
|
||||
/* wait about 40 bit times since reception */
|
||||
if (baud == 9600)
|
||||
turnaround_time = 4;
|
||||
else if (baud == 19200)
|
||||
turnaround_time = 2;
|
||||
else
|
||||
turnaround_time = 1;
|
||||
while (mstp_port->SilenceTimer < turnaround_time) {
|
||||
/* do nothing - wait for timer to increment */
|
||||
};
|
||||
}
|
||||
while (nbytes) {
|
||||
while (!(RS485_Interface->US_CSR & AT91C_US_TXRDY)) {
|
||||
/* do nothing - wait for Tx buffer to get empty */
|
||||
}
|
||||
RS485_Interface->US_THR = *buffer;
|
||||
buffer++;
|
||||
nbytes--;
|
||||
/* per MSTP spec */
|
||||
if (mstp_port) {
|
||||
mstp_port->SilenceTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* called by timer, interrupt(?) or other thread */
|
||||
void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
if (mstp_port->ReceiveError == true) {
|
||||
/* wait for state machine to clear this */
|
||||
}
|
||||
/* wait for state machine to read from the DataRegister */
|
||||
else if (mstp_port->DataAvailable == false) {
|
||||
/* check for data */
|
||||
if ( RS485_Interface->US_CSR & AT91C_US_RXRDY) {
|
||||
mstp_port->DataRegister = RS485_Interface->US_RHR;
|
||||
mstp_port->DataAvailable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TEST_RS485
|
||||
static struct mstp_port_struct_t MSTP_Port;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned i = 0;
|
||||
|
||||
RS485_Set_Baud_Rate(38400);
|
||||
RS485_Initialize();
|
||||
/* receive task */
|
||||
for (;;) {
|
||||
RS485_Check_UART_Data(&MSTP_Port);
|
||||
if (MSTP_Port.DataAvailable) {
|
||||
fprintf(stderr,"%02X ",MSTP_Port.DataRegister);
|
||||
MSTP_Port.DataAvailable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* TEST_ABORT */
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef RS485_H
|
||||
#define RS485_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mstp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void RS485_Set_Interface(char *ifname);
|
||||
|
||||
void RS485_Initialize(void);
|
||||
|
||||
void RS485_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
uint8_t * buffer, /* frame to send (up to 501 bytes of data) */
|
||||
uint16_t nbytes); /* number of bytes of data (up to 501) */
|
||||
|
||||
uint8_t RS485_Check_UART_Data(
|
||||
volatile struct mstp_port_struct_t *mstp_port); /* port specific data */
|
||||
|
||||
uint32_t RS485_Get_Baud_Rate(void);
|
||||
bool RS485_Set_Baud_Rate(uint32_t baud);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,310 @@
|
||||
// *****************************************************************************
|
||||
//
|
||||
// Purpose: Set up the 16-bit Timer/Counter
|
||||
//
|
||||
// We will use Timer Channel 0 to develop a 50 msec interrupt.
|
||||
//
|
||||
// The AT91SAM7S-EK board has a 18,432,000 hz crystal oscillator.
|
||||
//
|
||||
// MAINCK = 18432000 hz
|
||||
// PLLCK = (MAINCK / DIV) * (MUL + 1) = 18432000/14 * (72 + 1)
|
||||
// PLLCLK = 1316571 * 73 = 96109683 hz
|
||||
//
|
||||
// MCK = PLLCLK / 2 = 96109683 / 2 = 48054841 hz
|
||||
//
|
||||
// TIMER_CLOCK5 = MCK / 1024 = 48054841 / 1024 = 46928 hz
|
||||
//
|
||||
// TIMER_CLOCK5 Period = 1 / 46928 = 21.309239686 microseconds
|
||||
//
|
||||
// A little algebra: .050 sec = count * 21.3092396896*10**-6
|
||||
// count = .050 / 21.3092396896*10**-6
|
||||
// count = 2346
|
||||
//
|
||||
//
|
||||
// Therefore: set Timer Channel 0 register RC to 9835
|
||||
// turn on capture mode WAVE = 0
|
||||
// enable the clock CLKEN = 1
|
||||
// select TIMER_CLOCK5 TCCLKS = 100
|
||||
// clock is NOT inverted CLKI = 0
|
||||
// enable RC compare CPCTRG = 1
|
||||
// enable RC compare interrupt CPCS = 1
|
||||
// disable all the other timer 0 interrupts
|
||||
//
|
||||
// Author: James P Lynch May 12, 2007
|
||||
// *****************************************************************************
|
||||
|
||||
|
||||
/**********************************************************
|
||||
Header files
|
||||
**********************************************************/
|
||||
#include "AT91SAM7S256.h"
|
||||
#include "board.h"
|
||||
|
||||
// global variable counts interrupts
|
||||
volatile unsigned long Timer_Milliseconds = 0;
|
||||
|
||||
void TimerSetup(void) {
|
||||
|
||||
// TC Block Control Register TC_BCR (read/write)
|
||||
//
|
||||
// |------------------------------------------------------------------|------|
|
||||
// | SYNC |
|
||||
// |------------------------------------------------------------------|------|
|
||||
// 31 1 0
|
||||
//
|
||||
// SYNC = 0 (no effect) <===== take default
|
||||
// SYNC = 1 (generate software trigger for all 3 timer channels simultaneously)
|
||||
//
|
||||
AT91PS_TCB pTCB = AT91C_BASE_TCB; // create a pointer to TC Global Register structure
|
||||
pTCB->TCB_BCR = 0; // SYNC trigger not used
|
||||
|
||||
// TC Block Mode Register TC_BMR (read/write)
|
||||
//
|
||||
// |-------------------------------------|-----------|-----------|-----------|
|
||||
// | TC2XC2S TCXC1S TC0XC0S |
|
||||
// |-------------------------------------|-----------|-----------|-----------|
|
||||
// 31 5 4 3 2 1 0
|
||||
//
|
||||
// TC0XC0S Select = 00 TCLK0 (PA4)
|
||||
// = 01 none <===== we select this one
|
||||
// = 10 TIOA1 (PA15)
|
||||
// = 11 TIOA2 (PA26)
|
||||
//
|
||||
// TCXC1S Select = 00 TCLK1 (PA28)
|
||||
// = 01 none <===== we select this one
|
||||
// = 10 TIOA0 (PA15)
|
||||
// = 11 TIOA2 (PA26)
|
||||
//
|
||||
// TC2XC2S Select = 00 TCLK2 (PA29)
|
||||
// = 01 none <===== we select this one
|
||||
// = 10 TIOA0 (PA00)
|
||||
// = 11 TIOA1 (PA26)
|
||||
//
|
||||
pTCB->TCB_BMR = 0x15; // external clocks not used
|
||||
|
||||
|
||||
// TC Channel Control Register TC_CCR (read/write)
|
||||
//
|
||||
// |----------------------------------|--------------|------------|-----------|
|
||||
// | SWTRG CLKDIS CLKENS |
|
||||
// |----------------------------------|--------------|------------|-----------|
|
||||
// 31 2 1 0
|
||||
//
|
||||
// CLKEN = 0 no effect
|
||||
// CLKEN = 1 enables the clock <===== we select this one
|
||||
//
|
||||
// CLKDIS = 0 no effect <===== take default
|
||||
// CLKDIS = 1 disables the clock
|
||||
//
|
||||
// SWTRG = 0 no effect
|
||||
// SWTRG = 1 software trigger aserted counter reset and clock starts <===== we select this one
|
||||
//
|
||||
AT91PS_TC pTC = AT91C_BASE_TC0; // create a pointer to channel 0 Register structure
|
||||
pTC->TC_CCR = 0x5; // enable the clock and start it
|
||||
|
||||
// TC Channel Mode Register TC_CMR (read/write)
|
||||
//
|
||||
// |-----------------------------------|------------|---------------|
|
||||
// | LDRB LDRA |
|
||||
// |-----------------------------------|------------|---------------|
|
||||
// 31 19 18 17 16
|
||||
//
|
||||
// |----------|---------|--------------|------------|---------------|
|
||||
// |WAVE = 0 CPCTRG ABETRG ETRGEDG |
|
||||
// |----------|---------|--------------|------------|---------------|
|
||||
// 15 14 13 11 10 9 8
|
||||
//
|
||||
// |----------|---------|--------------|------------|---------------|
|
||||
// | LDBDIS LDBSTOP BURST CLKI TCCLKS |
|
||||
// |----------|---------|--------------|------------|---------------|
|
||||
// 7 6 5 4 3 2 0
|
||||
//
|
||||
// CLOCK SELECTION
|
||||
// TCCLKS = 000 TIMER_CLOCK1 (MCK/2 = 24027420 hz)
|
||||
// 001 TIMER_CLOCK2 (MCK/8 = 6006855 hz)
|
||||
// 010 TIMER_CLOCK3 (MCK/32 = 1501713 hz)
|
||||
// 011 TIMER_CLOCK4 (MCK/128 = 375428 hz)
|
||||
// 100 TIMER_CLOCK5 (MCK/1024 = 46928 hz) <===== we select this one
|
||||
// 101 XC0
|
||||
// 101 XC1
|
||||
// 101 XC2
|
||||
//
|
||||
// CLOCK INVERT
|
||||
// CLKI = 0 counter incremented on rising clock edge <===== we select this one
|
||||
// CLKI = 1 counter incremented on falling clock edge
|
||||
//
|
||||
// BURST SIGNAL SELECTION
|
||||
// BURST = 00 clock is not gated by any external system <===== take default
|
||||
// 01 XC0 is anded with the clock
|
||||
// 10 XC1 is anded with the clock
|
||||
// 11 XC2 is anded with the clock
|
||||
//
|
||||
// COUNTER CLOCK STOPPED WITH RB LOADING
|
||||
// LDBSTOP = 0 counter clock is not stopped when RB loading occurs <===== take default
|
||||
// = 1 counter clock is stopped when RB loading occur
|
||||
//
|
||||
// COUNTER CLOCK DISABLE WITH RB LOADING
|
||||
// LDBDIS = 0 counter clock is not disabled when RB loading occurs <===== take default
|
||||
// = 1 counter clock is disabled when RB loading occurs
|
||||
//
|
||||
// EXTERNAL TRIGGER EDGE SELECTION
|
||||
// ETRGEDG = 00 (none) <===== take default
|
||||
// 01 (rising edge)
|
||||
// 10 (falling edge)
|
||||
// 11 (each edge)
|
||||
//
|
||||
// TIOA OR TIOB EXTERNAL TRIGGER SELECTION
|
||||
// ABETRG = 0 (TIOA is used) <===== take default
|
||||
// 1 (TIOB is used)
|
||||
//
|
||||
// RC COMPARE TRIGGER ENABLE
|
||||
// CPCTRG = 0 (RC Compare has no effect on the counter and its clock)
|
||||
// 1 (RC Compare resets the counter and starts the clock) <===== we select this one
|
||||
//
|
||||
// WAVE
|
||||
// WAVE = 0 Capture Mode is enabled <===== we select this one
|
||||
// 1 Waveform Mode is enabled
|
||||
//
|
||||
// RA LOADING SELECTION
|
||||
// LDRA = 00 none) <===== take default
|
||||
// 01 (rising edge of TIOA)
|
||||
// 10 (falling edge of TIOA)
|
||||
// 11 (each edge of TIOA)
|
||||
//
|
||||
// RB LOADING SELECTION
|
||||
// LDRB = 00 (none) <===== take default
|
||||
// 01 (rising edge of TIOA)
|
||||
// 10 (falling edge of TIOA)
|
||||
// 11 (each edge of TIOA)
|
||||
//
|
||||
pTC->TC_CMR = 0x4004; // TCCLKS = 1 (TIMER_CLOCK5)
|
||||
// CPCTRG = 1 (RC Compare resets the counter and restarts the clock)
|
||||
// WAVE = 0 (Capture mode enabled)
|
||||
|
||||
// TC Register C TC_RC (read/write) Compare Register 16-bits
|
||||
//
|
||||
// |----------------------------------|----------------------------------------|
|
||||
// | not used RC |
|
||||
// |----------------------------------|----------------------------------------|
|
||||
// 31 16 15 0
|
||||
//
|
||||
// Timer Calculation: What count gives 1 msec time-out?
|
||||
//
|
||||
// TIMER_CLOCK5 = MCK / 1024 = 48054841 / 1024 = 46928 hz
|
||||
//
|
||||
// TIMER_CLOCK5 Period = 1 / 46928 = 21.309239686 microseconds
|
||||
//
|
||||
// A little algebra: .001 sec = count * 21.3092396896*10**-6
|
||||
// count = .001 / 21.3092396896*10**-6
|
||||
// count = 46.928
|
||||
//
|
||||
pTC->TC_RC = 47;
|
||||
|
||||
|
||||
// TC Interrupt Enable Register TC_IER (write-only)
|
||||
//
|
||||
//
|
||||
// |------------|-------|-------|-------|-------|--------|--------|--------|--------|
|
||||
// | ETRGS LDRBS LDRAS CPCS CPBS CPAS LOVRS COVFS |
|
||||
// |------------|-------|-------|-------|-------|--------|--------|--------|--------|
|
||||
// 31 8 7 6 5 4 3 2 1 0
|
||||
//
|
||||
// COVFS = 0 no effect <===== take default
|
||||
// 1 enable counter overflow interrupt
|
||||
//
|
||||
// LOVRS = 0 no effect <===== take default
|
||||
// 1 enable load overrun interrupt
|
||||
//
|
||||
// CPAS = 0 no effect <===== take default
|
||||
// 1 enable RA compare interrupt
|
||||
//
|
||||
// CPBS = 0 no effect <===== take default
|
||||
// 1 enable RB compare interrupt
|
||||
//
|
||||
// CPCS = 0 no effect
|
||||
// 1 enable RC compare interrupt <===== we select this one
|
||||
//
|
||||
// LDRAS = 0 no effect <===== take default
|
||||
// 1 enable RA load interrupt
|
||||
//
|
||||
// LDRBS = 0 no effect <===== take default
|
||||
// 1 enable RB load interrupt
|
||||
//
|
||||
// ETRGS = 0 no effect <===== take default
|
||||
// 1 enable External Trigger interrupt
|
||||
//
|
||||
pTC->TC_IER = 0x10; // enable RC compare interrupt
|
||||
|
||||
|
||||
// TC Interrupt Disable Register TC_IDR (write-only)
|
||||
//
|
||||
//
|
||||
// |------------|-------|-------|-------|-------|--------|--------|--------|--------|
|
||||
// | ETRGS LDRBS LDRAS CPCS CPBS CPAS LOVRS COVFS |
|
||||
// |------------|-------|-------|-------|-------|--------|--------|--------|--------|
|
||||
// 31 8 7 6 5 4 3 2 1 0
|
||||
//
|
||||
// COVFS = 0 no effect
|
||||
// 1 disable counter overflow interrupt <===== we select this one
|
||||
//
|
||||
// LOVRS = 0 no effect
|
||||
// 1 disable load overrun interrupt <===== we select this one
|
||||
//
|
||||
// CPAS = 0 no effect
|
||||
// 1 disable RA compare interrupt <===== we select this one
|
||||
//
|
||||
// CPBS = 0 no effect
|
||||
// 1 disable RB compare interrupt <===== we select this one
|
||||
//
|
||||
// CPCS = 0 no effect <===== take default
|
||||
// 1 disable RC compare interrupt
|
||||
//
|
||||
// LDRAS = 0 no effect
|
||||
// 1 disable RA load interrupt <===== we select this one
|
||||
//
|
||||
// LDRBS = 0 no effect
|
||||
// 1 disable RB load interrupt <===== we select this one
|
||||
//
|
||||
// ETRGS = 0 no effect
|
||||
// 1 disable External Trigger interrupt <===== we select this one
|
||||
//
|
||||
pTC->TC_IDR = 0xEF; // disable all except RC compare interrupt
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
//
|
||||
// Timer 0 Interrupt Service Routine
|
||||
//
|
||||
// entered when Timer0 RC compare interrupt asserts (200 msec period)
|
||||
// blinks LED2 (pin PA2)
|
||||
//
|
||||
// Author: James P Lynch May 12, 2007
|
||||
// *****************************************************************************
|
||||
void Timer0IrqHandler (void) {
|
||||
|
||||
volatile AT91PS_TC pTC = AT91C_BASE_TC0; // pointer to timer channel 0 register structure
|
||||
unsigned int dummy; // temporary
|
||||
|
||||
dummy = pTC->TC_SR; // read TC0 Status Register to clear interrupt
|
||||
Timer_Milliseconds++; // increment the tick count
|
||||
}
|
||||
|
||||
void TimerInit(void) {
|
||||
|
||||
// enable the Timer0 peripheral clock
|
||||
volatile AT91PS_PMC pPMC = AT91C_BASE_PMC;
|
||||
pPMC->PMC_PCER = pPMC->PMC_PCSR | (1<<AT91C_ID_TC0);
|
||||
// Set up the AIC registers for Timer 0
|
||||
volatile AT91PS_AIC pAIC = AT91C_BASE_AIC; // pointer to AIC data structure
|
||||
pAIC->AIC_IDCR = (1<<AT91C_ID_TC0); // Disable timer 0 interrupt in AIC Interrupt Disable Command Register
|
||||
pAIC->AIC_SVR[AT91C_ID_TC0] = // Set the TC0 IRQ handler address in AIC Source
|
||||
(unsigned int)Timer0IrqHandler; // Vector Register[12]
|
||||
pAIC->AIC_SMR[AT91C_ID_TC0] = // Set the interrupt source type and priority
|
||||
(AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE | 0x4 ); // in AIC Source Mode Register[12]
|
||||
pAIC->AIC_ICCR = (1<<AT91C_ID_TC0); // Clear the TC0 interrupt in AIC Interrupt Clear Command Register
|
||||
pAIC->AIC_IDCR = (0<<AT91C_ID_TC0); // Remove disable timer 0 interrupt in AIC Interrupt Disable Command Reg
|
||||
pAIC->AIC_IECR = (1<<AT91C_ID_TC0); // Enable the TC0 interrupt in AIC Interrupt Enable Command Register
|
||||
|
||||
// Setup timer0 to generate a 10 msec periodic interrupt
|
||||
TimerSetup();
|
||||
}
|
||||
Reference in New Issue
Block a user