Merge pull request #215 from thelsing/master

merge master into feature branch
This commit is contained in:
SirSydom 2022-10-11 12:30:36 +02:00 committed by GitHub
commit 376ab5c84b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 3709 additions and 371 deletions

View File

@ -22,7 +22,7 @@ jobs:
- name: Install bare metal ARM toolchain
run: sudo apt-get install binutils-arm-none-eabi gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
- name: Show toolchain version
run: arm-none-eabi-gcc --version
@ -36,8 +36,8 @@ jobs:
# access regardless of the host operating system
shell: bash
working-directory: ${{runner.workspace}}/build
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE/examples/knx-cc1310 -DCMAKE_BUILD_TYPE=$BUILD_TYPE
@ -50,6 +50,6 @@ jobs:
# - name: Test
# working-directory: ${{runner.workspace}}/build
# shell: bash
# Execute tests defined by the CMake configuration.
# Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
# run: ctest -C $BUILD_TYPE

View File

@ -27,8 +27,8 @@ jobs:
# access regardless of the host operating system
shell: bash
working-directory: ${{runner.workspace}}/build
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
@ -41,6 +41,6 @@ jobs:
# - name: Test
# working-directory: ${{runner.workspace}}/build
# shell: bash
# Execute tests defined by the CMake configuration.
# Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
# run: ctest -C $BUILD_TYPE

View File

@ -47,7 +47,7 @@ jobs:
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main

View File

