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:
Steve Karg
2024-09-27 14:42:47 -05:00
committed by GitHub
parent 6e4034a92d
commit 4781582204
60 changed files with 2943 additions and 5661 deletions
+1 -1
View File
@@ -209,7 +209,7 @@ jobs:
- name: ports-avr
run: |
avr-gcc --version
make LEGACY=true atmega168
make LEGACY=true atmega328
make LEGACY=true bdk-atxx4-mstp
make LEGACY=true xplained
+8 -8
View File
@@ -242,20 +242,20 @@ fuzz-afl:
# Add "ports" to the build, if desired
.PHONY: ports
ports: atmega168 bdk-atxx4-mstp at91sam7s stm32f10x stm32f4xx
ports: atmega328 bdk-atxx4-mstp at91sam7s stm32f10x stm32f4xx
@echo "Built the ARM7 and AVR ports"
.PHONY: ports-clean
ports-clean: atmega168-clean bdk-atxx4-mstp-clean at91sam7s-clean \
ports-clean: atmega328-clean bdk-atxx4-mstp-clean at91sam7s-clean \
stm32f10x-clean stm32f4xx-clean xplained-clean
.PHONY: atmega168
atmega168: ports/atmega168/Makefile
$(MAKE) -s -C ports/atmega168 clean all
.PHONY: atmega328
atmega328: ports/atmega328/Makefile
$(MAKE) -s -C ports/atmega328 clean all
.PHONY: atmega168-clean
atmega168-clean: ports/atmega168/Makefile
$(MAKE) -s -C ports/atmega168 clean
.PHONY: atmega328-clean
atmega328-clean: ports/atmega328/Makefile
$(MAKE) -s -C ports/atmega328 clean
.PHONY: bdk-atxx4-mstp
bdk-atxx4-mstp: ports/bdk-atxx4-mstp/Makefile
+8 -18
View File
@@ -4,28 +4,18 @@ Q-1: Do you know the typical footprint of the stack (MS/TP use)?
A-1a: It fits on a PIC18F6720 (128K bytes flash 3840 bytes RAM) and there is lots of room for the application - see ports/pic18f6720 project. In one device with 8 Binary Value objects, 8 Binary Input objects, 1 Analog Input object, and supporting ReadProperty, WriteProperty, DeviceCommunicationControl, TimeSync, ReinitializeDevice, Who-Is, I-Am services, the BACnet stack used about 32K words of the code space.
A-1b: It fits on a ATmega168 (16K bytes flash, 1024 bytes RAM) - see ports/atmega168 project. 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.
A-1b: It fits on an ATmega328 (32K bytes flash, 2048 bytes RAM) - see ports/atmega328 project. The BACnet Capabilities include WhoIs, I-Am, ReadProperty, and WriteProperty support. The BACnet objects include a Device object, 11 Binary Value objects, and 12 Analog Value objects. An LED is controlled by Binary Value object instance 99. All required object properties can be retrieved using ReadProperty. The Present_Value property of the Device, Analog Value and Binary Value objects can be written using WriteProperty. The Device ID, MS/TP MAC, Max_Info_Frames, Max_Master, and baud rate can be configured using WriteProperty to an Analog Value present-value. The Device object-name, description, and location 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=512)
avr-gcc.exe (GCC) 12.2.0
Program (.text+.data): 28366 bytes (86.6% Full)
Data (.bss+.data): 1180 bytes (57.6% Full) (CStack uses remaining RAM, max-depth=347 bytes)
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)
A-1c: It fits easily on an ATmega644p (64K bytes flash, 4096 bytes RAM) - see ports/bdk-atxx4-mstp/ project. The BACnet Capabilities of an Application Specific Controller include WhoIs, I-Am, WhoHas, I-Have, ReadProperty, ReadPropertyMultiple, WriteProperty, and DeviceCommunicationControl support. The BACnet objects include a Device object, 2 Analog Input objects, 2 Analog Value objects, 5 Binary Input objects, and 2 Binary Output objects. Two LEDs are controlled by Binary Output objects. All required object properties can be retrieved using ReadProperty or ReadPropertyMultiple. Most of the Present_Value properties of the objects can be written. The Object_Identifier, Object_Name, Max_Info_Frames, Max_Master, and baud rate (property 9600) of the Device object can be written using WriteProperty. The APDU size is 256 bytes.
A-1c: It fits on an ATmega644p (64K bytes flash, 4096 bytes RAM) - see ports/bdk-atxx4-mstp/ project. The BACnet Capabilities of an Application Specific Controller include WhoIs, I-Am, WhoHas, I-Have, ReadProperty, ReadPropertyMultiple, WriteProperty, and DeviceCommunicationControl support. The BACnet objects include a Device object, 2 Analog Input objects, 2 Analog Value objects, 5 Binary Input objects, and 2 Binary Output objects. Two LEDs are controlled by Binary Output objects. All required object properties can be retrieved using ReadProperty or ReadPropertyMultiple. Most of the Present_Value properties of the objects can be written. The Object_Identifier, Object_Name, Max_Info_Frames, Max_Master, and baud rate (property 9600) of the Device object can be written using WriteProperty. The APDU size is 256 bytes.
With full optimization, the statistics on this port are:
avr-gcc (GCC) 4.3.4
Program (.text+.data): 34172 bytes (52.1% Full)
Data (.data+.bss+.noinit): 2501 (61.1% Full) (not including CStack=1594 bytes)
CStack usage (from painting): 772 bytes
IAR C/C++ Compiler V5.40.2.50249/W32 for Atmel AVR
IAR Universal Linker V4.61L/W32
28 770 bytes of CODE memory (+ 8 range fill )
3 250 bytes of DATA memory (+ 44 absolute ) (includes CStack=1024)
avr-gcc.exe (GCC) 12.2.0
Program (.text+.data): 49746 bytes (75.9% Full)
Data (.bss+.data): 1850 (45.2% Full) (not including CStack)
Q-2: The homepage used to say that the MS/TP code does not work. Still true?
-7
View File
@@ -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
-13
View File
@@ -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.
-139
View File
@@ -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;
}
-76
View File
@@ -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
-201
View File
@@ -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;
}
-63
View File
@@ -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
-18
View File
@@ -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
-22
View File
@@ -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
-289
View File
@@ -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
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<workspace>
<project>
<path>$WS_DIR$\bacnet.ewp</path>
</project>
<batchBuild/>
</workspace>
-245
View File
@@ -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;
}
-58
View File
@@ -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
-150
View File
@@ -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
-26
View File
@@ -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.
-223
View File
@@ -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
-147
View File
@@ -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);
}
}
}
-154
View File
@@ -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>
-45
View File
@@ -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
-34
View File
@@ -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
-87
View File
@@ -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);
}
-27
View File
@@ -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
View File
@@ -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 \
+116
View File
@@ -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.
+193
View File
@@ -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));
}
+25
View File
@@ -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;
+598
View File
@@ -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);
}
+23
View File
@@ -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
+372
View File
@@ -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
![pin-definition](images/RS485-EN.png)
## 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
![schematic](images/DFR0259.jpg)
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

+53
View File
@@ -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;
}
+33
View File
@@ -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;
}
}
+34
View File
@@ -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
+289
View File
@@ -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
+186
View File
@@ -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);
}
}
}
+303
View File
@@ -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);
}
+97
View File
@@ -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();
}
/****************************************************************************
+41
View File
@@ -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"
+29
View File
@@ -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
+101
View File
@@ -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;
}
+2
View File
@@ -1944,6 +1944,7 @@ static int bacapp_snprintf_property_identifier(
return ret_val;
}
#if defined(BACAPP_NULL)
/**
* @brief Print an null value to a string for EPICS
* @param str - destination string, or NULL for length only
@@ -1954,6 +1955,7 @@ static int bacapp_snprintf_null(char *str, size_t str_len)
{
return bacapp_snprintf(str, str_len, "Null");
}
#endif
#if defined(BACAPP_BOOLEAN)
/**