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
+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
}
+204
View File
@@ -0,0 +1,204 @@
###############################################################################
# Makefile for BACnet
###############################################################################
## General Flags
MCU = atmega328p
AVRDUDE_MCU = m328p
TARGET = bacnet
## Tools
CC = avr-gcc
AR = avr-ar
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
AVRDUDE = avrdude
# programmer id--check the avrdude for complete list
# # of available opts. These should include stk500,
# # avr910, avrisp, bsd, pony and more. Set this to
# # one of the valid "-c PROGRAMMER-ID" values
# # described in the avrdude info page.
# #
AVRDUDE_PROGRAMMERID = arduino
#
# # port--serial or parallel port to which your
# # hardware programmer is attached
# #
ifeq ($(OS),Windows_NT)
AVRDUDE_PORT = COM21
else
AVRDUDE_PORT = /dev/ttyUSB0
endif
# Source locations
BACNET_SRC = ../../src
BACNET_CORE = $(BACNET_SRC)/bacnet
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 \
h_whois.c \
h_wp.c
# common demo files needed
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
# core BACnet stack files
CORESRC = \
$(BACNET_CORE)/datalink/crc.c \
$(BACNET_CORE)/npdu.c \
$(BACNET_CORE)/bacdcode.c \
$(BACNET_CORE)/bacint.c \
$(BACNET_CORE)/bacreal.c \
$(BACNET_CORE)/bacstr.c \
$(BACNET_CORE)/iam.c \
$(BACNET_CORE)/rp.c \
$(BACNET_CORE)/wp.c \
$(BACNET_CORE)/whois.c \
$(BACNET_CORE)/bacaddr.c \
$(BACNET_CORE)/abort.c \
$(BACNET_CORE)/reject.c \
$(BACNET_CORE)/bacerror.c \
$(BACNET_CORE)/bacapp.c
## Include Directories
INCLUDES = -I. -I$(BACNET_SRC) -I$(BACNET_CORE)
# Source to Object conversion
COBJ = $(CSRC:.c=.o)
BASICOBJ = $(BASICSRC:.c=.o)
COREOBJ = $(CORESRC:.c=.o)
LIBRARY = lib$(TARGET).a
## Options common to compile, link and assembly rules
COMMON = -mmcu=$(MCU)
OPTIMIZE_FLAGS = -mcall-prologues
#OPTIMIZE_FLAGS += -finline-functions
OPTIMIZE_FLAGS += -finline-functions-called-once
#OPTIMIZATION = -O0
#OPTIMIZATION = -Os
OPTIMIZATION = -Os $(OPTIMIZE_FLAGS)
#OPTIMIZATION = -O3 $(OPTIMIZE_FLAGS)
## Compile options common for all C compilation units.
BFLAGS = -DBACDL_MSTP
BFLAGS += -DMAX_APDU=50
BFLAGS += -DBIG_ENDIAN=0
BFLAGS += -DMAX_TSM_TRANSACTIONS=0
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 += -DBACNET_PROTOCOL_REVISION=9
ifeq (${LEGACY},true)
# disable deprecated function warnings for legacy builds
BFLAGS += -DBACNET_STACK_DEPRECATED_DISABLE
endif
CFLAGS = $(COMMON)
# dead code removal
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
## Assembly specific flags
ASMFLAGS = $(COMMON)
ASMFLAGS += $(CFLAGS)
ASMFLAGS += -x assembler-with-cpp -Wa,-gdwarf2
## Linker flags
LDFLAGS = $(COMMON)
#dead code removal
#LDFLAGS += -Wl,-nostartfiles,-nostdlib
LDFLAGS += -Wl,--gc-sections,-static
LDFLAGS += -Wl,-Map=$(TARGET).map,-L.,-l$(TARGET)
#LDFLAGS += -Wl,-Map=$(TARGET).map
## Intel Hex file production flags
HEX_FLASH_FLAGS = -R .eeprom
HEX_EEPROM_FLAGS = -j .eeprom
HEX_EEPROM_FLAGS += --set-section-flags=.eeprom="alloc,load"
HEX_EEPROM_FLAGS += --change-section-lma .eeprom=0 --no-change-warnings
## Objects that must be built in order to link
OBJECTS = $(COBJ) $(BASICOBJ)
#OBJECTS = $(COBJ)
## Build
TARGET_ELF=$(TARGET).elf
all: $(LIBRARY) $(TARGET_ELF) $(TARGET).hex $(TARGET).eep $(TARGET).lst \
size Makefile
##Link
$(TARGET_ELF): $(OBJECTS) $(LIBRARY)
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
%.hex: $(TARGET_ELF)
$(OBJCOPY) -O ihex $(HEX_FLASH_FLAGS) $< $@
%.eep: $(TARGET_ELF)
-$(OBJCOPY) $(HEX_EEPROM_FLAGS) -O ihex $< $@ || exit 0
%.lst: $(TARGET_ELF)
$(OBJDUMP) -h -S $< > $@
lib: $(LIBRARY)
$(LIBRARY): $(COREOBJ) Makefile
$(AR) rcs $@ $(COREOBJ)
$(OBJDUMP) --syms $@ > $(LIBRARY:.a=.lst)
.c.o:
$(CC) -c $(INCLUDES) $(CFLAGS) $*.c -o $@
size: ${TARGET_ELF}
@echo
@${SIZE} ${TARGET_ELF}
install: $(TARGET_ELF)
$(AVRDUDE) -c $(AVRDUDE_PROGRAMMERID) \
-p $(AVRDUDE_MCU) -P $(AVRDUDE_PORT) -e \
-U flash:w:$(TARGET).hex
## Clean target
.PHONY: clean
clean:
-rm -rf $(OBJECTS) $(TARGET_ELF) dep/*
-rm -rf $(LIBRARY) $(COREOBJ) $(LIBRARY:.a=.lst)
-rm -rf $(TARGET).hex $(TARGET).eep $(TARGET).lst $(TARGET).map
## Other dependencies
-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*)
+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
+147
View File
@@ -0,0 +1,147 @@
/**
* @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>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/apdu.h"
#include "bacnet/bacdcode.h"
#include "bacnet/basic/services.h"
static uint8_t Local_Network_Priority; /* Fixing test 10.1.2 Network priority */
/**
* @brief get the local network priority
* @return local network priority
*/
uint8_t apdu_network_priority(void)
{
return Local_Network_Priority;
}
/**
* @brief set the local network priority
* @param net - local network priority
*/
void apdu_network_priority_set(uint8_t pri)
{
Local_Network_Priority = pri & 0x03;
}
bool apdu_service_supported(BACNET_SERVICES_SUPPORTED service_supported)
{
bool status = false;
if (service_supported == SERVICE_SUPPORTED_READ_PROPERTY) {
status = true;
}
if (service_supported == SERVICE_SUPPORTED_WHO_IS) {
status = true;
}
#ifdef WRITE_PROPERTY
if (service_supported == SERVICE_SUPPORTED_WRITE_PROPERTY) {
status = true;
}
#endif
return status;
}
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,
uint8_t **service_request,
uint16_t *service_request_len)
{
uint16_t len = 0; /* counts where we are in PDU */
service_data->segmented_message = (apdu[0] & BIT(3)) ? true : false;
service_data->more_follows = (apdu[0] & BIT(2)) ? true : false;
service_data->segmented_response_accepted =
(apdu[0] & BIT(1)) ? true : false;
service_data->max_segs = decode_max_segs(apdu[1]);
service_data->max_resp = decode_max_apdu(apdu[1]);
service_data->invoke_id = apdu[2];
service_data->priority = apdu_network_priority();
len = 3;
if (service_data->segmented_message) {
service_data->sequence_number = apdu[len++];
service_data->proposed_window_number = apdu[len++];
}
*service_choice = apdu[len++];
*service_request = &apdu[len];
*service_request_len = apdu_len - len;
return len;
}
void apdu_handler(
BACNET_ADDRESS *src,
uint8_t *apdu, /* APDU data */
uint16_t apdu_len)
{
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
uint8_t service_choice = 0;
uint8_t *service_request = NULL;
uint16_t service_request_len = 0;
uint16_t len = 0; /* counts where we are in PDU */
if (apdu) {
/* PDU Type */
switch (apdu[0] & 0xF0) {
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
len = apdu_decode_confirmed_service_request(
&apdu[0], /* APDU data */
apdu_len, &service_data, &service_choice, &service_request,
&service_request_len);
if (len == 0) {
/* service data unable to be decoded - simply drop */
break;
}
if (service_choice == SERVICE_CONFIRMED_READ_PROPERTY) {
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);
}
#endif
else {
handler_unrecognized_service(
service_request, service_request_len, src,
&service_data);
}
(void)len;
break;
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
service_choice = apdu[1];
service_request = &apdu[2];
service_request_len = apdu_len - 2;
if (service_choice == SERVICE_UNCONFIRMED_WHO_IS) {
handler_who_is(service_request, service_request_len, src);
}
break;
case PDU_TYPE_SIMPLE_ACK:
case PDU_TYPE_COMPLEX_ACK:
case PDU_TYPE_SEGMENT_ACK:
case PDU_TYPE_ERROR:
case PDU_TYPE_REJECT:
case PDU_TYPE_ABORT:
default:
break;
}
}
return;
}
+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);
}
}
}
+439
View File
@@ -0,0 +1,439 @@
/**
* @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"
#include "bacnet/bacenum.h"
#include "bacnet/apdu.h"
#include "bacnet/dcc.h"
#include "bacnet/datalink/dlmstp.h"
#include "bacnet/version.h"
/* platform */
#include "rs485.h"
#include "nvdata.h"
/* objects */
#include "bacnet/basic/services.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/av.h"
#include "bacnet/basic/object/bv.h"
#include "bacnet/wp.h"
/* note: you really only need to define variables for
properties that are writable or that may change.
The properties that are constant can be hard coded
into the read-property encoding. */
static uint32_t Object_Instance_Number = 260001;
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
static const char *Model_Name = "ATmega328 Uno R3 Device";
/* methods to manipulate the data */
uint32_t Device_Object_Instance_Number(void)
{
return Object_Instance_Number;
}
bool Device_Set_Object_Instance_Number(uint32_t object_id)
{
bool status = true; /* return value */
if (object_id <= BACNET_MAX_INSTANCE) {
Object_Instance_Number = object_id;
/* FIXME: Write the data to the eeprom */
/* I2C_Write_Block(
EEPROM_DEVICE_ADDRESS,
(char *)&Object_Instance_Number,
sizeof(Object_Instance_Number),
EEPROM_BACNET_ID_ADDR); */
} else {
status = false;
}
return status;
}
bool Device_Valid_Object_Instance_Number(uint32_t object_id)
{
/* BACnet allows for a wildcard instance number */
return (Object_Instance_Number == object_id);
}
uint16_t Device_Vendor_Identifier(void)
{
return BACNET_VENDOR_ID;
}
unsigned Device_Object_List_Count(void)
{
unsigned count = 1; /* at least 1 for device object */
/* FIXME: add objects as needed */
count += Analog_Value_Count();
count += Binary_Value_Count();
return count;
}
bool Device_Object_List_Identifier(
uint32_t array_index, BACNET_OBJECT_TYPE *object_type, uint32_t *instance)
{
bool status = false;
uint32_t object_index = 0;
uint32_t object_count = 0;
/* device object */
if (array_index == 1) {
*object_type = OBJECT_DEVICE;
*instance = Object_Instance_Number;
status = true;
}
/* normalize the index since
we know it is not the previous objects */
/* array index starts at 1 */
object_index = array_index - 1;
/* 1 for the device object */
object_count = 1;
/* FIXME: add objects as needed */
/* analog value objects */
if (!status) {
/* array index starts at 1, and 1 for the device object */
object_index -= object_count;
object_count = Analog_Value_Count();
if (object_index < object_count) {
*object_type = OBJECT_ANALOG_VALUE;
*instance = Analog_Value_Index_To_Instance(object_index);
status = true;
}
}
/* binary value objects */
if (!status) {
object_index -= object_count;
object_count = Binary_Value_Count();
/* is it a valid index for this object? */
if (object_index < object_count) {
*object_type = OBJECT_BINARY_VALUE;
*instance = Binary_Value_Index_To_Instance(object_index);
status = true;
}
}
return status;
}
/**
* @brief Encode a BACnetARRAY property element
* @param object_instance [in] BACnet network port object instance number
* @param array_index [in] array index requested:
* 0 to N for individual array members
* @param apdu [out] Buffer in which the APDU contents are built, or NULL to
* return the length of buffer if it had been built
* @return The length of the apdu encoded or
* BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX
*/
int Device_Object_List_Element_Encode(
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu)
{
int apdu_len = BACNET_STATUS_ERROR;
BACNET_OBJECT_TYPE object_type;
uint32_t instance;
bool found;
if (object_instance == Device_Object_Instance_Number()) {
/* single element is zero based, add 1 for BACnetARRAY which is one
* based */
array_index++;
found =
Device_Object_List_Identifier(array_index, &object_type, &instance);
if (found) {
apdu_len =
encode_application_object_id(apdu, object_type, instance);
}
}
return apdu_len;
}
/* return the length of the apdu encoded or -1 for error */
int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
uint8_t *apdu;
int apdu_len = 0; /* return value */
int apdu_max = 0;
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
uint32_t i = 0;
uint32_t count = 0;
apdu = rpdata->application_data;
apdu_max = rpdata->application_data_len;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_DEVICE, Object_Instance_Number);
break;
case PROP_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;
case PROP_OBJECT_TYPE:
apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE);
break;
case PROP_SYSTEM_STATUS:
apdu_len = encode_application_enumerated(&apdu[0], System_Status);
break;
case PROP_VENDOR_NAME:
characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_VENDOR_IDENTIFIER:
apdu_len = encode_application_unsigned(
&apdu[0], Device_Vendor_Identifier());
break;
case PROP_MODEL_NAME:
characterstring_init_ansi(&char_string, Model_Name);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_FIRMWARE_REVISION:
characterstring_init_ansi(&char_string, BACNET_VERSION_TEXT);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_APPLICATION_SOFTWARE_VERSION:
characterstring_init_ansi(&char_string, "1.0");
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_PROTOCOL_VERSION:
apdu_len =
encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION);
break;
case PROP_PROTOCOL_REVISION:
apdu_len =
encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_REVISION);
break;
case PROP_PROTOCOL_SERVICES_SUPPORTED:
/* Note: list of services that are executed, not initiated. */
bitstring_init(&bit_string);
for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) {
/* automatic lookup based on handlers set */
bitstring_set_bit(
&bit_string, (uint8_t)i,
apdu_service_supported((BACNET_SERVICES_SUPPORTED)i));
}
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
/* Note: this is the list of objects that can be in this device,
not a list of objects that this device can access */
bitstring_init(&bit_string);
/* must have the bit string as big as it can be */
for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) {
/* initialize all the object types to not-supported */
bitstring_set_bit(&bit_string, (uint8_t)i, false);
}
/* FIXME: indicate the objects that YOU support */
bitstring_set_bit(&bit_string, OBJECT_DEVICE, true);
bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true);
bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
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);
if (apdu_len == BACNET_STATUS_ABORT) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
} else if (apdu_len == BACNET_STATUS_ERROR) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
break;
case PROP_MAX_APDU_LENGTH_ACCEPTED:
apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU);
break;
case PROP_SEGMENTATION_SUPPORTED:
apdu_len =
encode_application_enumerated(&apdu[0], SEGMENTATION_NONE);
break;
case PROP_APDU_TIMEOUT:
apdu_len = encode_application_unsigned(&apdu[0], 60000);
break;
case PROP_NUMBER_OF_APDU_RETRIES:
apdu_len = encode_application_unsigned(&apdu[0], 0);
break;
case PROP_DEVICE_ADDRESS_BINDING:
/* FIXME: encode the list here, if it exists */
break;
case PROP_DATABASE_REVISION:
apdu_len = encode_application_unsigned(&apdu[0], 0);
break;
case PROP_MAX_INFO_FRAMES:
apdu_len =
encode_application_unsigned(&apdu[0], dlmstp_max_info_frames());
break;
case PROP_MAX_MASTER:
apdu_len =
encode_application_unsigned(&apdu[0], dlmstp_max_master());
break;
case (BACNET_PROPERTY_ID)9600:
apdu_len =
encode_application_unsigned(&apdu[0], RS485_Get_Baud_Rate());
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = -1;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
bool Device_Write_Property(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;
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_OBJECT_LIST) &&
(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_OBJECT_NAME:
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_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_MODEL_NAME:
case PROP_VENDOR_NAME:
case PROP_FIRMWARE_REVISION:
case PROP_APPLICATION_SOFTWARE_VERSION:
case PROP_LOCAL_TIME:
case PROP_UTC_OFFSET:
case PROP_LOCAL_DATE:
case PROP_DAYLIGHT_SAVINGS_STATUS:
case PROP_PROTOCOL_VERSION:
case PROP_PROTOCOL_REVISION:
case PROP_PROTOCOL_SERVICES_SUPPORTED:
case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
case PROP_OBJECT_LIST:
case PROP_MAX_APDU_LENGTH_ACCEPTED:
case PROP_SEGMENTATION_SUPPORTED:
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;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
File diff suppressed because it is too large Load Diff
@@ -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
+132
View File
@@ -0,0 +1,132 @@
/**
* @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>
#include <string.h>
#include <errno.h>
#include "bacnet/config.h"
#include "bacnet/basic/tsm/tsm.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/basic/services.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacerror.h"
#include "bacnet/apdu.h"
#include "bacnet/npdu.h"
#include "bacnet/abort.h"
#include "bacnet/rp.h"
/* demo objects */
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/av.h"
#include "bacnet/basic/object/bv.h"
/* Encodes the property APDU and returns the length,
or sets the error, and returns -1 */
static int Encode_Property_APDU(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = -1;
/* handle each object type */
switch (rpdata->object_type) {
case OBJECT_DEVICE:
/* Test for case of indefinite Device object instance */
if (rpdata->object_instance == BACNET_MAX_INSTANCE) {
rpdata->object_instance = Device_Object_Instance_Number();
}
if (Device_Valid_Object_Instance_Number(rpdata->object_instance)) {
apdu_len = Device_Read_Property(rpdata);
}
break;
case OBJECT_ANALOG_VALUE:
if (Analog_Value_Valid_Instance(rpdata->object_instance)) {
apdu_len = Analog_Value_Read_Property(rpdata);
}
break;
case OBJECT_BINARY_VALUE:
if (Binary_Value_Valid_Instance(rpdata->object_instance)) {
apdu_len = Binary_Value_Read_Property(rpdata);
}
break;
default:
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
break;
}
return apdu_len;
}
void handler_read_property(
uint8_t *service_request,
uint16_t service_len,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_DATA *service_data)
{
BACNET_READ_PROPERTY_DATA data;
int len = 0;
int 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;
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
if (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);
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);
goto RP_ABORT;
}
/* 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 + ack_end_len);
data.error_class = ERROR_CLASS_OBJECT;
data.error_code = ERROR_CODE_UNKNOWN_OBJECT;
property_len = Encode_Property_APDU(&data);
if (property_len >= 0) {
len = rp_ack_encode_apdu_object_property_end(
&Handler_Transmit_Buffer[pdu_len + property_len + ack_len]);
len += ack_len + property_len;
} 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,
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);
}
RP_ABORT:
pdu_len += len;
bytes_sent = datalink_send_pdu(
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
(void)bytes_sent;
return;
}
+46
View File
@@ -0,0 +1,46 @@
/**
* @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>
#include <string.h>
#include <errno.h>
#include "bacnet/config.h"
#include "bacnet/basic/tsm/tsm.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/whois.h"
#include "bacnet/iam.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/tsm/tsm.h"
bool Send_I_Am_Flag = true;
void handler_who_is(
uint8_t *service_request, uint16_t service_len, BACNET_ADDRESS *src)
{
int len = 0;
int32_t low_limit = 0;
int32_t high_limit = 0;
int32_t target_device;
(void)src;
len = whois_decode_service_request(
service_request, service_len, &low_limit, &high_limit);
if (len == 0) {
Send_I_Am_Flag = true;
} else if (len != BACNET_STATUS_ERROR) {
/* is my device id within the limits? */
target_device = Device_Object_Instance_Number();
if ((target_device >= low_limit) && (target_device <= high_limit)) {
Send_I_Am_Flag = true;
}
}
return;
}
+119
View File
@@ -0,0 +1,119 @@
/**
* @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>
#include <string.h>
#include <errno.h>
#include "bacnet/config.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/basic/tsm/tsm.h"
#include "bacnet/basic/services.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacerror.h"
#include "bacnet/apdu.h"
#include "bacnet/npdu.h"
#include "bacnet/abort.h"
#include "bacnet/wp.h"
/* demo objects */
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/av.h"
#include "bacnet/basic/object/bv.h"
/* too big to reside on stack frame for PIC */
static BACNET_WRITE_PROPERTY_DATA wp_data;
void handler_write_property(
uint8_t *service_request,
uint16_t service_len,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_DATA *service_data)
{
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
int bytes_sent = 0;
BACNET_ADDRESS my_address;
/* decode the service request only */
len = wp_decode_service_request(service_request, service_len, &wp_data);
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
/* bad decoding or something we didn't understand - send an abort */
if (len <= 0) {
len = abort_encode_apdu(
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
ABORT_REASON_OTHER, true);
} else if (service_data->segmented_message) {
len = abort_encode_apdu(
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
} else {
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],
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);
}
break;
case OBJECT_ANALOG_VALUE:
if (Analog_Value_Write_Property(&wp_data)) {
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);
}
break;
case OBJECT_BINARY_VALUE:
if (Binary_Value_Write_Property(&wp_data)) {
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);
}
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);
break;
}
}
pdu_len += len;
bytes_sent = datalink_send_pdu(
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
(void)bytes_sent;
return;
}
+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
+276
View File
@@ -0,0 +1,276 @@
/**
* @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>
#include <stdlib.h>
#include <stdio.h>
/* This file has been customized for use with ATMEGA168 */
#include "hardware.h"
#include "rs485.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
* receive mode.
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Initialize(void)
{
/* enable Transmit and Receive */
UCSR0B = _BV(TXEN0) | _BV(RXEN0);
/* Set USART Control and Status Register n C */
/* Asynchronous USART 8-bit data, No parity, 1 stop */
/* Set USART Mode Select: UMSELn1 UMSELn0 = 00 for Asynchronous USART */
/* Set Parity Mode: UPMn1 UPMn0 = 00 for Parity Disabled */
/* Set Stop Bit Select: USBSn = 0 for 1 stop bit */
/* Set Character Size: UCSZn2 UCSZn1 UCSZn0 = 011 for 8-bit */
/* Clock Polarity: UCPOLn = 0 when asynchronous mode is used. */
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
/* Clear Power Reduction USART0 */
BIT_CLEAR(PRR, PRUSART0);
/* Use port PD2 for RTS - enable and disable of Transceiver Tx/Rx */
/* Set port bit as Output - initially receiving */
BIT_CLEAR(PORTD, PD2);
BIT_SET(DDRD, DDD2);
return;
}
/****************************************************************************
* DESCRIPTION: Returns the baud rate that we are currently running at
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
uint32_t RS485_Get_Baud_Rate(void)
{
return RS485_Baud;
}
/**
* @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
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
bool RS485_Set_Baud_Rate(uint32_t baud)
{
bool valid = true;
switch (baud) {
case 9600:
case 19200:
case 38400:
case 57600:
case 76800:
case 115200:
RS485_Baud = baud;
/* 2x speed mode */
BIT_SET(UCSR0A, U2X0);
/* configure baud rate */
UBRR0 = (F_CPU / (8UL * RS485_Baud)) - 1;
break;
default:
valid = false;
break;
}
return valid;
}
/****************************************************************************
* DESCRIPTION: Enable or disable the transmitter
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Transmitter_Enable(bool enable)
{
if (enable) {
BIT_SET(PORTD, PD2);
} else {
BIT_CLEAR(PORTD, PD2);
}
}
/****************************************************************************
* DESCRIPTION: Waits on the SilenceTimer for 40 bits.
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void RS485_Turnaround_Delay(void)
{
uint8_t nbytes = 4;
RS485_Transmitter_Enable(false);
while (nbytes) {
while (!BIT_CHECK(UCSR0A, UDRE0)) {
/* do nothing - wait until Tx buffer is empty */
}
/* Send the data byte */
UDR0 = 0xff;
nbytes--;
}
/* was the frame sent? */
while (!BIT_CHECK(UCSR0A, TXC0)) {
/* do nothing - wait until the entire frame in the
Transmit Shift Register has been shifted out */
}
/* Clear the Transmit Complete flag by writing a one to it. */
BIT_SET(UCSR0A, TXC0);
}
/**
* @brief Send some data and wait until it is sent
* @param buffer - data to send
* @param nbytes - number of bytes of data
*/
void RS485_Send_Data(const uint8_t *buffer, uint16_t nbytes)
{
while (nbytes) {
while (!BIT_CHECK(UCSR0A, UDRE0)) {
/* do nothing - wait until Tx buffer is empty */
}
/* Send the data byte */
UDR0 = *buffer;
buffer++;
nbytes--;
}
/* was the frame sent? */
while (!BIT_CHECK(UCSR0A, TXC0)) {
/* do nothing - wait until the entire frame in the
Transmit Shift Register has been shifted out */
}
/* Clear the Transmit Complete flag by writing a one to it. */
BIT_SET(UCSR0A, TXC0);
/* per MSTP spec, sort of */
RS485_Timer_Silence_Reset();
}
/****************************************************************************
* DESCRIPTION: Return true if a framing or overrun error is present
* RETURN: true if error
* ALGORITHM: autobaud - if there are a lot of errors, switch baud rate
* NOTES: Clears any error flags.
*****************************************************************************/
bool RS485_ReceiveError(void)
{
bool ReceiveError = false;
volatile uint8_t dummy_data;
/* check for framing error */
#if 0
if (BIT_CHECK(UCSR0A, FE0)) {
/* FIXME: how do I clear the error flags? */
BITMASK_CLEAR(UCSR0A, (_BV(FE0) | _BV(DOR0) | _BV(UPE0)));
ReceiveError = true;
}
#endif
/* check for overrun error */
if (BIT_CHECK(UCSR0A, DOR0)) {
/* flush the receive buffer */
do {
dummy_data = UDR0;
} while (BIT_CHECK(UCSR0A, RXC0));
ReceiveError = true;
}
(void)dummy_data;
return ReceiveError;
}
/****************************************************************************
* DESCRIPTION: Return true if data is available
* RETURN: true if data is available, with the data in the parameter set
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
bool RS485_DataAvailable(uint8_t *data)
{
bool DataAvailable = false;
/* check for data */
if (BIT_CHECK(UCSR0A, RXC0)) {
*data = UDR0;
DataAvailable = true;
}
return DataAvailable;
}
#ifdef TEST_RS485
int main(void)
{
unsigned i = 0;
uint8_t DataRegister;
RS485_Set_Baud_Rate(38400);
RS485_Initialize();
/* receive task */
for (;;) {
if (RS485_ReceiveError()) {
fprintf(stderr, "ERROR ");
} else if (RS485_DataAvailable(&DataRegister)) {
fprintf(stderr, "%02X ", DataRegister);
}
}
}
#endif /* TEST_RS485 */
+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
+83
View File
@@ -0,0 +1,83 @@
/**
* @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"
/* stack checking */
#if defined(__GNUC__)
extern uint8_t _end;
extern uint8_t __stack;
#endif
#if defined(__GNUC__)
#define STACK_CANARY (0xC5)
void stack_init(void) __attribute__((naked)) __attribute__((section(".init1")));
#endif
void stack_init(void)
{
#if defined(__GNUC__)
#if 0
uint8_t *p = &_end;
while (p <= &__stack) {
*p = STACK_CANARY;
p++;
}
#else
__asm volatile(" ldi r30,lo8(_end)\n"
" ldi r31,hi8(_end)\n"
" ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */
" ldi r25,hi8(__stack)\n"
" rjmp .cmp\n"
".loop:\n"
" st Z+,r24\n"
".cmp:\n"
" cpi r30,lo8(__stack)\n"
" cpc r31,r25\n"
" brlo .loop\n"
" breq .loop" ::);
#endif
#endif
}
unsigned stack_size(void)
{
#if defined(__GNUC__)
return (&__stack) - (&_end);
#else
return 0;
#endif
}
uint8_t stack_byte(unsigned offset)
{
#if defined(__GNUC__)
return *(&_end + offset);
#else
offset = offset;
return 0;
#endif
}
unsigned stack_unused(void)
{
unsigned count = 0;
#if defined(__GNUC__)
uint8_t *p = &_end;
while (p <= &__stack) {
if ((*p) != STACK_CANARY) {
count = p - (&_end);
break;
}
p++;
}
#endif
return count;
}
+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;
}