@ -8,8 +8,6 @@ For ESP8266 and ESP32 [WifiManager](https://github.com/tzapu/WiFiManager) is use
Don't forget to reset ESP8266 manually (disconnect power) after flashing. The reboot doen't work during configuration with ETS otherwise.
The SAMD21 version uses my version of the [FlashStorage](https://github.com/thelsing/FlashStorage) lib (Pull request pending).
Generated documentation can be found [here](https://knx.readthedocs.io/en/latest/).
## Stack configuration possibilities
@ -52,4 +50,4 @@ void main ()
}
```
More configuration options can be found in the examples.
More configuration options can be found in the examples.

View File

@ -15,7 +15,6 @@ board = adafruit_feather_m0
framework = arduino
lib_deps =
SPI
https://github.com/thelsing/FlashStorage.git
knx
build_flags =

View File

@ -24,7 +24,6 @@ lib_extra_dirs = ../../../
lib_deps =
SPI
https://github.com/thelsing/FlashStorage.git
knx
build_flags =

View File

@ -15,7 +15,6 @@ board = adafruit_feather_m0
framework = arduino
lib_deps =
SPI
https://github.com/thelsing/FlashStorage.git
knx
build_flags =

View File

@ -24,7 +24,6 @@ lib_extra_dirs = ../../../
lib_deps =
SPI
https://github.com/thelsing/FlashStorage.git
knx
build_flags =
@ -100,3 +99,50 @@ lib_deps =
build_flags =
-DMASK_VERSION=0x07B0
-Wno-unknown-pragmas
;--- STM32/GD32 ---
[env:h8i8o]
platform = ststm32
board = genericSTM32F103CB
framework = arduino
; We consider that the this projects is opened within its project directory
; while working with VS Code.
lib_extra_dirs = ../../../
lib_deps =
knx
build_flags =
-DENABLE_HWSERIAL1
-DPIN_SERIAL1_TX=PA9
-DPIN_SERIAL1_RX=PA10
-DKNX_SERIAL=Serial1
-DKNX_BUTTON=PA11
-DKNX_LED=PA12
-DMASK_VERSION=0x07B0
-Wno-unknown-pragmas
extra_scripts = ../scripts/stm32rdu.py
[env:h8c09]
platform = ststm32
board = genericSTM32F103CB
framework = arduino
; We consider that the this projects is opened within its project directory
; while working with VS Code.
lib_extra_dirs = ../../../
lib_deps =
knx
build_flags =
-DENABLE_HWSERIAL1
-DPIN_SERIAL1_TX=PA9
-DPIN_SERIAL1_RX=PA10
-DKNX_SERIAL=Serial1
-DKNX_BUTTON=PB0
-DKNX_LED=PB5
-DMASK_VERSION=0x07B0
-Wno-unknown-pragmas
extra_scripts = ../scripts/stm32rdu.py

View File

@ -0,0 +1,210 @@
#include <Arduino.h>
#include <knx.h>
#define NUMIOS 18
#define goInputOnOff(i) knx.getGroupObject(i + 1)
#define goOutputOnOff(i) knx.getGroupObject(i + 1 + NUMIOS)
#define goOutputScaling(i) knx.getGroupObject(i + 1 + NUMIOS * 2)
#define goOutputOnOffStatus(i) knx.getGroupObject(i + 1 + NUMIOS * 3)
#define goOutputScalingStatus(i) knx.getGroupObject(i + 1 + NUMIOS * 4)
typedef enum {
CONFIG_NONE = 0,
CONFIG_IN = 1,
CONFIG_IN_TOGGLE = 2,
CONFIG_IN_TOGGLE_ON = 3,
CONFIG_OUT = 4,
CONFIG_OUT_ON = 5,
CONFIG_OUT_PWM = 6,
CONFIG_OUT_PWM_ON = 7,
} config_t;
inline void isrIn(uint8_t pin, uint8_t toggle);
// workaround for the weird Arduino interrupt handling
#define ISR_IN(NUM, TOGGLE) void isrIn##NUM##_##TOGGLE(){isrIn(NUM, TOGGLE);}
ISR_IN(0,0);
ISR_IN(1,0);
ISR_IN(2,0);
ISR_IN(3,0);
ISR_IN(4,0);
ISR_IN(5,0);
ISR_IN(6,0);
ISR_IN(7,0);
ISR_IN(8,0);
ISR_IN(9,0);
ISR_IN(10,0);
ISR_IN(11,0);
ISR_IN(12,0);
ISR_IN(13,0);
ISR_IN(14,0);
ISR_IN(15,0);
ISR_IN(16,0);
ISR_IN(17,0);
ISR_IN(0,1);
ISR_IN(1,1);
ISR_IN(2,1);
ISR_IN(3,1);
ISR_IN(4,1);
ISR_IN(5,1);
ISR_IN(6,1);
ISR_IN(7,1);
ISR_IN(8,1);
ISR_IN(9,1);
ISR_IN(10,1);
ISR_IN(11,1);
ISR_IN(12,1);
ISR_IN(13,1);
ISR_IN(14,1);
ISR_IN(15,1);
ISR_IN(16,1);
ISR_IN(17,1);
const uint32_t pinTbl[NUMIOS] = {PA15, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PC13, PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PB0};
void (*inCbTbl[NUMIOS * 2])(void) = {isrIn0_0, isrIn1_0, isrIn2_0, isrIn3_0, isrIn4_0, isrIn5_0, isrIn6_0, isrIn7_0, isrIn8_0, isrIn9_0, isrIn10_0, isrIn11_0, isrIn12_0, isrIn13_0, isrIn14_0, isrIn15_0, isrIn16_0, isrIn17_0, isrIn0_1, isrIn1_1, isrIn2_1, isrIn3_1, isrIn4_1, isrIn5_1, isrIn6_1, isrIn7_1, isrIn8_1, isrIn9_1, isrIn10_1, isrIn11_1, isrIn12_1, isrIn13_1, isrIn14_1, isrIn15_1, isrIn16_1, isrIn17_1};
volatile uint16_t val[NUMIOS];
uint32_t lastEvent[NUMIOS];
// callback for OnOff events from KNX
void outOnOff(GroupObject& go)
{
uint8_t pin = go.asap() - 1 - NUMIOS;
val[pin] = go.value();
val[pin] &= 1;
digitalWrite(pinTbl[pin], val[pin]);
goOutputOnOffStatus(pin).value(val[pin]);
}
// callback for OnOff events from KNX on PWM pins
void outOnOffPWM(GroupObject& go)
{
uint8_t pin = go.asap() - 1 - NUMIOS;
uint8_t tmp;
if(go.value()){
val[pin] |= 0x100;
analogWrite(pinTbl[pin], val[pin] & 0xff);
}else{
val[pin] &= 0xff;
analogWrite(pinTbl[pin], 0);
}
tmp = val[pin] >> 8;
goOutputOnOffStatus(pin).value(tmp);
}
// callback for 0-100% events from KNX
void outScaling(GroupObject& go)
{
uint8_t pin = go.asap() - NUMIOS * 2 - 1;
uint8_t tmp;
if(val[pin] > 0xFF){
tmp = *go.valueRef();
val[pin] = tmp | 1 << 8;
analogWrite(pinTbl[pin], val[pin] & 0xff);
}else{
tmp = *go.valueRef();
val[pin] = tmp;
}
*goOutputScalingStatus(pin).valueRef() = tmp;
goOutputScalingStatus(pin).objectWritten();
}
// callback for input interrupts
inline void isrIn(uint8_t pin, uint8_t toggle)
{
uint32_t diff = millis() - lastEvent[pin];
if (diff >= 50 && diff <= 500){
if(toggle){
val[pin]++;
}else{
val[pin] = digitalRead(pinTbl[pin]);
}
val[pin] &= 1;
goInputOnOff(pin).value(val[pin]);
}
lastEvent[pin] = millis();
}
void setup()
{
// read adress table, association table, groupobject table and parameters from eeprom
knx.readMemory();
// print values of parameters if device is already configured
if (knx.configured())
{
uint8_t progIn = knx.paramByte(0); // programming input
uint8_t progLed = knx.paramByte(1); // programming LED
for(uint8_t i = 0; i < NUMIOS; i++){
config_t config = (config_t) knx.paramByte(i + 2);
// loop through all the pins and configure them correctly
switch(config){
case CONFIG_IN:
pinMode(pinTbl[i], INPUT_PULLUP);
if(progIn = i + 1){
knx.buttonPin(pinTbl[i]);
}else{
goInputOnOff(i).dataPointType(DPT_Switch);
#if (ARDUINO_API_VERSION >= 10200)
attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i], (PinStatus)CHANGE);
#else
attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i], CHANGE);
#endif
}
break;
case CONFIG_IN_TOGGLE:
case CONFIG_IN_TOGGLE_ON:
goInputOnOff(i).dataPointType(DPT_Switch);
val[i] = config == CONFIG_IN_TOGGLE_ON;
pinMode(pinTbl[i], INPUT_PULLUP);
#if (ARDUINO_API_VERSION >= 10200)
attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i + NUMIOS], (PinStatus)CHANGE);
#else
attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i + NUMIOS], CHANGE);
#endif
break;
case CONFIG_OUT:
case CONFIG_OUT_ON:
pinMode(pinTbl[i], OUTPUT);
val[i] = config == CONFIG_OUT_ON;
digitalWrite(pinTbl[i], val[i]);
if(progLed = i + 1){
knx.ledPin(pinTbl[i]);
}else{
goOutputOnOff(i).dataPointType(DPT_Switch);
goOutputOnOff(i).callback(outOnOff);
goOutputOnOffStatus(i).dataPointType(DPT_Switch);
}
break;
case CONFIG_OUT_PWM:
case CONFIG_OUT_PWM_ON:
pinMode(pinTbl[i], OUTPUT);
val[i] = config == CONFIG_OUT_PWM_ON ? 0 : 0x1ff;
analogWrite(pinTbl[i], val[i] & 0xff);
goOutputOnOff(i).dataPointType(DPT_Switch);
goOutputOnOff(i).callback(outOnOffPWM);
goOutputOnOffStatus(i).dataPointType(DPT_Switch);
goOutputScaling(i).dataPointType(DPT_Scaling);
goOutputScaling(i).callback(outScaling);
goOutputScalingStatus(i).dataPointType(DPT_Scaling);
break;
}
}
}
// start the framework.
knx.start();
}
void loop()
{
// don't delay here too much. Otherwise you might lose packages or mess up the timing with ETS
knx.loop();
// only run the application code if the device was configured with ETS
if (!knx.configured())
return;
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
;PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
; We have to keep libdeps dir out the project directory otherwise,
; library scanner seems to have issues so compilation fails
libdeps_dir = /tmp/libdeps
src_dir = .
;--- STM32/GD32 ---
[env:h8i8o]
platform = ststm32
board = genericSTM32F103CB
framework = arduino
; We consider that the this projects is opened within its project directory
; while working with VS Code.
lib_extra_dirs = ../../../
lib_deps =
knx
build_flags =
-DENABLE_HWSERIAL1
-DPIN_SERIAL1_TX=PA9
-DPIN_SERIAL1_RX=PA10
-DKNX_SERIAL=Serial1
-DKNX_BUTTON=PA11
-DKNX_LED=PA12
-DMASK_VERSION=0x07B0
-DKNX_NO_PRINT
-Wno-unknown-pragmas
extra_scripts = ../scripts/stm32rdu.py

View File

@ -20,7 +20,6 @@ board_build.usb_product="KNX RF - USB Interface"
lib_deps =
SPI
Adafruit TinyUSB Library@0.7.1
https://github.com/thelsing/FlashStorage.git
knx
build_flags =

34
examples/scripts/stm32rdu.py Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
from subprocess import run
from datetime import datetime, timedelta
from os.path import expanduser
ocddir = expanduser("~/.platformio/packages/tool-openocd/")
chip = "stm32f1x"
def unlock(*args, **kwargs):
print("Please connect the board within the next two minutes.")
endtime = datetime.now() + timedelta(minutes = 1)
ret = 1
while ret != 0 and datetime.now() < endtime:
ret = run(["bin/openocd", "-f", "interface/stlink.cfg", "-f", "target/" + chip + ".cfg", "-c", "init", "-c", "reset halt", "-c", chip + " unlock 0", "-c", "reset halt", "-c", "exit"], cwd = ocddir).returncode
if ret != 0:
print("Timeout")
return ret
try:
Import("env")
except NameError:
import sys
if len(sys.argv) > 1:
chip = sys.argv[1]
if len(sys.argv) > 2:
ocddir = sys.argv[2]
unlock(None, None)
else:
ocddir = env.PioPlatform().get_package_dir("tool-openocd")
options = env.GetProjectOptions()
for option in options:
if "unlock_chip" == option[0]:
chip = option[1]
env.AddCustomTarget("unlock", None, unlock)

View File

@ -7,7 +7,7 @@
#endif
#ifndef KNX_NO_PRINT
Stream* ArduinoPlatform::SerialDebug = &Serial;
Stream* ArduinoPlatform::SerialDebug = &KNX_DEBUG_SERIAL;
#endif
ArduinoPlatform::ArduinoPlatform() : _knxSerial(nullptr)
@ -22,13 +22,13 @@ void ArduinoPlatform::fatalError()
{
while (true)
{
#ifdef LED_BUILTIN
#ifdef KNX_LED
static const long LED_BLINK_PERIOD = 200;
if ((millis() % LED_BLINK_PERIOD) > (LED_BLINK_PERIOD / 2))
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(KNX_LED, HIGH);
else
digitalWrite(LED_BUILTIN, LOW);
digitalWrite(KNX_LED, LOW);
#endif
}
}

View File

@ -2,6 +2,10 @@
#include "Arduino.h"
#ifndef KNX_DEBUG_SERIAL
#define KNX_DEBUG_SERIAL Serial
#endif
class ArduinoPlatform : public Platform
{
public:

View File

@ -17,11 +17,11 @@ class CC1310Platform : public Platform
void init();
// basic stuff
virtual void restart() final;
virtual void fatalError() final;
void restart() final;
void fatalError() final;
virtual uint8_t* getEepromBuffer(uint16_t size) final;
virtual void commitToEeprom() final;
uint8_t* getEepromBuffer(uint16_t size) final;
void commitToEeprom() final;
};
#endif //DeviceFamily_CC13X0

View File

@ -6,9 +6,13 @@
#include "knx/bits.h"
#ifndef KNX_SERIAL
#define KNX_SERIAL Serial1
#endif
Esp32Platform::Esp32Platform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial1)
: ArduinoPlatform(&KNX_SERIAL)
#endif
{
}
@ -55,10 +59,10 @@ void Esp32Platform::setupMultiCast(uint32_t addr, uint16_t port)
{
IPAddress mcastaddr(htonl(addr));
Serial.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port,
KNX_DEBUG_SERIAL.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port,
WiFi.localIP().toString().c_str());
uint8_t result = _udp.beginMulticast(mcastaddr, port);
Serial.printf("result %d\n", result);
KNX_DEBUG_SERIAL.printf("result %d\n", result);
}
void Esp32Platform::closeMultiCast()
@ -83,7 +87,7 @@ int Esp32Platform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen)
if (len > maxLen)
{
Serial.printf("udp buffer to small. was %d, needed %d\n", maxLen, len);
KNX_DEBUG_SERIAL.printf("udp buffer to small. was %d, needed %d\n", maxLen, len);
fatalError();
}
@ -106,8 +110,12 @@ bool Esp32Platform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buff
uint8_t * Esp32Platform::getEepromBuffer(uint16_t size)
{
EEPROM.begin(size);
return EEPROM.getDataPtr();
uint8_t * eepromptr = EEPROM.getDataPtr();
if(eepromptr == nullptr) {
EEPROM.begin(size);
eepromptr = EEPROM.getDataPtr();
}
return eepromptr;
}
void Esp32Platform::commitToEeprom()

View File

@ -7,9 +7,13 @@
#include "knx/bits.h"
#ifndef KNX_SERIAL
#define KNX_SERIAL Serial
#endif
EspPlatform::EspPlatform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial)
: ArduinoPlatform(&KNX_SERIAL)
#endif
{
}
@ -55,10 +59,10 @@ void EspPlatform::setupMultiCast(uint32_t addr, uint16_t port)
_multicastPort = port;
IPAddress mcastaddr(_multicastAddr);
Serial.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port,
KNX_DEBUG_SERIAL.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port,
WiFi.localIP().toString().c_str());
uint8 result = _udp.beginMulticast(WiFi.localIP(), mcastaddr, port);
Serial.printf("result %d\n", result);
KNX_DEBUG_SERIAL.printf("result %d\n", result);
}
void EspPlatform::closeMultiCast()
@ -83,7 +87,7 @@ int EspPlatform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen)
if (len > maxLen)
{
Serial.printf("udp buffer to small. was %d, needed %d\n", maxLen, len);
KNX_DEBUG_SERIAL.printf("udp buffer to small. was %d, needed %d\n", maxLen, len);
fatalError();
}
@ -106,8 +110,12 @@ bool EspPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer
uint8_t * EspPlatform::getEepromBuffer(uint16_t size)
{
EEPROM.begin(size);
return EEPROM.getDataPtr();
uint8_t * eepromptr = EEPROM.getDataPtr();
if(eepromptr == nullptr) {
EEPROM.begin(size);
eepromptr = EEPROM.getDataPtr();
}
return eepromptr;
}
void EspPlatform::commitToEeprom()

View File

@ -19,7 +19,8 @@ AddressTableObject::AddressTableObject(Memory& memory)
uint16_t AddressTableObject::entryCount()
{
if (loadState() != LS_LOADED)
// after programming without GA the module hangs
if (loadState() != LS_LOADED || _groupAddresses[0] == 0xFFFF)
return 0;
return ntohs(_groupAddresses[0]);
@ -104,6 +105,7 @@ bool AddressTableObject::contains(uint16_t addr)
void AddressTableObject::beforeStateChange(LoadState& newState)
{
TableObject::beforeStateChange(newState);
if (newState != LS_LOADED)
return;

View File

@ -50,7 +50,7 @@ class AddressTableObject : public TableObject
bool contains(uint16_t groupAddress);
protected:
virtual void beforeStateChange(LoadState& newState) override;
void beforeStateChange(LoadState& newState) override;
private:
uint16_t* _groupAddresses = 0;

View File

@ -347,7 +347,7 @@ void ApplicationLayer::dataConnectedConfirm(uint16_t tsap)
void ApplicationLayer::dataConnectedConfirm(uint16_t tsap, const SecurityControl& secCtrl)
{
//FIXME: implement dataConnectedConfirm DataSecurity
}
#pragma endregion
void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl)

View File

@ -157,7 +157,6 @@ class ApplicationLayer
#pragma endregion
protected:
#pragma region hooks
void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl &secCtrl);
void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap,

View File

@ -84,6 +84,7 @@ int32_t AssociationTableObject::translateAsap(uint16_t asap)
void AssociationTableObject::beforeStateChange(LoadState& newState)
{
TableObject::beforeStateChange(newState);
if (newState != LS_LOADED)
return;

View File

@ -338,3 +338,12 @@ void BusAccessUnit::propertyValueWrite(ObjectType objectType, uint8_t objectInst
uint8_t* data, uint32_t length)
{
}
void BusAccessUnit::beforeRestartCallback(BeforeRestartCallback func)
{
}
BeforeRestartCallback BusAccessUnit::beforeRestartCallback()
{
return 0;
}

View File

@ -3,6 +3,8 @@
#include "knx_types.h"
#include "interface_object.h"
typedef void (*BeforeRestartCallback)(void);
class BusAccessUnit
{
public:
@ -161,4 +163,6 @@ class BusAccessUnit
virtual void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t* data, uint32_t length);
virtual void beforeRestartCallback(BeforeRestartCallback func);
virtual BeforeRestartCallback beforeRestartCallback();
};

View File

@ -12,16 +12,16 @@ class Bau07B0 : public BauSystemBDevice, public ITpUartCallBacks
{
public:
Bau07B0(Platform& platform);
virtual void loop() override;
virtual bool enabled() override;
virtual void enabled(bool value) override;
void loop() override;
bool enabled() override;
void enabled(bool value) override;
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
// For TP1 only
virtual bool isAckRequired(uint16_t address, bool isGrpAddr) override;
bool isAckRequired(uint16_t address, bool isGrpAddr) override;
private:
TpUartDataLinkLayer _dlLayer;

View File

@ -14,18 +14,18 @@ class Bau091A : public BauSystemBCoupler, public ITpUartCallBacks
{
public:
Bau091A(Platform& platform);
virtual void loop() override;
virtual bool enabled() override;
virtual void enabled(bool value) override;
void loop() override;
bool enabled() override;
void enabled(bool value) override;
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
// For TP1 only
virtual bool isAckRequired(uint16_t address, bool isGrpAddr) override;
bool isAckRequired(uint16_t address, bool isGrpAddr) override;
virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:
RouterObject _routerObj;
IpParameterObject _ipParameters;

View File

@ -18,15 +18,15 @@ class Bau27B0 : public BauSystemBDevice
{
public:
Bau27B0(Platform& platform);
virtual void loop() override;
virtual bool enabled() override;
virtual void enabled(bool value) override;
void loop() override;
bool enabled() override;
void enabled(bool value) override;
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:
RfDataLinkLayer _dlLayer;
RfMediumObject _rfMediumObj;

View File

@ -18,15 +18,15 @@ class Bau2920 : public BauSystemBCoupler
{
public:
Bau2920(Platform& platform);
virtual void loop() override;
virtual bool enabled() override;
virtual void enabled(bool value) override;
void loop() override;
bool enabled() override;
void enabled(bool value) override;
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:
RouterObject _rtObjPrimary;
RouterObject _rtObjSecondary;

View File

@ -12,15 +12,15 @@ class Bau57B0 : public BauSystemBDevice
{
public:
Bau57B0(Platform& platform);
virtual void loop() override;
virtual bool enabled() override;
virtual void enabled(bool value) override;
void loop() override;
bool enabled() override;
void enabled(bool value) override;
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:
IpParameterObject _ipParameters;
IpDataLinkLayer _dlLayer;

View File

@ -31,6 +31,11 @@ void BauSystemB::writeMemory()
_memory.writeMemory();
}
Platform& BauSystemB::platform()
{
return _platform;
}
ApplicationProgramObject& BauSystemB::parameters()
{
return _appProgram;
@ -112,9 +117,14 @@ void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType,
uint16_t memoryAddress, uint8_t * data)
{
_memory.writeMemory(memoryAddress, number, data);
if (_deviceObj.verifyMode())
memoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress);
memoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress, data);
}
void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t * data)
{
applicationLayer().memoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, data);
}
void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
@ -147,6 +157,8 @@ void BauSystemB::restartRequestIndication(Priority priority, HopCountType hopTyp
if (restartType == RestartType::BasicRestart)
{
println("Basic restart requested");
if (_beforeRestart != 0)
_beforeRestart();
}
else if (restartType == RestartType::MasterReset)
{
@ -606,3 +618,23 @@ Memory& BauSystemB::memory()
{
return _memory;
}
void BauSystemB::versionCheckCallback(VersionCheckCallback func)
{
_memory.versionCheckCallback(func);
}
VersionCheckCallback BauSystemB::versionCheckCallback()
{
return _memory.versionCheckCallback();
}
void BauSystemB::beforeRestartCallback(BeforeRestartCallback func)
{
_beforeRestart = func;
}
BeforeRestartCallback BauSystemB::beforeRestartCallback()
{
return _beforeRestart;
}

View File

@ -21,6 +21,7 @@ class BauSystemB : protected BusAccessUnit
virtual bool enabled() = 0;
virtual void enabled(bool value) = 0;
Platform& platform();
ApplicationProgramObject& parameters();
DeviceObject& deviceObject();
@ -38,6 +39,10 @@ class BauSystemB : protected BusAccessUnit
void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t* data, uint32_t length) override;
void versionCheckCallback(VersionCheckCallback func);
VersionCheckCallback versionCheckCallback();
void beforeRestartCallback(BeforeRestartCallback func);
BeforeRestartCallback beforeRestartCallback();
protected:
virtual ApplicationLayer& applicationLayer() = 0;
@ -48,6 +53,8 @@ class BauSystemB : protected BusAccessUnit
uint16_t memoryAddress, uint8_t* data) override;
void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress) override;
void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t * data);
void memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* data) override;
void memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
@ -105,4 +112,5 @@ class BauSystemB : protected BusAccessUnit
RestartState _restartState = Idle;
SecurityControl _restartSecurity;
uint32_t _restartDelay = 0;
BeforeRestartCallback _beforeRestart = 0;
};

View File

@ -21,7 +21,6 @@ BauSystemBCoupler::BauSystemBCoupler(Platform& platform) :
#ifdef USE_DATASECURE
_memory.addSaveRestore(&_secIfObj);
#endif
}
ApplicationLayer& BauSystemBCoupler::applicationLayer()

View File

@ -18,13 +18,13 @@ class BauSystemBCoupler : public BauSystemB
{
public:
BauSystemBCoupler(Platform& platform);
virtual void loop() override;
virtual bool configured() override;
void loop() override;
bool configured() override;
protected:
virtual ApplicationLayer& applicationLayer() override;
ApplicationLayer& applicationLayer() override;
virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
Platform& _platform;

View File

@ -23,9 +23,9 @@ BauSystemBDevice::BauSystemBDevice(Platform& platform) :
_transLayer.groupAddressTable(_addrTable);
_memory.addSaveRestore(&_deviceObj);
_memory.addSaveRestore(&_groupObjTable); // changed order for better memory management
_memory.addSaveRestore(&_addrTable);
_memory.addSaveRestore(&_assocTable);
_memory.addSaveRestore(&_groupObjTable);
#ifdef USE_DATASECURE
_memory.addSaveRestore(&_secIfObj);
#endif
@ -69,8 +69,21 @@ void BauSystemBDevice::sendNextGroupTelegram()
if (flag != ReadRequest && flag != WriteRequest)
continue;
if (flag == WriteRequest)
{
#ifdef SMALL_GROUPOBJECT
GroupObject::processClassCallback(go);
#else
GroupObjectUpdatedHandler handler = go.callback();
if (handler)
handler(go);
#endif
}
if (!go.communicationEnable())
{
go.commFlag(Ok);
continue;
}
SecurityControl goSecurity;
goSecurity.toolAccess = false; // Secured group communication never uses the toolkey. ETS knows all keys, also the group keys.
@ -113,14 +126,21 @@ void BauSystemBDevice::updateGroupObject(GroupObject & go, uint8_t * data, uint8
memcpy(goData, data, length);
go.commFlag(Updated);
if (go.commFlag() != WriteRequest)
{
go.commFlag(Updated);
#ifdef SMALL_GROUPOBJECT
GroupObject::processClassCallback(go);
GroupObject::processClassCallback(go);
#else
GroupObjectUpdatedHandler handler = go.callback();
if (handler)
handler(go);
GroupObjectUpdatedHandler handler = go.callback();
if (handler)
handler(go);
#endif
}
else
{
go.commFlag(Updated);
}
}
bool BauSystemBDevice::configured()

View File

@ -20,12 +20,12 @@ class BauSystemBDevice : public BauSystemB
{
public:
BauSystemBDevice(Platform& platform);
virtual void loop() override;
virtual bool configured() override;
void loop() override;
bool configured() override;
GroupObjectTableObject& groupObjectTable();
protected:
virtual ApplicationLayer& applicationLayer() override;
ApplicationLayer& applicationLayer() override;
void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl,
uint8_t* data, uint8_t dataLength, bool status) override;
@ -39,7 +39,7 @@ class BauSystemBDevice : public BauSystemB
void sendNextGroupTelegram();
void updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length);
virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
AddressTableObject _addrTable;
AssociationTableObject _assocTable;

View File

@ -124,21 +124,19 @@ uint64_t sixBytesToUInt64(uint8_t* data)
uint16_t crc16Ccitt(uint8_t* input, uint16_t length)
{
uint32_t polynom = 0x1021;
uint8_t padded[length+2];
uint32_t polynom = 0x1021;
memcpy(padded, input, length);
memset(padded+length, 0x00, 2);
uint32_t result = 0xffff;
for (uint32_t i = 0; i < 8 * (uint32_t)sizeof(padded); i++) {
result <<= 1;
uint32_t nextBit = (padded[i / 8] >> (7 - (i % 8))) & 0x1;
result |= nextBit;
if ((result & 0x10000) != 0)
result ^= polynom;
}
return result & 0xffff;
uint32_t result = 0xffff;
for (uint32_t i = 0; i < 8 * ((uint32_t)length + 2); i++)
{
result <<= 1;
uint32_t nextBit;
nextBit = ((i / 8) < length) ? ((input[i / 8] >> (7 - (i % 8))) & 0x1) : 0;
result |= nextBit;
if ((result & 0x10000) != 0)
result ^= polynom;
}
return result & 0xffff;
}
uint16_t crc16Dnp(uint8_t* input, uint16_t length)

View File

@ -18,14 +18,14 @@ template <class T> class CallbackProperty : public Property
: Property(id, writeEnable, type, maxElements, access), _interfaceObject(io), _readCallback(readCallback)
{}
virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
{
if (count == 0 || _readCallback == nullptr || start > _maxElements || start + count > _maxElements + 1)
return 0;
return _readCallback(_interfaceObject, start, count, data);
}
virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
{
if (count == 0 || start > _maxElements || start + count > _maxElements + 1 || _writeCallback == nullptr)
return 0;

View File

@ -10,12 +10,12 @@ class DataProperty : public Property
DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, uint16_t value);
DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, uint32_t value);
DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, const uint8_t* value);
virtual ~DataProperty() override;
virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override;
virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override;
virtual uint8_t* save(uint8_t* buffer) override;
virtual const uint8_t* restore(const uint8_t* buffer) override;
virtual uint16_t saveSize() override;
~DataProperty() override;
uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override;
uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override;
uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() override;
const uint8_t* data();
const uint8_t* data(uint16_t elementIndex);

View File

@ -91,7 +91,6 @@ DeviceObject::DeviceObject()
#ifdef USE_RF
new DataProperty(PID_RF_DOMAIN_ADDRESS_CEMI_SERVER, true, PDT_GENERIC_06, 1, ReadLv3 | WriteLv3),
#endif
};
initializeProperties(sizeof(properties), properties);
}

View File

@ -7,6 +7,10 @@
class DeviceObject: public InterfaceObject
{
public:
// increase this version anytime DeviceObject-API changes
// the following value represents the serialized representation of DeviceObject.
const uint16_t apiVersion = 1;
DeviceObject();
uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override;

View File

@ -135,7 +135,6 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp
int KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
if (datatype.mainGroup == 1 && datatype.subGroup >= 1 && datatype.subGroup <= 23 && datatype.subGroup != 20 && !datatype.index)
return valueToBusValueBinary(value, payload, payload_length, datatype);
// DPT 2.* - Binary Control
@ -1553,7 +1552,6 @@ int valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_
case 1: // Mask bits
unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0x0f);
break;
}
return true;
}
@ -1757,7 +1755,7 @@ void float16ToPayload(uint8_t* payload, size_t payload_length, int index, double
if (wasNegative)
mantissa *= -1;
println(mantissa);
// println(mantissa);
signed16ToPayload(payload, payload_length, index, mantissa, mask);
unsigned8ToPayload(payload, payload_length, index, exponent << 3, 0x78 & (mask >> 8));

View File

@ -14,17 +14,17 @@ template <class T> class FunctionProperty : public Property
/* max_elements is set to 1, read and write level any value so we use Lv0, see 3.3.7 Application Layer p.68 */
{}
virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
{
return 0;
}
virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
{
return 0;
}
virtual void command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
void command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
{
if (length == 0 || _commandCallback == nullptr )
{
@ -34,7 +34,7 @@ template <class T> class FunctionProperty : public Property
_commandCallback(_interfaceObject, data, length, resultData, resultLength);
}
virtual void state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
void state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
{
if (length == 0 || _stateCallback == nullptr )
{

View File

@ -12,7 +12,7 @@ GroupObjectTableObject* GroupObject::_table = 0;
GroupObject::GroupObject()
{
_data = 0;
_commFlag = Ok;
_commFlag = Uninitialized;
_dataLength = 0;
#ifndef SMALL_GROUPOBJECT
_updateHandler = 0;
@ -74,6 +74,10 @@ bool GroupObject::readEnable()
if (!_table)
return false;
// we forbid reading of new (uninitialized) go
if (_commFlag == Uninitialized)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 11) > 0;
}
@ -270,5 +274,8 @@ void GroupObject::valueNoSend(const KNXValue& value)
void GroupObject::valueNoSend(const KNXValue& value, const Dpt& type)
{
if (_commFlag == Uninitialized)
_commFlag = Ok;
KNX_Encode_Value(value, _data, _dataLength, type);
}

View File

@ -14,7 +14,8 @@ enum ComFlag
WriteRequest = 2, //!< Write was requested but was not processed
Transmitting = 3, //!< Group Object is processed a the moment (read or write)
Ok = 4, //!< read or write request were send successfully
Error = 5 //!< there was an error on processing a request
Error = 5, //!< there was an error on processing a request
Uninitialized = 6 //!< uninitialized Group Object, its value is not valid
};
class GroupObject;
@ -235,7 +236,7 @@ class GroupObject
size_t asapValueSize(uint8_t code);
size_t goSize();
uint16_t _asap = 0;
ComFlag _commFlag = Ok;
ComFlag _commFlag = Uninitialized;
uint8_t* _data = 0;
uint8_t _dataLength = 0;
#ifndef SMALL_GROUPOBJECT

View File

@ -77,6 +77,7 @@ void GroupObjectTableObject::groupObjects(GroupObject * objs, uint16_t size)
void GroupObjectTableObject::beforeStateChange(LoadState& newState)
{
TableObject::beforeStateChange(newState);
if (newState != LS_LOADED)
return;

View File

@ -183,12 +183,11 @@ class InterfaceObject : public SaveRestore
*/
const Property* property(PropertyID id) const;
virtual uint8_t* save(uint8_t* buffer) override;
virtual const uint8_t* restore(const uint8_t* buffer) override;
virtual uint16_t saveSize() override;
uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() override;
protected:
/**
* Intializes the Property-array the the supplied values.
*/

View File

@ -18,7 +18,7 @@ class IpDataLinkLayer : public DataLinkLayer
void loop();
void enabled(bool value);
bool enabled() const;
virtual DptMedium mediumType() const override;
DptMedium mediumType() const override;
private:
bool _enabled = false;

View File

@ -29,9 +29,6 @@ enum KnxIpServiceType
TunnelingAck = 0x421,
RoutingIndication = 0x530,
RoutingLostMessage = 0x531,
};
class KnxIpFrame

View File

@ -49,7 +49,6 @@ class KNXValue
KNXValue& operator=(const float value);
private:
bool boolValue() const;
uint8_t ucharValue() const;
uint16_t ushortValue() const;

View File

@ -1,73 +1,115 @@
#include "memory.h"
#include <string.h>
#include "bits.h"
Memory::Memory(Platform& platform, DeviceObject& deviceObject)
: _platform(platform), _deviceObject(deviceObject)
{
}
{}
Memory::~Memory()
{}
void Memory::readMemory()
{
println("readMemory");
if (_data != nullptr)
uint8_t* flashStart = _platform.getNonVolatileMemoryStart();
size_t flashSize = _platform.getNonVolatileMemorySize();
if (flashStart == nullptr)
{
println("no user flash available;");
return;
}
uint16_t flashSize = KNX_FLASH_SIZE;
_data = _platform.getEepromBuffer(flashSize);
printHex("RESTORED ", _data, _metadataSize);
printHex("RESTORED ", flashStart, _metadataSize);
uint16_t metadataBlockSize = alignToPageSize(_metadataSize);
_freeList = new MemoryBlock(_data + metadataBlockSize, flashSize - metadataBlockSize);
_freeList = new MemoryBlock(flashStart + metadataBlockSize, flashSize - metadataBlockSize);
uint16_t apiVersion = 0;
const uint8_t* buffer = popWord(apiVersion, flashStart);
uint16_t manufacturerId = 0;
const uint8_t* buffer = popWord(manufacturerId, _data);
buffer = popWord(manufacturerId, buffer);
uint8_t hardwareType[LEN_HARDWARE_TYPE] = {0};
buffer = popByteArray(hardwareType, LEN_HARDWARE_TYPE, buffer);
uint16_t version = 0;
buffer = popWord(version, buffer);
if (_deviceObject.manufacturerId() != manufacturerId
|| _deviceObject.version() != version
|| memcmp(_deviceObject.hardwareType(), hardwareType, LEN_HARDWARE_TYPE) != 0)
VersionCheckResult versionCheck = FlashAllInvalid;
// first check correct format of deviceObject-API
if (_deviceObject.apiVersion == apiVersion)
{
println("saved memory doesn't match manufacturerId, version or hardwaretype");
print("manufacturerId: ");
print(manufacturerId, HEX);
print(" ");
println(_deviceObject.manufacturerId(), HEX);
print("version: ");
print(version, HEX);
print(" ");
println(_deviceObject.version(), HEX);
print("hardwareType: ");
printHex("", hardwareType, LEN_HARDWARE_TYPE);
print(" ");
printHex("", _deviceObject.hardwareType(), LEN_HARDWARE_TYPE);
if (_versionCheckCallback != 0) {
versionCheck = _versionCheckCallback(manufacturerId, hardwareType, version);
// callback should provide infomation about version check failure reasons
}
else if (_deviceObject.manufacturerId() == manufacturerId &&
memcmp(_deviceObject.hardwareType(), hardwareType, LEN_HARDWARE_TYPE) == 0)
{
if (_deviceObject.version() == version) {
versionCheck = FlashValid;
}
else
{
versionCheck = FlashTablesInvalid;
}
}
else
{
println("manufacturerId or hardwareType are different");
print("expexted manufacturerId: ");
print(_deviceObject.manufacturerId(), HEX);
print(", stored manufacturerId: ");
println(manufacturerId, HEX);
print("expexted hardwareType: ");
printHex("", _deviceObject.hardwareType(), LEN_HARDWARE_TYPE);
print(", stored hardwareType: ");
printHex("", hardwareType, LEN_HARDWARE_TYPE);
println("");
}
}
else
{
println("DataObject api changed, any data stored in flash is invalid.");
print("expexted DataObject api version: ");
print(_deviceObject.apiVersion, HEX);
print(", stored api version: ");
println(apiVersion, HEX);
}
if (versionCheck == FlashAllInvalid)
{
println("ETS has to reprogram PA and application!");
return;
}
println("manufacturerId, version and hardwareType matches");
println("restoring data from flash...");
print("saverestores ");
println(_saveCount);
for (int i = 0; i < _saveCount; i++)
{
println(_data - buffer);
println(flashStart - buffer);
println(".");
buffer = _saveRestores[i]->restore(buffer);
}
println("restored saveRestores");
if (versionCheck == FlashTablesInvalid)
{
println("TableObjects are referring to an older firmware version and are not loaded");
return;
}
print("tableObjs ");
println(_tableObjCount);
for (int i = 0; i < _tableObjCount; i++)
{
println(_data - buffer);
println(flashStart - buffer);
println(".");
buffer = _tableObjects[i]->restore(buffer);
uint16_t memorySize = 0;
@ -84,48 +126,62 @@ void Memory::readMemory()
void Memory::writeMemory()
{
uint8_t* buffer = _data;
buffer = pushWord(_deviceObject.manufacturerId(), buffer);
buffer = pushByteArray(_deviceObject.hardwareType(), LEN_HARDWARE_TYPE, buffer);
buffer = pushWord(_deviceObject.version(), buffer);
// first get the necessary size of the writeBuffer
size_t writeBufferSize = _metadataSize;
for (int i = 0; i < _saveCount; i++)
writeBufferSize = MAX(writeBufferSize, _saveRestores[i]->saveSize());
for (int i = 0; i < _tableObjCount; i++)
writeBufferSize = MAX(writeBufferSize, _tableObjects[i]->saveSize() + 2 /*for memory pos*/);
uint8_t buffer[writeBufferSize];
uint32_t flashPos = 0;
uint8_t* bufferPos = buffer;
bufferPos = pushWord(_deviceObject.apiVersion, bufferPos);
bufferPos = pushWord(_deviceObject.manufacturerId(), bufferPos);
bufferPos = pushByteArray(_deviceObject.hardwareType(), LEN_HARDWARE_TYPE, bufferPos);
bufferPos = pushWord(_deviceObject.version(), bufferPos);
flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
print("save saveRestores ");
println(_saveCount);
for (int i = 0; i < _saveCount; i++)
{
println(_data - buffer);
println(".");
//println((long)_saveRestores[i], HEX);
buffer = _saveRestores[i]->save(buffer);
bufferPos = _saveRestores[i]->save(buffer);
flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
}
print("save tableobjs ");
println(_tableObjCount);
for (int i = 0; i < _tableObjCount; i++)
{
println(_data - buffer);
println(".");
//println((long)_tableObjects[i], HEX);
buffer = _tableObjects[i]->save(buffer);
bufferPos = _tableObjects[i]->save(buffer);
//save to size of the memoryblock for tableobject too, so that we can rebuild the usedList and freeList
if (_tableObjects[i]->_data != nullptr)
{
MemoryBlock* block = findBlockInList(_usedList, _tableObjects[i]->_data);
if (block == nullptr)
{
println("_data of TableObject not in errorlist");
println("_data of TableObject not in _usedList");
_platform.fatalError();
}
buffer = pushWord(block->size, buffer);
bufferPos = pushWord(block->size, bufferPos);
}
else
buffer = pushWord(0, buffer);
bufferPos = pushWord(0, bufferPos);
flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
}
_platform.commitToEeprom();
printHex("SAVED ", _data, _metadataSize);
_platform.commitNonVolatileMemory();
}
void Memory::saveMemory()
{
_platform.commitNonVolatileMemory();
}
void Memory::addSaveRestore(SaveRestore* obj)
@ -151,7 +207,7 @@ void Memory::addSaveRestore(TableObject* obj)
uint8_t* Memory::allocMemory(size_t size)
{
// always allocate aligned to 32 bit
// always allocate aligned to pagesize
size = alignToPageSize(size);
MemoryBlock* freeBlock = _freeList;
@ -220,19 +276,19 @@ void Memory::freeMemory(uint8_t* ptr)
void Memory::writeMemory(uint32_t relativeAddress, size_t size, uint8_t* data)
{
memcpy(toAbsolute(relativeAddress), data, size);
_platform.writeNonVolatileMemory(relativeAddress, data, size);
}
uint8_t* Memory::toAbsolute(uint32_t relativeAddress)
{
return _data + (ptrdiff_t)relativeAddress;
return _platform.getNonVolatileMemoryStart() + (ptrdiff_t)relativeAddress;
}
uint32_t Memory::toRelative(uint8_t* absoluteAddress)
{
return absoluteAddress - _data;
return absoluteAddress - _platform.getNonVolatileMemoryStart();
}
MemoryBlock* Memory::removeFromList(MemoryBlock* head, MemoryBlock* item)
@ -354,8 +410,9 @@ void Memory::addToFreeList(MemoryBlock* block)
uint16_t Memory::alignToPageSize(size_t size)
{
// to 32 bit for now
return (size + 3) & ~0x3;
size_t pageSize = 4; //_platform.flashPageSize(); // align to 32bit for now, as aligning to flash-page-size causes side effects in programming
// pagesize should be a multiply of two
return (size + pageSize - 1) & (-1*pageSize);
}
MemoryBlock* Memory::findBlockInList(MemoryBlock* head, uint8_t* address)
@ -429,3 +486,13 @@ void Memory::addNewUsedBlock(uint8_t* address, size_t size)
MemoryBlock* newUsedBlock = new MemoryBlock(address, size);
addToUsedList(newUsedBlock);
}
void Memory::versionCheckCallback(VersionCheckCallback func)
{
_versionCheckCallback = func;
}
VersionCheckCallback Memory::versionCheckCallback()
{
return _versionCheckCallback;
}

View File

@ -24,12 +24,23 @@ class MemoryBlock
MemoryBlock* next = nullptr;
};
enum VersionCheckResult
{
FlashAllInvalid = 0, //!< All flash content is not valid for this firmware, we delete it
FlashTablesInvalid = 1,//!< All table objects are invalid for this firmware, device object and saveRestores are OK
FlashValid = 2 //!< Flash content is valid and will be used
};
typedef VersionCheckResult (*VersionCheckCallback)(uint16_t manufacturerId, uint8_t* hardwareType, uint16_t version);
class Memory
{
public:
Memory(Platform& platform, DeviceObject& deviceObject);
virtual ~Memory();
void readMemory();
void writeMemory();
void saveMemory();
void addSaveRestore(SaveRestore* obj);
void addSaveRestore(TableObject* obj);
@ -39,6 +50,9 @@ public:
uint8_t* toAbsolute(uint32_t relativeAddress);
uint32_t toRelative(uint8_t* absoluteAddress);
void versionCheckCallback(VersionCheckCallback func);
VersionCheckCallback versionCheckCallback();
private:
void removeFromFreeList(MemoryBlock* block);
void addToUsedList(MemoryBlock* block);
@ -49,14 +63,19 @@ public:
MemoryBlock* findBlockInList(MemoryBlock* head, uint8_t* address);
void addNewUsedBlock(uint8_t* address, size_t size);
void readEraseBlockToBuffer(uint32_t blockNum);
uint8_t* eraseBlockStart(uint32_t blockNum);
uint8_t* eraseBlockEnd(uint32_t blockNum);
void saveBufferdEraseBlock();
VersionCheckCallback _versionCheckCallback = 0;
Platform& _platform;
DeviceObject& _deviceObject;
SaveRestore* _saveRestores[MAXSAVE] = {0};
TableObject* _tableObjects[MAXTABLEOBJ] = {0};
uint8_t _saveCount = 0;
uint8_t _tableObjCount = 0;
uint8_t* _data = nullptr;
MemoryBlock* _freeList = nullptr;
MemoryBlock* _usedList = nullptr;
uint16_t _metadataSize = 4 + LEN_HARDWARE_TYPE; // accounting for 2x pushWord and pushByteArray of length LEN_HARDWARE_TYPE
uint16_t _metadataSize = 6 + LEN_HARDWARE_TYPE; // accounting for 3x pushWord and pushByteArray of length LEN_HARDWARE_TYPE
};

View File

@ -27,10 +27,10 @@ class NetworkLayerCoupler : public NetworkLayer
void rtObj(RouterObject& rtObj); // Coupler model 1.x
// from transport layer
virtual void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override;
virtual void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override;
virtual void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override;
void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override;
void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
private:
enum CouplerType
@ -46,16 +46,16 @@ class NetworkLayerCoupler : public NetworkLayer
static constexpr uint8_t kLocalIfIndex = 99;
// from entities
virtual void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu,
void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source, uint8_t srcIfIdx) override;
virtual void dataConfirm(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, Priority priority,
void dataConfirm(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, Priority priority,
uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
virtual void broadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
void broadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source, uint8_t srcIfIdx) override;
virtual void broadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
virtual void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
void broadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source, uint8_t srcIfIdx) override;
virtual void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
void routeDataIndividual(AckType ack, uint16_t destination, NPDU& npdu, Priority priority, uint16_t source, uint8_t srcIfIndex);
void sendMsgHopCount(AckType ack, AddressType addrType, uint16_t destination, NPDU& npdu, Priority priority,

View File

@ -19,23 +19,23 @@ class NetworkLayerDevice : public NetworkLayer
NetworkLayerEntity& getInterface();
// from transport layer
virtual void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override;
virtual void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override;
virtual void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override;
void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override;
void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
private:
// from entities
virtual void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu,
void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source, uint8_t srcIfIdx) override;
virtual void dataConfirm(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, Priority priority,
void dataConfirm(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, Priority priority,
uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
virtual void broadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
void broadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source, uint8_t srcIfIdx) override;
virtual void broadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
virtual void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
void broadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source, uint8_t srcIfIdx) override;
virtual void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override;
// Support only a single physical interface for normal devices
NetworkLayerEntity _netLayerEntities[1];

View File

@ -1,12 +1,15 @@
#include "platform.h"
#include "bits.h"
#include <cstring>
#include <cstdlib>
NvMemoryType Platform::NonVolatileMemoryType()
{
return _memoryType;
}
void Platform::NonVolatileMemoryType(NvMemoryType type)
{
_memoryType = type;
@ -97,3 +100,155 @@ int Platform::readBytesMultiCast(uint8_t *buffer, uint16_t maxLen)
{
return 0;
}
size_t Platform::flashEraseBlockSize()
{
return 0;
}
size_t Platform::flashPageSize()
{
// align to 32bit as default for Eeprom Emulation plattforms
return 4;
}
uint8_t *Platform::userFlashStart()
{
return nullptr;
}
size_t Platform::userFlashSizeEraseBlocks()
{
return 0;
}
void Platform::flashErase(uint16_t eraseBlockNum)
{}
void Platform::flashWritePage(uint16_t pageNumber, uint8_t* data)
{}
uint8_t * Platform::getEepromBuffer(uint16_t size)
{
return nullptr;
}
void Platform::commitToEeprom()
{}
uint8_t* Platform::getNonVolatileMemoryStart()
{
if(_memoryType == Flash)
return userFlashStart();
else
return getEepromBuffer(KNX_FLASH_SIZE);
}
size_t Platform::getNonVolatileMemorySize()
{
if(_memoryType == Flash)
return userFlashSizeEraseBlocks() * flashEraseBlockSize() * flashPageSize();
else
return KNX_FLASH_SIZE;
}
void Platform::commitNonVolatileMemory()
{
if(_memoryType == Flash)
{
if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty)
{
writeBufferedEraseBlock();
free(_eraseblockBuffer);
_eraseblockBuffer = nullptr;
_bufferedEraseblockNumber = -1; // does that make sense?
}
}
else
{
commitToEeprom();
}
}
uint32_t Platform::writeNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size)
{
if(_memoryType == Flash)
{
while (size > 0)
{
loadEraseblockContaining(relativeAddress);
uint32_t start = _bufferedEraseblockNumber * (flashEraseBlockSize() * flashPageSize());
uint32_t end = start + (flashEraseBlockSize() * flashPageSize());
ptrdiff_t offset = relativeAddress - start;
ptrdiff_t length = end - relativeAddress;
if(length > size)
length = size;
memcpy(_eraseblockBuffer + offset, buffer, length);
_bufferedEraseblockDirty = true;
relativeAddress += length;
buffer += length;
size -= length;
}
return relativeAddress;
}
else
{
memcpy(getEepromBuffer(KNX_FLASH_SIZE)+relativeAddress, buffer, size);
return relativeAddress+size;
}
}
void Platform::loadEraseblockContaining(uint32_t relativeAddress)
{
int32_t blockNum = getEraseBlockNumberOf(relativeAddress);
if (blockNum < 0)
{
println("loadEraseblockContaining could not get valid eraseblock number");
fatalError();
}
if (blockNum != _bufferedEraseblockNumber && _bufferedEraseblockNumber >= 0)
writeBufferedEraseBlock();
bufferEraseBlock(blockNum);
}
int32_t Platform::getEraseBlockNumberOf(uint32_t relativeAddress)
{
return relativeAddress / (flashEraseBlockSize() * flashPageSize());
}
void Platform::writeBufferedEraseBlock()
{
if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty)
{
flashErase(_bufferedEraseblockNumber);
for(int i = 0; i < flashEraseBlockSize(); i++)
{
int32_t pageNumber = _bufferedEraseblockNumber * flashEraseBlockSize() + i;
uint8_t *data = _eraseblockBuffer + flashPageSize() * i;
flashWritePage(pageNumber, data);
}
_bufferedEraseblockDirty = false;
}
}
void Platform::bufferEraseBlock(uint32_t eraseBlockNumber)
{
if(_bufferedEraseblockNumber == eraseBlockNumber)
return;
if(_eraseblockBuffer == nullptr)
{
_eraseblockBuffer = (uint8_t*)malloc(flashEraseBlockSize() * flashPageSize());
}
memcpy(_eraseblockBuffer, userFlashStart() + eraseBlockNumber * flashEraseBlockSize() * flashPageSize(), flashEraseBlockSize() * flashPageSize());
_bufferedEraseblockNumber = eraseBlockNumber;
_bufferedEraseblockDirty = false;
}

View File

@ -4,6 +4,11 @@
#include <stddef.h>
#include "save_restore.h"
#ifndef KNX_FLASH_SIZE
#define KNX_FLASH_SIZE 1024
#pragma warning "KNX_FLASH_SIZE not defined, using 1024"
#endif
enum NvMemoryType
{
Eeprom,
@ -49,21 +54,62 @@ class Platform
virtual void setupSpi();
virtual void closeSpi();
virtual int readWriteSpi(uint8_t *data, size_t len);
#if 0
// Flash memory
virtual size_t flashEraseBlockSize(); // in pages
virtual size_t flashPageSize(); // in bytes
virtual uint8_t* userFlashStart(); // start of user flash aligned to start of an erase block
virtual size_t userFlashSizeEraseBlocks(); // in eraseBlocks
virtual void flashErase(uint16_t eraseBlockNum); //relativ to userFlashStart
virtual void flashWritePage(uint16_t pageNumber, uint8_t* data); //write a single page to flash (pageNumber relative to userFashStart
#endif
virtual uint8_t* getEepromBuffer(uint16_t size) = 0;
virtual void commitToEeprom() = 0;
//Memory
// --- Overwrite these methods in the device-plattform to use the EEPROM Emulation API for UserMemory ----
//
// --- changes to the UserMemory are written directly into the address space starting at getEepromBuffer
// --- commitToEeprom must save this to a non-volatile area if neccessary
virtual uint8_t* getEepromBuffer(uint16_t size);
virtual void commitToEeprom();
// -------------------------------------------------------------------------------------------------------
virtual uint8_t* getNonVolatileMemoryStart();
virtual size_t getNonVolatileMemorySize();
virtual void commitNonVolatileMemory();
// address is relative to start of nonvolatile memory
virtual uint32_t writeNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size);
NvMemoryType NonVolatileMemoryType();
void NonVolatileMemoryType(NvMemoryType type);
// --- Overwrite these methods in the device-plattform to use flash memory handling by the knx stack ---
// --- also set _memoryType = Flash in the device-plattform's contructor
// --- optional: overwrite writeBufferedEraseBlock() in the device-plattform to reduce overhead when flashing multiple pages
// size of one flash page in bytes
virtual size_t flashPageSize();
protected:
// size of one EraseBlock in pages
virtual size_t flashEraseBlockSize();
// start of user flash aligned to start of an erase block
virtual uint8_t* userFlashStart();
// size of the user flash in EraseBlocks
virtual size_t userFlashSizeEraseBlocks();
//relativ to userFlashStart
virtual void flashErase(uint16_t eraseBlockNum);
//write a single page to flash (pageNumber relative to userFashStart
virtual void flashWritePage(uint16_t pageNumber, uint8_t* data);
// -------------------------------------------------------------------------------------------------------
NvMemoryType _memoryType = Eeprom;
void loadEraseblockContaining(uint32_t relativeAddress);
int32_t getEraseBlockNumberOf(uint32_t relativeAddress);
// writes _eraseblockBuffer to flash
virtual void writeBufferedEraseBlock();
// copies a EraseBlock into the _eraseblockBuffer
void bufferEraseBlock(uint32_t eraseBlockNumber);
// in theory we would have to use this buffer for memory reads too,
// but because ets always restarts the device after programming it
// we can ignore this issue
uint8_t* _eraseblockBuffer = nullptr;
int32_t _bufferedEraseblockNumber = -1;
bool _bufferedEraseblockDirty = false;
};

View File

@ -27,7 +27,7 @@ class RfDataLinkLayer : public DataLinkLayer
void loop();
void enabled(bool value);
bool enabled() const;
virtual DptMedium mediumType() const override;
DptMedium mediumType() const override;
private:
bool _enabled = false;

View File

@ -679,7 +679,6 @@ void RfPhysicalLayerCC1101::loop()
bytesLeft -= (32 - 1);
pByteIndex += (32 - 1);
}
}
}
@ -719,7 +718,6 @@ void RfPhysicalLayerCC1101::loop()
packetStartTime = millis();
syncStart = true;
}
}
}
break;

View File

@ -26,9 +26,9 @@ class RfPhysicalLayerCC1310 : public RfPhysicalLayer
public:
RfPhysicalLayerCC1310(RfDataLinkLayer& rfDataLinkLayer, Platform& platform);
virtual bool InitChip() override;
virtual void stopChip() override;
virtual void loop() override;
bool InitChip() override;
void stopChip() override;
void loop() override;
void setOutputPowerLevel(int8_t dBm);

View File

@ -34,12 +34,12 @@ public:
bool isRfSbcRoutingEnabled();
bool isIpSbcRoutingEnabled();
virtual void masterReset(EraseCode eraseCode, uint8_t channel) override;
void masterReset(EraseCode eraseCode, uint8_t channel) override;
const uint8_t* restore(const uint8_t* buffer) override;
protected:
virtual void beforeStateChange(LoadState& newState) override;
void beforeStateChange(LoadState& newState) override;
private:
// Function properties

View File

@ -21,7 +21,6 @@ class BusAccessUnit;
*/
class SecureApplicationLayer : public ApplicationLayer
{
public:
/**
* The constructor.
@ -33,31 +32,29 @@ class SecureApplicationLayer : public ApplicationLayer
void groupAddressTable(AddressTableObject& addrTable);
// from transport layer
virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) override;
virtual void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap,
void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) override;
void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap,
APDU& apdu, bool status) override;
virtual void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override;
virtual void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) override;
virtual void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override;
virtual void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) override;
virtual void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override;
virtual void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) override;
virtual void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) override;
virtual void dataConnectedConfirm(uint16_t tsap) override;
void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override;
void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) override;
void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override;
void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) override;
void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override;
void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) override;
void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) override;
void dataConnectedConfirm(uint16_t tsap) override;
void loop();
protected:
// to transport layer
virtual void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) override;
virtual void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override;
virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override;
virtual void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu, const SecurityControl& secCtrl) override;
virtual void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; // apdu must be valid until it was confirmed
void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) override;
void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override;
void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override;
void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu, const SecurityControl& secCtrl) override;
void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; // apdu must be valid until it was confirmed
private:
enum class AddrType : uint8_t
{
group,

View File

@ -490,7 +490,6 @@ uint16_t SecurityInterfaceObject::getNumberOfElements(PropertyID propId)
uint64_t SecurityInterfaceObject::getLastValidSequenceNumber(uint16_t deviceAddr)
{
// Get number of entries for this property
uint16_t numElements = getNumberOfElements(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE);

View File

@ -11,7 +11,7 @@ class SecurityInterfaceObject: public InterfaceObject
public:
SecurityInterfaceObject();
virtual void masterReset(EraseCode eraseCode, uint8_t channel) override;
void masterReset(EraseCode eraseCode, uint8_t channel) override;
bool isSecurityModeEnabled();

View File

@ -6,6 +6,19 @@
#include "callback_property.h"
#include "data_property.h"
BeforeTablesUnloadCallback TableObject::_beforeTablesUnload = 0;
uint8_t TableObject::_tableUnloadCount = 0;
void TableObject::beforeTablesUnloadCallback(BeforeTablesUnloadCallback func)
{
_beforeTablesUnload = func;
}
BeforeTablesUnloadCallback TableObject::beforeTablesUnloadCallback()
{
return _beforeTablesUnload;
}
TableObject::TableObject(Memory& memory)
: _memory(memory)
{}
@ -13,6 +26,19 @@ TableObject::TableObject(Memory& memory)
TableObject::~TableObject()
{}
void TableObject::beforeStateChange(LoadState& newState)
{
if (newState == LS_LOADED && _tableUnloadCount > 0)
_tableUnloadCount--;
if (_tableUnloadCount > 0)
return;
if (newState == LS_UNLOADED) {
_tableUnloadCount++;
if (_beforeTablesUnload != 0)
_beforeTablesUnload();
}
}
LoadState TableObject::loadState()
{
return _state;
@ -82,7 +108,11 @@ bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte)
return false;
if (doFill)
memset(_data, fillByte, size);
{
uint32_t addr = _memory.toRelative(_data);
for(int i = 0; i< size;i++)
_memory.writeMemory(addr+i, 1, &fillByte);
}
_size = size;
@ -139,6 +169,7 @@ void TableObject::loadEventLoading(const uint8_t* data)
case LE_START_LOADING:
break;
case LE_LOAD_COMPLETED:
_memory.saveMemory();
loadState(LS_LOADED);
break;
case LE_UNLOAD:
@ -293,7 +324,6 @@ void TableObject::initializeProperties(size_t propertiesSize, Property** propert
//TODO: missing
// 23 PID_TABLE 3 / (3)
// 27 PID_MCB_TABLE 3 / 3
uint8_t ownPropertiesCount = sizeof(ownProperties) / sizeof(Property*);

View File

@ -3,6 +3,9 @@
#include "interface_object.h"
class Memory;
typedef void (*BeforeTablesUnloadCallback)();
/**
* This class provides common functionality for interface objects that are configured by ETS with MemorWrite.
*/
@ -28,14 +31,18 @@ class TableObject: public InterfaceObject
uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() override;
protected:
static void beforeTablesUnloadCallback(BeforeTablesUnloadCallback func);
static BeforeTablesUnloadCallback beforeTablesUnloadCallback();
protected:
/**
* This method is called before the interface object enters a new ::LoadState.
* If there is a error changing the state newState should be set to ::LS_ERROR and errorCode()
* to a reason for the failure.
*/
virtual void beforeStateChange(LoadState& newState) {}
virtual void beforeStateChange(LoadState& newState);
/**
* returns the internal data of the interface object. This pointer belongs to the TableObject class and
* must not be freed.
@ -47,7 +54,9 @@ class TableObject: public InterfaceObject
void errorCode(ErrorCode errorCode);
void initializeProperties(size_t propertiesSize, Property** properties) override;
static BeforeTablesUnloadCallback _beforeTablesUnload;
private:
uint32_t tableReference();
bool allocTable(uint32_t size, bool doFill, uint8_t fillByte);
@ -68,6 +77,7 @@ class TableObject: public InterfaceObject
LoadState _state = LS_UNLOADED;
Memory& _memory;
uint8_t *_data = 0;
static uint8_t _tableUnloadCount;
/**
* used to store size of data() in allocTable(), needed for calculation of crc in PID_MCB_TABLE.

View File

@ -31,6 +31,9 @@
#define U_STOP_MODE_REQ 0x0E
#define U_EXIT_STOP_MODE_REQ 0x0F
#define U_ACK_REQ 0x10 //-0x17
#define U_ACK_REQ_NACK 0x04
#define U_ACK_REQ_BUSY 0x02
#define U_ACK_REQ_ADRESSED 0x01
#define U_CONFIGURE_REQ 0x18
#define U_INT_REG_WR_REQ 0x28
#define U_INT_REG_RD_REQ 0x38
@ -82,7 +85,6 @@
enum {
TX_IDLE,
TX_FRAME,
TX_WAIT_ECHO,
TX_WAIT_CONN
};
@ -100,10 +102,23 @@ enum {
#define RESET_TIMEOUT 100 //milli seconds
#define TX_TIMEPAUSE 0 // 0 means 1 milli seconds
#define OVERRUN_COUNT 7 //bytes; max. allowed bytes in receive buffer (on start) to see it as overrun
// If this threshold is reached loop() goes into
// "hog mode" where it stays in loop() while L2 address reception
#define HOGMODE_THRESHOLD 3 // milli seconds
void TpUartDataLinkLayer::enterRxWaitEOP()
{
// Flush input
while (_platform.uartAvailable())
{
_platform.readUart();
}
_lastByteRxTime = millis();
_rxState = RX_WAIT_EOP;
}
void TpUartDataLinkLayer::loop()
{
if (!_enabled)
@ -122,7 +137,6 @@ void TpUartDataLinkLayer::loop()
// Loop once and repeat as long we have rx data available
do {
// Signals to communicate from rx part with the tx part
bool isEchoComplete = false; // Flag that a complete echo is received
uint8_t dataConnMsg = 0; // The DATA_CONN message just seen or 0
#ifdef KNX_WAIT_FOR_ADDR
@ -150,6 +164,12 @@ void TpUartDataLinkLayer::loop()
case RX_WAIT_START:
if (_platform.uartAvailable())
{
if (_platform.uartAvailable() > OVERRUN_COUNT)
{
print("input buffer overrun: "); println(_platform.uartAvailable());
enterRxWaitEOP();
break;
}
rxByte = _platform.readUart();
#ifdef DBG_TRACE
print(rxByte, HEX);
@ -258,7 +278,7 @@ void TpUartDataLinkLayer::loop()
if (millis() - _lastByteRxTime > EOPR_TIMEOUT)
{
_rxState = RX_WAIT_START;
print("EOPR inside RX_L_ADDR");
println("EOPR @ RX_L_ADDR");
break;
}
if (!_platform.uartAvailable())
@ -274,27 +294,19 @@ void TpUartDataLinkLayer::loop()
if (_RxByteCnt == 7)
{
//Destination Address + payload available
//check if echo
if (_sendBuffer != nullptr && (!((buffer[0] ^ _sendBuffer[0]) & ~0x20) && !memcmp(buffer + _convert + 1, _sendBuffer + 1, 5)))
{ //ignore repeated bit of control byte
_isEcho = true;
}
else
{
_isEcho = false;
}
//check if echo; ignore repeat bit of control byte
_isEcho = (_sendBuffer != nullptr && (!((buffer[0] ^ _sendBuffer[0]) & ~0x20) && !memcmp(buffer + _convert + 1, _sendBuffer + 1, 5)));
//convert into Extended.ind
if (_convert)
{
uint8_t payloadLength = buffer[6] & 0x0F;
buffer[1] = buffer[6] & 0xF0;
buffer[6] = payloadLength;
buffer[6] &= 0x0F;
}
if (!_isEcho)
{
uint8_t c = 0x10;
uint8_t c = U_ACK_REQ;
// The bau knows everything and could either check the address table object (normal device)
// or any filter tables (coupler) to see if we are addressed.
@ -305,11 +317,11 @@ void TpUartDataLinkLayer::loop()
if (_cb.isAckRequired(addr, isGroupAddress))
{
c |= 0x01;
c |= U_ACK_REQ_ADRESSED;
}
// Hint: We can send directly here, this doesn't disturb other transmissions
// We don't have to update _lastByteTxTime because after ACK the timing is not so tight
// We don't have to update _lastByteTxTime because after U_ACK_REQ the timing is not so tight
_platform.writeUart(c);
}
_rxState = RX_L_DATA;
@ -325,8 +337,8 @@ void TpUartDataLinkLayer::loop()
#endif
if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE)
{
_rxState = RX_WAIT_EOP;
println("invalid telegram size");
enterRxWaitEOP();
}
else
{
@ -336,26 +348,25 @@ void TpUartDataLinkLayer::loop()
if (_RxByteCnt == buffer[6] + 7 + 2)
{
//complete Frame received, payloadLength+1 for TCPI +1 for CRC
//check if crc is correct
if (rxByte == (uint8_t)(~_xorSum))
{
//check if crc is correct
if (_isEcho && _sendBuffer != NULL)
{
//check if it is realy an echo, rx_crc = tx_crc
if (rxByte == _sendBuffer[_sendBufferLength - 1])
_isEcho = true;
else
_isEcho = false;
}
if (_isEcho)
{
isEchoComplete = true;
}
else
if (!_isEcho)
{
_receiveBuffer[0] = 0x29;
_receiveBuffer[1] = 0;
#ifdef DBG_TRACE
unsigned long runTime = millis();
#endif
frameBytesReceived(_receiveBuffer, _RxByteCnt + 2);
#ifdef DBG_TRACE
runTime = millis() - runTime;
if (runTime > (OVERRUN_COUNT*14)/10)
{
// complain when the runtime was long than the OVERRUN_COUNT allows
print("processing received frame took: "); print(runTime); println(" ms");
}
#endif
}
_rxState = RX_WAIT_START;
#ifdef DBG_TRACE
@ -365,7 +376,7 @@ void TpUartDataLinkLayer::loop()
else
{
println("frame with invalid crc ignored");
_rxState = RX_WAIT_EOP;
enterRxWaitEOP();
}
}
else
@ -376,20 +387,18 @@ void TpUartDataLinkLayer::loop()
case RX_WAIT_EOP:
if (millis() - _lastByteRxTime > EOP_TIMEOUT)
{
_RxByteCnt = 0;
// found a gap
_rxState = RX_WAIT_START;
#ifdef DBG_TRACE
println("RX_WAIT_START");
#endif
break;
}
if (!_platform.uartAvailable())
break;
_lastByteRxTime = millis();
rxByte = _platform.readUart();
#ifdef DBG_TRACE
print(rxByte, HEX);
#endif
if (_platform.uartAvailable())
{
_platform.readUart();
_lastByteRxTime = millis();
}
break;
default:
break;
@ -397,8 +406,8 @@ void TpUartDataLinkLayer::loop()
} while (_rxState == RX_L_ADDR && (stayInRx || _platform.uartAvailable()));
// Check for spurios DATA_CONN message
if (dataConnMsg && _txState != TX_WAIT_CONN && _txState != TX_WAIT_ECHO) {
println("got unexpected L_DATA_CON");
if (dataConnMsg && _txState != TX_WAIT_CONN) {
println("unexpected L_DATA_CON");
}
switch (_txState)
@ -419,9 +428,9 @@ void TpUartDataLinkLayer::loop()
if (sendSingleFrameByte() == false)
{
_waitConfirmStartTime = millis();
_txState = TX_WAIT_ECHO;
_txState = TX_WAIT_CONN;
#ifdef DBG_TRACE
println("TX_WAIT_ECHO");
println("TX_WAIT_CONN");
#endif
}
else
@ -430,22 +439,10 @@ void TpUartDataLinkLayer::loop()
}
}
break;
case TX_WAIT_ECHO:
case TX_WAIT_CONN:
if (isEchoComplete)
if (dataConnMsg)
{
_txState = TX_WAIT_CONN;
#ifdef DBG_TRACE
println("TX_WAIT_CONN");
#endif
}
else if (dataConnMsg)
{
bool waitEcho = (_txState == TX_WAIT_ECHO);
if (waitEcho) {
println("L_DATA_CON without echo");
}
dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, !waitEcho && ((dataConnMsg & SUCCESS) > 0));
dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, (dataConnMsg & SUCCESS));
delete[] _sendBuffer;
_sendBuffer = 0;
_sendBufferLength = 0;
@ -579,7 +576,8 @@ DptMedium TpUartDataLinkLayer::mediumType() const
bool TpUartDataLinkLayer::sendSingleFrameByte()
{
uint8_t cmd[2];
uint8_t idx = _TxByteCnt / 64;
uint8_t idx = _TxByteCnt >> 6;
if (_sendBuffer == NULL)
return false;
@ -594,9 +592,9 @@ bool TpUartDataLinkLayer::sendSingleFrameByte()
}
if (_TxByteCnt != _sendBufferLength - 1)
cmd[0] = U_L_DATA_START_CONT_REQ | _TxByteCnt;
cmd[0] = U_L_DATA_START_CONT_REQ | (_TxByteCnt & 0x3F);
else
cmd[0] = U_L_DATA_END_REQ | _TxByteCnt;
cmd[0] = U_L_DATA_END_REQ | (_TxByteCnt & 0x3F);
cmd[1] = _sendBuffer[_TxByteCnt];
#ifdef DBG_TRACE
@ -618,7 +616,6 @@ bool TpUartDataLinkLayer::sendSingleFrameByte()
void TpUartDataLinkLayer::addFrameTxQueue(CemiFrame& frame)
{
_tx_queue_frame_t* tx_frame = new _tx_queue_frame_t;
tx_frame->length = frame.telegramLengthtTP();
tx_frame->data = new uint8_t[tx_frame->length];

View File

@ -27,7 +27,7 @@ class TpUartDataLinkLayer : public DataLinkLayer
void loop();
void enabled(bool value);
bool enabled() const;
virtual DptMedium mediumType() const override;
DptMedium mediumType() const override;
private:
bool _enabled = false;
@ -68,6 +68,7 @@ class TpUartDataLinkLayer : public DataLinkLayer
bool sendFrame(CemiFrame& frame);
void frameBytesReceived(uint8_t* buffer, uint16_t length);
void dataConBytesReceived(uint8_t* buffer, uint16_t length, bool success);
void enterRxWaitEOP();
bool resetChip();
void stopChip();

View File

@ -343,7 +343,6 @@ void TransportLayer::dataIndividualConfirm(AckType ack, uint16_t destination, Ho
A5(destination);
break;
}
}
break;
case Disconnect:

View File

@ -63,7 +63,6 @@ void UsbTunnelInterface::loop()
handleHidReportRxQueue();
rxHaveCompletePacket = false;
}
}
/* USB TX */
@ -136,7 +135,6 @@ void UsbTunnelInterface::loadNextTxFrame(uint8_t** sendBuffer, uint16_t* sendBuf
}
println("");
#endif
}
void UsbTunnelInterface::sendKnxHidReport(ProtocolIdType protId, ServiceIdType servId, uint8_t* data, uint16_t length)

View File

@ -15,35 +15,46 @@
#define ICACHE_RAM_ATTR
#endif
ICACHE_RAM_ATTR void buttonUp()
#ifndef PROG_BTN_PRESS_MIN_MILLIS
#define PROG_BTN_PRESS_MIN_MILLIS 50
#endif
#ifndef PROG_BTN_PRESS_MAX_MILLIS
#define PROG_BTN_PRESS_MAX_MILLIS 500
#endif
ICACHE_RAM_ATTR void buttonEvent()
{
static uint32_t lastpressed=0;
if (millis() - lastpressed > 200){
static uint32_t lastEvent=0;
uint32_t diff = millis() - lastEvent;
if (diff >= PROG_BTN_PRESS_MIN_MILLIS && diff <= PROG_BTN_PRESS_MAX_MILLIS){
knx.toggleProgMode();
lastpressed = millis();
}
lastEvent = millis();
}
#endif
#ifdef ARDUINO_ARCH_SAMD
// predefined global instance for TP or RF or TP/RF coupler
#if MASK_VERSION == 0x07B0
KnxFacade<SamdPlatform, Bau07B0> knx(buttonUp);
KnxFacade<SamdPlatform, Bau07B0> knx(buttonEvent);
#elif MASK_VERSION == 0x27B0
KnxFacade<SamdPlatform, Bau27B0> knx(buttonUp);
KnxFacade<SamdPlatform, Bau27B0> knx(buttonEvent);
#elif MASK_VERSION == 0x2920
KnxFacade<SamdPlatform, Bau2920> knx(buttonUp);
KnxFacade<SamdPlatform, Bau2920> knx(buttonEvent);
#else
#error "Mask version not supported on ARDUINO_ARCH_SAMD"
#endif
#elif defined(ARDUINO_ARCH_RP2040)
// predefined global instance for TP or RF or TP/RF coupler
#if MASK_VERSION == 0x07B0
KnxFacade<RP2040ArduinoPlatform, Bau07B0> knx(buttonUp);
KnxFacade<RP2040ArduinoPlatform, Bau07B0> knx(buttonEvent);
#elif MASK_VERSION == 0x27B0
KnxFacade<RP2040ArduinoPlatform, Bau27B0> knx(buttonUp);
KnxFacade<RP2040ArduinoPlatform, Bau27B0> knx(buttonEvent);
#elif MASK_VERSION == 0x2920
KnxFacade<RP2040ArduinoPlatform, Bau2920> knx(buttonUp);
KnxFacade<RP2040ArduinoPlatform, Bau2920> knx(buttonEvent);
#else
#error "Mask version not supported on ARDUINO_ARCH_RP2040"
#endif
@ -51,11 +62,11 @@
#elif defined(ARDUINO_ARCH_ESP8266)
// predefined global instance for TP or IP or TP/IP coupler
#if MASK_VERSION == 0x07B0
KnxFacade<EspPlatform, Bau07B0> knx(buttonUp);
KnxFacade<EspPlatform, Bau07B0> knx(buttonEvent);
#elif MASK_VERSION == 0x57B0
KnxFacade<EspPlatform, Bau57B0> knx(buttonUp);
KnxFacade<EspPlatform, Bau57B0> knx(buttonEvent);
#elif MASK_VERSION == 0x091A
KnxFacade<EspPlatform, Bau091A> knx(buttonUp);
KnxFacade<EspPlatform, Bau091A> knx(buttonEvent);
#else
#error "Mask version not supported on ARDUINO_ARCH_ESP8266"
#endif
@ -63,18 +74,18 @@
#elif defined(ARDUINO_ARCH_ESP32)
// predefined global instance for TP or IP or TP/IP coupler
#if MASK_VERSION == 0x07B0
KnxFacade<Esp32Platform, Bau07B0> knx(buttonUp);
KnxFacade<Esp32Platform, Bau07B0> knx(buttonEvent);
#elif MASK_VERSION == 0x57B0
KnxFacade<Esp32Platform, Bau57B0> knx(buttonUp);
KnxFacade<Esp32Platform, Bau57B0> knx(buttonEvent);
#elif MASK_VERSION == 0x091A
KnxFacade<Esp32Platform, Bau091A> knx(buttonUp);
KnxFacade<Esp32Platform, Bau091A> knx(buttonEvent);
#else
#error "Mask version not supported on ARDUINO_ARCH_ESP32"
#endif
#elif defined(ARDUINO_ARCH_STM32)
#if MASK_VERSION == 0x07B0
KnxFacade<Stm32Platform, Bau07B0> knx(buttonUp);
KnxFacade<Stm32Platform, Bau07B0> knx(buttonEvent);
#else
#error "Mask version not supported on ARDUINO_ARCH_STM32"
#endif

View File

@ -48,13 +48,20 @@
#else
#if !defined(LED_BUILTIN)
#define LED_BUILTIN 5 // see GPIO_PinConfig gpioPinConfigs[]
#endif
#endif
#include "cc1310_platform.h"
#ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE
extern void buttonUp();
#endif
#endif
#ifndef KNX_LED
#define KNX_LED LED_BUILTIN
#endif
#ifndef KNX_BUTTON
#define KNX_BUTTON 0
#endif
typedef const uint8_t* (*RestoreCallback)(const uint8_t* buffer);
typedef uint8_t* (*SaveCallback)(uint8_t* buffer);
typedef void (*IsrFunctionPtr)();
@ -73,6 +80,7 @@ template <class P, class B> class KnxFacade : private SaveRestore
KnxFacade(B& bau) : _bau(bau)
{
_platformPtr = static_cast<P*>(&bau.platform());
manufacturerId(0xfa);
bauNumber(platform().uniqueSerialNumber());
_bau.addSaveRestore(this);
@ -176,24 +184,7 @@ template <class P, class B> class KnxFacade : private SaveRestore
_progLedOnCallback = progLedOnCallback;
}
/**
* returns RISING if interrupt is created in a rising signal, FALLING otherwise
*/
uint32_t buttonPinInterruptOn()
{
return _buttonPinInterruptOn;
}
/**
* Sets if the programming button creates a RISING or a FALLING signal.
*
* Set to RISING for GPIO--BUTTON--VDD or to FALLING for GPIO--BUTTON--GND
*/
void buttonPinInterruptOn(uint32_t value)
{
_buttonPinInterruptOn = value;
}
uint32_t buttonPin()
{
return _buttonPin;
@ -280,9 +271,9 @@ template <class P, class B> class KnxFacade : private SaveRestore
{
// Workaround for https://github.com/arduino/ArduinoCore-samd/issues/587
#if (ARDUINO_API_VERSION >= 10200)
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, (PinStatus)_buttonPinInterruptOn);
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, (PinStatus)CHANGE);
#else
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, _buttonPinInterruptOn);
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, CHANGE);
#endif
}
@ -400,7 +391,18 @@ template <class P, class B> class KnxFacade : private SaveRestore
void restart(uint16_t individualAddress)
{
_bau.restartRequest(individualAddress);
SecurityControl sc = {false, None};
_bau.restartRequest(individualAddress, sc);
}
void beforeRestartCallback(BeforeRestartCallback func)
{
_bau.beforeRestartCallback(func);
}
BeforeRestartCallback beforeRestartCallback()
{
return _bau.beforeRestartCallback();
}
private:
@ -410,9 +412,8 @@ template <class P, class B> class KnxFacade : private SaveRestore
ProgLedOnCallback _progLedOnCallback = 0;
ProgLedOffCallback _progLedOffCallback = 0;
uint32_t _ledPinActiveOn = LOW;
uint32_t _ledPin = LED_BUILTIN;
uint32_t _buttonPinInterruptOn = RISING;
uint32_t _buttonPin = 0;
uint32_t _ledPin = KNX_LED;
uint32_t _buttonPin = KNX_BUTTON;
SaveCallback _saveCallback = 0;
RestoreCallback _restoreCallback = 0;
volatile bool _toggleProgMode = false;

View File

@ -1,16 +1,22 @@
/*-----------------------------------------------------
Plattform for Raspberry Pi Pico and other RP2040 boards
by SirSydom <com@sirsydom.de> 2021-2022
made to work with arduino-pico - "Raspberry Pi Pico Arduino core, for all RP2040 boards"
by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico V1.11.0
by SirSydom <com@sirsydom.de> 2021-2022
RTTI must be set to enabled in the board options
A maximum of 4kB emulated EEPROM is supported.
For more, use or own emulation (maybe with littlefs)
Uses direct flash reading/writing.
Size ist defined by KNX_FLASH_SIZE (default 4k) - must be a multiple of 4096.
Offset in Flash is defined by KNX_FLASH_OFFSET (default 1,5MiB / 0x180000) - must be a multiple of 4096.
EEPROM Emulation from arduino-pico core (max 4k) can be use by defining USE_RP2040_EEPROM_EMULATION
A RAM-buffered Flash can be use by defining USE_RP2040_LARGE_EEPROM_EMULATION
----------------------------------------------------*/
@ -25,17 +31,37 @@ For more, use or own emulation (maybe with littlefs)
#include <EEPROM.h> // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support
#include <pico/unique_id.h> // from Pico SDK
#include <hardware/watchdog.h> // from Pico SDK
#include <hardware/flash.h> // from Pico SDK
#define FLASHPTR ((uint8_t*)XIP_BASE + KNX_FLASH_OFFSET)
#if KNX_FLASH_SIZE%4096
#error "KNX_FLASH_SIZE must be multiple of 4096"
#endif
#if KNX_FLASH_OFFSET%4096
#error "KNX_FLASH_OFFSET must be multiple of 4096"
#endif
#ifndef KNX_SERIAL
#define KNX_SERIAL Serial1
#endif
RP2040ArduinoPlatform::RP2040ArduinoPlatform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial1)
: ArduinoPlatform(&KNX_SERIAL)
#endif
{
#ifndef USE_RP2040_EEPROM_EMULATION
_memoryType = Flash;
#endif
}
RP2040ArduinoPlatform::RP2040ArduinoPlatform( HardwareSerial* s) : ArduinoPlatform(s)
{
#ifndef USE_RP2040_EEPROM_EMULATION
_memoryType = Flash;
#endif
}
void RP2040ArduinoPlatform::setupUart()
@ -54,7 +80,14 @@ void RP2040ArduinoPlatform::setupUart()
uint32_t RP2040ArduinoPlatform::uniqueSerialNumber()
{
pico_unique_board_id_t id; // 64Bit unique serial number from the QSPI flash
pico_get_unique_board_id(&id);
noInterrupts();
rp2040.idleOtherCore();
flash_get_unique_id(id.id); //pico_get_unique_board_id(&id);
rp2040.resumeOtherCore();
interrupts();
// use lower 4 byte and convert to unit32_t
uint32_t uid = ((uint32_t)(id.id[4]) << 24) | ((uint32_t)(id.id[5]) << 16) | ((uint32_t)(id.id[6]) << 8) | (uint32_t)(id.id[7]);
@ -68,6 +101,47 @@ void RP2040ArduinoPlatform::restart()
watchdog_reboot(0,0,0);
}
#ifdef USE_RP2040_EEPROM_EMULATION
#pragma warning "Using EEPROM Simulation"
#ifdef USE_RP2040_LARGE_EEPROM_EMULATION
uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint16_t size)
{
if(size%4096)
{
println("KNX_FLASH_SIZE must be a multiple of 4096");
fatalError();
}
if(!_rambuff_initialized)
{
memcpy(_rambuff, FLASHPTR, KNX_FLASH_SIZE);
_rambuff_initialized = true;
}
return _rambuff;
}
void RP2040ArduinoPlatform::commitToEeprom()
{
noInterrupts();
rp2040.idleOtherCore();
//ToDo: write block-by-block to prevent writing of untouched blocks
if(memcmp(_rambuff, FLASHPTR, KNX_FLASH_SIZE))
{
flash_range_erase (KNX_FLASH_OFFSET, KNX_FLASH_SIZE);
flash_range_program(KNX_FLASH_OFFSET, _rambuff, KNX_FLASH_SIZE);
}
rp2040.resumeOtherCore();
interrupts();
}
#else
uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint16_t size)
{
if(size > 4096)
@ -91,6 +165,73 @@ void RP2040ArduinoPlatform::commitToEeprom()
{
EEPROM.commit();
}
#endif
#else
size_t RP2040ArduinoPlatform::flashEraseBlockSize()
{
return 16; // 16 pages x 256byte/page = 4096byte
}
size_t RP2040ArduinoPlatform::flashPageSize()
{
return 256;
}
uint8_t* RP2040ArduinoPlatform::userFlashStart()
{
return (uint8_t*)XIP_BASE + KNX_FLASH_OFFSET;
}
size_t RP2040ArduinoPlatform::userFlashSizeEraseBlocks()
{
if(KNX_FLASH_SIZE <= 0)
return 0;
else
return ( (KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1;
}
void RP2040ArduinoPlatform::flashErase(uint16_t eraseBlockNum)
{
noInterrupts();
rp2040.idleOtherCore();
flash_range_erase (KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
rp2040.resumeOtherCore();
interrupts();
}
void RP2040ArduinoPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data)
{
noInterrupts();
rp2040.idleOtherCore();
flash_range_program(KNX_FLASH_OFFSET + pageNumber * flashPageSize(), data, flashPageSize());
rp2040.resumeOtherCore();
interrupts();
}
void RP2040ArduinoPlatform::writeBufferedEraseBlock()
{
if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty)
{
noInterrupts();
rp2040.idleOtherCore();
flash_range_erase (KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
flash_range_program(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize());
rp2040.resumeOtherCore();
interrupts();
_bufferedEraseblockDirty = false;
}
}
#endif
#endif

View File

@ -4,6 +4,16 @@
#ifdef ARDUINO_ARCH_RP2040
#ifndef KNX_FLASH_OFFSET
#define KNX_FLASH_OFFSET 0x180000 // 1.5MiB
#pragma warning "KNX_FLASH_OFFSET not defined, using 0x180000"
#endif
#ifdef USE_RP2040_LARGE_EEPROM_EMULATION
#define USE_RP2040_EEPROM_EMULATION
#endif
class RP2040ArduinoPlatform : public ArduinoPlatform
{
public:
@ -13,11 +23,36 @@ public:
void setupUart();
// unique serial number
uint32_t uniqueSerialNumber() override;
uint32_t uniqueSerialNumber() override;
void restart();
#ifdef USE_RP2040_EEPROM_EMULATION
uint8_t* getEepromBuffer(uint16_t size);
void commitToEeprom();
#ifdef USE_RP2040_LARGE_EEPROM_EMULATION
uint8_t _rambuff[KNX_FLASH_SIZE];
bool _rambuff_initialized = false;
#endif
#else
// size of one EraseBlock in pages
virtual size_t flashEraseBlockSize();
// size of one flash page in bytes
virtual size_t flashPageSize();
// start of user flash aligned to start of an erase block
virtual uint8_t* userFlashStart();
// size of the user flash in EraseBlocks
virtual size_t userFlashSizeEraseBlocks();
//relativ to userFlashStart
virtual void flashErase(uint16_t eraseBlockNum);
//write a single page to flash (pageNumber relative to userFashStart
virtual void flashWritePage(uint16_t pageNumber, uint8_t* data);
// writes _eraseblockBuffer to flash - overrides Plattform::writeBufferedEraseBlock() for performance optimization only
void writeBufferedEraseBlock();
#endif
};
#endif

View File

@ -4,17 +4,33 @@
#include <knx/bits.h>
#include <Arduino.h>
#ifdef USE_SAMD_EEPROM_EMULATION
#include <FlashAsEEPROM.h>
#endif
#if KNX_FLASH_SIZE % 1024
#error "KNX_FLASH_SIZE must be multiple of 1024"
#endif
#ifndef KNX_SERIAL
#define KNX_SERIAL Serial1
#endif
SamdPlatform::SamdPlatform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial1)
: ArduinoPlatform(&KNX_SERIAL)
#endif
{
#ifndef USE_SAMD_EEPROM_EMULATION
init();
#endif
}
SamdPlatform::SamdPlatform( HardwareSerial* s) : ArduinoPlatform(s)
{
#ifndef USE_SAMD_EEPROM_EMULATION
init();
#endif
}
uint32_t SamdPlatform::uniqueSerialNumber()
@ -43,7 +59,9 @@ void SamdPlatform::restart()
NVIC_SystemReset();
}
uint8_t * SamdPlatform::getEepromBuffer(uint16_t size)
#ifdef USE_SAMD_EEPROM_EMULATION
#pragma warning "Using EEPROM Simulation"
uint8_t* SamdPlatform::getEepromBuffer(uint16_t size)
{
//EEPROM.begin(size);
if(size > EEPROM_EMULATION_SIZE)
@ -56,6 +74,156 @@ void SamdPlatform::commitToEeprom()
{
EEPROM.commit();
}
#else
extern uint32_t __etext;
extern uint32_t __data_start__;
extern uint32_t __data_end__;
static const uint32_t pageSizes[] = {8, 16, 32, 64, 128, 256, 512, 1024};
void SamdPlatform::init()
{
_memoryType = Flash;
_pageSize = pageSizes[NVMCTRL->PARAM.bit.PSZ];
_pageCnt = NVMCTRL->PARAM.bit.NVMP;
_rowSize = PAGES_PER_ROW * _pageSize;
// find end of program flash and set limit to next row
uint32_t endEddr = (uint32_t)(&__etext + (&__data_end__ - &__data_start__)); // text + data MemoryBlock
_MemoryStart = getRowAddr(_pageSize * _pageCnt - KNX_FLASH_SIZE - 1); // 23295
_MemoryEnd = getRowAddr(_pageSize * _pageCnt - 1);
// chosen flash size is not available anymore
if (_MemoryStart < endEddr) {
println("KNX_FLASH_SIZE is not available (possible too much flash use by firmware)");
fatalError();
}
}
size_t SamdPlatform::flashEraseBlockSize()
{
return PAGES_PER_ROW;
}
size_t SamdPlatform::flashPageSize()
{
return _pageSize;
}
uint8_t* SamdPlatform::userFlashStart()
{
return (uint8_t*)_MemoryStart;
}
size_t SamdPlatform::userFlashSizeEraseBlocks()
{
if (KNX_FLASH_SIZE <= 0)
return 0;
else
return ((KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1;
}
void SamdPlatform::flashErase(uint16_t eraseBlockNum)
{
noInterrupts();
eraseRow((void *)(_MemoryStart + eraseBlockNum * _rowSize));
// flash_range_erase(KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
interrupts();
}
void SamdPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data)
{
noInterrupts();
write((void *)(_MemoryStart + pageNumber * _pageSize), data, _pageSize);
// flash_range_program(KNX_FLASH_OFFSET + pageNumber * flashPageSize(), data, flashPageSize());
interrupts();
}
void SamdPlatform::writeBufferedEraseBlock()
{
if (_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty)
{
noInterrupts();
eraseRow((void *)(_MemoryStart + _bufferedEraseblockNumber * _rowSize));
write((void *)(_MemoryStart + _bufferedEraseblockNumber * _rowSize), _eraseblockBuffer, _rowSize);
// flash_range_erase(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
// flash_range_program(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize());
interrupts();
_bufferedEraseblockDirty = false;
}
}
uint32_t SamdPlatform::getRowAddr(uint32_t flasAddr)
{
return flasAddr & ~(_rowSize - 1);
}
void SamdPlatform::write(const volatile void *flash_ptr, const void *data, uint32_t size)
{
// Calculate data boundaries
size = (size + 3) / 4;
volatile uint32_t *src_addr = (volatile uint32_t *)data;
volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr;
// volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr;
// const uint8_t *src_addr = (uint8_t *)data;
// Disable automatic page write
NVMCTRL->CTRLB.bit.MANW = 1;
// Do writes in pages
while (size)
{
// Execute "PBC" Page Buffer Clear
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
while (NVMCTRL->INTFLAG.bit.READY == 0)
{
}
// Fill page buffer
uint32_t i;
for (i = 0; i < (_pageSize / 4) && size; i++)
{
*dst_addr = *src_addr;
src_addr++;
dst_addr++;
size--;
}
// Execute "WP" Write Page
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
while (NVMCTRL->INTFLAG.bit.READY == 0)
{
}
}
}
void SamdPlatform::erase(const volatile void *flash_ptr, uint32_t size)
{
const uint8_t *ptr = (const uint8_t *)flash_ptr;
while (size > _rowSize)
{
eraseRow(ptr);
ptr += _rowSize;
size -= _rowSize;
}
eraseRow(ptr);
}
void SamdPlatform::eraseRow(const volatile void *flash_ptr)
{
NVMCTRL->ADDR.reg = ((uint32_t)flash_ptr) / 2;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
while (!NVMCTRL->INTFLAG.bit.READY)
{
}
}
#endif
#endif

View File

@ -4,6 +4,8 @@
#ifdef ARDUINO_ARCH_SAMD
#define PAGES_PER_ROW 4
class SamdPlatform : public ArduinoPlatform
{
public:
@ -14,8 +16,40 @@ public:
uint32_t uniqueSerialNumber() override;
void restart();
#ifdef USE_SAMD_EEPROM_EMULATION
uint8_t* getEepromBuffer(uint16_t size);
void commitToEeprom();
#else
// size of one EraseBlock in pages
virtual size_t flashEraseBlockSize();
// size of one flash page in bytes
virtual size_t flashPageSize();
// start of user flash aligned to start of an erase block
virtual uint8_t* userFlashStart();
// size of the user flash in EraseBlocks
virtual size_t userFlashSizeEraseBlocks();
// relativ to userFlashStart
virtual void flashErase(uint16_t eraseBlockNum);
// write a single page to flash (pageNumber relative to userFashStart
virtual void flashWritePage(uint16_t pageNumber, uint8_t* data);
// writes _eraseblockBuffer to flash - overrides Plattform::writeBufferedEraseBlock() for performance optimization only
void writeBufferedEraseBlock();
private:
void init();
uint32_t _MemoryEnd = 0;
uint32_t _MemoryStart = 0;
uint32_t _pageSize;
uint32_t _rowSize;
uint32_t _pageCnt;
uint32_t getRowAddr(uint32_t flasAddr);
void write(const volatile void* flash_ptr, const void* data, uint32_t size);
void erase(const volatile void* flash_ptr, uint32_t size);
void eraseRow(const volatile void* flash_ptr);
#endif
};
#endif

View File

@ -4,9 +4,13 @@
#include <EEPROM.h>
#include "knx/bits.h"
#ifndef KNX_SERIAL
#define KNX_SERIAL Serial2
#endif
Stm32Platform::Stm32Platform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial2)
: ArduinoPlatform(&KNX_SERIAL)
#endif
{
}
@ -32,16 +36,21 @@ void Stm32Platform::restart()
uint8_t * Stm32Platform::getEepromBuffer(uint16_t size)
{
if (size > E2END + 1)
// check if the buffer already exists
if (_eepromPtr == nullptr) // we need to initialize the buffer first
{
fatalError();
if (size > E2END + 1)
{
fatalError();
}
_eepromSize = size;
_eepromPtr = new uint8_t[size];
eeprom_buffer_fill();
for (uint16_t i = 0; i < size; ++i)
_eepromPtr[i] = eeprom_buffered_read_byte(i);
}
_eepromSize = size;
delete [] _eepromPtr;
_eepromPtr = new uint8_t[size];
eeprom_buffer_fill();
for (uint16_t i = 0; i < size; ++i)
_eepromPtr[i] = eeprom_buffered_read_byte(i);
return _eepromPtr;
}
@ -51,6 +60,11 @@ void Stm32Platform::commitToEeprom()
return;
for (uint16_t i = 0; i < _eepromSize; ++i)
eeprom_buffered_write_byte(i, _eepromPtr[i]);
// For some GD32 chips, the flash needs to be unlocked twice
// and the first call will fail. If the first call is
// successful, the second one (inside eeprom_buffer_flush)
// does nothing.
HAL_FLASH_Unlock();
eeprom_buffer_flush();
}