Changed ATmega168 port to ATmega328 on Uno R3 with DFR0259 RS485 shield (#784)
* Changed ATmega168 example for ATmega328 on Arduino Uno R3 with DFR0259 RS485 shield. Added ADC interface from BDK port and mapped to some AV objects. Removed MS/TP MAC address DIP switch GPIO and moved MS/TP configuration to AV objects. Added AV units property. Added some Uno R3 Digital Inputs and outputs mapped to some BV. Added AVR EEPROM from BDK port and mapped some non-volatile data including MAC address and max manager and baud rate, device ID and names and description and location.
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
---
|
||||
# Disable formatting for now as there is external code. We should move external
|
||||
# code to a separate directory and enable formatting for our code.
|
||||
DisableFormat: true
|
||||
|
||||
# DisableFormat will not disable include sorting with some versions.
|
||||
SortIncludes: Never
|
||||
@@ -1,13 +0,0 @@
|
||||
-sysdirs C:\WinAVR~1\avr\include;C:\WinAVR~1\lib\gcc\avr\4.2.2\include;C:\WinAVR~1\avr\include\avr;C:\WinAVR~1\avr\include\compat;C:\WinAVR~1\avr\include\util
|
||||
-IC:\WinAVR~1\avr\include
|
||||
-IC:\WinAVR~1\avr\include\avr
|
||||
-IC:\WinAVR~1\avr\include\compat
|
||||
-IC:\WinAVR~1\avr\include\util
|
||||
-IC:\WinAVR~1\lib\gcc\avr\4.2.2\include
|
||||
-I../../include
|
||||
-I.
|
||||
-castfcnptr
|
||||
-fullinitblock
|
||||
-weak
|
||||
-D__AVR_ATmega168__
|
||||
-D__GNUC__
|
||||
Binary file not shown.
@@ -1,139 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Analog Input Objects customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/config.h"
|
||||
|
||||
/* Analog Input = Photocell */
|
||||
#define MAX_ANALOG_INPUTS 9
|
||||
#if (MAX_ANALOG_INPUTS > 9)
|
||||
#error Modify the Analog_Input_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
float 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;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. */
|
||||
unsigned Analog_Input_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
char *Analog_Input_Name(uint32_t object_instance)
|
||||
{
|
||||
static char text_string[5] = "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;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
uint32_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;
|
||||
unsigned object_index;
|
||||
|
||||
(void)array_index;
|
||||
switch (property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_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.
|
||||
Note that Object-Name must be unique in this device */
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
characterstring_init_ansi(
|
||||
&char_string, Analog_Input_Name(object_instance));
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_ANALOG_INPUT);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
object_index = Analog_Input_Instance_To_Index(object_instance);
|
||||
apdu_len =
|
||||
encode_application_real(&apdu[0], Present_Value[object_index]);
|
||||
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_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
apdu_len = encode_application_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_UNITS:
|
||||
apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT);
|
||||
break;
|
||||
default:
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (array_index != BACNET_ARRAY_ALL)) {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = -1;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef AI_H
|
||||
#define AI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
void Analog_Input_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
|
||||
bool Analog_Input_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Analog_Input_Count(
|
||||
void);
|
||||
uint32_t Analog_Input_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Analog_Input_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Analog_Input_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
char *Analog_Input_Name(
|
||||
uint32_t object_instance);
|
||||
bool Analog_Input_Name_Set(
|
||||
uint32_t object_instance,
|
||||
const char *new_name);
|
||||
|
||||
const char *Analog_Input_Description(
|
||||
uint32_t instance);
|
||||
bool Analog_Input_Description_Set(
|
||||
uint32_t instance,
|
||||
const char *new_name);
|
||||
|
||||
bool Analog_Input_Units_Set(
|
||||
uint32_t instance,
|
||||
uint16_t units);
|
||||
uint16_t Analog_Input_Units(
|
||||
uint32_t instance);
|
||||
|
||||
int Analog_Input_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Analog_Input_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
float Analog_Input_Present_Value(
|
||||
uint32_t object_instance);
|
||||
void Analog_Input_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
float value);
|
||||
|
||||
void Analog_Input_Init(
|
||||
void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#define ANALOG_INPUT_OBJ_FUNCTIONS \
|
||||
OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count, \
|
||||
Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance, \
|
||||
Analog_Input_Name, Analog_Input_Read_Property, NULL, \
|
||||
Analog_Input_Property_Lists, NULL, NULL
|
||||
#endif
|
||||
@@ -1,201 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Analog Value Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/object/av.h"
|
||||
|
||||
#ifndef MAX_ANALOG_VALUES
|
||||
#define MAX_ANALOG_VALUES 10
|
||||
#endif
|
||||
|
||||
#if (MAX_ANALOG_VALUES > 10)
|
||||
#error Modify the Analog_Value_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
float AV_Present_Value[MAX_ANALOG_VALUES];
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
char *Analog_Value_Name(uint32_t object_instance)
|
||||
{
|
||||
static char text_string[5] = "AV-"; /* okay for single thread */
|
||||
|
||||
text_string[3] = '0' + (uint8_t)object_instance;
|
||||
|
||||
return text_string;
|
||||
}
|
||||
|
||||
/* return apdu len, or -1 on error */
|
||||
int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
unsigned object_index;
|
||||
uint8_t *apdu;
|
||||
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_ANALOG_VALUE, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
characterstring_init_ansi(
|
||||
&char_string, Analog_Value_Name(rpdata->object_instance));
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
object_index =
|
||||
Analog_Value_Instance_To_Index(rpdata->object_instance);
|
||||
apdu_len = encode_application_real(
|
||||
&apdu[0], AV_Present_Value[object_index]);
|
||||
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_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
apdu_len = encode_application_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_UNITS:
|
||||
apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT);
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned int object_index = 0;
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
if (!Analog_Value_Valid_Instance(wp_data->object_instance)) {
|
||||
wp_data->error_class = ERROR_CLASS_OBJECT;
|
||||
wp_data->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? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
/* only array properties can have array options */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_REAL) {
|
||||
object_index =
|
||||
Analog_Value_Instance_To_Index(wp_data->object_instance);
|
||||
AV_Present_Value[object_index] = value.type.Real;
|
||||
status = true;
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
case PROP_DESCRIPTION:
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef AV_H
|
||||
#define AV_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
#ifndef MAX_ANALOG_VALUES
|
||||
#define MAX_ANALOG_VALUES 4
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
void Analog_Value_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Analog_Value_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Analog_Value_Count(
|
||||
void);
|
||||
uint32_t Analog_Value_Index_To_Instance(
|
||||
unsigned index);
|
||||
char *Analog_Value_Name(
|
||||
uint32_t object_instance);
|
||||
|
||||
int Analog_Value_Encode_Property_APDU(
|
||||
uint8_t * apdu,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID property,
|
||||
uint32_t array_index,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
bool Analog_Value_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
bool Analog_Value_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
float value,
|
||||
uint8_t priority);
|
||||
float Analog_Value_Present_Value(
|
||||
uint32_t object_instance);
|
||||
|
||||
void Analog_Value_Init(
|
||||
void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef AVR035_H
|
||||
#define AVR035_H
|
||||
|
||||
/* from AVR035: Efficient C Coding for AVR */
|
||||
|
||||
/* a=register, b=bit number to act upon */
|
||||
#define BIT_SET(a,b) ((a) |= (1<<(b)))
|
||||
#define BIT_CLEAR(a,b) ((a) &= ~(1<<(b)))
|
||||
#define BIT_FLIP(a,b) ((a) ^= (1<<(b)))
|
||||
#define BIT_CHECK(a,b) ((a) & (1<<(b)))
|
||||
|
||||
/* x=target variable, y=mask */
|
||||
#define BITMASK_SET(x,y) ((x) |= (y))
|
||||
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
|
||||
#define BITMASK_FLIP(x,y) ((x) ^= (y))
|
||||
#define BITMASK_CHECK(x,y) ((x) & (y))
|
||||
|
||||
#endif
|
||||
File diff suppressed because one or more lines are too long
@@ -1,22 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Atmel Studio Solution File, Format Version 11.00
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "bacnet", "bacnet.cproj", "{1CEFD571-4B50-48FD-B75E-0E968EBBD698}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|AVR = Debug|AVR
|
||||
Release|AVR = Release|AVR
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1CEFD571-4B50-48FD-B75E-0E968EBBD698}.Debug|AVR.ActiveCfg = Debug|AVR
|
||||
{1CEFD571-4B50-48FD-B75E-0E968EBBD698}.Debug|AVR.Build.0 = Debug|AVR
|
||||
{1CEFD571-4B50-48FD-B75E-0E968EBBD698}.Release|AVR.ActiveCfg = Release|AVR
|
||||
{1CEFD571-4B50-48FD-B75E-0E968EBBD698}.Release|AVR.Build.0 = Release|AVR
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,289 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectVersion>7.0</ProjectVersion>
|
||||
<ToolchainName>com.Atmel.AVRGCC8.C</ToolchainName>
|
||||
<ProjectGuid>{1cefd571-4b50-48fd-b75e-0e968ebbd698}</ProjectGuid>
|
||||
<avrdevice>ATmega328</avrdevice>
|
||||
<avrdeviceseries>mega</avrdeviceseries>
|
||||
<OutputType>Executable</OutputType>
|
||||
<Language>C</Language>
|
||||
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
|
||||
<OutputFileExtension>.elf</OutputFileExtension>
|
||||
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
|
||||
<AssemblyName>BACnet Atmel ATmega328</AssemblyName>
|
||||
<Name>BACnet Atmel ATmega328</Name>
|
||||
<RootNamespace>BACnet Atmel ATmega328</RootNamespace>
|
||||
<ToolchainFlavour>Native</ToolchainFlavour>
|
||||
<KeepTimersRunning>true</KeepTimersRunning>
|
||||
<OverrideVtor>false</OverrideVtor>
|
||||
<OverrideVtorValue>exception_table</OverrideVtorValue>
|
||||
<eraseonlaunchrule>0</eraseonlaunchrule>
|
||||
<ProgFlashFromRam>true</ProgFlashFromRam>
|
||||
<RamSnippetAddress>0x20000000</RamSnippetAddress>
|
||||
<CacheFlash>true</CacheFlash>
|
||||
<UncachedRange />
|
||||
<BootSegment>2</BootSegment>
|
||||
<AsfFrameworkConfig>
|
||||
<framework-data>
|
||||
<options>
|
||||
<option id="common.boards" value="Add" config="" content-id="Atmel.ASF" />
|
||||
</options>
|
||||
<configurations />
|
||||
<files>
|
||||
<file path="src/config/conf_board.h" framework="" version="" source="common/applications/user_application/user_board/config/conf_board.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/common/boards/board.h" framework="" version="" source="common/boards/board.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/common/boards/user_board/init.c" framework="" version="" source="common/boards/user_board/init.c" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/common/boards/user_board/user_board.h" framework="" version="" source="common/boards/user_board/user_board.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/common/utils/interrupt.h" framework="" version="" source="common/utils/interrupt.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/common/utils/interrupt/interrupt_avr8.h" framework="" version="" source="common/utils/interrupt/interrupt_avr8.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/common/utils/make/Makefile.avr.in" framework="" version="" source="common/utils/make/Makefile.avr.in" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/common/utils/parts.h" framework="" version="" source="common/utils/parts.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/mega/utils/assembler.h" framework="" version="" source="mega/utils/assembler.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/mega/utils/assembler/gas.h" framework="" version="" source="mega/utils/assembler/gas.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/mega/utils/compiler.h" framework="" version="" source="mega/utils/compiler.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/mega/utils/preprocessor/mrepeat.h" framework="" version="" source="mega/utils/preprocessor/mrepeat.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/mega/utils/preprocessor/preprocessor.h" framework="" version="" source="mega/utils/preprocessor/preprocessor.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/mega/utils/preprocessor/stringz.h" framework="" version="" source="mega/utils/preprocessor/stringz.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/mega/utils/preprocessor/tpaste.h" framework="" version="" source="mega/utils/preprocessor/tpaste.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/mega/utils/progmem.h" framework="" version="" source="mega/utils/progmem.h" changed="False" content-id="Atmel.ASF" />
|
||||
<file path="src/ASF/mega/utils/status_codes.h" framework="" version="" source="mega/utils/status_codes.h" changed="False" content-id="Atmel.ASF" />
|
||||
</files>
|
||||
<documentation help="http://asf.atmel.com/docs/3.39.0/common.applications.user_application.user_board.mega/html/index.html" />
|
||||
<offline-documentation help="" />
|
||||
<dependencies>
|
||||
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.39.0" />
|
||||
</dependencies>
|
||||
<project id="common.applications.user_application.user_board.mega" value="Add" config="" content-id="Atmel.ASF" />
|
||||
<board id="board.user_board.mega" value="Add" config="" content-id="Atmel.ASF" />
|
||||
</framework-data>
|
||||
</AsfFrameworkConfig>
|
||||
<preserveEEPROM>true</preserveEEPROM>
|
||||
<ResetRule>0</ResetRule>
|
||||
<EraseKey />
|
||||
<avrtool />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<ToolchainSettings>
|
||||
<AvrGcc>
|
||||
<avrgcc.common.Device>-mmcu=atmega328 -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.3.300\gcc\dev\atmega328"</avrgcc.common.Device>
|
||||
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
|
||||
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
|
||||
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
|
||||
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
|
||||
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
|
||||
<avrgcc.compiler.symbols.DefSymbols><ListValues>
|
||||
<Value>BACDL_MSTP</Value>
|
||||
<Value>MAX_APDU=50</Value>
|
||||
<Value>MAX_TSM_TRANSACTIONS=0</Value>
|
||||
<Value>BACAPP_REAL</Value>
|
||||
<Value>BACAPP_UNSIGNED</Value>
|
||||
<Value>BACAPP_ENUMERATED</Value>
|
||||
<Value>BACAPP_CHARACTER_STRING</Value>
|
||||
<Value>BACAPP_OBJECT_ID</Value>
|
||||
<Value>WRITE_PROPERTY</Value>
|
||||
<Value>BOARD=USER_BOARD</Value>
|
||||
<Value>NDEBUG</Value>
|
||||
</ListValues></avrgcc.compiler.symbols.DefSymbols>
|
||||
<avrgcc.compiler.directories.IncludePaths><ListValues>
|
||||
<Value>..</Value>
|
||||
<Value>../../../src</Value>
|
||||
<Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.3.300\include</Value>
|
||||
<Value>../../../ports/atmega168</Value>
|
||||
</ListValues></avrgcc.compiler.directories.IncludePaths>
|
||||
<avrgcc.compiler.optimization.OtherFlags>-fdata-sections</avrgcc.compiler.optimization.OtherFlags>
|
||||
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
|
||||
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
|
||||
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
|
||||
<avrgcc.compiler.miscellaneous.OtherFlags>-std=gnu99 -fno-strict-aliasing -Wstrict-prototypes -Wmissing-prototypes -Werror-implicit-function-declaration -Wpointer-arith -mrelax -Wno-switch</avrgcc.compiler.miscellaneous.OtherFlags>
|
||||
<avrgcc.linker.libraries.Libraries><ListValues><Value>libm</Value></ListValues></avrgcc.linker.libraries.Libraries>
|
||||
<avrgcc.linker.miscellaneous.LinkerFlags>-Wl,--relax</avrgcc.linker.miscellaneous.LinkerFlags>
|
||||
<avrgcc.assembler.general.AssemblerFlags>-mrelax -DBOARD=USER_BOARD</avrgcc.assembler.general.AssemblerFlags>
|
||||
<avrgcc.assembler.general.IncludePaths><ListValues><Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.3.300\include</Value><Value>../src/ASF/common/boards/user_board</Value><Value>../src/ASF/common/boards</Value><Value>../src/ASF/mega/utils/preprocessor</Value><Value>../src/ASF/mega/utils</Value><Value>../src/ASF/common/utils</Value><Value>../src</Value><Value>../src/config</Value></ListValues></avrgcc.assembler.general.IncludePaths>
|
||||
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
|
||||
</AvrGcc>
|
||||
</ToolchainSettings>
|
||||
<UsesExternalMakeFile>False</UsesExternalMakeFile>
|
||||
<BuildTarget>all</BuildTarget>
|
||||
<CleanTarget>clean</CleanTarget>
|
||||
<ExternalMakeFilePath>Makefile</ExternalMakeFilePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<ToolchainSettings>
|
||||
<AvrGcc>
|
||||
<avrgcc.common.Device>-mmcu=atmega328 -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.3.300\gcc\dev\atmega328"</avrgcc.common.Device>
|
||||
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
|
||||
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
|
||||
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
|
||||
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
|
||||
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
|
||||
<avrgcc.compiler.symbols.DefSymbols><ListValues>
|
||||
<Value>BACDL_MSTP</Value>
|
||||
<Value>MAX_APDU=50</Value>
|
||||
<Value>MAX_TSM_TRANSACTIONS=0</Value>
|
||||
<Value>BACAPP_REAL</Value>
|
||||
<Value>BACAPP_UNSIGNED</Value>
|
||||
<Value>BACAPP_ENUMERATED</Value>
|
||||
<Value>BACAPP_CHARACTER_STRING</Value>
|
||||
<Value>BACAPP_OBJECT_ID</Value>
|
||||
<Value>WRITE_PROPERTY</Value>
|
||||
<Value>BOARD=USER_BOARD</Value>
|
||||
<Value>DEBUG</Value>
|
||||
</ListValues></avrgcc.compiler.symbols.DefSymbols>
|
||||
<avrgcc.compiler.directories.IncludePaths><ListValues><Value>..</Value><Value>../../../src</Value><Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.3.300\include</Value></ListValues></avrgcc.compiler.directories.IncludePaths>
|
||||
<avrgcc.compiler.optimization.OtherFlags>-fdata-sections</avrgcc.compiler.optimization.OtherFlags>
|
||||
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
|
||||
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
|
||||
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
|
||||
<avrgcc.compiler.miscellaneous.OtherFlags>-std=gnu99 -fno-strict-aliasing -Wstrict-prototypes -Wmissing-prototypes -Werror-implicit-function-declaration -Wpointer-arith -mrelax -Wno-switch</avrgcc.compiler.miscellaneous.OtherFlags>
|
||||
<avrgcc.linker.libraries.Libraries><ListValues><Value>libm</Value></ListValues></avrgcc.linker.libraries.Libraries>
|
||||
<avrgcc.linker.miscellaneous.LinkerFlags>-Wl,--relax</avrgcc.linker.miscellaneous.LinkerFlags>
|
||||
<avrgcc.assembler.general.AssemblerFlags>-mrelax -DBOARD=USER_BOARD</avrgcc.assembler.general.AssemblerFlags>
|
||||
<avrgcc.assembler.general.IncludePaths><ListValues><Value>%24(PackRepoDir)\atmel\ATmega_DFP\1.3.300\include</Value><Value>../src/ASF/common/boards/user_board</Value><Value>../src/ASF/common/boards</Value><Value>../src/ASF/mega/utils/preprocessor</Value><Value>../src/ASF/mega/utils</Value><Value>../src/ASF/common/utils</Value><Value>../src</Value><Value>../src/config</Value></ListValues></avrgcc.assembler.general.IncludePaths>
|
||||
<avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
|
||||
<avrgcc.compiler.optimization.DebugLevel>Maximum (-g3)</avrgcc.compiler.optimization.DebugLevel>
|
||||
<avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel>
|
||||
</AvrGcc>
|
||||
</ToolchainSettings>
|
||||
<UsesExternalMakeFile>False</UsesExternalMakeFile>
|
||||
<BuildTarget>all</BuildTarget>
|
||||
<CleanTarget>clean</CleanTarget>
|
||||
<ExternalMakeFilePath>Makefile</ExternalMakeFilePath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\src\bacnet\basic\sys\bigend.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Handlers\bigend.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\basic\sys\debug.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Handlers\debug.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\basic\npdu\h_npdu.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Handlers\h_npdu.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\basic\service\h_noserv.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Handlers\h_noserv.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\basic\service\s_iam.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Handlers\s_iam.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\basic\tsm\tsm.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Handlers\tsm.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\datalink\crc.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Handlers\crc.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\apdu.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>apdu.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\av.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>av.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\bv.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>bv.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\device.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>device.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\dlmstp.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>dlmstp.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\h_rp.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>h_rp.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\h_whois.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>h_whois.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\h_wp.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>h_wp.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\main.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>main.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\rs485.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>rs485.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\stack.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>stack.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\ports\atmega168\timer.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>timer.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\abort.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\abort.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\bacapp.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\bacapp.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\bacdcode.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\bacdcode.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\bacerror.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\bacerror.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\bacint.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\bacint.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\bacreal.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\bacreal.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\bacstr.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\bacstr.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\iam.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\iam.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\npdu.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\npdu.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\reject.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\reject.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\rp.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\rp.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\whois.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\whois.c</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\bacnet\wp.c">
|
||||
<SubType>compile</SubType>
|
||||
<Link>BACnet Core\wp.c</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="BACnet Handlers" />
|
||||
<Folder Include="BACnet Core" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
|
||||
<workspace>
|
||||
<project>
|
||||
<path>$WS_DIR$\bacnet.ewp</path>
|
||||
</project>
|
||||
<batchBuild/>
|
||||
</workspace>
|
||||
@@ -1,245 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Binary Value Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "hardware.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/object/bv.h"
|
||||
|
||||
#ifndef MAX_BINARY_VALUES
|
||||
#define MAX_BINARY_VALUES 10
|
||||
#endif
|
||||
|
||||
#if (MAX_BINARY_VALUES > 10)
|
||||
#error Modify the Binary_Value_Name to handle multiple digits
|
||||
#endif
|
||||
|
||||
static BACNET_BINARY_PV Present_Value[MAX_BINARY_VALUES];
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
BACNET_BINARY_PV value = BINARY_INACTIVE;
|
||||
|
||||
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[5] = "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_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
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;
|
||||
uint8_t *apdu;
|
||||
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_BINARY_VALUE, rpdata->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:
|
||||
characterstring_init_ansi(
|
||||
&char_string, Binary_Value_Name(rpdata->object_instance));
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
present_value = Binary_Value_Present_Value(rpdata->object_instance);
|
||||
apdu_len = encode_application_enumerated(&apdu[0], present_value);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
/* note: see the details in the standard on how to use these */
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
/* note: see the details in the standard on how to use this */
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
apdu_len = encode_application_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_POLARITY:
|
||||
/* FIXME: figure out the polarity */
|
||||
apdu_len = encode_application_enumerated(&apdu[0], polarity);
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned int object_index = 0;
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
if (!Binary_Value_Valid_Instance(wp_data->object_instance)) {
|
||||
wp_data->error_class = ERROR_CLASS_OBJECT;
|
||||
wp_data->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? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
/* only array properties can have array options */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
if ((value.type.Enumerated == BINARY_ACTIVE) ||
|
||||
(value.type.Enumerated == BINARY_INACTIVE)) {
|
||||
object_index = Binary_Value_Instance_To_Index(
|
||||
wp_data->object_instance);
|
||||
/* NOTE: this Binary value has no priority array */
|
||||
Present_Value[object_index] =
|
||||
(BACNET_BINARY_PV)value.type.Enumerated;
|
||||
/* 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. */
|
||||
if (Present_Value[0] == BINARY_ACTIVE) {
|
||||
/* GPIO - turn ON */
|
||||
} else {
|
||||
/* GPIO - turn OFF */
|
||||
}
|
||||
status = true;
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
#if 0
|
||||
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 {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_POLARITY:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BV_H
|
||||
#define BV_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
#ifndef MAX_BINARY_VALUES
|
||||
#define MAX_BINARY_VALUES 10
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void Binary_Value_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Binary_Value_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Binary_Value_Count(
|
||||
void);
|
||||
uint32_t Binary_Value_Index_To_Instance(
|
||||
unsigned index);
|
||||
char *Binary_Value_Name(
|
||||
uint32_t object_instance);
|
||||
|
||||
void Binary_Value_Init(
|
||||
void);
|
||||
|
||||
int Binary_Value_Encode_Property_APDU(
|
||||
uint8_t * apdu,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID property,
|
||||
uint32_t array_index,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
bool Binary_Value_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -1,150 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef DEVICE_H
|
||||
#define DEVICE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/readrange.h"
|
||||
|
||||
typedef unsigned (
|
||||
*object_count_function) (
|
||||
void);
|
||||
typedef uint32_t(
|
||||
*object_index_to_instance_function)
|
||||
(
|
||||
unsigned index);
|
||||
typedef char *(
|
||||
*object_name_function)
|
||||
(
|
||||
uint32_t object_instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void Device_Object_Function_Set(
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
object_count_function count_function,
|
||||
object_index_to_instance_function index_function,
|
||||
object_name_function name_function);
|
||||
|
||||
void Device_Init(
|
||||
void);
|
||||
|
||||
void Device_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
|
||||
uint32_t Device_Object_Instance_Number(
|
||||
void);
|
||||
bool Device_Set_Object_Instance_Number(
|
||||
uint32_t object_id);
|
||||
bool Device_Valid_Object_Instance_Number(
|
||||
uint32_t object_id);
|
||||
unsigned Device_Object_List_Count(
|
||||
void);
|
||||
bool Device_Object_List_Identifier(
|
||||
uint32_t array_index,
|
||||
BACNET_OBJECT_TYPE *object_type,
|
||||
uint32_t * instance);
|
||||
|
||||
BACNET_DEVICE_STATUS Device_System_Status(
|
||||
void);
|
||||
void Device_Set_System_Status(
|
||||
BACNET_DEVICE_STATUS status);
|
||||
|
||||
const char *Device_Vendor_Name(
|
||||
void);
|
||||
|
||||
uint16_t Device_Vendor_Identifier(
|
||||
void);
|
||||
|
||||
const char *Device_Model_Name(
|
||||
void);
|
||||
bool Device_Set_Model_Name(
|
||||
const char *name,
|
||||
size_t length);
|
||||
|
||||
const char *Device_Firmware_Revision(
|
||||
void);
|
||||
|
||||
const char *Device_Application_Software_Version(
|
||||
void);
|
||||
bool Device_Set_Application_Software_Version(
|
||||
const char *name,
|
||||
size_t length);
|
||||
|
||||
bool Device_Set_Object_Name(
|
||||
const char *name,
|
||||
size_t length);
|
||||
const char *Device_Object_Name(
|
||||
void);
|
||||
|
||||
const char *Device_Description(
|
||||
void);
|
||||
bool Device_Set_Description(
|
||||
const char *name,
|
||||
size_t length);
|
||||
|
||||
const char *Device_Location(
|
||||
void);
|
||||
bool Device_Set_Location(
|
||||
const char *name,
|
||||
size_t length);
|
||||
|
||||
/* some stack-centric constant values - no set methods */
|
||||
uint8_t Device_Protocol_Version(
|
||||
void);
|
||||
uint8_t Device_Protocol_Revision(
|
||||
void);
|
||||
BACNET_SEGMENTATION Device_Segmentation_Supported(
|
||||
void);
|
||||
|
||||
uint8_t Device_Database_Revision(
|
||||
void);
|
||||
void Device_Set_Database_Revision(
|
||||
uint8_t revision);
|
||||
|
||||
bool Device_Valid_Object_Name(
|
||||
const char *object_name,
|
||||
BACNET_OBJECT_TYPE *object_type,
|
||||
uint32_t * object_instance);
|
||||
char *Device_Valid_Object_Id(
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance);
|
||||
|
||||
int Device_Encode_Property_APDU(
|
||||
uint8_t * apdu,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID property,
|
||||
uint32_t array_index,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
bool Device_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
bool DeviceGetRRInfo(
|
||||
uint32_t Object, /* Which particular object - obviously not important for device object */
|
||||
BACNET_PROPERTY_ID Property, /* Which property */
|
||||
RR_PROP_INFO * pInfo, /* Where to put the information */
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef HARDWARE_H
|
||||
#define HARDWARE_H
|
||||
|
||||
#if !defined(F_CPU)
|
||||
/* The processor clock frequency */
|
||||
#define F_CPU 7372800UL
|
||||
#endif
|
||||
|
||||
#if defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ASM__)
|
||||
#include <ioavr.h>
|
||||
#else
|
||||
#if !defined(__AVR_ATmega328__)
|
||||
#error Firmware is configured for ATmega328 only (-mmcu=atmega328)
|
||||
#endif
|
||||
#endif
|
||||
#include "iar2gcc.h"
|
||||
#include "bacnet/basic/sys/bits.h"
|
||||
|
||||
#endif
|
||||
Binary file not shown.
@@ -1,223 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef IAR2GCC_H
|
||||
#define IAR2GCC_H
|
||||
|
||||
#if !defined(F_CPU)
|
||||
#define F_CPU (7372800)
|
||||
#endif
|
||||
|
||||
/* IAR */
|
||||
#if defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ASM__)
|
||||
#include <inavr.h>
|
||||
#include <ioavr.h>
|
||||
/* BitValue is used alot in GCC examples */
|
||||
#ifndef _BV
|
||||
#define _BV(bit_num) (1 << (bit_num))
|
||||
#endif
|
||||
|
||||
/* inline function */
|
||||
static inline void _delay_us(
|
||||
uint8_t microseconds)
|
||||
{
|
||||
do {
|
||||
__delay_cycles(F_CPU / 1000000UL);
|
||||
} while (microseconds--);
|
||||
}
|
||||
|
||||
#if (__VER__ > 700)
|
||||
#define DDA0 DDRA0
|
||||
#define DDA1 DDRA1
|
||||
#define DDA2 DDRA2
|
||||
#define DDA3 DDRA3
|
||||
#define DDA4 DDRA4
|
||||
#define DDA5 DDRA5
|
||||
#define DDA6 DDRA6
|
||||
#define DDA7 DDRA7
|
||||
|
||||
#define DDD0 DDRD0
|
||||
#define DDD1 DDRD1
|
||||
#define DDD2 DDRD2
|
||||
#define DDD3 DDRD3
|
||||
#define DDD4 DDRD4
|
||||
#define DDD5 DDRD5
|
||||
#define DDD6 DDRD6
|
||||
#define DDD7 DDRD7
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Input/Output Registers */
|
||||
#if defined(__GNUC__)
|
||||
#include <avr/io.h>
|
||||
|
||||
typedef struct {
|
||||
unsigned char bit0:1;
|
||||
unsigned char bit1:1;
|
||||
unsigned char bit2:1;
|
||||
unsigned char bit3:1;
|
||||
unsigned char bit4:1;
|
||||
unsigned char bit5:1;
|
||||
unsigned char bit6:1;
|
||||
unsigned char bit7:1;
|
||||
} BitRegisterType;
|
||||
|
||||
#ifndef true
|
||||
#define true 1
|
||||
#endif
|
||||
|
||||
#ifndef false
|
||||
#define false 0
|
||||
#endif
|
||||
|
||||
#define GPIO_BITREG(port,bitnum) \
|
||||
((volatile BitRegisterType*)_SFR_MEM_ADDR(port) \
|
||||
)->bit ## bitnum
|
||||
|
||||
#define PINA_Bit0 GPIO_BITREG(PINA,0)
|
||||
#define PINA_Bit1 GPIO_BITREG(PINA,1)
|
||||
#define PINA_Bit2 GPIO_BITREG(PINA,2)
|
||||
#define PINA_Bit3 GPIO_BITREG(PINA,3)
|
||||
#define PINA_Bit4 GPIO_BITREG(PINA,4)
|
||||
#define PINA_Bit5 GPIO_BITREG(PINA,5)
|
||||
#define PINA_Bit6 GPIO_BITREG(PINA,6)
|
||||
#define PINA_Bit7 GPIO_BITREG(PINA,7)
|
||||
|
||||
#define PORTA_Bit0 GPIO_BITREG(PORTA,0)
|
||||
#define PORTA_Bit1 GPIO_BITREG(PORTA,1)
|
||||
#define PORTA_Bit2 GPIO_BITREG(PORTA,2)
|
||||
#define PORTA_Bit3 GPIO_BITREG(PORTA,3)
|
||||
#define PORTA_Bit4 GPIO_BITREG(PORTA,4)
|
||||
#define PORTA_Bit5 GPIO_BITREG(PORTA,5)
|
||||
#define PORTA_Bit6 GPIO_BITREG(PORTA,6)
|
||||
#define PORTA_Bit7 GPIO_BITREG(PORTA,7)
|
||||
|
||||
#define PINB_Bit0 GPIO_BITREG(PINB,0)
|
||||
#define PINB_Bit1 GPIO_BITREG(PINB,1)
|
||||
#define PINB_Bit2 GPIO_BITREG(PINB,2)
|
||||
#define PINB_Bit3 GPIO_BITREG(PINB,3)
|
||||
#define PINB_Bit4 GPIO_BITREG(PINB,4)
|
||||
#define PINB_Bit5 GPIO_BITREG(PINB,5)
|
||||
#define PINB_Bit6 GPIO_BITREG(PINB,6)
|
||||
#define PINB_Bit7 GPIO_BITREG(PINB,7)
|
||||
|
||||
#define PORTB_Bit0 GPIO_BITREG(PORTB,0)
|
||||
#define PORTB_Bit1 GPIO_BITREG(PORTB,1)
|
||||
#define PORTB_Bit2 GPIO_BITREG(PORTB,2)
|
||||
#define PORTB_Bit3 GPIO_BITREG(PORTB,3)
|
||||
#define PORTB_Bit4 GPIO_BITREG(PORTB,4)
|
||||
#define PORTB_Bit5 GPIO_BITREG(PORTB,5)
|
||||
#define PORTB_Bit6 GPIO_BITREG(PORTB,6)
|
||||
#define PORTB_Bit7 GPIO_BITREG(PORTB,7)
|
||||
|
||||
#define PINC_Bit0 GPIO_BITREG(PINC,0)
|
||||
#define PINC_Bit1 GPIO_BITREG(PINC,1)
|
||||
#define PINC_Bit2 GPIO_BITREG(PINC,2)
|
||||
#define PINC_Bit3 GPIO_BITREG(PINC,3)
|
||||
#define PINC_Bit4 GPIO_BITREG(PINC,4)
|
||||
#define PINC_Bit5 GPIO_BITREG(PINC,5)
|
||||
#define PINC_Bit6 GPIO_BITREG(PINC,6)
|
||||
#define PINC_Bit7 GPIO_BITREG(PINC,7)
|
||||
|
||||
#define PORTC_Bit0 GPIO_BITREG(PORTC,0)
|
||||
#define PORTC_Bit1 GPIO_BITREG(PORTC,1)
|
||||
#define PORTC_Bit2 GPIO_BITREG(PORTC,2)
|
||||
#define PORTC_Bit3 GPIO_BITREG(PORTC,3)
|
||||
#define PORTC_Bit4 GPIO_BITREG(PORTC,4)
|
||||
#define PORTC_Bit5 GPIO_BITREG(PORTC,5)
|
||||
#define PORTC_Bit6 GPIO_BITREG(PORTC,6)
|
||||
#define PORTC_Bit7 GPIO_BITREG(PORTC,7)
|
||||
|
||||
#define PIND_Bit0 GPIO_BITREG(PIND,0)
|
||||
#define PIND_Bit1 GPIO_BITREG(PIND,1)
|
||||
#define PIND_Bit2 GPIO_BITREG(PIND,2)
|
||||
#define PIND_Bit3 GPIO_BITREG(PIND,3)
|
||||
#define PIND_Bit4 GPIO_BITREG(PIND,4)
|
||||
#define PIND_Bit5 GPIO_BITREG(PIND,5)
|
||||
#define PIND_Bit6 GPIO_BITREG(PIND,6)
|
||||
#define PIND_Bit7 GPIO_BITREG(PIND,7)
|
||||
|
||||
#define PORTD_Bit0 GPIO_BITREG(PORTD,0)
|
||||
#define PORTD_Bit1 GPIO_BITREG(PORTD,1)
|
||||
#define PORTD_Bit2 GPIO_BITREG(PORTD,2)
|
||||
#define PORTD_Bit3 GPIO_BITREG(PORTD,3)
|
||||
#define PORTD_Bit4 GPIO_BITREG(PORTD,4)
|
||||
#define PORTD_Bit5 GPIO_BITREG(PORTD,5)
|
||||
#define PORTD_Bit6 GPIO_BITREG(PORTD,6)
|
||||
#define PORTD_Bit7 GPIO_BITREG(PORTD,7)
|
||||
|
||||
#define GPIOR0_Bit0 GPIO_BITREG(GPIOR0,0)
|
||||
#define GPIOR0_Bit1 GPIO_BITREG(GPIOR0,1)
|
||||
#define GPIOR0_Bit2 GPIO_BITREG(GPIOR0,2)
|
||||
#define GPIOR0_Bit3 GPIO_BITREG(GPIOR0,3)
|
||||
#define GPIOR0_Bit4 GPIO_BITREG(GPIOR0,4)
|
||||
#define GPIOR0_Bit5 GPIO_BITREG(GPIOR0,5)
|
||||
#define GPIOR0_Bit6 GPIO_BITREG(GPIOR0,6)
|
||||
#define GPIOR0_Bit7 GPIO_BITREG(GPIOR0,7)
|
||||
|
||||
#define GPIOR1_Bit0 GPIO_BITREG(GPIOR1,0)
|
||||
#define GPIOR1_Bit1 GPIO_BITREG(GPIOR1,1)
|
||||
#define GPIOR1_Bit2 GPIO_BITREG(GPIOR1,2)
|
||||
#define GPIOR1_Bit3 GPIO_BITREG(GPIOR1,3)
|
||||
#define GPIOR1_Bit4 GPIO_BITREG(GPIOR1,4)
|
||||
#define GPIOR1_Bit5 GPIO_BITREG(GPIOR1,5)
|
||||
#define GPIOR1_Bit6 GPIO_BITREG(GPIOR1,6)
|
||||
#define GPIOR1_Bit7 GPIO_BITREG(GPIOR1,7)
|
||||
|
||||
#define GPIOR2_Bit0 GPIO_BITREG(GPIOR2,0)
|
||||
#define GPIOR2_Bit1 GPIO_BITREG(GPIOR2,1)
|
||||
#define GPIOR2_Bit2 GPIO_BITREG(GPIOR2,2)
|
||||
#define GPIOR2_Bit3 GPIO_BITREG(GPIOR2,3)
|
||||
#define GPIOR2_Bit4 GPIO_BITREG(GPIOR2,4)
|
||||
#define GPIOR2_Bit5 GPIO_BITREG(GPIOR2,5)
|
||||
#define GPIOR2_Bit6 GPIO_BITREG(GPIOR2,6)
|
||||
#define GPIOR2_Bit7 GPIO_BITREG(GPIOR2,7)
|
||||
|
||||
#endif
|
||||
|
||||
/* Global Interrupts */
|
||||
#if defined(__GNUC__)
|
||||
#define __enable_interrupt() sei()
|
||||
#define __disable_interrupt() cli()
|
||||
#endif
|
||||
|
||||
/* Interrupts */
|
||||
#if defined(__ICCAVR__)
|
||||
#define PRAGMA(x) _Pragma( #x )
|
||||
#define ISR(vec) PRAGMA( vector=vec ) __interrupt void handler_##vec(void)
|
||||
#endif
|
||||
#if defined(__GNUC__)
|
||||
#include <avr/interrupt.h>
|
||||
#endif
|
||||
|
||||
/* Flash */
|
||||
#if defined(__ICCAVR__)
|
||||
#define FLASH_DECLARE(x) __flash x
|
||||
#endif
|
||||
#if defined(__GNUC__)
|
||||
#define FLASH_DECLARE(x) x __attribute__((__progmem__))
|
||||
#endif
|
||||
|
||||
/* EEPROM */
|
||||
#if defined(__ICCAVR__)
|
||||
#define EEPROM_DECLARE(x) __eeprom x
|
||||
#endif
|
||||
#if defined(__GNUC__)
|
||||
#include <avr/eeprom.h>
|
||||
#define EEPROM_DECLARE(x) x __attribute__((section (".eeprom")))
|
||||
#endif
|
||||
|
||||
/* IAR intrinsic routines */
|
||||
#if defined(__GNUC__)
|
||||
/* FIXME: intrinsic routines: map to assembler for size/speed */
|
||||
#define __multiply_unsigned(x,y) ((x)*(y))
|
||||
/* FIXME: __root means to not optimize or strip */
|
||||
#define __root
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,147 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
#include "timer.h"
|
||||
#include "rs485.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/object/av.h"
|
||||
|
||||
/* From the WhoIs hander - performed by the DLMSTP module */
|
||||
extern bool Send_I_Am_Flag;
|
||||
/* local version override */
|
||||
const char *BACnet_Version = "1.0";
|
||||
|
||||
/* For porting to IAR, see:
|
||||
http://www.avrfreaks.net/wiki/index.php/Documentation:AVR_GCC/IarToAvrgcc*/
|
||||
|
||||
/* dummy function - so we can use default demo handlers */
|
||||
bool dcc_communication_enabled(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
/* Initialize the Clock Prescaler for ATmega48/88/168 */
|
||||
/* The default CLKPSx bits are factory set to 0011 */
|
||||
/* Enbable the Clock Prescaler */
|
||||
CLKPR = _BV(CLKPCE);
|
||||
/* CLKPS3 CLKPS2 CLKPS1 CLKPS0 Clock Division Factor
|
||||
------ ------ ------ ------ ---------------------
|
||||
0 0 0 0 1
|
||||
0 0 0 1 2
|
||||
0 0 1 0 4
|
||||
0 0 1 1 8
|
||||
0 1 0 0 16
|
||||
0 1 0 1 32
|
||||
0 1 1 0 64
|
||||
0 1 1 1 128
|
||||
1 0 0 0 256
|
||||
1 x x x Reserved
|
||||
*/
|
||||
/* Set the CLKPS3..0 bits to Prescaler of 1 */
|
||||
CLKPR = 0;
|
||||
/* Initialize I/O ports */
|
||||
/* For Port DDRx (Data Direction) Input=0, Output=1 */
|
||||
/* For Port PORTx (Bit Value) TriState=0, High=1 */
|
||||
DDRB = 0;
|
||||
PORTB = 0;
|
||||
DDRC = 0;
|
||||
PORTC = 0;
|
||||
DDRD = 0;
|
||||
PORTD = 0;
|
||||
|
||||
/* Configure the watchdog timer - Disabled for testing */
|
||||
BIT_CLEAR(MCUSR, WDRF);
|
||||
WDTCSR = 0;
|
||||
|
||||
/* Configure Specialized Hardware */
|
||||
RS485_Initialize();
|
||||
|
||||
/* Configure Timer0 for millisecond timer */
|
||||
Timer_Initialize();
|
||||
|
||||
/* Enable global interrupts */
|
||||
__enable_interrupt();
|
||||
}
|
||||
|
||||
static void task_milliseconds(void)
|
||||
{
|
||||
while (Timer_Milliseconds) {
|
||||
Timer_Milliseconds--;
|
||||
/* add other millisecond timer tasks here */
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t Address_Switch;
|
||||
|
||||
static void input_switch_read(void)
|
||||
{
|
||||
uint8_t value;
|
||||
static uint8_t old_value = 0;
|
||||
|
||||
value = BITMASK_CHECK(PINC, 0x0F);
|
||||
value |= (BITMASK_CHECK(PINB, 0x07) << 4);
|
||||
if (value != old_value) {
|
||||
old_value = value;
|
||||
} else {
|
||||
if (old_value != Address_Switch) {
|
||||
Address_Switch = old_value;
|
||||
#if defined(BACDL_MSTP)
|
||||
dlmstp_set_mac_address(Address_Switch);
|
||||
#endif
|
||||
Device_Set_Object_Instance_Number(86000 + Address_Switch);
|
||||
Send_I_Am_Flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Static receive buffer, initialized with zeros by the C Library Startup Code. */
|
||||
|
||||
static uint8_t PDUBuffer[MAX_MPDU + 16 /* Add a little safety margin to the buffer,
|
||||
* so that in the rare case, the message
|
||||
* would be filled up to MAX_MPDU and some
|
||||
* decoding functions would overrun, these
|
||||
* decoding functions will just end up in
|
||||
* a safe field of static zeros. */];
|
||||
|
||||
/** Main */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
BACNET_ADDRESS src; /* source address */
|
||||
|
||||
init();
|
||||
#if defined(BACDL_MSTP)
|
||||
RS485_Set_Baud_Rate(38400);
|
||||
dlmstp_set_max_master(127);
|
||||
dlmstp_set_max_info_frames(1);
|
||||
#endif
|
||||
datalink_init(NULL);
|
||||
for (;;) {
|
||||
input_switch_read();
|
||||
task_milliseconds();
|
||||
/* other tasks */
|
||||
/* BACnet handling */
|
||||
pdu_len = datalink_receive(&src, &PDUBuffer[0], MAX_MPDU, 0);
|
||||
if (pdu_len) {
|
||||
npdu_handler(&src, &PDUBuffer[0], pdu_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
This port was done with the Atmel ATmega168 using two tools:
|
||||
1. The WinAVR compiler avr-gcc (GCC) 4.1.2 (WinAVR 20070525)
|
||||
and tools from <http://winavr.sourceforge.net/>, hints and
|
||||
sample code from <http://www.avrfreaks.net/> and
|
||||
<http://savannah.gnu.org/projects/avr-libc/>.
|
||||
"avr-binutils, avr-gcc, and avr-libc form the heart of the
|
||||
Free Software toolchain for the Atmel AVR microcontrollers."
|
||||
2. AVR Studio from Atmel <http://atmel.com/>
|
||||
|
||||
Alternatively, the project also builds using IAR Embedded Workbench AVR.
|
||||
|
||||
The hardware is expected to utilize the signals as defined
|
||||
in the spreadsheet hardware.ods (OpenOffice.org calc).
|
||||
Attach a DS75176 RS-485 transceiver (or similar) to the USART.
|
||||
DS75176 ATmega168
|
||||
------ ---------
|
||||
RO RXD
|
||||
/RE --choice of I/O
|
||||
DE --choice of I/O
|
||||
DI TXD
|
||||
GND GND
|
||||
DO --to RS-485 wire
|
||||
DO --to RS-485 wire
|
||||
+5V From 5V Regulator
|
||||
|
||||
The makefile allows you to build just the dlmstp or a simple
|
||||
server. dlmstp is the datalink layer for MS/TP over RS-485.
|
||||
|
||||
I used the makefile from the command line on Windows:
|
||||
C:\code\bacnet-stack\ports\atmega168> make clean all
|
||||
|
||||
CStack check for GCC is included in the device object as property 512.
|
||||
The compile shows 648 bytes of RAM used, and the ATmega168 has 1024 bytes
|
||||
of RAM, leaving 376 for the CStack. Property 512 index 0 returns 376 from
|
||||
a ReadProperty request. My understanding is that the remaining unallocated
|
||||
RAM is used for the CStack. Keep this in mind when developing.
|
||||
After some ReadProperty and WriteProperty requests, the CStack shows
|
||||
159 CStack bytes free, meaning that 216 bytes of CStack are used.
|
||||
Note that the value 0xC5 (197) was used to paint the CStack.
|
||||
|
||||
I also used the bacnet.aps project file in AVR Studio to
|
||||
make the project and simulate it, but have not kept it updated (FIXME).
|
||||
|
||||
Compiler settings for IAR Embedded Workbench (FIXME: makefile?):
|
||||
General Options
|
||||
---------------
|
||||
Target
|
||||
Processor configuration: --cpu=m168. ATmega168
|
||||
Memory Model: Small
|
||||
System configuration: Configure system using dialogs (not in .XCL file)
|
||||
Output
|
||||
Executable
|
||||
Output Directories: Debug\Exe, Debug\Obj, Debug\List
|
||||
Library Configuration
|
||||
Library: CLIB
|
||||
Library Options
|
||||
Printf formatter: Small
|
||||
Scanf formatter: Medium
|
||||
Heap Configuration
|
||||
CLIB heap size: 0x10
|
||||
System
|
||||
CSTACK: 0x200
|
||||
RSTACK: 32
|
||||
Initialize unused interrupt vectors with RETI instructions (enabled)
|
||||
Enable bit defnitions in I/O-Include files. (enabled)
|
||||
MISRA C
|
||||
not enabled
|
||||
|
||||
C/C++ Compiler
|
||||
--------------
|
||||
Language
|
||||
Language: C
|
||||
Require prototypes (not enabled)
|
||||
Allow IAR extensions
|
||||
Plain 'char' is Signed
|
||||
Enable multibyte support (not enabled)
|
||||
Code
|
||||
Memory utilization:
|
||||
Place aggregate initializers in flash memory (enabled)
|
||||
Force generation of all global and static variables (not enabled)
|
||||
Register utilization:
|
||||
Number of registers to lock for global variables: 0
|
||||
Use ICCA90 1.x calling convention (not enabled)
|
||||
Optimizations
|
||||
Size: High (Maximum optimization)
|
||||
Number of cross-call passes: Unlimited
|
||||
Always do cross call optimization (not enabled)
|
||||
Output
|
||||
Module type: Override default (not enabled)
|
||||
Object module name (not enabled)
|
||||
Generate debug information (enabled)
|
||||
No error messages in output files (not enabled)
|
||||
List
|
||||
Output list file (not enabled)
|
||||
Output assembler file (enabled)
|
||||
Preprocessor
|
||||
Ignore standard include paths (not enabled)
|
||||
Include paths:
|
||||
$PROJ_DIR$
|
||||
$PROJ_DIR$\..\..\include
|
||||
Preinclude file: (none)
|
||||
Defined symbols:
|
||||
BACDL_MSTP
|
||||
MAX_APDU=50
|
||||
MAX_TSM_TRANSACTIONS=0
|
||||
BACAPP_REAL
|
||||
BACAPP_UNSIGNED
|
||||
BACAPP_ENUMERATED
|
||||
BACAPP_CHARACTER_STRING
|
||||
BACAPP_OBJECT_ID
|
||||
WRITE_PROPERTY
|
||||
Diagnostics
|
||||
(not enabled)
|
||||
MISRA C
|
||||
(not enabled)
|
||||
Extra Options
|
||||
Use command line options (not enabled)
|
||||
|
||||
Note: The BACnet Stack at Sourceforge source code has to be built
|
||||
with lots of different compilers. The IAR compiler has particularly
|
||||
strong (pedantic) source checking and generates several warnings when
|
||||
compiling the source code. Unfortunately not all warnings can be
|
||||
fixed by modifying the source code. Some warnings have therefore been
|
||||
disabled in the project file.
|
||||
Compiler Diagnostics:
|
||||
(Pe550) I initilize all local variables as a best practice.
|
||||
Linker Diagnostics:
|
||||
(w31) The supplied standard libraries expect char parameters to
|
||||
be unsigned (in functions such as strncpy(), etc.). It may
|
||||
be possible to recompile the libraries with signed plain char's.
|
||||
|
||||
The BACnet Capabilities include WhoIs, I-Am, ReadProperty, and
|
||||
WriteProperty support. The BACnet objects include a Device object,
|
||||
10 Binary Value objects, and 10 Analog Value objects. An LED is
|
||||
controlled by Binary Value object instance 0. All required object
|
||||
properties can be retrieved using ReadProperty. The Present_Value
|
||||
property of the Analog Value and Binary Value objects can be
|
||||
written using WriteProperty. The Object_Identifier, Object_Name,
|
||||
Max_Info_Frames, Max_Master, and baud rate (property 9600) of the
|
||||
Device object can be written using WriteProperty.
|
||||
|
||||
With full optimization, the statistics on the demo are:
|
||||
|
||||
IAR Atmel AVR C/C++ Compiler V5.10A/W32
|
||||
12 732 bytes of CODE memory (+ 36 range fill )
|
||||
955 bytes of DATA memory (+ 24 absolute ) (includes CStack=0×200)
|
||||
|
||||
avr-gcc (GCC) 4.2.2 (WinAVR 20071221rc1)
|
||||
Program: 15790 bytes (96.4% Full)
|
||||
Data: 414 bytes (40.4% Full) (does not include CStack=0×262)
|
||||
|
||||
Hopefully you find this code useful!
|
||||
|
||||
Steve Karg <skarg@users.sourceforge.net>
|
||||
@@ -1,45 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2004 Steve Karg
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef RS485_H
|
||||
#define RS485_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void RS485_Initialize(
|
||||
void);
|
||||
|
||||
void RS485_Transmitter_Enable(
|
||||
bool enable);
|
||||
|
||||
void RS485_Send_Data(
|
||||
const uint8_t * buffer, /* data to send */
|
||||
uint16_t nbytes); /* number of bytes of data */
|
||||
|
||||
bool RS485_ReceiveError(
|
||||
void);
|
||||
bool RS485_DataAvailable(
|
||||
uint8_t * data);
|
||||
|
||||
void RS485_Turnaround_Delay(
|
||||
void);
|
||||
uint32_t RS485_Get_Baud_Rate(
|
||||
void);
|
||||
bool RS485_Set_Baud_Rate(
|
||||
uint32_t baud);
|
||||
|
||||
void RS485_LED_Timers(
|
||||
void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -1,34 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef STACK_H
|
||||
#define STACK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* C stack checking */
|
||||
void stack_init(
|
||||
void);
|
||||
|
||||
unsigned stack_size(
|
||||
void);
|
||||
|
||||
uint8_t stack_byte(
|
||||
unsigned offset);
|
||||
|
||||
unsigned stack_unused(
|
||||
void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -1,87 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* This module is a 1 millisecond timer */
|
||||
|
||||
/* Prescaling: 1, 8, 64, 256, 1024 */
|
||||
#define TIMER_PRESCALER 64
|
||||
/* Count: Timer0 counts up to 0xFF and then signals overflow */
|
||||
#define TIMER_TICKS (F_CPU / TIMER_PRESCALER / 1000)
|
||||
#if (TIMER_TICKS > 0xFF)
|
||||
#error Timer Prescaler value is too small
|
||||
#endif
|
||||
#define TIMER_COUNT (0xFF - TIMER_TICKS)
|
||||
/* Global variable millisecond timer - used by main.c for timers task */
|
||||
volatile uint8_t Timer_Milliseconds = 0;
|
||||
/* MS/TP Silence Timer */
|
||||
static volatile uint16_t SilenceTime;
|
||||
|
||||
/* Configure the Timer */
|
||||
void Timer_Initialize(void)
|
||||
{
|
||||
/* Normal Operation */
|
||||
TCCR1A = 0;
|
||||
/* CSn2 CSn1 CSn0 Description
|
||||
---- ---- ---- -----------
|
||||
0 0 0 No Clock Source
|
||||
0 0 1 No prescaling
|
||||
0 1 0 CLKio/8
|
||||
0 1 1 CLKio/64
|
||||
1 0 0 CLKio/256
|
||||
1 0 1 CLKio/1024
|
||||
1 1 0 Falling Edge of T0 (external)
|
||||
1 1 1 Rising Edge of T0 (external)
|
||||
*/
|
||||
TCCR0B = _BV(CS01) | _BV(CS00);
|
||||
/* Clear any TOV1 Flag set when the timer overflowed */
|
||||
BIT_CLEAR(TIFR0, TOV0);
|
||||
/* Initial value */
|
||||
TCNT0 = TIMER_COUNT;
|
||||
/* Enable the overflow interrupt */
|
||||
BIT_SET(TIMSK0, TOIE0);
|
||||
/* Clear the Power Reduction Timer/Counter0 */
|
||||
BIT_CLEAR(PRR, PRTIM0);
|
||||
}
|
||||
|
||||
/* Timer interupt */
|
||||
/* note: Global interupts must be enabled - sei() */
|
||||
/* Timer Overflowed! Increment the time. */
|
||||
ISR(TIMER0_OVF_vect)
|
||||
{
|
||||
/* Set the counter for the next interrupt */
|
||||
TCNT0 = TIMER_COUNT;
|
||||
/* Overflow Flag is automatically cleared */
|
||||
/* Update the global timer */
|
||||
if (Timer_Milliseconds < 0xFF)
|
||||
Timer_Milliseconds++;
|
||||
if (SilenceTime < 0xFFFF)
|
||||
SilenceTime++;
|
||||
}
|
||||
|
||||
/* Public access to the Silence Timer */
|
||||
uint16_t Timer_Silence(void)
|
||||
{
|
||||
uint16_t timer;
|
||||
|
||||
BIT_CLEAR(TIMSK0, TOIE0);
|
||||
timer = SilenceTime;
|
||||
BIT_SET(TIMSK0, TOIE0);
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
/* Public reset of the Silence Timer */
|
||||
void Timer_Silence_Reset(void)
|
||||
{
|
||||
BIT_CLEAR(TIMSK0, TOIE0);
|
||||
SilenceTime = 0;
|
||||
BIT_SET(TIMSK0, TOIE0);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
extern volatile uint8_t Timer_Milliseconds;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void Timer_Initialize(
|
||||
void);
|
||||
uint16_t Timer_Silence(
|
||||
void);
|
||||
void Timer_Silence_Reset(
|
||||
void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "MinGW64",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../src",
|
||||
"C:/msys64/mingw64/avr/include/"
|
||||
],
|
||||
"defines": [
|
||||
"__GNUC__",
|
||||
"__AVR_ATmega328P__"
|
||||
],
|
||||
"compilerPath": "C:/msys64/mingw64/bin/avr-gcc.exe",
|
||||
"cStandard": "gnu11",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "windows-gcc-x64",
|
||||
"compilerArgs": [
|
||||
"-mmcu=atmega328p",
|
||||
// Will ensure MCU defines are set correctly
|
||||
"-DF_CPU=16000000UL",
|
||||
// Will ensure F_CPU is set correctly
|
||||
"-Os" // Will avoid optimization warnings re: _delay
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/../../src",
|
||||
"/usr/lib/avr/include"
|
||||
],
|
||||
"defines": [
|
||||
"__GNUC__",
|
||||
"__AVR_ATmega328P__"
|
||||
],
|
||||
"compilerPath": "/usr/bin/avr-gcc",
|
||||
"cStandard": "gnu11",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "${default}",
|
||||
"compilerArgs": [
|
||||
"-mmcu=atmega328p",
|
||||
// Will ensure MCU defines are set correctly
|
||||
"-DF_CPU=16000000UL",
|
||||
// Will ensure F_CPU is set correctly
|
||||
"-Os" // Will avoid optimization warnings re: _delay
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
###############################################################################
|
||||
|
||||
## General Flags
|
||||
MCU = atmega328
|
||||
AVRDUDE_MCU = m328
|
||||
MCU = atmega328p
|
||||
AVRDUDE_MCU = m328p
|
||||
TARGET = bacnet
|
||||
## Tools
|
||||
CC = avr-gcc
|
||||
@@ -13,7 +13,6 @@ OBJCOPY = avr-objcopy
|
||||
OBJDUMP = avr-objdump
|
||||
SIZE = avr-size
|
||||
AVRDUDE = avrdude
|
||||
LINT = splint
|
||||
|
||||
# programmer id--check the avrdude for complete list
|
||||
# # of available opts. These should include stk500,
|
||||
@@ -21,12 +20,16 @@ LINT = splint
|
||||
# # one of the valid "-c PROGRAMMER-ID" values
|
||||
# # described in the avrdude info page.
|
||||
# #
|
||||
AVRDUDE_PROGRAMMERID = avrispmkII
|
||||
AVRDUDE_PROGRAMMERID = arduino
|
||||
#
|
||||
# # port--serial or parallel port to which your
|
||||
# # hardware programmer is attached
|
||||
# #
|
||||
AVRDUDE_PORT = /dev/ttyUSB0
|
||||
ifeq ($(OS),Windows_NT)
|
||||
AVRDUDE_PORT = COM21
|
||||
else
|
||||
AVRDUDE_PORT = /dev/ttyUSB0
|
||||
endif
|
||||
|
||||
# Source locations
|
||||
BACNET_SRC = ../../src
|
||||
@@ -35,12 +38,15 @@ BACNET_BASIC = $(BACNET_CORE)/basic
|
||||
|
||||
# local files for this project
|
||||
CSRC = main.c \
|
||||
adc.c \
|
||||
timer.c \
|
||||
stack.c \
|
||||
rs485.c \
|
||||
dlmstp.c \
|
||||
apdu.c \
|
||||
h_rp.c \
|
||||
eeprom.c \
|
||||
nvdata.c \
|
||||
device.c \
|
||||
av.c \
|
||||
bv.c \
|
||||
@@ -52,6 +58,7 @@ BASICSRC = $(BACNET_BASIC)/tsm/tsm.c \
|
||||
$(BACNET_BASIC)/npdu/h_npdu.c \
|
||||
$(BACNET_BASIC)/sys/bigend.c \
|
||||
$(BACNET_BASIC)/sys/debug.c \
|
||||
$(BACNET_BASIC)/sys/mstimer.c \
|
||||
$(BACNET_BASIC)/service/s_iam.c \
|
||||
$(BACNET_BASIC)/service/h_noserv.c
|
||||
|
||||
@@ -99,15 +106,14 @@ BFLAGS = -DBACDL_MSTP
|
||||
BFLAGS += -DMAX_APDU=50
|
||||
BFLAGS += -DBIG_ENDIAN=0
|
||||
BFLAGS += -DMAX_TSM_TRANSACTIONS=0
|
||||
#BFLAGS += -DCRC_USE_TABLE
|
||||
BFLAGS += -DBACNET_SVC_SERVER
|
||||
BFLAGS += -DBACAPP_REAL
|
||||
BFLAGS += -DBACAPP_OBJECT_ID
|
||||
BFLAGS += -DBACAPP_UNSIGNED
|
||||
BFLAGS += -DBACAPP_ENUMERATED
|
||||
BFLAGS += -DBACAPP_CHARACTER_STRING
|
||||
BFLAGS += -DWRITE_PROPERTY
|
||||
BFLAGS += -DMAX_ANALOG_VALUES=10
|
||||
BFLAGS += -DMAX_BINARY_VALUES=10
|
||||
BFLAGS += -DBACNET_PROTOCOL_REVISION=9
|
||||
ifeq (${LEGACY},true)
|
||||
# disable deprecated function warnings for legacy builds
|
||||
BFLAGS += -DBACNET_STACK_DEPRECATED_DISABLE
|
||||
@@ -118,6 +124,12 @@ CFLAGS = $(COMMON)
|
||||
CFLAGS += -ffunction-sections -fdata-sections
|
||||
CFLAGS += -Wall -gdwarf-2 $(BFLAGS) $(OPTIMIZATION) -fsigned-char
|
||||
CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d
|
||||
# fix warnings about 'array subscript 0 is outside array bounds' new in GCC 12
|
||||
GCCVERSIONGTEQ12 := $(shell expr `avr-gcc -dumpversion | cut -f1 -d.` \>= 12)
|
||||
ifeq "$(GCCVERSIONGTEQ12)" "1"
|
||||
CFLAGS += --param=min-pagesize=0
|
||||
endif
|
||||
CFLAGS += -DF_CPU=16000000UL
|
||||
# silence some warnings
|
||||
CFLAGS += -Wno-switch
|
||||
|
||||
@@ -176,9 +188,6 @@ size: ${TARGET_ELF}
|
||||
@echo
|
||||
@${SIZE} ${TARGET_ELF}
|
||||
|
||||
lint:
|
||||
$(LINT) $(BFLAGS) $(CSRC)
|
||||
|
||||
install: $(TARGET_ELF)
|
||||
$(AVRDUDE) -c $(AVRDUDE_PROGRAMMERID) \
|
||||
-p $(AVRDUDE_MCU) -P $(AVRDUDE_PORT) -e \
|
||||
@@ -0,0 +1,116 @@
|
||||
### BACnet MS/TP on Arduino Uno R3 Platform - Atmega328p
|
||||
|
||||
The Arduino Uno R3 platform for this example uses the following peripherals:
|
||||
|
||||
1) USB bootloader using AVR-119 protocol
|
||||
|
||||
2) 7-position DIP switch for MS/TP MAC address
|
||||
|
||||
3) Arduino Uno V3 compatible RS485 shield (DFR0259)
|
||||
|
||||
### Arduino Uno V3 Pin Mapping
|
||||
|
||||
| ATmega328p | Arduino | RS485 DFR0259 | BACnet Object |
|
||||
|:------------|:------------|:---------------|:----------------|
|
||||
| NC | NC | | |
|
||||
| +5V | IOREF | | |
|
||||
| RESET | RESET | RST BUTTON | |
|
||||
| +3V3 | +3V3 | | |
|
||||
| +5V | +5V | +5V | |
|
||||
| GND | GND | GND | |
|
||||
| GND | GND | GND | |
|
||||
| VIN | VIN | | |
|
||||
| | | | |
|
||||
| ADC0/PC0 | A0 | | AV 0 Millivolts |
|
||||
| ADC1/PC1 | A1 | | AV 1 Millivolts |
|
||||
| ADC2/PC2 | A2 | | AV 2 Millivolts |
|
||||
| ADC3/PC3 | A3 | | AV 3 Millivolts |
|
||||
| ADC4/PC4 | A4* | | |
|
||||
| ADC5/PC5 | A5* | | |
|
||||
| | | | |
|
||||
| ADC5/PC5 | SCL* | I2C | |
|
||||
| ADC4/PC4 | SDA* | I2C | |
|
||||
| AVDD | AREF | | |
|
||||
| GND | GND | | |
|
||||
| SCK/PB5 | D13 | LED-L ANODE (+)| BV 99 (output) |
|
||||
| MISO/PB4 | D12 | | BV 9 (output) |
|
||||
| MOSI/PB3 | D11 | | BV 8 (output) |
|
||||
| SS/PB2 | D10 | | BV 7 (output) |
|
||||
| OC1/PB1 | D9 | | BV 6 (output) |
|
||||
| ICP/PB0 | D8 | | BV 5 (output) |
|
||||
| | | | |
|
||||
| AIN1/PD7 | D7 | | BV 4 (input) |
|
||||
| AIN0/PD6 | D6 | | BV 3 (input) |
|
||||
| T1/PD5 | D5 | | BV 2 (input) |
|
||||
| T0/PD4 | D4 | | BV 1 (input) |
|
||||
| INT1/PD3 | D3 | | BV 0 (input) |
|
||||
| INT0/PD2 | D2 | CE DE /RE RTS | RS485 |
|
||||
| TXD/PD1 | D1/Tx** | TXD | RS485 |
|
||||
| RXD/PD0 | D0/Rx** | RXD | RS485 |
|
||||
|
||||
\* ADC4/PC4: A4 and SDA are shared I/O
|
||||
|
||||
\* ADC5/PC5: A5 and SCL are shared I/O
|
||||
|
||||
\** shared with Uno R3 USB Tx/Rx. DFR0259 switch ON to disable.
|
||||
|
||||
### Building this Project
|
||||
|
||||
#### GNU Makefile
|
||||
|
||||
There is a GNU Makefile that uses avr-gcc and avr-libc to build the project.
|
||||
avrdude can be used to program the Arduino Uno R3 board via USB.
|
||||
|
||||
The GNU Makefile is used in the continuous integration pipeline to validate
|
||||
the build is not broken. The Makefile is called from an Ubuntu image
|
||||
container after installing the necessary tools:
|
||||
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq build-essential
|
||||
sudo apt-get install -qq gcc-avr avr-libc binutils-avr avrdude
|
||||
|
||||
To add the build and debug tools to MinGW64 environment:
|
||||
|
||||
pacman --noconfirm -S mingw-w64-x86_64-gcc-avr
|
||||
pacman --noconfirm -S mingw-w64-x86_64-avr-libc
|
||||
pacman --noconfirm -S mingw-w64-x86_64-binutils-avr
|
||||
pacman --noconfirm -S mingw-w64-x86_64-avrdude
|
||||
|
||||
The build sequence is usually:
|
||||
|
||||
make clean all
|
||||
|
||||
The Makefile includes a recipe to use avrdude to program the Uno R3 via USB
|
||||
|
||||
make install
|
||||
|
||||
#### GCC and CStack Usage
|
||||
|
||||
CStack check for GCC is included in the device object as property 512.
|
||||
The compile shows 352 (.bss) + 136 (.data) = 488 bytes of RAM used, and
|
||||
the ATmega328 has 2048 bytes of RAM, leaving 1560 bytes for the CStack.
|
||||
BACnet Device object proprietary property 512 is mapped to the CStack Size
|
||||
and returns 1376 using ReadProperty request. After some ReadProperty and
|
||||
WriteProperty requests, the CStack shows 1159 CStack bytes free.
|
||||
Note that the value 0xC5 (197) was used to paint the CStack.
|
||||
Keep this in mind when developing.
|
||||
|
||||
##### Shield option
|
||||
|
||||
The DFR0259 shield for RS485 was used, but any RS485 circuit could be
|
||||
attached to the Arduino Uno R3 using the same pins for Tx, Rx, CE RE/DE RTS.
|
||||
|
||||
A 7-position DIP switch was connected to GPIO for the MS/TP MAC address,
|
||||
but the value could also be stored in EEPROM.
|
||||
|
||||
### BACnet Capabilities
|
||||
|
||||
The BACnet Capabilities include WhoIs, I-Am, ReadProperty, and
|
||||
WriteProperty support. The BACnet objects include a Device object,
|
||||
10 Binary Value objects, and 10 Analog Value objects. An GPIO output
|
||||
is controlled by Binary Value object instance 0. All required object
|
||||
properties can be retrieved using ReadProperty. The Present_Value
|
||||
property of the Analog Value and Binary Value objects can be
|
||||
written using WriteProperty. The Object_Identifier, Object_Name,
|
||||
Max_Info_Frames, Max_Master, and baud rate (property 9600) of the
|
||||
Device object can be written using WriteProperty.
|
||||
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* @brief This module manages the Analog to Digital Converter (ADC)
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
/* me */
|
||||
#include "adc.h"
|
||||
|
||||
/* prescale select bits */
|
||||
#if (F_CPU >> 1) < 1000000
|
||||
#define ADPS_8BIT (1)
|
||||
#define ADPS_10BIT (3)
|
||||
#elif (F_CPU >> 2) < 1000000
|
||||
#define ADPS_8BIT (2)
|
||||
#define ADPS_10BIT (4)
|
||||
#elif (F_CPU >> 3) < 1000000
|
||||
#define ADPS_8BIT (3)
|
||||
#define ADPS_10BIT (5)
|
||||
#elif (F_CPU >> 4) < 1000000
|
||||
#define ADPS_8BIT (4)
|
||||
#define ADPS_10BIT (6)
|
||||
#elif (F_CPU >> 5) < 1000000
|
||||
#define ADPS_8BIT (5)
|
||||
#define ADPS_10BIT (7)
|
||||
#else
|
||||
#error "ADC: F_CPU too large for accuracy."
|
||||
#endif
|
||||
|
||||
/* full scale ADC voltage constant in millivolts */
|
||||
#ifndef ADC_MILLIVOLTS_MAX
|
||||
#define ADC_MILLIVOLTS_MAX 5000L
|
||||
#endif
|
||||
/* full scale ADC value */
|
||||
#ifndef ADC_VALUE_MAX
|
||||
#define ADC_VALUE_MAX 1024L
|
||||
#endif
|
||||
|
||||
/* Array of ADC results */
|
||||
#define ADC_CHANNELS_MAX 8
|
||||
static volatile uint16_t Sample_Result[ADC_CHANNELS_MAX];
|
||||
static volatile uint8_t Enabled_Channels;
|
||||
|
||||
/**
|
||||
* @brief ADC interrupt based acquisition ISR
|
||||
*/
|
||||
ISR(ADC_vect)
|
||||
{
|
||||
uint8_t index;
|
||||
uint8_t mask;
|
||||
uint8_t channels;
|
||||
uint16_t value = 0;
|
||||
|
||||
/* determine which conversion finished */
|
||||
index = BITMASK_CHECK(ADMUX, ((1 << MUX2) | (1 << MUX1) | (1 << MUX0)));
|
||||
/* read the results */
|
||||
value = ADCL;
|
||||
value |= (ADCH << 8);
|
||||
Sample_Result[index] = value;
|
||||
channels = Enabled_Channels;
|
||||
__enable_interrupt();
|
||||
/* clear the mux */
|
||||
BITMASK_CLEAR(ADMUX, ((1 << MUX2) | (1 << MUX1) | (1 << MUX0)));
|
||||
/* find the next enabled channel */
|
||||
while (channels) {
|
||||
index++;
|
||||
if (index >= ADC_CHANNELS_MAX) {
|
||||
index = 0;
|
||||
}
|
||||
mask = 1 << index;
|
||||
if (channels & mask) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* configure the next channel */
|
||||
BITMASK_SET(ADMUX, ((index) << MUX0));
|
||||
/* Start the next conversion */
|
||||
BIT_SET(ADCSRA, ADSC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the ADC channel for interrupt based acquisition
|
||||
* @param index - 0..8 = ADC0..ADC8, respectively
|
||||
*/
|
||||
void adc_enable(uint8_t index)
|
||||
{
|
||||
if (Enabled_Channels) {
|
||||
/* ADC interupt is already started */
|
||||
BIT_SET(Enabled_Channels, index);
|
||||
} else {
|
||||
if (index < ADC_CHANNELS_MAX) {
|
||||
/* not running yet */
|
||||
BIT_SET(Enabled_Channels, index);
|
||||
/* clear the mux */
|
||||
BITMASK_CLEAR(ADMUX, ((1 << MUX2) | (1 << MUX1) | (1 << MUX0)));
|
||||
/* configure the channel */
|
||||
BITMASK_SET(ADMUX, ((index) << MUX0));
|
||||
/* Start the next conversion */
|
||||
BIT_SET(ADCSRA, ADSC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the latest ADC channel value (8-bit)
|
||||
* @param index - 0..8 = ADC0..ADC8, respectively
|
||||
*/
|
||||
uint8_t adc_result_8bit(uint8_t index)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
uint8_t sreg;
|
||||
|
||||
if (index < ADC_CHANNELS_MAX) {
|
||||
adc_enable(index);
|
||||
sreg = SREG;
|
||||
__disable_interrupt();
|
||||
result = (uint8_t)(Sample_Result[index] >> 2);
|
||||
SREG = sreg;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the latest ADC channel value (10-bit)
|
||||
* @param index - 0..8 = ADC0..ADC8, respectively
|
||||
*/
|
||||
uint16_t adc_result_10bit(uint8_t index)
|
||||
{ /* 0..7 = ADC0..ADC7, respectively */
|
||||
uint16_t result = 0;
|
||||
uint8_t sreg;
|
||||
|
||||
if (index < ADC_CHANNELS_MAX) {
|
||||
adc_enable(index);
|
||||
sreg = SREG;
|
||||
__disable_interrupt();
|
||||
result = Sample_Result[index];
|
||||
SREG = sreg;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the latest ADC channel value in millivolts
|
||||
* @param index - 0..8 = ADC0..ADC8, respectively
|
||||
* @return millivolts
|
||||
*/
|
||||
uint16_t adc_millivolts(uint8_t index)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
value = adc_result_10bit(index);
|
||||
value = (value * ADC_MILLIVOLTS_MAX) / ADC_VALUE_MAX;
|
||||
|
||||
return (uint16_t)(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the ADC for interrupt based acquisition
|
||||
*/
|
||||
void adc_init(void)
|
||||
{
|
||||
/* Initial channel selection */
|
||||
/* ADLAR = Left Adjust Result
|
||||
REFSx = hardware setup: cap on AREF
|
||||
*/
|
||||
ADMUX = (0 << ADLAR) | (0 << REFS1) | (1 << REFS0);
|
||||
/* ADEN = Enable
|
||||
ADSC = Start conversion
|
||||
ADIF = Interrupt Flag - write 1 to clear!
|
||||
ADIE = Interrupt Enable
|
||||
ADATE = Auto Trigger Enable
|
||||
*/
|
||||
ADCSRA =
|
||||
(1 << ADEN) | (1 << ADIE) | (1 << ADIF) | (0 << ADATE) | ADPS_10BIT;
|
||||
/* trigger selection bits
|
||||
0 0 0 Free Running mode
|
||||
0 0 1 Analog Comparator
|
||||
0 1 0 External Interrupt Request 0
|
||||
0 1 1 Timer/Counter0 Compare Match
|
||||
1 0 0 Timer/Counter0 Overflow
|
||||
1 0 1 Timer/Counter1 Compare Match B
|
||||
1 1 0 Timer/Counter1 Overflow
|
||||
1 1 1 Timer/Counter1 Capture Event
|
||||
*/
|
||||
ADCSRB = (0 << ADTS2) | (0 << ADTS1) | (0 << ADTS0);
|
||||
/* disable ADC power reduction */
|
||||
PRR &= ((uint8_t) ~(1 << PRADC));
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @brief This module manages the Analog to Digital Converter (ADC)
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef ADC_H
|
||||
#define ADC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void adc_enable(uint8_t index);
|
||||
uint8_t adc_result_8bit(uint8_t index);
|
||||
uint16_t adc_result_10bit(uint8_t index);
|
||||
uint16_t adc_millivolts(uint8_t index);
|
||||
void adc_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -1,10 +1,9 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*
|
||||
*********************************************************************/
|
||||
/**
|
||||
* @brief This module manages the BACnet Application Protocol Data Unit (APDU)
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
@@ -54,7 +53,8 @@ bool apdu_service_supported(BACNET_SERVICES_SUPPORTED service_supported)
|
||||
return status;
|
||||
}
|
||||
|
||||
uint16_t apdu_decode_confirmed_service_request(uint8_t *apdu, /* APDU data */
|
||||
uint16_t apdu_decode_confirmed_service_request(
|
||||
uint8_t *apdu, /* APDU data */
|
||||
uint16_t apdu_len,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data,
|
||||
uint8_t *service_choice,
|
||||
@@ -83,7 +83,8 @@ uint16_t apdu_decode_confirmed_service_request(uint8_t *apdu, /* APDU data */
|
||||
return len;
|
||||
}
|
||||
|
||||
void apdu_handler(BACNET_ADDRESS *src,
|
||||
void apdu_handler(
|
||||
BACNET_ADDRESS *src,
|
||||
uint8_t *apdu, /* APDU data */
|
||||
uint16_t apdu_len)
|
||||
{
|
||||
@@ -106,18 +107,21 @@ void apdu_handler(BACNET_ADDRESS *src,
|
||||
break;
|
||||
}
|
||||
if (service_choice == SERVICE_CONFIRMED_READ_PROPERTY) {
|
||||
handler_read_property(service_request, service_request_len,
|
||||
src, &service_data);
|
||||
handler_read_property(
|
||||
service_request, service_request_len, src,
|
||||
&service_data);
|
||||
}
|
||||
#ifdef WRITE_PROPERTY
|
||||
else if (service_choice == SERVICE_CONFIRMED_WRITE_PROPERTY) {
|
||||
handler_write_property(service_request, service_request_len,
|
||||
src, &service_data);
|
||||
handler_write_property(
|
||||
service_request, service_request_len, src,
|
||||
&service_data);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
handler_unrecognized_service(service_request,
|
||||
service_request_len, src, &service_data);
|
||||
handler_unrecognized_service(
|
||||
service_request, service_request_len, src,
|
||||
&service_data);
|
||||
}
|
||||
(void)len;
|
||||
break;
|
||||
@@ -0,0 +1,598 @@
|
||||
/**
|
||||
* @brief This module manages the BACnet Analog Value objects
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
#include "adc.h"
|
||||
#include "nvdata.h"
|
||||
#include "rs485.h"
|
||||
#include "stack.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/object/av.h"
|
||||
|
||||
/* functions to get the present value when requested */
|
||||
typedef float (*object_present_value_read_callback)(void);
|
||||
/* functions to get the present value when requested */
|
||||
typedef bool (*object_present_value_write_callback)(float value);
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the ADC0 object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float adc0_value(void)
|
||||
{
|
||||
return (float)adc_millivolts(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the ADC1 object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float adc1_value(void)
|
||||
{
|
||||
return (float)adc_millivolts(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the ADC2 object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float adc2_value(void)
|
||||
{
|
||||
return (float)adc_millivolts(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the ADC3 object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float adc3_value(void)
|
||||
{
|
||||
return (float)adc_millivolts(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the stack size object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float stack_size_value(void)
|
||||
{
|
||||
return (float)stack_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the stack unused object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float stack_unused_value(void)
|
||||
{
|
||||
return (float)stack_unused();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the MS/TP baud rate object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float mstp_baud(void)
|
||||
{
|
||||
uint8_t kbaud;
|
||||
float value;
|
||||
|
||||
kbaud = nvdata_unsigned8(NV_EEPROM_MSTP_BAUD_K);
|
||||
value = (float)RS485_Baud_Rate_From_Kilo(kbaud);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the present value for the MS/TP baud rate object.
|
||||
* @param value - The new value.
|
||||
* @return true if the value was in range and written.
|
||||
*/
|
||||
static bool mstp_baud_write(float value)
|
||||
{
|
||||
bool status = false;
|
||||
uint8_t kbaud;
|
||||
int32_t baud;
|
||||
|
||||
baud = (int32_t)value;
|
||||
if ((baud >= 9600L) && (baud <= 1152000L)) {
|
||||
kbaud = (uint8_t)(baud / 1000UL);
|
||||
nvdata_unsigned8_set(NV_EEPROM_MSTP_BAUD_K, kbaud);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the MS/TP MAC address object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float mstp_mac(void)
|
||||
{
|
||||
return (float)nvdata_unsigned8(NV_EEPROM_MSTP_MAC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the present value for the MS/TP address object.
|
||||
* @param value - The new value.
|
||||
* @return true if the value was in range and written.
|
||||
*/
|
||||
static bool mstp_mac_write(float value)
|
||||
{
|
||||
bool status = false;
|
||||
uint8_t mac = 0;
|
||||
int32_t value32;
|
||||
|
||||
value32 = (int32_t)value;
|
||||
if ((value32 >= 0L) && (value32 <= 127L)) {
|
||||
mac = (uint8_t)value32;
|
||||
nvdata_unsigned8_set(NV_EEPROM_MSTP_MAC, mac);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the MS/TP max manager object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float mstp_manager(void)
|
||||
{
|
||||
return (float)nvdata_unsigned8(NV_EEPROM_MSTP_MAX_MASTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the present value for the MS/TP max manager object.
|
||||
* @param value - The new value.
|
||||
* @return true if the value was in range and written.
|
||||
*/
|
||||
static bool mstp_manager_write(float value)
|
||||
{
|
||||
bool status = false;
|
||||
uint8_t manager = 0;
|
||||
int32_t value32;
|
||||
|
||||
value32 = (int32_t)value;
|
||||
if ((value32 >= 0) && (value32 <= 127)) {
|
||||
manager = (uint8_t)value32;
|
||||
nvdata_unsigned8_set(NV_EEPROM_MSTP_MAX_MASTER, manager);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the present value for the Device ID object.
|
||||
* @return The present value.
|
||||
*/
|
||||
static float device_id(void)
|
||||
{
|
||||
return (float)nvdata_unsigned24(NV_EEPROM_DEVICE_0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the present value for the Device ID object.
|
||||
* @param value - The new value.
|
||||
* @return true if the value was in range and written.
|
||||
*/
|
||||
static bool device_id_write(float value)
|
||||
{
|
||||
bool status = false;
|
||||
int32_t value32;
|
||||
|
||||
value32 = (int32_t)value;
|
||||
if ((value32 >= 0) && (value32 <= BACNET_MAX_INSTANCE)) {
|
||||
nvdata_unsigned24_set(NV_EEPROM_DEVICE_0, value32);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
struct object_data {
|
||||
const uint8_t object_id;
|
||||
const char *object_name;
|
||||
uint16_t units;
|
||||
object_present_value_read_callback read_callback;
|
||||
object_present_value_write_callback write_callback;
|
||||
float present_value;
|
||||
};
|
||||
/* clang-format off */
|
||||
static struct object_data Object_List[] = {
|
||||
/* device ADC inputs */
|
||||
{ 0, "ADC0", UNITS_MILLIVOLTS, adc0_value, NULL, 0.0f },
|
||||
{ 1, "ADC1", UNITS_MILLIVOLTS, adc1_value, NULL, 0.0f },
|
||||
{ 2, "ADC2", UNITS_MILLIVOLTS, adc2_value, NULL, 0.0f },
|
||||
{ 3, "ADC3", UNITS_MILLIVOLTS, adc3_value, NULL, 0.0f },
|
||||
/* device configuration */
|
||||
{ 92, "Device ID", UNITS_NO_UNITS,
|
||||
device_id, device_id_write, 0.0f },
|
||||
{ 93, "MS/TP Baud", UNITS_BITS_PER_SECOND,
|
||||
mstp_baud, mstp_baud_write, 0.0f },
|
||||
{ 94, "MS/TP MAC", UNITS_NO_UNITS,
|
||||
mstp_mac, mstp_mac_write, 0.0f },
|
||||
{ 95, "MS/TP Max Manager", UNITS_NO_UNITS,
|
||||
mstp_manager, mstp_manager_write, 0.0f },
|
||||
/* device status */
|
||||
{ 96, "MCU Frequency", UNITS_HERTZ, NULL, NULL, (float)F_CPU },
|
||||
{ 97, "CStack Size", UNITS_NO_UNITS, stack_size_value, NULL, 0.0f },
|
||||
{ 98, "CStack Unused", UNITS_NO_UNITS, stack_unused_value, NULL, 0.0f },
|
||||
{ 99, "Uptime", UNITS_HOURS, NULL, NULL, 0.0f }
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
/* number of objects */
|
||||
static const unsigned Objects_Max =
|
||||
sizeof(Object_List) / sizeof(Object_List[0]);
|
||||
|
||||
/**
|
||||
* @brief Return the object or NULL if not found.
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return object if the instance is found, and NULL if not
|
||||
*/
|
||||
static struct object_data *Object_List_Element(uint32_t object_instance)
|
||||
{
|
||||
unsigned index;
|
||||
uint32_t object_id;
|
||||
|
||||
for (index = 0; index < Objects_Max; index++) {
|
||||
object_id = Object_List[index].object_id;
|
||||
if (object_id == object_instance) {
|
||||
return &Object_List[index];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines if a given Analog Value instance is valid
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return true if the instance is valid, and false if not
|
||||
*/
|
||||
bool Analog_Value_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (Object_List_Element(object_instance)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines the number of objects
|
||||
* @return Number of objects
|
||||
*/
|
||||
unsigned Analog_Value_Count(void)
|
||||
{
|
||||
return Objects_Max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines the object instance-number for a given 0..N index
|
||||
* @param index - 0..N value
|
||||
* @return object instance-number for the given index
|
||||
*/
|
||||
uint32_t Analog_Value_Index_To_Instance(unsigned index)
|
||||
{
|
||||
uint32_t object_instance = UINT32_MAX;
|
||||
|
||||
if (index < Objects_Max) {
|
||||
object_instance = Object_List[index].object_id;
|
||||
}
|
||||
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, determines a 0..N index
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return index for the given instance-number, or N if not valid.
|
||||
*/
|
||||
unsigned Analog_Value_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
for (index = 0; index < Objects_Max; index++) {
|
||||
if (Object_List[index].object_id == object_instance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, sets the object-name
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param new_name - holds the object-name to be set
|
||||
* @return true if object-name was set
|
||||
*/
|
||||
bool Analog_Value_Name_Set(uint32_t object_instance, const char *value)
|
||||
{
|
||||
struct object_data *object;
|
||||
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
object->object_name = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the object name C string
|
||||
* @param object_instance [in] BACnet object instance number
|
||||
* @return object name or NULL if not found
|
||||
*/
|
||||
const char *Analog_Value_Name_ASCII(uint32_t object_instance)
|
||||
{
|
||||
const char *object_name = "AV-X";
|
||||
struct object_data *object;
|
||||
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
object_name = object->object_name;
|
||||
}
|
||||
|
||||
return object_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, determines the present-value
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return present-value of the object
|
||||
*/
|
||||
float Analog_Value_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
float value = 0.0;
|
||||
struct object_data *object;
|
||||
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
if (object->read_callback) {
|
||||
value = object->read_callback();
|
||||
} else {
|
||||
value = object->present_value;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, sets the present-value
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - value to set
|
||||
* @return true if value is within range and present-value is set.
|
||||
*/
|
||||
bool Analog_Value_Present_Value_Set(
|
||||
uint32_t object_instance, float value, uint8_t priority)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *object;
|
||||
|
||||
(void)priority;
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
if (object->write_callback) {
|
||||
status = object->write_callback(value);
|
||||
} else {
|
||||
object->present_value = value;
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, determines the units
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return units from the given object, or UNITS_NO_UNITS if not found
|
||||
*/
|
||||
uint16_t Analog_Value_Units(uint32_t object_instance)
|
||||
{
|
||||
uint16_t units = UNITS_NO_UNITS;
|
||||
struct object_data *object;
|
||||
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
units = object->units;
|
||||
}
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, sets the units
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param units - property value to set
|
||||
* @return true if valid instance and property was set
|
||||
*/
|
||||
bool Analog_Value_Units_Set(uint32_t object_instance, uint16_t units)
|
||||
{
|
||||
struct object_data *object;
|
||||
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
object->units = units;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ReadProperty handler for this object. For the given ReadProperty
|
||||
* data, the application_data is loaded or the error flags are set.
|
||||
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
|
||||
* requested data and space for the reply, or error response.
|
||||
* @return number of APDU bytes in the response, or
|
||||
* BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
uint8_t *apdu;
|
||||
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_ANALOG_VALUE, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
characterstring_init_ansi(
|
||||
&char_string, Analog_Value_Name_ASCII(rpdata->object_instance));
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
apdu_len = encode_application_real(
|
||||
&apdu[0], Analog_Value_Present_Value(rpdata->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_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
apdu_len = encode_application_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_UNITS:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], Analog_Value_Units(rpdata->object_instance));
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief WriteProperty handler for this object. For the given WriteProperty
|
||||
* data, the application_data is loaded or the error flags are set.
|
||||
* @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including
|
||||
* requested data and space for the reply, or error response.
|
||||
* @return false if an error is loaded, true if no errors
|
||||
*/
|
||||
bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
if (!Analog_Value_Valid_Instance(wp_data->object_instance)) {
|
||||
wp_data->error_class = ERROR_CLASS_OBJECT;
|
||||
wp_data->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? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
/* only array properties can have array options */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_REAL) {
|
||||
status = Analog_Value_Present_Value_Set(
|
||||
wp_data->object_instance, value.type.Real,
|
||||
wp_data->priority);
|
||||
if (!status) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_UNITS:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
status = Analog_Value_Units_Set(
|
||||
wp_data->object_instance, value.type.Enumerated);
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
case PROP_DESCRIPTION:
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure some analog pins for ADC operation
|
||||
*/
|
||||
void Analog_Value_Init(void)
|
||||
{
|
||||
adc_enable(0);
|
||||
adc_enable(1);
|
||||
adc_enable(2);
|
||||
adc_enable(3);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @brief This module manages effective C bit manipulation for AVR
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
* @note From AVR035: Efficient C Coding for AVR
|
||||
*/
|
||||
#ifndef AVR035_H
|
||||
#define AVR035_H
|
||||
|
||||
/* a=register, b=bit number to act upon */
|
||||
#define BIT_SET(a, b) ((a) |= (1 << (b)))
|
||||
#define BIT_CLEAR(a, b) ((a) &= ~(1 << (b)))
|
||||
#define BIT_FLIP(a, b) ((a) ^= (1 << (b)))
|
||||
#define BIT_CHECK(a, b) ((a) & (1 << (b)))
|
||||
|
||||
/* x=target variable, y=mask */
|
||||
#define BITMASK_SET(x, y) ((x) |= (y))
|
||||
#define BITMASK_CLEAR(x, y) ((x) &= (~(y)))
|
||||
#define BITMASK_FLIP(x, y) ((x) ^= (y))
|
||||
#define BITMASK_CHECK(x, y) ((x) & (y))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
* @brief This module manages the BACnet Binary Value objects
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "hardware.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/object/bv.h"
|
||||
|
||||
struct object_data {
|
||||
const uint8_t object_id;
|
||||
const char *object_name;
|
||||
volatile uint8_t *port;
|
||||
volatile uint8_t *pin;
|
||||
volatile uint8_t *ddr;
|
||||
const uint8_t bit;
|
||||
};
|
||||
/* clang-format off */
|
||||
static struct object_data Object_List[] = {
|
||||
{ 0, "D3", NULL, &PIND, &DDRD, PD3 },
|
||||
{ 1, "D4", NULL, &PIND, &DDRD, PD4 },
|
||||
{ 2, "D5", NULL, &PIND, &DDRD, PD5 },
|
||||
{ 3, "D6", NULL, &PIND, &DDRD, PD6 },
|
||||
{ 4, "D7", NULL, &PIND, &DDRD, PD7 },
|
||||
{ 5, "D8", &PORTB, &PINB, &DDRB, PB0 },
|
||||
{ 6, "D9", &PORTB, &PINB, &DDRB, PB1 },
|
||||
{ 7, "D10", &PORTB, &PINB, &DDRB, PB2 },
|
||||
{ 8, "D11", &PORTB, &PINB, &DDRB, PB3 },
|
||||
{ 9, "D12", &PORTB, &PINB, &DDRB, PB4 },
|
||||
{ 99, "LED", &PORTB, &PINB, &DDRB, PB5 }
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
/* number of objects */
|
||||
static const unsigned Objects_Max =
|
||||
sizeof(Object_List) / sizeof(Object_List[0]);
|
||||
|
||||
/**
|
||||
* @brief Return the object or NULL if not found.
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return object if the instance is found, and NULL if not
|
||||
*/
|
||||
static struct object_data *Object_List_Element(uint32_t object_instance)
|
||||
{
|
||||
unsigned index;
|
||||
uint32_t object_id;
|
||||
|
||||
for (index = 0; index < Objects_Max; index++) {
|
||||
object_id = Object_List[index].object_id;
|
||||
if (object_id == object_instance) {
|
||||
return &Object_List[index];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines if a given Analog Value instance is valid
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return true if the instance is valid, and false if not
|
||||
*/
|
||||
bool Binary_Value_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (Object_List_Element(object_instance)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines the number of objects
|
||||
* @return Number of objects
|
||||
*/
|
||||
unsigned Binary_Value_Count(void)
|
||||
{
|
||||
return Objects_Max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines the object instance-number for a given 0..N index
|
||||
* @param index - 0..N value
|
||||
* @return object instance-number for the given index
|
||||
*/
|
||||
uint32_t Binary_Value_Index_To_Instance(unsigned index)
|
||||
{
|
||||
uint32_t object_instance = UINT32_MAX;
|
||||
|
||||
if (index < Objects_Max) {
|
||||
object_instance = Object_List[index].object_id;
|
||||
}
|
||||
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, determines a 0..N index
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return index for the given instance-number, or N if not valid.
|
||||
*/
|
||||
unsigned Binary_Value_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
for (index = 0; index < Objects_Max; index++) {
|
||||
if (Object_List[index].object_id == object_instance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, sets the object-name
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param new_name - holds the object-name to be set
|
||||
* @return true if object-name was set
|
||||
*/
|
||||
bool Binary_Value_Name_Set(uint32_t object_instance, const char *value)
|
||||
{
|
||||
struct object_data *object;
|
||||
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
object->object_name = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the object name C string
|
||||
* @param object_instance [in] BACnet object instance number
|
||||
* @return object name or NULL if not found
|
||||
*/
|
||||
const char *Binary_Value_Name_ASCII(uint32_t object_instance)
|
||||
{
|
||||
const char *object_name = "BV-X";
|
||||
struct object_data *object;
|
||||
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
object_name = object->object_name;
|
||||
}
|
||||
|
||||
return object_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, determines the present-value
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return present-value of the object
|
||||
*/
|
||||
BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
BACNET_BINARY_PV value = BINARY_INACTIVE;
|
||||
struct object_data *object;
|
||||
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
if (BIT_CHECK(*object->pin, object->bit)) {
|
||||
value = BINARY_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For a given object instance-number, sets the present-value
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - value to set
|
||||
* @return true if value is within range and present-value is set.
|
||||
*/
|
||||
bool Binary_Value_Present_Value_Set(
|
||||
uint32_t object_instance, BACNET_BINARY_PV value)
|
||||
{
|
||||
struct object_data *object;
|
||||
|
||||
object = Object_List_Element(object_instance);
|
||||
if (object) {
|
||||
if (object->port) {
|
||||
if (value == BINARY_ACTIVE) {
|
||||
BIT_SET(*object->port, object->bit);
|
||||
} else {
|
||||
BIT_CLEAR(*object->port, object->bit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ReadProperty handler for this object. For the given ReadProperty
|
||||
* data, the application_data is loaded or the error flags are set.
|
||||
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
|
||||
* requested data and space for the reply, or error response.
|
||||
* @return number of APDU bytes in the response, or
|
||||
* BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
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;
|
||||
uint8_t *apdu;
|
||||
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_BINARY_VALUE, rpdata->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:
|
||||
characterstring_init_ansi(
|
||||
&char_string, Binary_Value_Name_ASCII(rpdata->object_instance));
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
present_value = Binary_Value_Present_Value(rpdata->object_instance);
|
||||
apdu_len = encode_application_enumerated(&apdu[0], present_value);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
/* note: see the details in the standard on how to use these */
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
/* note: see the details in the standard on how to use this */
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
apdu_len = encode_application_boolean(&apdu[0], false);
|
||||
break;
|
||||
case PROP_POLARITY:
|
||||
/* FIXME: figure out the polarity */
|
||||
apdu_len = encode_application_enumerated(&apdu[0], polarity);
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
if (!Binary_Value_Valid_Instance(wp_data->object_instance)) {
|
||||
wp_data->error_class = ERROR_CLASS_OBJECT;
|
||||
wp_data->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? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
/* only array properties can have array options */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
if ((value.type.Enumerated == BINARY_ACTIVE) ||
|
||||
(value.type.Enumerated == BINARY_INACTIVE)) {
|
||||
status = Binary_Value_Present_Value_Set(
|
||||
wp_data->object_instance,
|
||||
(BACNET_BINARY_PV)value.type.Enumerated);
|
||||
if (!status) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_POLARITY:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure some digital pins as inputs or outputs
|
||||
*/
|
||||
void Binary_Value_Init(void)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
for (index = 0; index < Objects_Max; index++) {
|
||||
if (Object_List[index].port) {
|
||||
/* Configure the pin as an output */
|
||||
BIT_CLEAR(*Object_List[index].port, Object_List[index].bit);
|
||||
BIT_SET(*Object_List[index].ddr, Object_List[index].bit);
|
||||
/* Turn off the pin */
|
||||
BIT_CLEAR(*Object_List[index].port, Object_List[index].bit);
|
||||
} else {
|
||||
/* Configure the pin as an input */
|
||||
BIT_CLEAR(*Object_List[index].ddr, Object_List[index].bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* @brief This module manages the BACnet Device object
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
/* stack */
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
@@ -15,9 +14,10 @@
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/datalink/dlmstp.h"
|
||||
#include "rs485.h"
|
||||
#include "bacnet/version.h"
|
||||
#include "stack.h"
|
||||
/* platform */
|
||||
#include "rs485.h"
|
||||
#include "nvdata.h"
|
||||
/* objects */
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
@@ -30,20 +30,8 @@
|
||||
The properties that are constant can be hard coded
|
||||
into the read-property encoding. */
|
||||
static uint32_t Object_Instance_Number = 260001;
|
||||
static char Object_Name[20] = "My Device";
|
||||
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
|
||||
|
||||
void Device_Init(object_functions_t *object_table)
|
||||
{
|
||||
(void)object_table;
|
||||
/* Reinitialize_State = BACNET_REINIT_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); */
|
||||
}
|
||||
static const char *Model_Name = "ATmega328 Uno R3 Device";
|
||||
|
||||
/* methods to manipulate the data */
|
||||
uint32_t Device_Object_Instance_Number(void)
|
||||
@@ -63,8 +51,9 @@ bool Device_Set_Object_Instance_Number(uint32_t object_id)
|
||||
(char *)&Object_Instance_Number,
|
||||
sizeof(Object_Instance_Number),
|
||||
EEPROM_BACNET_ID_ADDR); */
|
||||
} else
|
||||
} else {
|
||||
status = false;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -189,7 +178,18 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
&apdu[0], OBJECT_DEVICE, Object_Instance_Number);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
characterstring_init_ansi(&char_string, Object_Name);
|
||||
nvdata_name(NV_EEPROM_DEVICE_NAME, &char_string, "BACnet Device");
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_DESCRIPTION:
|
||||
nvdata_name(
|
||||
NV_EEPROM_DEVICE_DESCRIPTION, &char_string, "Description");
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_LOCATION:
|
||||
nvdata_name(NV_EEPROM_DEVICE_LOCATION, &char_string, "Location");
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
@@ -209,7 +209,7 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
&apdu[0], Device_Vendor_Identifier());
|
||||
break;
|
||||
case PROP_MODEL_NAME:
|
||||
characterstring_init_ansi(&char_string, "GNU Demo");
|
||||
characterstring_init_ansi(&char_string, Model_Name);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
@@ -236,7 +236,8 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
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,
|
||||
bitstring_set_bit(
|
||||
&bit_string, (uint8_t)i,
|
||||
apdu_service_supported((BACNET_SERVICES_SUPPORTED)i));
|
||||
}
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
@@ -258,10 +259,9 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
break;
|
||||
case PROP_OBJECT_LIST:
|
||||
count = Device_Object_List_Count();
|
||||
apdu_len = bacnet_array_encode(rpdata->object_instance,
|
||||
rpdata->array_index,
|
||||
Device_Object_List_Element_Encode,
|
||||
count, apdu, apdu_max);
|
||||
apdu_len = bacnet_array_encode(
|
||||
rpdata->object_instance, rpdata->array_index,
|
||||
Device_Object_List_Element_Encode, count, apdu, apdu_max);
|
||||
if (apdu_len == BACNET_STATUS_ABORT) {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
@@ -301,12 +301,6 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
apdu_len =
|
||||
encode_application_unsigned(&apdu[0], RS485_Get_Baud_Rate());
|
||||
break;
|
||||
case (BACNET_PROPERTY_ID)512:
|
||||
apdu_len = encode_application_unsigned(&apdu[0], stack_size());
|
||||
break;
|
||||
case (BACNET_PROPERTY_ID)513:
|
||||
apdu_len = encode_application_unsigned(&apdu[0], stack_unused());
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
@@ -329,6 +323,7 @@ bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
size_t name_length;
|
||||
|
||||
if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) {
|
||||
wp_data->error_class = ERROR_CLASS_OBJECT;
|
||||
@@ -353,97 +348,65 @@ bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) {
|
||||
if ((value.type.Object_Id.type == OBJECT_DEVICE) &&
|
||||
(Device_Set_Object_Instance_Number(
|
||||
value.type.Object_Id.instance))) {
|
||||
/* we could send an I-Am broadcast to let the world know */
|
||||
status = true;
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_MAX_INFO_FRAMES:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
if (value.type.Unsigned_Int <= 255) {
|
||||
dlmstp_set_max_info_frames(value.type.Unsigned_Int);
|
||||
status = true;
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_MAX_MASTER:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
if ((value.type.Unsigned_Int > 0) &&
|
||||
(value.type.Unsigned_Int <= 127)) {
|
||||
dlmstp_set_max_master(value.type.Unsigned_Int);
|
||||
status = true;
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
|
||||
uint8_t encoding;
|
||||
|
||||
encoding =
|
||||
characterstring_encoding(&value.type.Character_String);
|
||||
if (encoding == CHARACTER_ANSI_X34) {
|
||||
if (characterstring_ansi_copy(&Object_Name[0],
|
||||
sizeof(Object_Name),
|
||||
&value.type.Character_String)) {
|
||||
status = true;
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code =
|
||||
ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code =
|
||||
ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case (BACNET_PROPERTY_ID)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 {
|
||||
name_length =
|
||||
characterstring_length(&value.type.Character_String);
|
||||
if (name_length == 0) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
} else {
|
||||
status = nvdata_name_write(
|
||||
NV_EEPROM_DEVICE_NAME, &value.type.Character_String,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_DESCRIPTION:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
|
||||
name_length =
|
||||
characterstring_length(&value.type.Character_String);
|
||||
if (name_length == 0) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
} else {
|
||||
status = nvdata_name_write(
|
||||
NV_EEPROM_DEVICE_DESCRIPTION,
|
||||
&value.type.Character_String, &wp_data->error_class,
|
||||
&wp_data->error_code);
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_LOCATION:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
|
||||
name_length =
|
||||
characterstring_length(&value.type.Character_String);
|
||||
if (name_length == 0) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
} else {
|
||||
status = nvdata_name_write(
|
||||
NV_EEPROM_DEVICE_LOCATION, &value.type.Character_String,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_MAX_INFO_FRAMES:
|
||||
case PROP_MAX_MASTER:
|
||||
case PROP_NUMBER_OF_APDU_RETRIES:
|
||||
case PROP_APDU_TIMEOUT:
|
||||
case PROP_VENDOR_IDENTIFIER:
|
||||
case PROP_SYSTEM_STATUS:
|
||||
case PROP_LOCATION:
|
||||
case PROP_DESCRIPTION:
|
||||
case PROP_MODEL_NAME:
|
||||
case PROP_VENDOR_NAME:
|
||||
case PROP_FIRMWARE_REVISION:
|
||||
@@ -462,6 +425,7 @@ bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
case PROP_DEVICE_ADDRESS_BINDING:
|
||||
case PROP_DATABASE_REVISION:
|
||||
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
|
||||
case 9600:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
@@ -1,10 +1,9 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*
|
||||
*********************************************************************/
|
||||
/**
|
||||
* @brief This module manages the BACnet MS/TP datalink layer
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
@@ -22,13 +21,11 @@
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
/* port specific */
|
||||
#include "rs485.h"
|
||||
|
||||
/* This file has been customized for use with small microprocessors */
|
||||
/* Assumptions:
|
||||
Only one MS/TP datalink layer
|
||||
*/
|
||||
#include "hardware.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* The state of the Receive State Machine */
|
||||
static MSTP_RECEIVE_STATE Receive_State;
|
||||
@@ -289,10 +286,10 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
*/
|
||||
if (RS485_ReceiveError()) {
|
||||
/* EatAnError */
|
||||
Timer_Silence_Reset();
|
||||
RS485_Timer_Silence_Reset();
|
||||
INCREMENT_AND_LIMIT_UINT8(EventCount);
|
||||
} else if (RS485_DataAvailable(&DataRegister)) {
|
||||
Timer_Silence_Reset();
|
||||
RS485_Timer_Silence_Reset();
|
||||
INCREMENT_AND_LIMIT_UINT8(EventCount);
|
||||
if (DataRegister == 0x55) {
|
||||
/* Preamble1 */
|
||||
@@ -304,19 +301,19 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
case MSTP_RECEIVE_STATE_PREAMBLE:
|
||||
/* In the PREAMBLE state, the node waits for the
|
||||
second octet of the preamble. */
|
||||
if (Timer_Silence() > Tframe_abort) {
|
||||
if (RS485_Timer_Silence() > Tframe_abort) {
|
||||
/* Timeout */
|
||||
/* a correct preamble has not been received */
|
||||
/* wait for the start of a frame. */
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_ReceiveError()) {
|
||||
/* Error */
|
||||
Timer_Silence_Reset();
|
||||
RS485_Timer_Silence_Reset();
|
||||
INCREMENT_AND_LIMIT_UINT8(EventCount);
|
||||
/* wait for the start of a frame. */
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_DataAvailable(&DataRegister)) {
|
||||
Timer_Silence_Reset();
|
||||
RS485_Timer_Silence_Reset();
|
||||
INCREMENT_AND_LIMIT_UINT8(EventCount);
|
||||
if (DataRegister == 0xFF) {
|
||||
/* Preamble2 */
|
||||
@@ -338,7 +335,7 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
case MSTP_RECEIVE_STATE_HEADER:
|
||||
/* In the HEADER state, the node waits for the fixed message header.
|
||||
*/
|
||||
if (Timer_Silence() > Tframe_abort) {
|
||||
if (RS485_Timer_Silence() > Tframe_abort) {
|
||||
/* Timeout */
|
||||
/* indicate that an error has occurred during the reception of a
|
||||
* frame */
|
||||
@@ -347,7 +344,7 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_ReceiveError()) {
|
||||
/* Error */
|
||||
Timer_Silence_Reset();
|
||||
RS485_Timer_Silence_Reset();
|
||||
INCREMENT_AND_LIMIT_UINT8(EventCount);
|
||||
/* indicate that an error has occurred during the reception of a
|
||||
* frame */
|
||||
@@ -355,7 +352,7 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
/* wait for the start of a frame. */
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_DataAvailable(&DataRegister)) {
|
||||
Timer_Silence_Reset();
|
||||
RS485_Timer_Silence_Reset();
|
||||
INCREMENT_AND_LIMIT_UINT8(EventCount);
|
||||
if (Index == 0) {
|
||||
/* FrameType */
|
||||
@@ -410,7 +407,7 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
/* NoData */
|
||||
if ((DestinationAddress == This_Station) ||
|
||||
(DestinationAddress ==
|
||||
MSTP_BROADCAST_ADDRESS)) {
|
||||
MSTP_BROADCAST_ADDRESS)) {
|
||||
/* ForUs */
|
||||
/* indicate that a frame with
|
||||
no data has been received */
|
||||
@@ -440,7 +437,7 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
case MSTP_RECEIVE_STATE_DATA:
|
||||
/* In the DATA state, the node waits for the data portion of a
|
||||
* frame. */
|
||||
if (Timer_Silence() > Tframe_abort) {
|
||||
if (RS485_Timer_Silence() > Tframe_abort) {
|
||||
/* Timeout */
|
||||
/* indicate that an error has occurred during the reception of a
|
||||
* frame */
|
||||
@@ -449,7 +446,7 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_ReceiveError()) {
|
||||
/* Error */
|
||||
Timer_Silence_Reset();
|
||||
RS485_Timer_Silence_Reset();
|
||||
INCREMENT_AND_LIMIT_UINT8(EventCount);
|
||||
/* indicate that an error has occurred during
|
||||
the reception of a frame */
|
||||
@@ -457,7 +454,7 @@ static void MSTP_Receive_Frame_FSM(void)
|
||||
/* wait for the start of the next frame. */
|
||||
Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (RS485_DataAvailable(&DataRegister)) {
|
||||
Timer_Silence_Reset();
|
||||
RS485_Timer_Silence_Reset();
|
||||
INCREMENT_AND_LIMIT_UINT8(EventCount);
|
||||
if (Index < DataLength) {
|
||||
/* DataOctet */
|
||||
@@ -550,7 +547,7 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
break;
|
||||
case MSTP_MASTER_STATE_IDLE:
|
||||
/* In the IDLE state, the node waits for a frame. */
|
||||
if (Timer_Silence() >= Tno_token) {
|
||||
if (RS485_Timer_Silence() >= Tno_token) {
|
||||
/* LostToken */
|
||||
/* assume that the token has been lost */
|
||||
EventCount = 0; /* Addendum 135-2004d-8 */
|
||||
@@ -570,8 +567,9 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
case FRAME_TYPE_TOKEN:
|
||||
/* ReceivedToken */
|
||||
/* tokens can't be broadcast */
|
||||
if (DestinationAddress == MSTP_BROADCAST_ADDRESS)
|
||||
if (DestinationAddress == MSTP_BROADCAST_ADDRESS) {
|
||||
break;
|
||||
}
|
||||
MSTP_Flag.ReceivedValidFrame = false;
|
||||
FrameCount = 0;
|
||||
MSTP_Flag.SoleMaster = false;
|
||||
@@ -582,7 +580,8 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* ReceivedPFM */
|
||||
/* DestinationAddress is equal to TS */
|
||||
if (DestinationAddress == This_Station) {
|
||||
MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER,
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER,
|
||||
SourceAddress, This_Station, NULL, 0);
|
||||
}
|
||||
break;
|
||||
@@ -610,7 +609,8 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
}
|
||||
break;
|
||||
case FRAME_TYPE_TEST_REQUEST:
|
||||
MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, SourceAddress,
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_TEST_RESPONSE, SourceAddress,
|
||||
This_Station, &InputBuffer[0], DataLength);
|
||||
break;
|
||||
case FRAME_TYPE_TEST_RESPONSE:
|
||||
@@ -633,9 +633,10 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* Note: We could wait for up to Tusage_delay */
|
||||
TransmitPacketLen = dlmstp_encode_unconfirmed_frame();
|
||||
if (TransmitPacketLen) {
|
||||
MSTP_Send_Frame(FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY,
|
||||
MSTP_BROADCAST_ADDRESS, This_Station,
|
||||
&TransmitPacket[0], TransmitPacketLen);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY,
|
||||
MSTP_BROADCAST_ADDRESS, This_Station, &TransmitPacket[0],
|
||||
TransmitPacketLen);
|
||||
FrameCount++;
|
||||
} else {
|
||||
/* NothingToSend */
|
||||
@@ -647,7 +648,7 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
case MSTP_MASTER_STATE_WAIT_FOR_REPLY:
|
||||
/* In the WAIT_FOR_REPLY state, the node waits for */
|
||||
/* a reply from another node. */
|
||||
if (Timer_Silence() >= Treply_timeout) {
|
||||
if (RS485_Timer_Silence() >= Treply_timeout) {
|
||||
/* ReplyTimeout */
|
||||
/* assume that the request has failed */
|
||||
FrameCount = Nmax_info_frames;
|
||||
@@ -719,14 +720,16 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* before passing the token. */
|
||||
Master_State = MSTP_MASTER_STATE_USE_TOKEN;
|
||||
transition_now = true;
|
||||
} else if ((MSTP_Flag.SoleMaster == false) &&
|
||||
} else if (
|
||||
(MSTP_Flag.SoleMaster == false) &&
|
||||
(Next_Station == This_Station)) {
|
||||
/* NextStationUnknown - added in Addendum 135-2008v-1 */
|
||||
/* then the next station to which the token
|
||||
should be sent is unknown - so PollForMaster */
|
||||
Poll_Station = next_this_station;
|
||||
MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
|
||||
This_Station, NULL, 0);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station,
|
||||
NULL, 0);
|
||||
RetryCount = 0;
|
||||
Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER;
|
||||
}
|
||||
@@ -763,8 +766,9 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
if (MSTP_Flag.SoleMaster == true) {
|
||||
/* SoleMasterRestartMaintenancePFM */
|
||||
Poll_Station = next_next_station;
|
||||
MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
|
||||
This_Station, NULL, 0);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station,
|
||||
NULL, 0);
|
||||
/* no known successor node */
|
||||
Next_Station = This_Station;
|
||||
RetryCount = 0;
|
||||
@@ -786,8 +790,9 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
} else {
|
||||
/* SendMaintenancePFM */
|
||||
Poll_Station = next_poll_station;
|
||||
MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
|
||||
This_Station, NULL, 0);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station,
|
||||
NULL, 0);
|
||||
RetryCount = 0;
|
||||
Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER;
|
||||
}
|
||||
@@ -795,7 +800,7 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* The PASS_TOKEN state listens for a successor to begin using */
|
||||
/* the token that this node has just attempted to pass. */
|
||||
case MSTP_MASTER_STATE_PASS_TOKEN:
|
||||
if (Timer_Silence() <= Tusage_timeout) {
|
||||
if (RS485_Timer_Silence() <= Tusage_timeout) {
|
||||
if (EventCount > Nmin_octets) {
|
||||
/* SawTokenUser */
|
||||
/* Assume that a frame has been sent by the new token user.
|
||||
@@ -820,8 +825,9 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* note: if NS=TS-1, this node could send PFM to self! */
|
||||
Poll_Station = next_next_station;
|
||||
/* Transmit a Poll For Master frame to PS. */
|
||||
MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
|
||||
This_Station, NULL, 0);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station,
|
||||
NULL, 0);
|
||||
/* no known successor node */
|
||||
Next_Station = This_Station;
|
||||
RetryCount = 0;
|
||||
@@ -840,7 +846,7 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* whether or not this node may create a token. */
|
||||
case MSTP_MASTER_STATE_NO_TOKEN:
|
||||
my_timeout = Tno_token + (Tslot * This_Station);
|
||||
if (Timer_Silence() < my_timeout) {
|
||||
if (RS485_Timer_Silence() < my_timeout) {
|
||||
if (EventCount > Nmin_octets) {
|
||||
/* SawFrame */
|
||||
/* Some other node exists at a lower address. */
|
||||
@@ -851,14 +857,15 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
}
|
||||
} else {
|
||||
ns_timeout = Tno_token + (Tslot * (This_Station + 1));
|
||||
if (Timer_Silence() < ns_timeout) {
|
||||
if (RS485_Timer_Silence() < ns_timeout) {
|
||||
/* GenerateToken */
|
||||
/* Assume that this node is the lowest numerical address */
|
||||
/* on the network and is empowered to create a token. */
|
||||
Poll_Station = next_this_station;
|
||||
/* Transmit a Poll For Master frame to PS. */
|
||||
MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
|
||||
This_Station, NULL, 0);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station,
|
||||
NULL, 0);
|
||||
/* indicate that the next station is unknown */
|
||||
Next_Station = This_Station;
|
||||
RetryCount = 0;
|
||||
@@ -898,7 +905,8 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
transition_now = true;
|
||||
}
|
||||
MSTP_Flag.ReceivedValidFrame = false;
|
||||
} else if ((Timer_Silence() > Tusage_timeout) ||
|
||||
} else if (
|
||||
(RS485_Timer_Silence() > Tusage_timeout) ||
|
||||
(MSTP_Flag.ReceivedInvalidFrame == true)) {
|
||||
if (MSTP_Flag.SoleMaster == true) {
|
||||
/* SoleMaster */
|
||||
@@ -915,8 +923,9 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* poll for a master at address PS. */
|
||||
EventCount = 0;
|
||||
/* transmit a Token frame to NS */
|
||||
MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station,
|
||||
This_Station, NULL, 0);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL,
|
||||
0);
|
||||
RetryCount = 0;
|
||||
Master_State = MSTP_MASTER_STATE_PASS_TOKEN;
|
||||
} else {
|
||||
@@ -924,8 +933,9 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* SendNextPFM */
|
||||
Poll_Station = next_poll_station;
|
||||
/* Transmit a Poll For Master frame to PS. */
|
||||
MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER,
|
||||
Poll_Station, This_Station, NULL, 0);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_POLL_FOR_MASTER, Poll_Station,
|
||||
This_Station, NULL, 0);
|
||||
RetryCount = 0;
|
||||
/* Re-enter the current state. */
|
||||
} else {
|
||||
@@ -957,9 +967,10 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* then call MSTP_Send_Frame to transmit the reply frame */
|
||||
/* and enter the IDLE state to wait for the next frame. */
|
||||
/* Note: optimized such that we are never a client */
|
||||
MSTP_Send_Frame(FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY,
|
||||
TransmitPacketDest, This_Station,
|
||||
&TransmitPacket[0], TransmitPacketLen);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY,
|
||||
TransmitPacketDest, This_Station, &TransmitPacket[0],
|
||||
TransmitPacketLen);
|
||||
MSTP_Flag.TransmitPacketPending = false;
|
||||
Master_State = MSTP_MASTER_STATE_IDLE;
|
||||
} else {
|
||||
@@ -972,8 +983,9 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
/* Any reply shall wait until this node receives the token. */
|
||||
/* Call MSTP_Send_Frame to transmit a Reply Postponed frame, */
|
||||
/* and enter the IDLE state. */
|
||||
MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress,
|
||||
This_Station, NULL, 0);
|
||||
MSTP_Send_Frame(
|
||||
FRAME_TYPE_REPLY_POSTPONED, SourceAddress, This_Station,
|
||||
NULL, 0);
|
||||
Master_State = MSTP_MASTER_STATE_IDLE;
|
||||
}
|
||||
/* clear our flag we were holding for comparison */
|
||||
@@ -988,7 +1000,8 @@ static bool MSTP_Master_Node_FSM(void)
|
||||
}
|
||||
|
||||
/* returns number of bytes sent on success, zero on failure */
|
||||
int dlmstp_send_pdu(BACNET_ADDRESS *dest, /* destination address */
|
||||
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)
|
||||
@@ -1013,7 +1026,8 @@ int dlmstp_send_pdu(BACNET_ADDRESS *dest, /* destination address */
|
||||
}
|
||||
|
||||
/* Return the length of the packet */
|
||||
uint16_t dlmstp_receive(BACNET_ADDRESS *src, /* source address */
|
||||
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)
|
||||
@@ -1032,12 +1046,15 @@ uint16_t dlmstp_receive(BACNET_ADDRESS *src, /* source address */
|
||||
(MSTP_Flag.ReceivedInvalidFrame == false)) {
|
||||
for (;;) {
|
||||
MSTP_Receive_Frame_FSM();
|
||||
if (MSTP_Flag.ReceivedValidFrame || MSTP_Flag.ReceivedInvalidFrame)
|
||||
if (MSTP_Flag.ReceivedValidFrame ||
|
||||
MSTP_Flag.ReceivedInvalidFrame) {
|
||||
break;
|
||||
}
|
||||
/* if we are not idle, then we are
|
||||
receiving a frame or timing out */
|
||||
if (Receive_State == MSTP_RECEIVE_STATE_IDLE)
|
||||
if (Receive_State == MSTP_RECEIVE_STATE_IDLE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* only do master state machine while rx is idle */
|
||||
@@ -1065,13 +1082,9 @@ void dlmstp_set_mac_address(uint8_t mac_address)
|
||||
/* Master Nodes can only have address 0-127 */
|
||||
if (mac_address <= 127) {
|
||||
This_Station = mac_address;
|
||||
/* FIXME: implement your data storage */
|
||||
/* I2C_Write_Byte(
|
||||
EEPROM_DEVICE_ADDRESS,
|
||||
mac_address,
|
||||
EEPROM_MSTP_MAC_ADDR); */
|
||||
if (mac_address > Nmax_master)
|
||||
if (mac_address > Nmax_master) {
|
||||
dlmstp_set_max_master(mac_address);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -1093,11 +1106,6 @@ void dlmstp_set_max_info_frames(uint8_t max_info_frames)
|
||||
{
|
||||
if (max_info_frames >= 1) {
|
||||
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;
|
||||
@@ -1118,11 +1126,6 @@ void dlmstp_set_max_master(uint8_t max_master)
|
||||
if (max_master <= 127) {
|
||||
if (This_Station <= max_master) {
|
||||
Nmax_master = max_master;
|
||||
/* FIXME: implement your data storage */
|
||||
/* I2C_Write_Byte(
|
||||
EEPROM_DEVICE_ADDRESS,
|
||||
max_master,
|
||||
EEPROM_MSTP_MAX_MASTER_ADDR); */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Introduction
|
||||
|
||||
This is an Arduino RS485 shield, especially designed for the Arduino
|
||||
controller board. It can easily convert the UART to the RS485.
|
||||
This shield integrates:
|
||||
|
||||
* a standard RS485 port
|
||||
* a mini RS485 port (PH2.0 interface)
|
||||
* RS485 headers
|
||||
* provides the prototyping areas
|
||||
* configurable automatic or manual transmission (RTS) mode
|
||||
* configurable operation or programming mode
|
||||
|
||||
# Specification
|
||||
|
||||
* Power module: +5.0V
|
||||
* Module size: 55mmx53mm
|
||||
* 16 digital IO port (including a I2C interface)
|
||||
* 6 analog IO port and power
|
||||
* Operation/programming mode switch
|
||||
* Automatic/manual transceiver mode switch (When in manual transceiver mode, the enable end is the 2nd digital IO port)
|
||||
* Transceiver indicating LED
|
||||
* Standard RS485 interface, mini RS485 interface(PH2.0) and RS485 pins
|
||||
* prototyping area
|
||||
* Reset button
|
||||
|
||||
# Pin Definition
|
||||
|
||||

|
||||
|
||||
## Operation and programming mode switch:
|
||||
|
||||
* To OFF, you can download the program for the Arduino controller.
|
||||
* To ON, the shield will convert the UART to the RS485.
|
||||
|
||||
## Transceiver mode switch:
|
||||
|
||||
* To AUTO, the shield is automatically sending and receiving data so you
|
||||
do not care about operation timing
|
||||
* To MANU, the shield is in manual transceiver status, and the enable end
|
||||
is the 2nd digital IO port. Enabling high-side power Ping to sending
|
||||
state while the low level of the receiving state.
|
||||
|
||||
# Usage Setup
|
||||
|
||||
When the RS485 shield is installed on your Arduino controller,
|
||||
and then you will wire the RS485 shiled with USB-RS485 converter
|
||||
to connect terminals, namely: A pair of A, B on B, GND on GND,
|
||||
then download the following sample code.
|
||||
|
||||
Note: Downloading code, run / program mode switch must hit the OFF position,
|
||||
otherwise it will lead to the download fails. When downloaded, it is complete
|
||||
to ON, and the shield to normal use).
|
||||
|
||||
# Schematic
|
||||
|
||||

|
||||
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1020 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 175 KiB |
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @brief This module manages the internal AVR non-volatile data storage
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "hardware.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
/**
|
||||
* @brief Read a block of bytes from the EEPROM
|
||||
* @param eeaddr - EEPROM starting memory address (offset of zero)
|
||||
* @param buf - where to store the data that is read
|
||||
* @param len - number of bytes of data to read
|
||||
* @return The number of bytes read
|
||||
*/
|
||||
int eeprom_bytes_read(uint16_t eeaddr, uint8_t *buf, int len)
|
||||
{
|
||||
int count = 0; /* return value */
|
||||
|
||||
while (len) {
|
||||
__EEGET(buf[count], eeaddr);
|
||||
count++;
|
||||
eeaddr++;
|
||||
len--;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a block of bytes to the EEPROM
|
||||
* @param eeaddr - EEPROM starting memory address (offset of zero)
|
||||
* @param buf - data to write to the EEPROM
|
||||
* @param len - number of bytes of data
|
||||
* @return The number of bytes written
|
||||
*/
|
||||
int eeprom_bytes_write(uint16_t eeaddr, const uint8_t *buf, int len)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
while (len) {
|
||||
__EEPUT(eeaddr, buf[count]);
|
||||
count++;
|
||||
eeaddr++;
|
||||
len--;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @brief This module contains the API for internal AVR non-volatile data
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef EEPROM_H
|
||||
#define EEPROM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef EEPROM_BYTES_MAX
|
||||
#define EEPROM_BYTES_MAX (1 * 1024)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int eeprom_bytes_read(
|
||||
uint16_t ee_address, /* EEPROM starting memory address */
|
||||
uint8_t *buffer, /* data to store */
|
||||
int nbytes); /* number of bytes of data to read */
|
||||
int eeprom_bytes_write(
|
||||
uint16_t ee_address, /* EEPROM starting memory address */
|
||||
const uint8_t *buffer, /* data to send */
|
||||
int nbytes); /* number of bytes of data */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -1,10 +1,9 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
/**
|
||||
* @brief This module manages the BACnet ReadProperty handler
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2005
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -62,7 +61,8 @@ static int Encode_Property_APDU(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
void handler_read_property(uint8_t *service_request,
|
||||
void handler_read_property(
|
||||
uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data)
|
||||
@@ -72,6 +72,7 @@ void handler_read_property(uint8_t *service_request,
|
||||
int ack_len = 0;
|
||||
int property_len = 0;
|
||||
int pdu_len = 0;
|
||||
int ack_end_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
@@ -83,24 +84,26 @@ void handler_read_property(uint8_t *service_request,
|
||||
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
|
||||
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);
|
||||
len = abort_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
goto RP_ABORT;
|
||||
}
|
||||
len = rp_decode_service_request(service_request, service_len, &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);
|
||||
len = abort_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
||||
ABORT_REASON_OTHER, true);
|
||||
goto RP_ABORT;
|
||||
}
|
||||
/* most cases will be error */
|
||||
/* default case will be error */
|
||||
ack_len = rp_ack_encode_apdu_init(
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, &data);
|
||||
ack_end_len = rp_ack_encode_apdu_object_property_end(NULL);
|
||||
data.application_data = &Handler_Transmit_Buffer[pdu_len + ack_len];
|
||||
data.application_data_len = sizeof(Handler_Transmit_Buffer) -
|
||||
(pdu_len + ack_len);
|
||||
data.application_data_len =
|
||||
sizeof(Handler_Transmit_Buffer) - (pdu_len + ack_len + ack_end_len);
|
||||
data.error_class = ERROR_CLASS_OBJECT;
|
||||
data.error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
property_len = Encode_Property_APDU(&data);
|
||||
@@ -111,13 +114,13 @@ void handler_read_property(uint8_t *service_request,
|
||||
} else if (property_len == BACNET_STATUS_ABORT) {
|
||||
/* BACnet APDU too small to fit data,
|
||||
so proper response is Abort */
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
len = abort_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
} else {
|
||||
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
data.error_class, data.error_code);
|
||||
len = bacerror_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, data.error_class, data.error_code);
|
||||
}
|
||||
RP_ABORT:
|
||||
pdu_len += len;
|
||||
@@ -1,10 +1,9 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
/**
|
||||
* @brief This module manages the BACnet Who-Is handler
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2005
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -1,10 +1,9 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
/**
|
||||
* @brief This module manages the BACnet WriteProperty handler
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2005
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -29,7 +28,8 @@
|
||||
/* too big to reside on stack frame for PIC */
|
||||
static BACNET_WRITE_PROPERTY_DATA wp_data;
|
||||
|
||||
void handler_write_property(uint8_t *service_request,
|
||||
void handler_write_property(
|
||||
uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data)
|
||||
@@ -49,59 +49,64 @@ void handler_write_property(uint8_t *service_request,
|
||||
&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);
|
||||
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);
|
||||
len = abort_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
} else {
|
||||
wp_data.error_class = ERROR_CLASS_OBJECT;
|
||||
wp_data.error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
switch (wp_data.object_type) {
|
||||
case OBJECT_DEVICE:
|
||||
if (Device_Write_Property(&wp_data)) {
|
||||
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
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,
|
||||
wp_data.error_class, wp_data.error_code);
|
||||
len = bacerror_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, wp_data.error_class,
|
||||
wp_data.error_code);
|
||||
}
|
||||
break;
|
||||
case OBJECT_ANALOG_VALUE:
|
||||
if (Analog_Value_Write_Property(&wp_data)) {
|
||||
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
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,
|
||||
wp_data.error_class, wp_data.error_code);
|
||||
len = bacerror_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, wp_data.error_class,
|
||||
wp_data.error_code);
|
||||
}
|
||||
break;
|
||||
case OBJECT_BINARY_VALUE:
|
||||
if (Binary_Value_Write_Property(&wp_data)) {
|
||||
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
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,
|
||||
wp_data.error_class, wp_data.error_code);
|
||||
len = bacerror_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, wp_data.error_class,
|
||||
wp_data.error_code);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
wp_data.error_class, wp_data.error_code);
|
||||
len = bacerror_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, wp_data.error_class,
|
||||
wp_data.error_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @brief This module manages the project hardware defines
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef HARDWARE_H
|
||||
#define HARDWARE_H
|
||||
|
||||
#if !defined(F_CPU)
|
||||
#error Set F_CPU in the Makefile
|
||||
#endif
|
||||
|
||||
/* IAR compiler specific configuration */
|
||||
#if defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ASM__)
|
||||
#include <ioavr.h>
|
||||
#include <iom328p.h>
|
||||
#endif
|
||||
|
||||
/* AVR-GCC compiler specific configuration */
|
||||
#if defined(__GNUC__)
|
||||
#include <avr/io.h>
|
||||
#include <avr/wdt.h>
|
||||
#include <avr/power.h>
|
||||
#endif
|
||||
|
||||
#if !defined(__AVR_ATmega328P__)
|
||||
#error Firmware is configured for ATmega328P only (-mmcu=atmega328p)
|
||||
#endif
|
||||
|
||||
#include "iar2gcc.h"
|
||||
#include "bacnet/basic/sys/bits.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* @brief This module manages the common IAR and GCC compiler differences
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef IAR2GCC_H
|
||||
#define IAR2GCC_H
|
||||
|
||||
#if !defined(F_CPU)
|
||||
#error You must define F_CPU - clock frequency!
|
||||
#endif
|
||||
/* IAR */
|
||||
#if defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ASM__)
|
||||
#include <inavr.h>
|
||||
#include <ioavr.h>
|
||||
#include <intrinsics.h>
|
||||
#include <stdint.h>
|
||||
/* BitValue is used alot in GCC examples */
|
||||
#ifndef _BV
|
||||
#define _BV(bit_num) (1 << (bit_num))
|
||||
#endif
|
||||
|
||||
/* inline function */
|
||||
static inline void _delay_us(uint8_t microseconds)
|
||||
{
|
||||
do {
|
||||
__delay_cycles(F_CPU / 1000000UL);
|
||||
} while (microseconds--);
|
||||
}
|
||||
|
||||
#if (__VER__ > 700)
|
||||
#define DDA0 DDRA0
|
||||
#define DDA1 DDRA1
|
||||
#define DDA2 DDRA2
|
||||
#define DDA3 DDRA3
|
||||
#define DDA4 DDRA4
|
||||
#define DDA5 DDRA5
|
||||
#define DDA6 DDRA6
|
||||
#define DDA7 DDRA7
|
||||
|
||||
#define DDB0 DDRB0
|
||||
#define DDB1 DDRB1
|
||||
#define DDB2 DDRB2
|
||||
#define DDB3 DDRB3
|
||||
#define DDB4 DDRB4
|
||||
#define DDB5 DDRB5
|
||||
#define DDB6 DDRB6
|
||||
#define DDB7 DDRB7
|
||||
|
||||
#define DDC0 DDRC0
|
||||
#define DDC1 DDRC1
|
||||
#define DDC2 DDRC2
|
||||
#define DDC3 DDRC3
|
||||
#define DDC4 DDRC4
|
||||
#define DDC5 DDRC5
|
||||
#define DDC6 DDRC6
|
||||
#define DDC7 DDRC7
|
||||
|
||||
#define DDD0 DDRD0
|
||||
#define DDD1 DDRD1
|
||||
#define DDD2 DDRD2
|
||||
#define DDD3 DDRD3
|
||||
#define DDD4 DDRD4
|
||||
#define DDD5 DDRD5
|
||||
#define DDD6 DDRD6
|
||||
#define DDD7 DDRD7
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#include <util/delay.h>
|
||||
#endif
|
||||
|
||||
/* adjust some definitions to common versions */
|
||||
#if defined(__CROSSWORKS_AVR)
|
||||
#if (__TARGET_PROCESSOR == ATmega644P)
|
||||
#define PRR PRR0
|
||||
#define UBRR0 UBRR0W
|
||||
#define UBRR1 UBRR1W
|
||||
|
||||
#define PA0 PORTA0
|
||||
#define PA1 PORTA1
|
||||
#define PA2 PORTA2
|
||||
#define PA3 PORTA3
|
||||
#define PA4 PORTA4
|
||||
#define PA5 PORTA5
|
||||
#define PA6 PORTA6
|
||||
#define PA7 PORTA7
|
||||
|
||||
#define PB0 PORTB0
|
||||
#define PB1 PORTB1
|
||||
#define PB2 PORTB2
|
||||
#define PB3 PORTB3
|
||||
#define PB4 PORTB4
|
||||
#define PB5 PORTB5
|
||||
#define PB6 PORTB6
|
||||
#define PB7 PORTB7
|
||||
|
||||
#define PC0 PORTC0
|
||||
#define PC1 PORTC1
|
||||
#define PC2 PORTC2
|
||||
#define PC3 PORTC3
|
||||
#define PC4 PORTC4
|
||||
#define PC5 PORTC5
|
||||
#define PC6 PORTC6
|
||||
#define PC7 PORTC7
|
||||
|
||||
#define PD0 PORTD0
|
||||
#define PD1 PORTD1
|
||||
#define PD2 PORTD2
|
||||
#define PD3 PORTD3
|
||||
#define PD4 PORTD4
|
||||
#define PD5 PORTD5
|
||||
#define PD6 PORTD6
|
||||
#define PD7 PORTD7
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Input/Output Registers */
|
||||
#if defined(__GNUC__)
|
||||
#include <avr/io.h>
|
||||
|
||||
typedef struct {
|
||||
unsigned char bit0 : 1;
|
||||
unsigned char bit1 : 1;
|
||||
unsigned char bit2 : 1;
|
||||
unsigned char bit3 : 1;
|
||||
unsigned char bit4 : 1;
|
||||
unsigned char bit5 : 1;
|
||||
unsigned char bit6 : 1;
|
||||
unsigned char bit7 : 1;
|
||||
} BitRegisterType;
|
||||
|
||||
#ifndef true
|
||||
#define true 1
|
||||
#endif
|
||||
|
||||
#ifndef false
|
||||
#define false 0
|
||||
#endif
|
||||
|
||||
#define GPIO_BITREG(port, bitnum) \
|
||||
((volatile BitRegisterType *)_SFR_MEM_ADDR(port))->bit##bitnum
|
||||
|
||||
#define PINA_Bit0 GPIO_BITREG(PINA, 0)
|
||||
#define PINA_Bit1 GPIO_BITREG(PINA, 1)
|
||||
#define PINA_Bit2 GPIO_BITREG(PINA, 2)
|
||||
#define PINA_Bit3 GPIO_BITREG(PINA, 3)
|
||||
#define PINA_Bit4 GPIO_BITREG(PINA, 4)
|
||||
#define PINA_Bit5 GPIO_BITREG(PINA, 5)
|
||||
#define PINA_Bit6 GPIO_BITREG(PINA, 6)
|
||||
#define PINA_Bit7 GPIO_BITREG(PINA, 7)
|
||||
|
||||
#define PORTA_Bit0 GPIO_BITREG(PORTA, 0)
|
||||
#define PORTA_Bit1 GPIO_BITREG(PORTA, 1)
|
||||
#define PORTA_Bit2 GPIO_BITREG(PORTA, 2)
|
||||
#define PORTA_Bit3 GPIO_BITREG(PORTA, 3)
|
||||
#define PORTA_Bit4 GPIO_BITREG(PORTA, 4)
|
||||
#define PORTA_Bit5 GPIO_BITREG(PORTA, 5)
|
||||
#define PORTA_Bit6 GPIO_BITREG(PORTA, 6)
|
||||
#define PORTA_Bit7 GPIO_BITREG(PORTA, 7)
|
||||
|
||||
#define PINB_Bit0 GPIO_BITREG(PINB, 0)
|
||||
#define PINB_Bit1 GPIO_BITREG(PINB, 1)
|
||||
#define PINB_Bit2 GPIO_BITREG(PINB, 2)
|
||||
#define PINB_Bit3 GPIO_BITREG(PINB, 3)
|
||||
#define PINB_Bit4 GPIO_BITREG(PINB, 4)
|
||||
#define PINB_Bit5 GPIO_BITREG(PINB, 5)
|
||||
#define PINB_Bit6 GPIO_BITREG(PINB, 6)
|
||||
#define PINB_Bit7 GPIO_BITREG(PINB, 7)
|
||||
|
||||
#define PORTB_Bit0 GPIO_BITREG(PORTB, 0)
|
||||
#define PORTB_Bit1 GPIO_BITREG(PORTB, 1)
|
||||
#define PORTB_Bit2 GPIO_BITREG(PORTB, 2)
|
||||
#define PORTB_Bit3 GPIO_BITREG(PORTB, 3)
|
||||
#define PORTB_Bit4 GPIO_BITREG(PORTB, 4)
|
||||
#define PORTB_Bit5 GPIO_BITREG(PORTB, 5)
|
||||
#define PORTB_Bit6 GPIO_BITREG(PORTB, 6)
|
||||
#define PORTB_Bit7 GPIO_BITREG(PORTB, 7)
|
||||
|
||||
#define PINC_Bit0 GPIO_BITREG(PINC, 0)
|
||||
#define PINC_Bit1 GPIO_BITREG(PINC, 1)
|
||||
#define PINC_Bit2 GPIO_BITREG(PINC, 2)
|
||||
#define PINC_Bit3 GPIO_BITREG(PINC, 3)
|
||||
#define PINC_Bit4 GPIO_BITREG(PINC, 4)
|
||||
#define PINC_Bit5 GPIO_BITREG(PINC, 5)
|
||||
#define PINC_Bit6 GPIO_BITREG(PINC, 6)
|
||||
#define PINC_Bit7 GPIO_BITREG(PINC, 7)
|
||||
|
||||
#define PORTC_Bit0 GPIO_BITREG(PORTC, 0)
|
||||
#define PORTC_Bit1 GPIO_BITREG(PORTC, 1)
|
||||
#define PORTC_Bit2 GPIO_BITREG(PORTC, 2)
|
||||
#define PORTC_Bit3 GPIO_BITREG(PORTC, 3)
|
||||
#define PORTC_Bit4 GPIO_BITREG(PORTC, 4)
|
||||
#define PORTC_Bit5 GPIO_BITREG(PORTC, 5)
|
||||
#define PORTC_Bit6 GPIO_BITREG(PORTC, 6)
|
||||
#define PORTC_Bit7 GPIO_BITREG(PORTC, 7)
|
||||
|
||||
#define PIND_Bit0 GPIO_BITREG(PIND, 0)
|
||||
#define PIND_Bit1 GPIO_BITREG(PIND, 1)
|
||||
#define PIND_Bit2 GPIO_BITREG(PIND, 2)
|
||||
#define PIND_Bit3 GPIO_BITREG(PIND, 3)
|
||||
#define PIND_Bit4 GPIO_BITREG(PIND, 4)
|
||||
#define PIND_Bit5 GPIO_BITREG(PIND, 5)
|
||||
#define PIND_Bit6 GPIO_BITREG(PIND, 6)
|
||||
#define PIND_Bit7 GPIO_BITREG(PIND, 7)
|
||||
|
||||
#define PORTD_Bit0 GPIO_BITREG(PORTD, 0)
|
||||
#define PORTD_Bit1 GPIO_BITREG(PORTD, 1)
|
||||
#define PORTD_Bit2 GPIO_BITREG(PORTD, 2)
|
||||
#define PORTD_Bit3 GPIO_BITREG(PORTD, 3)
|
||||
#define PORTD_Bit4 GPIO_BITREG(PORTD, 4)
|
||||
#define PORTD_Bit5 GPIO_BITREG(PORTD, 5)
|
||||
#define PORTD_Bit6 GPIO_BITREG(PORTD, 6)
|
||||
#define PORTD_Bit7 GPIO_BITREG(PORTD, 7)
|
||||
|
||||
#define GPIOR0_Bit0 GPIO_BITREG(GPIOR0, 0)
|
||||
#define GPIOR0_Bit1 GPIO_BITREG(GPIOR0, 1)
|
||||
#define GPIOR0_Bit2 GPIO_BITREG(GPIOR0, 2)
|
||||
#define GPIOR0_Bit3 GPIO_BITREG(GPIOR0, 3)
|
||||
#define GPIOR0_Bit4 GPIO_BITREG(GPIOR0, 4)
|
||||
#define GPIOR0_Bit5 GPIO_BITREG(GPIOR0, 5)
|
||||
#define GPIOR0_Bit6 GPIO_BITREG(GPIOR0, 6)
|
||||
#define GPIOR0_Bit7 GPIO_BITREG(GPIOR0, 7)
|
||||
|
||||
#define GPIOR1_Bit0 GPIO_BITREG(GPIOR1, 0)
|
||||
#define GPIOR1_Bit1 GPIO_BITREG(GPIOR1, 1)
|
||||
#define GPIOR1_Bit2 GPIO_BITREG(GPIOR1, 2)
|
||||
#define GPIOR1_Bit3 GPIO_BITREG(GPIOR1, 3)
|
||||
#define GPIOR1_Bit4 GPIO_BITREG(GPIOR1, 4)
|
||||
#define GPIOR1_Bit5 GPIO_BITREG(GPIOR1, 5)
|
||||
#define GPIOR1_Bit6 GPIO_BITREG(GPIOR1, 6)
|
||||
#define GPIOR1_Bit7 GPIO_BITREG(GPIOR1, 7)
|
||||
|
||||
#define GPIOR2_Bit0 GPIO_BITREG(GPIOR2, 0)
|
||||
#define GPIOR2_Bit1 GPIO_BITREG(GPIOR2, 1)
|
||||
#define GPIOR2_Bit2 GPIO_BITREG(GPIOR2, 2)
|
||||
#define GPIOR2_Bit3 GPIO_BITREG(GPIOR2, 3)
|
||||
#define GPIOR2_Bit4 GPIO_BITREG(GPIOR2, 4)
|
||||
#define GPIOR2_Bit5 GPIO_BITREG(GPIOR2, 5)
|
||||
#define GPIOR2_Bit6 GPIO_BITREG(GPIOR2, 6)
|
||||
#define GPIOR2_Bit7 GPIO_BITREG(GPIOR2, 7)
|
||||
|
||||
#endif
|
||||
|
||||
/* Global Interrupts */
|
||||
#if defined(__GNUC__)
|
||||
#define __enable_interrupt() sei()
|
||||
#define __disable_interrupt() cli()
|
||||
#endif
|
||||
|
||||
/* Interrupts */
|
||||
#if defined(__ICCAVR__)
|
||||
#define PRAGMA(x) _Pragma(#x)
|
||||
#define ISR(vec) \
|
||||
/* function prototype for use with "require protoptypes" option. */ \
|
||||
PRAGMA(vector = vec) __interrupt void handler_##vec(void); \
|
||||
PRAGMA(vector = vec) __interrupt void handler_##vec(void)
|
||||
#elif defined(__GNUC__)
|
||||
#include <avr/interrupt.h>
|
||||
#endif
|
||||
|
||||
/* Flash */
|
||||
#if defined(__ICCAVR__)
|
||||
#define FLASH_DECLARE(x) __flash x
|
||||
#elif defined(__GNUC__)
|
||||
#define FLASH_DECLARE(x) x __attribute__((__progmem__))
|
||||
#endif
|
||||
|
||||
/* EEPROM */
|
||||
#if defined(__ICCAVR__)
|
||||
#define EEPROM_DECLARE(x) __eeprom x
|
||||
#endif
|
||||
#if defined(__GNUC__)
|
||||
#include <avr/eeprom.h>
|
||||
#define EEPROM_DECLARE(x) x __attribute__((section(".eeprom")))
|
||||
#endif
|
||||
|
||||
/* IAR intrinsic routines */
|
||||
#if defined(__GNUC__)
|
||||
/* FIXME: intrinsic routines: map to assembler for size/speed */
|
||||
#define __multiply_unsigned(x, y) ((x) * (y))
|
||||
/* FIXME: __root means to not optimize or strip */
|
||||
#define __root
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @brief This module manages the main C code for the BACnet demo
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
#include "adc.h"
|
||||
#include "nvdata.h"
|
||||
#include "rs485.h"
|
||||
#include "stack.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/object/av.h"
|
||||
#include "bacnet/basic/object/bv.h"
|
||||
|
||||
/* From the WhoIs hander - performed by the DLMSTP module */
|
||||
extern bool Send_I_Am_Flag;
|
||||
/* main loop task timer */
|
||||
static struct mstimer Task_Timer;
|
||||
/* uptime */
|
||||
static uint32_t Uptime_Seconds;
|
||||
|
||||
/* dummy function - so we can use default demo handlers */
|
||||
bool dcc_communication_enabled(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hardware_init(void)
|
||||
{
|
||||
/* Initialize the Clock Prescaler for ATmega48/88/168 */
|
||||
/* The default CLKPSx bits are factory set to 0011 */
|
||||
/* Enbable the Clock Prescaler */
|
||||
CLKPR = _BV(CLKPCE);
|
||||
/* CLKPS3 CLKPS2 CLKPS1 CLKPS0 Clock Division Factor
|
||||
------ ------ ------ ------ ---------------------
|
||||
0 0 0 0 1
|
||||
0 0 0 1 2
|
||||
0 0 1 0 4
|
||||
0 0 1 1 8
|
||||
0 1 0 0 16
|
||||
0 1 0 1 32
|
||||
0 1 1 0 64
|
||||
0 1 1 1 128
|
||||
1 0 0 0 256
|
||||
1 x x x Reserved
|
||||
*/
|
||||
/* Set the CLKPS3..0 bits to Prescaler of 1 */
|
||||
CLKPR = 0;
|
||||
/* Initialize I/O ports */
|
||||
/* For Port DDRx (Data Direction) Input=0, Output=1 */
|
||||
/* For Port PORTx (Bit Value) TriState=0, High=1 */
|
||||
DDRB = 0;
|
||||
PORTB = 0;
|
||||
DDRC = 0;
|
||||
PORTC = 0;
|
||||
DDRD = 0;
|
||||
PORTD = 0;
|
||||
|
||||
/* Configure the watchdog timer - Disabled for testing */
|
||||
BIT_CLEAR(MCUSR, WDRF);
|
||||
WDTCSR = 0;
|
||||
|
||||
/* Configure Specialized Hardware */
|
||||
RS485_Initialize();
|
||||
mstimer_init();
|
||||
adc_init();
|
||||
|
||||
/* Enable global interrupts */
|
||||
__enable_interrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief process some values once per second
|
||||
*/
|
||||
static void one_second_task(void)
|
||||
{
|
||||
BACNET_BINARY_PV value;
|
||||
float hours;
|
||||
|
||||
/* LED toggling */
|
||||
value = Binary_Value_Present_Value(99);
|
||||
if (value == BINARY_ACTIVE) {
|
||||
value = BINARY_INACTIVE;
|
||||
} else {
|
||||
value = BINARY_ACTIVE;
|
||||
}
|
||||
Binary_Value_Present_Value_Set(99, value);
|
||||
/* uptime */
|
||||
Uptime_Seconds += 1;
|
||||
hours = (float)Uptime_Seconds / 3600.0f;
|
||||
Analog_Value_Present_Value_Set(99, hours, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the device's non-volatile data
|
||||
*/
|
||||
static void device_nvdata_init(void)
|
||||
{
|
||||
uint8_t value8;
|
||||
uint16_t value16;
|
||||
uint32_t value32;
|
||||
const char *default_name = "AVR Device";
|
||||
const char *default_description = "Description";
|
||||
const char *default_location = "Location";
|
||||
|
||||
value16 = nvdata_unsigned16(NV_EEPROM_TYPE_0);
|
||||
if (value16 != NV_EEPROM_TYPE_ID) {
|
||||
/* set to defaults */
|
||||
nvdata_unsigned16_set(NV_EEPROM_TYPE_0, NV_EEPROM_TYPE_ID);
|
||||
nvdata_unsigned8_set(NV_EEPROM_VERSION, NV_EEPROM_VERSION_ID);
|
||||
nvdata_unsigned8_set(NV_EEPROM_MSTP_MAC, 123);
|
||||
nvdata_unsigned8_set(NV_EEPROM_MSTP_BAUD_K, 38);
|
||||
nvdata_unsigned8_set(NV_EEPROM_MSTP_MAX_MASTER, 127);
|
||||
nvdata_unsigned24_set(NV_EEPROM_DEVICE_0, 260123);
|
||||
nvdata_name_set(
|
||||
NV_EEPROM_DEVICE_NAME, CHARACTER_UTF8, default_name,
|
||||
strlen(default_name));
|
||||
nvdata_name_set(
|
||||
NV_EEPROM_DEVICE_DESCRIPTION, CHARACTER_UTF8, default_description,
|
||||
strlen(default_description));
|
||||
nvdata_name_set(
|
||||
NV_EEPROM_DEVICE_LOCATION, CHARACTER_UTF8, default_location,
|
||||
strlen(default_location));
|
||||
}
|
||||
value8 = nvdata_unsigned8(NV_EEPROM_MSTP_MAC);
|
||||
dlmstp_set_mac_address(value8);
|
||||
value8 = nvdata_unsigned8(NV_EEPROM_MSTP_BAUD_K);
|
||||
value32 = RS485_Baud_Rate_From_Kilo(value8);
|
||||
RS485_Set_Baud_Rate(value32);
|
||||
value8 = nvdata_unsigned8(NV_EEPROM_MSTP_MAX_MASTER);
|
||||
if (value8 > 127) {
|
||||
value8 = 127;
|
||||
}
|
||||
dlmstp_set_max_master(value8);
|
||||
dlmstp_set_max_info_frames(1);
|
||||
Send_I_Am_Flag = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static receive buffer,
|
||||
* initialized with zeros by the C Library Startup Code.
|
||||
* Note: Added a little safety margin to the buffer,
|
||||
* so that in the rare case, the message would be filled
|
||||
* up to MAX_MPDU and some decoding functions would overrun,
|
||||
* these decoding functions will just end up in a safe field
|
||||
* of static zeros. */
|
||||
static uint8_t PDUBuffer[MAX_MPDU + 16];
|
||||
|
||||
/**
|
||||
* Main function
|
||||
* @return 0, should never reach this
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
BACNET_ADDRESS src; /* source address */
|
||||
|
||||
hardware_init();
|
||||
Analog_Value_Init();
|
||||
Binary_Value_Init();
|
||||
device_nvdata_init();
|
||||
dlmstp_init(NULL);
|
||||
mstimer_set(&Task_Timer, 1000);
|
||||
for (;;) {
|
||||
/* process */
|
||||
if (mstimer_expired(&Task_Timer)) {
|
||||
mstimer_reset(&Task_Timer);
|
||||
one_second_task();
|
||||
}
|
||||
/* BACnet handling */
|
||||
pdu_len = dlmstp_receive(&src, &PDUBuffer[0], MAX_MPDU, 0);
|
||||
if (pdu_len) {
|
||||
npdu_handler(&src, &PDUBuffer[0], pdu_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Handle Get/Set of non-volatile data
|
||||
* @date July 8, 2015
|
||||
* @author Steve Karg
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "nvdata.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
/**
|
||||
* Gets 8-byte value from non-volatile memory
|
||||
*
|
||||
* @return device ID
|
||||
*/
|
||||
uint64_t nvdata_unsigned64(uint32_t ee_address)
|
||||
{
|
||||
uint64_t long_value = 0;
|
||||
uint8_t buffer[8];
|
||||
|
||||
if (eeprom_bytes_read(ee_address, buffer, 8) == 8) {
|
||||
decode_unsigned64(buffer, &long_value);
|
||||
}
|
||||
|
||||
return long_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a 8-byte value in non-volatile memory
|
||||
*
|
||||
* @param long_value 8-byte value
|
||||
*/
|
||||
int nvdata_unsigned64_set(uint32_t ee_address, uint64_t long_value)
|
||||
{
|
||||
uint8_t buffer[8];
|
||||
|
||||
encode_unsigned64(buffer, long_value);
|
||||
return eeprom_bytes_write(ee_address, buffer, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a 4-byte value from non-volatile memory
|
||||
*
|
||||
* @return device ID
|
||||
*/
|
||||
uint32_t nvdata_unsigned32(uint32_t ee_address)
|
||||
{
|
||||
uint32_t long_value = 0;
|
||||
uint8_t buffer[4];
|
||||
|
||||
if (eeprom_bytes_read(ee_address, buffer, 4) == 4) {
|
||||
decode_unsigned32(buffer, &long_value);
|
||||
}
|
||||
|
||||
return long_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a 4-byte value in non-volatile memory
|
||||
*
|
||||
* @param long_value 4-byte value
|
||||
*/
|
||||
int nvdata_unsigned32_set(uint32_t ee_address, uint32_t long_value)
|
||||
{
|
||||
uint8_t buffer[4];
|
||||
|
||||
encode_unsigned32(buffer, long_value);
|
||||
return eeprom_bytes_write(ee_address, buffer, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a 3-byte value from non-volatile memory
|
||||
*
|
||||
* @return device ID
|
||||
*/
|
||||
uint32_t nvdata_unsigned24(uint32_t ee_address)
|
||||
{
|
||||
uint32_t long_value = 0;
|
||||
uint8_t buffer[3];
|
||||
|
||||
if (eeprom_bytes_read(ee_address, buffer, 3) == 3) {
|
||||
decode_unsigned24(buffer, &long_value);
|
||||
}
|
||||
|
||||
return long_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a 3-byte value in non-volatile memory
|
||||
*
|
||||
* @param long_value 3-byte value
|
||||
*/
|
||||
int nvdata_unsigned24_set(uint32_t ee_address, uint32_t long_value)
|
||||
{
|
||||
uint8_t buffer[3];
|
||||
|
||||
encode_unsigned24(buffer, long_value);
|
||||
return eeprom_bytes_write(ee_address, buffer, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a 2-byte value from non-volatile memory
|
||||
*
|
||||
* @return device ID
|
||||
*/
|
||||
uint16_t nvdata_unsigned16(uint32_t ee_address)
|
||||
{
|
||||
uint16_t short_value = 0;
|
||||
uint8_t buffer[2];
|
||||
|
||||
if (eeprom_bytes_read(ee_address, buffer, 2) == 2) {
|
||||
decode_unsigned16(buffer, &short_value);
|
||||
}
|
||||
|
||||
return short_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a 2-byte value in non-volatile memory
|
||||
*
|
||||
* @param short_value 2-byte value
|
||||
*/
|
||||
int nvdata_unsigned16_set(uint32_t ee_address, uint16_t short_value)
|
||||
{
|
||||
uint8_t buffer[2];
|
||||
|
||||
encode_unsigned16(buffer, short_value);
|
||||
return eeprom_bytes_write(ee_address, buffer, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a 1-byte value from non-volatile memory
|
||||
*
|
||||
* @return device ID
|
||||
*/
|
||||
uint8_t nvdata_unsigned8(uint32_t ee_address)
|
||||
{
|
||||
uint8_t buffer = 0;
|
||||
|
||||
if (eeprom_bytes_read(ee_address, &buffer, 1) == 1) {
|
||||
/* something useful */
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a 1-byte value in non-volatile memory
|
||||
*
|
||||
* @param byte_value 1-byte value
|
||||
*/
|
||||
int nvdata_unsigned8_set(uint32_t ee_address, uint8_t byte_value)
|
||||
{
|
||||
return eeprom_bytes_write(ee_address, &byte_value, 1);
|
||||
}
|
||||
|
||||
bool nvdata_name_isvalid(uint8_t encoding, uint8_t length, const char *str)
|
||||
{
|
||||
bool valid = false;
|
||||
|
||||
if ((encoding < MAX_CHARACTER_STRING_ENCODING) &&
|
||||
(length <= NV_EEPROM_NAME_SIZE)) {
|
||||
if (encoding == CHARACTER_UTF8) {
|
||||
valid = utf8_isvalid(str, length);
|
||||
} else {
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a name to the EEPROM
|
||||
* @param offset - starting memory address
|
||||
* @param encoding - BACnet CharacterString encoding
|
||||
* @param str - string to write
|
||||
* @param length - number of bytes to write
|
||||
* @return true if successful
|
||||
*/
|
||||
bool nvdata_name_set(
|
||||
uint16_t offset, uint8_t encoding, const char *str, uint8_t length)
|
||||
{
|
||||
uint8_t buffer[NV_EEPROM_NAME_SIZE] = { 0 };
|
||||
uint8_t i = 0;
|
||||
|
||||
if (nvdata_name_isvalid(encoding, length, str)) {
|
||||
eeprom_bytes_write(NV_EEPROM_NAME_LENGTH(offset), &length, 1);
|
||||
eeprom_bytes_write(
|
||||
NV_EEPROM_NAME_ENCODING(offset), (uint8_t *)&encoding, 1);
|
||||
for (i = 0; i < length; i++) {
|
||||
buffer[i] = str[i];
|
||||
}
|
||||
eeprom_bytes_write(
|
||||
NV_EEPROM_NAME_STRING(offset), &buffer[0], NV_EEPROM_NAME_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the BACnet CharacterString to non-volatile memory at offset
|
||||
* @param offset - starting memory address
|
||||
* @param char_string - BACnet CharacterString value
|
||||
* @param error_class - BACnet error class
|
||||
* @param error_code - BACnet error code
|
||||
* @return true if name is a valid set of characters, valid length, and
|
||||
* valid character set encoding.
|
||||
* error_code and error_class are filled if false.
|
||||
*/
|
||||
bool nvdata_name_write(
|
||||
uint32_t offset,
|
||||
BACNET_CHARACTER_STRING *char_string,
|
||||
BACNET_ERROR_CLASS *error_class,
|
||||
BACNET_ERROR_CODE *error_code)
|
||||
{
|
||||
bool status = false;
|
||||
size_t length;
|
||||
uint8_t encoding;
|
||||
const char *str;
|
||||
|
||||
length = characterstring_length(char_string);
|
||||
if (length <= NV_EEPROM_NAME_SIZE) {
|
||||
encoding = characterstring_encoding(char_string);
|
||||
if (encoding < MAX_CHARACTER_STRING_ENCODING) {
|
||||
str = characterstring_value(char_string);
|
||||
status = nvdata_name_set(offset, encoding, str, length);
|
||||
if (!status) {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_PROPERTY;
|
||||
*error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a name from the EEPROM
|
||||
* @param offset - starting memory address
|
||||
* @param char_string - BACnet CharacterString value
|
||||
* @param default_string - default string to use if not valid
|
||||
*/
|
||||
void nvdata_name(
|
||||
uint32_t offset,
|
||||
BACNET_CHARACTER_STRING *char_string,
|
||||
const char *default_string)
|
||||
{
|
||||
uint8_t encoding = 0;
|
||||
uint8_t length = 0;
|
||||
char name[NV_EEPROM_NAME_SIZE + 1] = "";
|
||||
|
||||
eeprom_bytes_read(NV_EEPROM_NAME_ENCODING(offset), &encoding, 1);
|
||||
eeprom_bytes_read(NV_EEPROM_NAME_LENGTH(offset), &length, 1);
|
||||
eeprom_bytes_read(
|
||||
NV_EEPROM_NAME_STRING(offset), (uint8_t *)&name, NV_EEPROM_NAME_SIZE);
|
||||
if (nvdata_name_isvalid(encoding, length, name)) {
|
||||
characterstring_init(char_string, encoding, &name[0], length);
|
||||
} else if (default_string) {
|
||||
(void)nvdata_name_set(
|
||||
offset, CHARACTER_UTF8, default_string, strlen(default_string));
|
||||
characterstring_init_ansi(char_string, default_string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bytes from SEEPROM memory at address
|
||||
*
|
||||
* @param start_address - SEEPROM starting memory address
|
||||
* @param buffer - place to store data that has been read
|
||||
* @param length - number of bytes to read
|
||||
*
|
||||
* @return number of bytes read, or 0 on error
|
||||
*/
|
||||
uint32_t nvdata_get(uint32_t ee_address, uint8_t *buffer, uint32_t nbytes)
|
||||
{
|
||||
return eeprom_bytes_read(ee_address, buffer, nbytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write some data and wait until it is sent
|
||||
*
|
||||
* @param ee_address - SEEPROM starting memory address
|
||||
* @param buffer - data to send
|
||||
* @param length - number of bytes of data
|
||||
*
|
||||
* @return number of bytes written, or 0 on error
|
||||
*/
|
||||
uint32_t
|
||||
nvdata_set(uint32_t ee_address, uint8_t *buffer, uint32_t buffer_length)
|
||||
{
|
||||
return eeprom_bytes_write(ee_address, buffer, buffer_length);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @brief This module contains the map for internal AVR non-volatile data
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef NVDATA_H
|
||||
#define NVDATA_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
/*=============== EEPROM ================*/
|
||||
#define NV_EEPROM_TYPE_ID 0xBAC0U
|
||||
#define NV_EEPROM_VERSION_ID 1
|
||||
#define NV_EEPROM_BYTES_MAX EEPROM_BYTES_MAX
|
||||
|
||||
/* note to developers: define each byte,
|
||||
even if they are not used explicitly */
|
||||
|
||||
/* EEPROM Type ID to indicate initialized or not */
|
||||
#define NV_EEPROM_TYPE_0 0
|
||||
#define NV_EEPROM_TYPE_1 1
|
||||
/* EEPROM Version ID to indicate schema change */
|
||||
#define NV_EEPROM_VERSION 2
|
||||
|
||||
/* EEPROM free space - 3..9 */
|
||||
|
||||
/* BACnet MS/TP datalink layer */
|
||||
#define NV_EEPROM_MSTP_MAC 10
|
||||
/* 9=9.6k, 19=19.2k, 38=38.4k, 57=57.6k, 76=76.8k, 115=115.2k */
|
||||
#define NV_EEPROM_MSTP_BAUD_K 11
|
||||
#define NV_EEPROM_MSTP_MAX_MASTER 12
|
||||
/* device instance is only 22 bits */
|
||||
#define NV_EEPROM_DEVICE_0 13
|
||||
#define NV_EEPROM_DEVICE_1 14
|
||||
#define NV_EEPROM_DEVICE_2 15
|
||||
|
||||
/* EEPROM free space - 16..31 */
|
||||
|
||||
/* BACnet Names - 32 bytes of data each */
|
||||
#define NV_EEPROM_NAME_LENGTH(n) ((n) + 0)
|
||||
#define NV_EEPROM_NAME_ENCODING(n) ((n) + 1)
|
||||
#define NV_EEPROM_NAME_STRING(n) ((n) + 2)
|
||||
#define NV_EEPROM_NAME_SIZE 30
|
||||
#define NV_EEPROM_NAME_OFFSET (1 + 1 + NV_EEPROM_NAME_SIZE)
|
||||
/* Device Name - starting offset */
|
||||
#define NV_EEPROM_DEVICE_NAME 32
|
||||
/* Device Description - starting offset */
|
||||
#define NV_EEPROM_DEVICE_DESCRIPTION \
|
||||
(NV_EEPROM_DEVICE_NAME + NV_EEPROM_NAME_OFFSET)
|
||||
/* Device Location - starting offset */
|
||||
#define NV_EEPROM_DEVICE_LOCATION \
|
||||
(NV_EEPROM_DEVICE_DESCRIPTION + NV_EEPROM_NAME_OFFSET)
|
||||
|
||||
/* EEPROM free space 128..1024 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
uint64_t nvdata_unsigned64(uint32_t ee_address);
|
||||
int nvdata_unsigned64_set(uint32_t ee_address, uint64_t long_value);
|
||||
int nvdata_unsigned32_set(uint32_t ee_address, uint32_t long_value);
|
||||
uint32_t nvdata_unsigned32(uint32_t ee_address);
|
||||
int nvdata_unsigned24_set(uint32_t ee_address, uint32_t long_value);
|
||||
uint32_t nvdata_unsigned24(uint32_t ee_address);
|
||||
uint16_t nvdata_unsigned16(uint32_t ee_address);
|
||||
int nvdata_unsigned16_set(uint32_t ee_address, uint16_t short_value);
|
||||
uint8_t nvdata_unsigned8(uint32_t ee_address);
|
||||
int nvdata_unsigned8_set(uint32_t ee_address, uint8_t byte_value);
|
||||
|
||||
bool nvdata_name_isvalid(uint8_t encoding, uint8_t length, const char *str);
|
||||
bool nvdata_name_set(
|
||||
uint16_t offset, uint8_t encoding, const char *str, uint8_t length);
|
||||
|
||||
bool nvdata_name_write(
|
||||
uint32_t offset,
|
||||
BACNET_CHARACTER_STRING *char_string,
|
||||
BACNET_ERROR_CLASS *error_class,
|
||||
BACNET_ERROR_CODE *error_code);
|
||||
void nvdata_name(
|
||||
uint32_t offset,
|
||||
BACNET_CHARACTER_STRING *char_string,
|
||||
const char *default_string);
|
||||
|
||||
uint32_t nvdata_get(uint32_t ee_address, uint8_t *buffer, uint32_t nbytes);
|
||||
uint32_t nvdata_set(uint32_t ee_address, uint8_t *buffer, uint32_t nbytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -1,14 +1,10 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* 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 */
|
||||
/**
|
||||
* @brief The module handles sending data out the RS-485 port
|
||||
* and handles receiving data from the RS-485 port.
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
@@ -18,10 +14,24 @@
|
||||
/* This file has been customized for use with ATMEGA168 */
|
||||
#include "hardware.h"
|
||||
#include "rs485.h"
|
||||
#include "timer.h"
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
|
||||
/* baud rate */
|
||||
static uint32_t RS485_Baud = 9600;
|
||||
/* amount of silence on the wire */
|
||||
static struct mstimer Silence_Timer;
|
||||
|
||||
/* Public access to the Silence Timer */
|
||||
unsigned long RS485_Timer_Silence(void)
|
||||
{
|
||||
return mstimer_elapsed(&Silence_Timer);
|
||||
}
|
||||
|
||||
/* Public reset of the Silence Timer */
|
||||
void RS485_Timer_Silence_Reset(void)
|
||||
{
|
||||
mstimer_set(&Silence_Timer, 0);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in
|
||||
@@ -64,6 +74,36 @@ uint32_t RS485_Get_Baud_Rate(void)
|
||||
return RS485_Baud;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts kili-baud to baud
|
||||
* @param baud_k - baud rate in approximate kilobaud
|
||||
* @return: baud rate in bps
|
||||
*/
|
||||
uint32_t RS485_Baud_Rate_From_Kilo(uint8_t baud_k)
|
||||
{
|
||||
uint32_t baud = 0;
|
||||
|
||||
if (baud_k == 255) {
|
||||
baud = 38400;
|
||||
} else if (baud_k >= 115) {
|
||||
baud = 115200;
|
||||
} else if (baud_k >= 76) {
|
||||
baud = 76800;
|
||||
} else if (baud_k >= 57) {
|
||||
baud = 57600;
|
||||
} else if (baud_k >= 38) {
|
||||
baud = 38400;
|
||||
} else if (baud_k >= 19) {
|
||||
baud = 19200;
|
||||
} else if (baud_k >= 9) {
|
||||
baud = 9600;
|
||||
} else {
|
||||
baud = 38400;
|
||||
}
|
||||
|
||||
return baud;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* DESCRIPTION: Sets the baud rate for the chip USART
|
||||
* RETURN: true if valid baud rate
|
||||
@@ -86,7 +126,6 @@ bool RS485_Set_Baud_Rate(uint32_t baud)
|
||||
BIT_SET(UCSR0A, U2X0);
|
||||
/* configure baud rate */
|
||||
UBRR0 = (F_CPU / (8UL * RS485_Baud)) - 1;
|
||||
/* FIXME: store the baud rate */
|
||||
break;
|
||||
default:
|
||||
valid = false;
|
||||
@@ -144,8 +183,7 @@ void RS485_Turnaround_Delay(void)
|
||||
* @param buffer - data to send
|
||||
* @param nbytes - number of bytes of data
|
||||
*/
|
||||
void RS485_Send_Data(const uint8_t *buffer,
|
||||
uint16_t nbytes)
|
||||
void RS485_Send_Data(const uint8_t *buffer, uint16_t nbytes)
|
||||
{
|
||||
while (nbytes) {
|
||||
while (!BIT_CHECK(UCSR0A, UDRE0)) {
|
||||
@@ -164,7 +202,7 @@ void RS485_Send_Data(const uint8_t *buffer,
|
||||
/* Clear the Transmit Complete flag by writing a one to it. */
|
||||
BIT_SET(UCSR0A, TXC0);
|
||||
/* per MSTP spec, sort of */
|
||||
Timer_Silence_Reset();
|
||||
RS485_Timer_Silence_Reset();
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @brief The module handles API for the RS-485 port
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef RS485_H
|
||||
#define RS485_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
unsigned long RS485_Timer_Silence(void);
|
||||
|
||||
void RS485_Timer_Silence_Reset(void);
|
||||
|
||||
void RS485_Initialize(void);
|
||||
|
||||
void RS485_Transmitter_Enable(bool enable);
|
||||
|
||||
void RS485_Send_Data(
|
||||
const uint8_t *buffer, /* data to send */
|
||||
uint16_t nbytes); /* number of bytes of data */
|
||||
|
||||
bool RS485_ReceiveError(void);
|
||||
bool RS485_DataAvailable(uint8_t *data);
|
||||
|
||||
void RS485_Turnaround_Delay(void);
|
||||
uint32_t RS485_Get_Baud_Rate(void);
|
||||
bool RS485_Set_Baud_Rate(uint32_t baud);
|
||||
uint32_t RS485_Baud_Rate_From_Kilo(uint8_t baud_k);
|
||||
|
||||
void RS485_LED_Timers(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -1,10 +1,9 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*********************************************************************/
|
||||
/**
|
||||
* @brief The module handles CSTACK monitoring
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2009
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
#include "stack.h"
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @brief The module handles the API for the CSTACK monitoring
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2009
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef STACK_H
|
||||
#define STACK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* C stack checking */
|
||||
void stack_init(void);
|
||||
|
||||
unsigned stack_size(void);
|
||||
|
||||
uint8_t stack_byte(unsigned offset);
|
||||
|
||||
unsigned stack_unused(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @brief This module handles the 1 millisecond timer for the mstimer library
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2007
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "hardware.h"
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
|
||||
/* Prescaling: 1, 8, 64, 256, 1024 */
|
||||
#define TIMER_MICROSECONDS 1000UL
|
||||
#define TIMER_TICKS(p) \
|
||||
(((((F_CPU) / (p)) / 1000UL) * (TIMER_MICROSECONDS)) / 1000UL)
|
||||
#define TIMER_TICKS_MAX 255UL
|
||||
/* adjust the prescaler for the processor clock */
|
||||
#if (TIMER_TICKS(1) <= TIMER_TICKS_MAX)
|
||||
#define TIMER0_PRESCALER 1
|
||||
#elif (TIMER_TICKS(8) <= TIMER_TICKS_MAX)
|
||||
#define TIMER0_PRESCALER 8
|
||||
#elif (TIMER_TICKS(64) <= TIMER_TICKS_MAX)
|
||||
#define TIMER0_PRESCALER 64
|
||||
#elif (TIMER_TICKS(256) <= TIMER_TICKS_MAX)
|
||||
#define TIMER0_PRESCALER 256
|
||||
#elif (TIMER_TICKS(1024) <= TIMER_TICKS_MAX)
|
||||
#define TIMER0_PRESCALER 1024
|
||||
#else
|
||||
#error "TIMER0: F_CPU too large for timer prescaler."
|
||||
#endif
|
||||
#define TIMER0_TICKS TIMER_TICKS(TIMER0_PRESCALER)
|
||||
/* Timer counts up from count to TIMER_TICKS_MAX and then signals overflow */
|
||||
#define TIMER0_COUNT (TIMER_TICKS_MAX - TIMER0_TICKS)
|
||||
|
||||
/* millisecond time counter */
|
||||
static volatile unsigned long Millisecond_Counter;
|
||||
|
||||
/* Configure the Timer */
|
||||
void mstimer_init(void)
|
||||
{
|
||||
/* Normal Operation */
|
||||
TCCR1A = 0;
|
||||
/* CSn2 CSn1 CSn0 Description
|
||||
---- ---- ---- -----------
|
||||
0 0 0 No Clock Source
|
||||
0 0 1 No prescaling
|
||||
0 1 0 CLKio/8
|
||||
0 1 1 CLKio/64
|
||||
1 0 0 CLKio/256
|
||||
1 0 1 CLKio/1024
|
||||
1 1 0 Falling Edge of T0 (external)
|
||||
1 1 1 Rising Edge of T0 (external)
|
||||
*/
|
||||
#if (TIMER0_PRESCALER == 1)
|
||||
TCCR0B = _BV(CS00);
|
||||
#elif (TIMER0_PRESCALER == 8)
|
||||
TCCR0B = _BV(CS01);
|
||||
#elif (TIMER0_PRESCALER == 64)
|
||||
TCCR0B = _BV(CS01) | _BV(CS00);
|
||||
#elif (TIMER0_PRESCALER == 256)
|
||||
TCCR0B = _BV(CS02);
|
||||
#elif (TIMER0_PRESCALER == 1024)
|
||||
TCCR0B = _BV(CS02) | _BV(CS00);
|
||||
#else
|
||||
#error Timer0 Prescale: Invalid Value
|
||||
#endif
|
||||
/* Clear any TOV1 Flag set when the timer overflowed */
|
||||
BIT_CLEAR(TIFR0, TOV0);
|
||||
/* Initial value */
|
||||
TCNT0 = TIMER0_COUNT;
|
||||
/* Enable the overflow interrupt */
|
||||
BIT_SET(TIMSK0, TOIE0);
|
||||
/* Clear the Power Reduction Timer/Counter0 */
|
||||
BIT_CLEAR(PRR, PRTIM0);
|
||||
}
|
||||
|
||||
/* Timer interupt */
|
||||
/* note: Global interupts must be enabled - sei() */
|
||||
/* Timer Overflowed! Increment the time. */
|
||||
ISR(TIMER0_OVF_vect)
|
||||
{
|
||||
/* Set the counter for the next interrupt */
|
||||
TCNT0 = TIMER0_COUNT;
|
||||
/* Overflow Flag is automatically cleared */
|
||||
/* Update the global timer */
|
||||
Millisecond_Counter++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current time in milliseconds
|
||||
* @return milliseconds
|
||||
*/
|
||||
unsigned long mstimer_now(void)
|
||||
{
|
||||
unsigned long milliseconds;
|
||||
|
||||
BIT_CLEAR(TIMSK0, TOIE0);
|
||||
milliseconds = Millisecond_Counter;
|
||||
BIT_SET(TIMSK0, TOIE0);
|
||||
|
||||
return milliseconds;
|
||||
}
|
||||
Reference in New Issue
Block a user