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 - 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 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 - name: Show toolchain version
run: arm-none-eabi-gcc --version run: arm-none-eabi-gcc --version
@ -36,8 +36,8 @@ jobs:
# access regardless of the host operating system # access regardless of the host operating system
shell: bash shell: bash
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
# Note the current convention is to use the -S and -B options here to specify source # 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. # 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 # 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 run: cmake $GITHUB_WORKSPACE/examples/knx-cc1310 -DCMAKE_BUILD_TYPE=$BUILD_TYPE
@ -50,6 +50,6 @@ jobs:
# - name: Test # - name: Test
# working-directory: ${{runner.workspace}}/build # working-directory: ${{runner.workspace}}/build
# shell: bash # 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 # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
# run: ctest -C $BUILD_TYPE # run: ctest -C $BUILD_TYPE

View File

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

View File

@ -47,7 +47,7 @@ jobs:
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # 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. # 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 # 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. 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/). Generated documentation can be found [here](https://knx.readthedocs.io/en/latest/).
## Stack configuration possibilities ## 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 framework = arduino
lib_deps = lib_deps =
SPI SPI
https://github.com/thelsing/FlashStorage.git
knx knx
build_flags = build_flags =

View File

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

View File

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

View File

@ -24,7 +24,6 @@ lib_extra_dirs = ../../../
lib_deps = lib_deps =
SPI SPI
https://github.com/thelsing/FlashStorage.git
knx knx
build_flags = build_flags =
@ -100,3 +99,50 @@ lib_deps =
build_flags = build_flags =
-DMASK_VERSION=0x07B0 -DMASK_VERSION=0x07B0
-Wno-unknown-pragmas -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 = lib_deps =
SPI SPI
Adafruit TinyUSB Library@0.7.1 Adafruit TinyUSB Library@0.7.1
https://github.com/thelsing/FlashStorage.git
knx knx
build_flags = 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 #endif
#ifndef KNX_NO_PRINT #ifndef KNX_NO_PRINT
Stream* ArduinoPlatform::SerialDebug = &Serial; Stream* ArduinoPlatform::SerialDebug = &KNX_DEBUG_SERIAL;
#endif #endif
ArduinoPlatform::ArduinoPlatform() : _knxSerial(nullptr) ArduinoPlatform::ArduinoPlatform() : _knxSerial(nullptr)
@ -22,13 +22,13 @@ void ArduinoPlatform::fatalError()
{ {
while (true) while (true)
{ {
#ifdef LED_BUILTIN #ifdef KNX_LED
static const long LED_BLINK_PERIOD = 200; static const long LED_BLINK_PERIOD = 200;
if ((millis() % LED_BLINK_PERIOD) > (LED_BLINK_PERIOD / 2)) if ((millis() % LED_BLINK_PERIOD) > (LED_BLINK_PERIOD / 2))
digitalWrite(LED_BUILTIN, HIGH); digitalWrite(KNX_LED, HIGH);
else else
digitalWrite(LED_BUILTIN, LOW); digitalWrite(KNX_LED, LOW);
#endif #endif
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,8 @@ AddressTableObject::AddressTableObject(Memory& memory)
uint16_t AddressTableObject::entryCount() 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 0;
return ntohs(_groupAddresses[0]); return ntohs(_groupAddresses[0]);
@ -104,6 +105,7 @@ bool AddressTableObject::contains(uint16_t addr)
void AddressTableObject::beforeStateChange(LoadState& newState) void AddressTableObject::beforeStateChange(LoadState& newState)
{ {
TableObject::beforeStateChange(newState);
if (newState != LS_LOADED) if (newState != LS_LOADED)
return; return;

View File

@ -50,7 +50,7 @@ class AddressTableObject : public TableObject
bool contains(uint16_t groupAddress); bool contains(uint16_t groupAddress);
protected: protected:
virtual void beforeStateChange(LoadState& newState) override; void beforeStateChange(LoadState& newState) override;
private: private:
uint16_t* _groupAddresses = 0; 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) void ApplicationLayer::dataConnectedConfirm(uint16_t tsap, const SecurityControl& secCtrl)
{ {
//FIXME: implement dataConnectedConfirm DataSecurity
} }
#pragma endregion #pragma endregion
void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl) 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 #pragma endregion
protected: protected:
#pragma region hooks #pragma region hooks
void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl &secCtrl); 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, 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) void AssociationTableObject::beforeStateChange(LoadState& newState)
{ {
TableObject::beforeStateChange(newState);
if (newState != LS_LOADED) if (newState != LS_LOADED)
return; return;

View File

@ -338,3 +338,12 @@ void BusAccessUnit::propertyValueWrite(ObjectType objectType, uint8_t objectInst
uint8_t* data, uint32_t length) 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 "knx_types.h"
#include "interface_object.h" #include "interface_object.h"
typedef void (*BeforeRestartCallback)(void);
class BusAccessUnit class BusAccessUnit
{ {
public: public:
@ -161,4 +163,6 @@ class BusAccessUnit
virtual void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, virtual void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex, uint8_t& numberOfElements, uint16_t startIndex,
uint8_t* data, uint32_t length); 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: public:
Bau07B0(Platform& platform); Bau07B0(Platform& platform);
virtual void loop() override; void loop() override;
virtual bool enabled() override; bool enabled() override;
virtual void enabled(bool value) override; void enabled(bool value) override;
protected: protected:
InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
// For TP1 only // For TP1 only
virtual bool isAckRequired(uint16_t address, bool isGrpAddr) override; bool isAckRequired(uint16_t address, bool isGrpAddr) override;
private: private:
TpUartDataLinkLayer _dlLayer; TpUartDataLinkLayer _dlLayer;

View File

@ -14,18 +14,18 @@ class Bau091A : public BauSystemBCoupler, public ITpUartCallBacks
{ {
public: public:
Bau091A(Platform& platform); Bau091A(Platform& platform);
virtual void loop() override; void loop() override;
virtual bool enabled() override; bool enabled() override;
virtual void enabled(bool value) override; void enabled(bool value) override;
protected: protected:
InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
// For TP1 only // 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: private:
RouterObject _routerObj; RouterObject _routerObj;
IpParameterObject _ipParameters; IpParameterObject _ipParameters;

View File

@ -18,15 +18,15 @@ class Bau27B0 : public BauSystemBDevice
{ {
public: public:
Bau27B0(Platform& platform); Bau27B0(Platform& platform);
virtual void loop() override; void loop() override;
virtual bool enabled() override; bool enabled() override;
virtual void enabled(bool value) override; void enabled(bool value) override;
protected: protected:
InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); 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: private:
RfDataLinkLayer _dlLayer; RfDataLinkLayer _dlLayer;
RfMediumObject _rfMediumObj; RfMediumObject _rfMediumObj;

View File

@ -18,15 +18,15 @@ class Bau2920 : public BauSystemBCoupler
{ {
public: public:
Bau2920(Platform& platform); Bau2920(Platform& platform);
virtual void loop() override; void loop() override;
virtual bool enabled() override; bool enabled() override;
virtual void enabled(bool value) override; void enabled(bool value) override;
protected: protected:
InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); 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: private:
RouterObject _rtObjPrimary; RouterObject _rtObjPrimary;
RouterObject _rtObjSecondary; RouterObject _rtObjSecondary;

View File

@ -12,15 +12,15 @@ class Bau57B0 : public BauSystemBDevice
{ {
public: public:
Bau57B0(Platform& platform); Bau57B0(Platform& platform);
virtual void loop() override; void loop() override;
virtual bool enabled() override; bool enabled() override;
virtual void enabled(bool value) override; void enabled(bool value) override;
protected: protected:
InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); 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: private:
IpParameterObject _ipParameters; IpParameterObject _ipParameters;
IpDataLinkLayer _dlLayer; IpDataLinkLayer _dlLayer;

View File

@ -31,6 +31,11 @@ void BauSystemB::writeMemory()
_memory.writeMemory(); _memory.writeMemory();
} }
Platform& BauSystemB::platform()
{
return _platform;
}
ApplicationProgramObject& BauSystemB::parameters() ApplicationProgramObject& BauSystemB::parameters()
{ {
return _appProgram; return _appProgram;
@ -112,9 +117,14 @@ void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType,
uint16_t memoryAddress, uint8_t * data) uint16_t memoryAddress, uint8_t * data)
{ {
_memory.writeMemory(memoryAddress, number, data); _memory.writeMemory(memoryAddress, number, data);
if (_deviceObj.verifyMode()) 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, 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) if (restartType == RestartType::BasicRestart)
{ {
println("Basic restart requested"); println("Basic restart requested");
if (_beforeRestart != 0)
_beforeRestart();
} }
else if (restartType == RestartType::MasterReset) else if (restartType == RestartType::MasterReset)
{ {
@ -606,3 +618,23 @@ Memory& BauSystemB::memory()
{ {
return _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 bool enabled() = 0;
virtual void enabled(bool value) = 0; virtual void enabled(bool value) = 0;
Platform& platform();
ApplicationProgramObject& parameters(); ApplicationProgramObject& parameters();
DeviceObject& deviceObject(); DeviceObject& deviceObject();
@ -38,6 +39,10 @@ class BauSystemB : protected BusAccessUnit
void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex, uint8_t& numberOfElements, uint16_t startIndex,
uint8_t* data, uint32_t length) override; uint8_t* data, uint32_t length) override;
void versionCheckCallback(VersionCheckCallback func);
VersionCheckCallback versionCheckCallback();
void beforeRestartCallback(BeforeRestartCallback func);
BeforeRestartCallback beforeRestartCallback();
protected: protected:
virtual ApplicationLayer& applicationLayer() = 0; virtual ApplicationLayer& applicationLayer() = 0;
@ -48,6 +53,8 @@ class BauSystemB : protected BusAccessUnit
uint16_t memoryAddress, uint8_t* data) override; uint16_t memoryAddress, uint8_t* data) override;
void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress) override; 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, void memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* data) override; uint32_t memoryAddress, uint8_t* data) override;
void memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, 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; RestartState _restartState = Idle;
SecurityControl _restartSecurity; SecurityControl _restartSecurity;
uint32_t _restartDelay = 0; uint32_t _restartDelay = 0;
BeforeRestartCallback _beforeRestart = 0;
}; };

View File

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

View File

@ -18,13 +18,13 @@ class BauSystemBCoupler : public BauSystemB
{ {
public: public:
BauSystemBCoupler(Platform& platform); BauSystemBCoupler(Platform& platform);
virtual void loop() override; void loop() override;
virtual bool configured() override; bool configured() override;
protected: 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; Platform& _platform;

View File

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

View File

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

View File

@ -124,21 +124,19 @@ uint64_t sixBytesToUInt64(uint8_t* data)
uint16_t crc16Ccitt(uint8_t* input, uint16_t length) uint16_t crc16Ccitt(uint8_t* input, uint16_t length)
{ {
uint32_t polynom = 0x1021; uint32_t polynom = 0x1021;
uint8_t padded[length+2];
memcpy(padded, input, length); uint32_t result = 0xffff;
memset(padded+length, 0x00, 2); for (uint32_t i = 0; i < 8 * ((uint32_t)length + 2); i++)
{
uint32_t result = 0xffff; result <<= 1;
for (uint32_t i = 0; i < 8 * (uint32_t)sizeof(padded); i++) { uint32_t nextBit;
result <<= 1; nextBit = ((i / 8) < length) ? ((input[i / 8] >> (7 - (i % 8))) & 0x1) : 0;
uint32_t nextBit = (padded[i / 8] >> (7 - (i % 8))) & 0x1; result |= nextBit;
result |= nextBit; if ((result & 0x10000) != 0)
if ((result & 0x10000) != 0) result ^= polynom;
result ^= polynom; }
} return result & 0xffff;
return result & 0xffff;
} }
uint16_t crc16Dnp(uint8_t* input, uint16_t length) 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) : 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) if (count == 0 || _readCallback == nullptr || start > _maxElements || start + count > _maxElements + 1)
return 0; return 0;
return _readCallback(_interfaceObject, start, count, data); 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) if (count == 0 || start > _maxElements || start + count > _maxElements + 1 || _writeCallback == nullptr)
return 0; 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, 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, uint32_t value);
DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, const uint8_t* value); DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, const uint8_t* value);
virtual ~DataProperty() override; ~DataProperty() override;
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;
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;
virtual uint8_t* save(uint8_t* buffer) override; uint8_t* save(uint8_t* buffer) override;
virtual const uint8_t* restore(const uint8_t* buffer) override; const uint8_t* restore(const uint8_t* buffer) override;
virtual uint16_t saveSize() override; uint16_t saveSize() override;
const uint8_t* data(); const uint8_t* data();
const uint8_t* data(uint16_t elementIndex); const uint8_t* data(uint16_t elementIndex);

View File

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

View File

@ -7,6 +7,10 @@
class DeviceObject: public InterfaceObject class DeviceObject: public InterfaceObject
{ {
public: public:
// increase this version anytime DeviceObject-API changes
// the following value represents the serialized representation of DeviceObject.
const uint16_t apiVersion = 1;
DeviceObject(); DeviceObject();
uint8_t* save(uint8_t* buffer) override; uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const 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) 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) if (datatype.mainGroup == 1 && datatype.subGroup >= 1 && datatype.subGroup <= 23 && datatype.subGroup != 20 && !datatype.index)
return valueToBusValueBinary(value, payload, payload_length, datatype); return valueToBusValueBinary(value, payload, payload_length, datatype);
// DPT 2.* - Binary Control // DPT 2.* - Binary Control
@ -1553,7 +1552,6 @@ int valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_
case 1: // Mask bits case 1: // Mask bits
unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0x0f); unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0x0f);
break; break;
} }
return true; return true;
} }
@ -1757,7 +1755,7 @@ void float16ToPayload(uint8_t* payload, size_t payload_length, int index, double
if (wasNegative) if (wasNegative)
mantissa *= -1; mantissa *= -1;
println(mantissa); // println(mantissa);
signed16ToPayload(payload, payload_length, index, mantissa, mask); signed16ToPayload(payload, payload_length, index, mantissa, mask);
unsigned8ToPayload(payload, payload_length, index, exponent << 3, 0x78 & (mask >> 8)); 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 */ /* 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; 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; 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 ) if (length == 0 || _commandCallback == nullptr )
{ {
@ -34,7 +34,7 @@ template <class T> class FunctionProperty : public Property
_commandCallback(_interfaceObject, data, length, resultData, resultLength); _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 ) if (length == 0 || _stateCallback == nullptr )
{ {

View File

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

View File

@ -14,7 +14,8 @@ enum ComFlag
WriteRequest = 2, //!< Write was requested but was not processed WriteRequest = 2, //!< Write was requested but was not processed
Transmitting = 3, //!< Group Object is processed a the moment (read or write) Transmitting = 3, //!< Group Object is processed a the moment (read or write)
Ok = 4, //!< read or write request were send successfully 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; class GroupObject;
@ -235,7 +236,7 @@ class GroupObject
size_t asapValueSize(uint8_t code); size_t asapValueSize(uint8_t code);
size_t goSize(); size_t goSize();
uint16_t _asap = 0; uint16_t _asap = 0;
ComFlag _commFlag = Ok; ComFlag _commFlag = Uninitialized;
uint8_t* _data = 0; uint8_t* _data = 0;
uint8_t _dataLength = 0; uint8_t _dataLength = 0;
#ifndef SMALL_GROUPOBJECT #ifndef SMALL_GROUPOBJECT

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,73 +1,115 @@
#include "memory.h" #include "memory.h"
#include <string.h> #include <string.h>
#include "bits.h" #include "bits.h"
Memory::Memory(Platform& platform, DeviceObject& deviceObject) Memory::Memory(Platform& platform, DeviceObject& deviceObject)
: _platform(platform), _deviceObject(deviceObject) : _platform(platform), _deviceObject(deviceObject)
{ {}
}
Memory::~Memory()
{}
void Memory::readMemory() void Memory::readMemory()
{ {
println("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; return;
}
uint16_t flashSize = KNX_FLASH_SIZE; printHex("RESTORED ", flashStart, _metadataSize);
_data = _platform.getEepromBuffer(flashSize);
printHex("RESTORED ", _data, _metadataSize);
uint16_t metadataBlockSize = alignToPageSize(_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; uint16_t manufacturerId = 0;
const uint8_t* buffer = popWord(manufacturerId, _data); buffer = popWord(manufacturerId, buffer);
uint8_t hardwareType[LEN_HARDWARE_TYPE] = {0}; uint8_t hardwareType[LEN_HARDWARE_TYPE] = {0};
buffer = popByteArray(hardwareType, LEN_HARDWARE_TYPE, buffer); buffer = popByteArray(hardwareType, LEN_HARDWARE_TYPE, buffer);
uint16_t version = 0; uint16_t version = 0;
buffer = popWord(version, buffer); buffer = popWord(version, buffer);
VersionCheckResult versionCheck = FlashAllInvalid;
if (_deviceObject.manufacturerId() != manufacturerId // first check correct format of deviceObject-API
|| _deviceObject.version() != version if (_deviceObject.apiVersion == apiVersion)
|| memcmp(_deviceObject.hardwareType(), hardwareType, LEN_HARDWARE_TYPE) != 0)
{ {
println("saved memory doesn't match manufacturerId, version or hardwaretype"); if (_versionCheckCallback != 0) {
print("manufacturerId: "); versionCheck = _versionCheckCallback(manufacturerId, hardwareType, version);
print(manufacturerId, HEX); // callback should provide infomation about version check failure reasons
print(" "); }
println(_deviceObject.manufacturerId(), HEX); else if (_deviceObject.manufacturerId() == manufacturerId &&
print("version: "); memcmp(_deviceObject.hardwareType(), hardwareType, LEN_HARDWARE_TYPE) == 0)
print(version, HEX); {
print(" "); if (_deviceObject.version() == version) {
println(_deviceObject.version(), HEX); versionCheck = FlashValid;
print("hardwareType: "); }
printHex("", hardwareType, LEN_HARDWARE_TYPE); else
print(" "); {
printHex("", _deviceObject.hardwareType(), LEN_HARDWARE_TYPE); 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; return;
} }
println("manufacturerId, version and hardwareType matches"); println("restoring data from flash...");
print("saverestores "); print("saverestores ");
println(_saveCount); println(_saveCount);
for (int i = 0; i < _saveCount; i++) for (int i = 0; i < _saveCount; i++)
{ {
println(_data - buffer); println(flashStart - buffer);
println("."); println(".");
buffer = _saveRestores[i]->restore(buffer); buffer = _saveRestores[i]->restore(buffer);
} }
println("restored saveRestores"); println("restored saveRestores");
if (versionCheck == FlashTablesInvalid)
{
println("TableObjects are referring to an older firmware version and are not loaded");
return;
}
print("tableObjs "); print("tableObjs ");
println(_tableObjCount); println(_tableObjCount);
for (int i = 0; i < _tableObjCount; i++) for (int i = 0; i < _tableObjCount; i++)
{ {
println(_data - buffer); println(flashStart - buffer);
println("."); println(".");
buffer = _tableObjects[i]->restore(buffer); buffer = _tableObjects[i]->restore(buffer);
uint16_t memorySize = 0; uint16_t memorySize = 0;
@ -84,48 +126,62 @@ void Memory::readMemory()
void Memory::writeMemory() void Memory::writeMemory()
{ {
uint8_t* buffer = _data; // first get the necessary size of the writeBuffer
buffer = pushWord(_deviceObject.manufacturerId(), buffer); size_t writeBufferSize = _metadataSize;
buffer = pushByteArray(_deviceObject.hardwareType(), LEN_HARDWARE_TYPE, buffer); for (int i = 0; i < _saveCount; i++)
buffer = pushWord(_deviceObject.version(), buffer); 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 "); print("save saveRestores ");
println(_saveCount); println(_saveCount);
for (int i = 0; i < _saveCount; i++) for (int i = 0; i < _saveCount; i++)
{ {
println(_data - buffer); bufferPos = _saveRestores[i]->save(buffer);
println("."); flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
//println((long)_saveRestores[i], HEX);
buffer = _saveRestores[i]->save(buffer);
} }
print("save tableobjs "); print("save tableobjs ");
println(_tableObjCount); println(_tableObjCount);
for (int i = 0; i < _tableObjCount; i++) for (int i = 0; i < _tableObjCount; i++)
{ {
println(_data - buffer); bufferPos = _tableObjects[i]->save(buffer);
println(".");
//println((long)_tableObjects[i], HEX);
buffer = _tableObjects[i]->save(buffer);
//save to size of the memoryblock for tableobject too, so that we can rebuild the usedList and freeList //save to size of the memoryblock for tableobject too, so that we can rebuild the usedList and freeList
if (_tableObjects[i]->_data != nullptr) if (_tableObjects[i]->_data != nullptr)
{ {
MemoryBlock* block = findBlockInList(_usedList, _tableObjects[i]->_data); MemoryBlock* block = findBlockInList(_usedList, _tableObjects[i]->_data);
if (block == nullptr) if (block == nullptr)
{ {
println("_data of TableObject not in errorlist"); println("_data of TableObject not in _usedList");
_platform.fatalError(); _platform.fatalError();
} }
buffer = pushWord(block->size, buffer); bufferPos = pushWord(block->size, bufferPos);
} }
else else
buffer = pushWord(0, buffer); bufferPos = pushWord(0, bufferPos);
flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
} }
_platform.commitToEeprom(); _platform.commitNonVolatileMemory();
printHex("SAVED ", _data, _metadataSize); }
void Memory::saveMemory()
{
_platform.commitNonVolatileMemory();
} }
void Memory::addSaveRestore(SaveRestore* obj) void Memory::addSaveRestore(SaveRestore* obj)
@ -151,7 +207,7 @@ void Memory::addSaveRestore(TableObject* obj)
uint8_t* Memory::allocMemory(size_t size) uint8_t* Memory::allocMemory(size_t size)
{ {
// always allocate aligned to 32 bit // always allocate aligned to pagesize
size = alignToPageSize(size); size = alignToPageSize(size);
MemoryBlock* freeBlock = _freeList; 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) 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) 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) uint32_t Memory::toRelative(uint8_t* absoluteAddress)
{ {
return absoluteAddress - _data; return absoluteAddress - _platform.getNonVolatileMemoryStart();
} }
MemoryBlock* Memory::removeFromList(MemoryBlock* head, MemoryBlock* item) MemoryBlock* Memory::removeFromList(MemoryBlock* head, MemoryBlock* item)
@ -354,8 +410,9 @@ void Memory::addToFreeList(MemoryBlock* block)
uint16_t Memory::alignToPageSize(size_t size) uint16_t Memory::alignToPageSize(size_t size)
{ {
// to 32 bit for now size_t pageSize = 4; //_platform.flashPageSize(); // align to 32bit for now, as aligning to flash-page-size causes side effects in programming
return (size + 3) & ~0x3; // pagesize should be a multiply of two
return (size + pageSize - 1) & (-1*pageSize);
} }
MemoryBlock* Memory::findBlockInList(MemoryBlock* head, uint8_t* address) 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); MemoryBlock* newUsedBlock = new MemoryBlock(address, size);
addToUsedList(newUsedBlock); 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; 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 class Memory
{ {
public: public:
Memory(Platform& platform, DeviceObject& deviceObject); Memory(Platform& platform, DeviceObject& deviceObject);
virtual ~Memory();
void readMemory(); void readMemory();
void writeMemory(); void writeMemory();
void saveMemory();
void addSaveRestore(SaveRestore* obj); void addSaveRestore(SaveRestore* obj);
void addSaveRestore(TableObject* obj); void addSaveRestore(TableObject* obj);
@ -39,6 +50,9 @@ public:
uint8_t* toAbsolute(uint32_t relativeAddress); uint8_t* toAbsolute(uint32_t relativeAddress);
uint32_t toRelative(uint8_t* absoluteAddress); uint32_t toRelative(uint8_t* absoluteAddress);
void versionCheckCallback(VersionCheckCallback func);
VersionCheckCallback versionCheckCallback();
private: private:
void removeFromFreeList(MemoryBlock* block); void removeFromFreeList(MemoryBlock* block);
void addToUsedList(MemoryBlock* block); void addToUsedList(MemoryBlock* block);
@ -49,14 +63,19 @@ public:
MemoryBlock* findBlockInList(MemoryBlock* head, uint8_t* address); MemoryBlock* findBlockInList(MemoryBlock* head, uint8_t* address);
void addNewUsedBlock(uint8_t* address, size_t size); 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; Platform& _platform;
DeviceObject& _deviceObject; DeviceObject& _deviceObject;
SaveRestore* _saveRestores[MAXSAVE] = {0}; SaveRestore* _saveRestores[MAXSAVE] = {0};
TableObject* _tableObjects[MAXTABLEOBJ] = {0}; TableObject* _tableObjects[MAXTABLEOBJ] = {0};
uint8_t _saveCount = 0; uint8_t _saveCount = 0;
uint8_t _tableObjCount = 0; uint8_t _tableObjCount = 0;
uint8_t* _data = nullptr;
MemoryBlock* _freeList = nullptr; MemoryBlock* _freeList = nullptr;
MemoryBlock* _usedList = 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 void rtObj(RouterObject& rtObj); // Coupler model 1.x
// from transport layer // from transport layer
virtual void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; 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; 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; void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
private: private:
enum CouplerType enum CouplerType
@ -46,16 +46,16 @@ class NetworkLayerCoupler : public NetworkLayer
static constexpr uint8_t kLocalIfIndex = 99; static constexpr uint8_t kLocalIfIndex = 99;
// from entities // 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; 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; 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; 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; 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 systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source, uint8_t srcIfIdx) override; 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 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, 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(); NetworkLayerEntity& getInterface();
// from transport layer // from transport layer
virtual void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; 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; 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; void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override;
private: private:
// from entities // 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; 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; 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; 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; 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 systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu,
Priority priority, uint16_t source, uint8_t srcIfIdx) override; 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 // Support only a single physical interface for normal devices
NetworkLayerEntity _netLayerEntities[1]; NetworkLayerEntity _netLayerEntities[1];

View File

@ -1,12 +1,15 @@
#include "platform.h" #include "platform.h"
#include "bits.h"
#include <cstring>
#include <cstdlib>
NvMemoryType Platform::NonVolatileMemoryType() NvMemoryType Platform::NonVolatileMemoryType()
{ {
return _memoryType; return _memoryType;
} }
void Platform::NonVolatileMemoryType(NvMemoryType type) void Platform::NonVolatileMemoryType(NvMemoryType type)
{ {
_memoryType = type; _memoryType = type;
@ -97,3 +100,155 @@ int Platform::readBytesMultiCast(uint8_t *buffer, uint16_t maxLen)
{ {
return 0; 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 <stddef.h>
#include "save_restore.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 enum NvMemoryType
{ {
Eeprom, Eeprom,
@ -49,21 +54,62 @@ class Platform
virtual void setupSpi(); virtual void setupSpi();
virtual void closeSpi(); virtual void closeSpi();
virtual int readWriteSpi(uint8_t *data, size_t len); virtual int readWriteSpi(uint8_t *data, size_t len);
#if 0
// Flash memory //Memory
virtual size_t flashEraseBlockSize(); // in pages
virtual size_t flashPageSize(); // in bytes // --- Overwrite these methods in the device-plattform to use the EEPROM Emulation API for UserMemory ----
virtual uint8_t* userFlashStart(); // start of user flash aligned to start of an erase block //
virtual size_t userFlashSizeEraseBlocks(); // in eraseBlocks // --- changes to the UserMemory are written directly into the address space starting at getEepromBuffer
virtual void flashErase(uint16_t eraseBlockNum); //relativ to userFlashStart // --- commitToEeprom must save this to a non-volatile area if neccessary
virtual void flashWritePage(uint16_t pageNumber, uint8_t* data); //write a single page to flash (pageNumber relative to userFashStart virtual uint8_t* getEepromBuffer(uint16_t size);
#endif virtual void commitToEeprom();
virtual uint8_t* getEepromBuffer(uint16_t size) = 0; // -------------------------------------------------------------------------------------------------------
virtual void commitToEeprom() = 0;
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(); NvMemoryType NonVolatileMemoryType();
void NonVolatileMemoryType(NvMemoryType type); 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: 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; 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 loop();
void enabled(bool value); void enabled(bool value);
bool enabled() const; bool enabled() const;
virtual DptMedium mediumType() const override; DptMedium mediumType() const override;
private: private:
bool _enabled = false; bool _enabled = false;

View File

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

View File

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

View File

@ -34,12 +34,12 @@ public:
bool isRfSbcRoutingEnabled(); bool isRfSbcRoutingEnabled();
bool isIpSbcRoutingEnabled(); 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; const uint8_t* restore(const uint8_t* buffer) override;
protected: protected:
virtual void beforeStateChange(LoadState& newState) override; void beforeStateChange(LoadState& newState) override;
private: private:
// Function properties // Function properties

View File

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

View File

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

View File

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

View File

@ -6,6 +6,19 @@
#include "callback_property.h" #include "callback_property.h"
#include "data_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) TableObject::TableObject(Memory& memory)
: _memory(memory) : _memory(memory)
{} {}
@ -13,6 +26,19 @@ TableObject::TableObject(Memory& memory)
TableObject::~TableObject() 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() LoadState TableObject::loadState()
{ {
return _state; return _state;
@ -82,7 +108,11 @@ bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte)
return false; return false;
if (doFill) 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; _size = size;
@ -139,6 +169,7 @@ void TableObject::loadEventLoading(const uint8_t* data)
case LE_START_LOADING: case LE_START_LOADING:
break; break;
case LE_LOAD_COMPLETED: case LE_LOAD_COMPLETED:
_memory.saveMemory();
loadState(LS_LOADED); loadState(LS_LOADED);
break; break;
case LE_UNLOAD: case LE_UNLOAD:
@ -293,7 +324,6 @@ void TableObject::initializeProperties(size_t propertiesSize, Property** propert
//TODO: missing //TODO: missing
// 23 PID_TABLE 3 / (3) // 23 PID_TABLE 3 / (3)
// 27 PID_MCB_TABLE 3 / 3
uint8_t ownPropertiesCount = sizeof(ownProperties) / sizeof(Property*); uint8_t ownPropertiesCount = sizeof(ownProperties) / sizeof(Property*);

View File

@ -3,6 +3,9 @@
#include "interface_object.h" #include "interface_object.h"
class Memory; class Memory;
typedef void (*BeforeTablesUnloadCallback)();
/** /**
* This class provides common functionality for interface objects that are configured by ETS with MemorWrite. * 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; uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override; const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() 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. * 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() * If there is a error changing the state newState should be set to ::LS_ERROR and errorCode()
* to a reason for the failure. * 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 * returns the internal data of the interface object. This pointer belongs to the TableObject class and
* must not be freed. * must not be freed.
@ -47,7 +54,9 @@ class TableObject: public InterfaceObject
void errorCode(ErrorCode errorCode); void errorCode(ErrorCode errorCode);
void initializeProperties(size_t propertiesSize, Property** properties) override; void initializeProperties(size_t propertiesSize, Property** properties) override;
static BeforeTablesUnloadCallback _beforeTablesUnload;
private: private:
uint32_t tableReference(); uint32_t tableReference();
bool allocTable(uint32_t size, bool doFill, uint8_t fillByte); bool allocTable(uint32_t size, bool doFill, uint8_t fillByte);
@ -68,6 +77,7 @@ class TableObject: public InterfaceObject
LoadState _state = LS_UNLOADED; LoadState _state = LS_UNLOADED;
Memory& _memory; Memory& _memory;
uint8_t *_data = 0; 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. * 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_STOP_MODE_REQ 0x0E
#define U_EXIT_STOP_MODE_REQ 0x0F #define U_EXIT_STOP_MODE_REQ 0x0F
#define U_ACK_REQ 0x10 //-0x17 #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_CONFIGURE_REQ 0x18
#define U_INT_REG_WR_REQ 0x28 #define U_INT_REG_WR_REQ 0x28
#define U_INT_REG_RD_REQ 0x38 #define U_INT_REG_RD_REQ 0x38
@ -82,7 +85,6 @@
enum { enum {
TX_IDLE, TX_IDLE,
TX_FRAME, TX_FRAME,
TX_WAIT_ECHO,
TX_WAIT_CONN TX_WAIT_CONN
}; };
@ -100,10 +102,23 @@ enum {
#define RESET_TIMEOUT 100 //milli seconds #define RESET_TIMEOUT 100 //milli seconds
#define TX_TIMEPAUSE 0 // 0 means 1 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 // If this threshold is reached loop() goes into
// "hog mode" where it stays in loop() while L2 address reception // "hog mode" where it stays in loop() while L2 address reception
#define HOGMODE_THRESHOLD 3 // milli seconds #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() void TpUartDataLinkLayer::loop()
{ {
if (!_enabled) if (!_enabled)
@ -122,7 +137,6 @@ void TpUartDataLinkLayer::loop()
// Loop once and repeat as long we have rx data available // Loop once and repeat as long we have rx data available
do { do {
// Signals to communicate from rx part with the tx part // 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 uint8_t dataConnMsg = 0; // The DATA_CONN message just seen or 0
#ifdef KNX_WAIT_FOR_ADDR #ifdef KNX_WAIT_FOR_ADDR
@ -150,6 +164,12 @@ void TpUartDataLinkLayer::loop()
case RX_WAIT_START: case RX_WAIT_START:
if (_platform.uartAvailable()) if (_platform.uartAvailable())
{ {
if (_platform.uartAvailable() > OVERRUN_COUNT)
{
print("input buffer overrun: "); println(_platform.uartAvailable());
enterRxWaitEOP();
break;
}
rxByte = _platform.readUart(); rxByte = _platform.readUart();
#ifdef DBG_TRACE #ifdef DBG_TRACE
print(rxByte, HEX); print(rxByte, HEX);
@ -258,7 +278,7 @@ void TpUartDataLinkLayer::loop()
if (millis() - _lastByteRxTime > EOPR_TIMEOUT) if (millis() - _lastByteRxTime > EOPR_TIMEOUT)
{ {
_rxState = RX_WAIT_START; _rxState = RX_WAIT_START;
print("EOPR inside RX_L_ADDR"); println("EOPR @ RX_L_ADDR");
break; break;
} }
if (!_platform.uartAvailable()) if (!_platform.uartAvailable())
@ -274,27 +294,19 @@ void TpUartDataLinkLayer::loop()
if (_RxByteCnt == 7) if (_RxByteCnt == 7)
{ {
//Destination Address + payload available //Destination Address + payload available
//check if echo //check if echo; ignore repeat bit of control byte
if (_sendBuffer != nullptr && (!((buffer[0] ^ _sendBuffer[0]) & ~0x20) && !memcmp(buffer + _convert + 1, _sendBuffer + 1, 5))) _isEcho = (_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;
}
//convert into Extended.ind //convert into Extended.ind
if (_convert) if (_convert)
{ {
uint8_t payloadLength = buffer[6] & 0x0F;
buffer[1] = buffer[6] & 0xF0; buffer[1] = buffer[6] & 0xF0;
buffer[6] = payloadLength; buffer[6] &= 0x0F;
} }
if (!_isEcho) 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) // 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. // or any filter tables (coupler) to see if we are addressed.
@ -305,11 +317,11 @@ void TpUartDataLinkLayer::loop()
if (_cb.isAckRequired(addr, isGroupAddress)) if (_cb.isAckRequired(addr, isGroupAddress))
{ {
c |= 0x01; c |= U_ACK_REQ_ADRESSED;
} }
// Hint: We can send directly here, this doesn't disturb other transmissions // 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); _platform.writeUart(c);
} }
_rxState = RX_L_DATA; _rxState = RX_L_DATA;
@ -325,8 +337,8 @@ void TpUartDataLinkLayer::loop()
#endif #endif
if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE) if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE)
{ {
_rxState = RX_WAIT_EOP;
println("invalid telegram size"); println("invalid telegram size");
enterRxWaitEOP();
} }
else else
{ {
@ -336,26 +348,25 @@ void TpUartDataLinkLayer::loop()
if (_RxByteCnt == buffer[6] + 7 + 2) if (_RxByteCnt == buffer[6] + 7 + 2)
{ {
//complete Frame received, payloadLength+1 for TCPI +1 for CRC //complete Frame received, payloadLength+1 for TCPI +1 for CRC
//check if crc is correct
if (rxByte == (uint8_t)(~_xorSum)) if (rxByte == (uint8_t)(~_xorSum))
{ {
//check if crc is correct if (!_isEcho)
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
{ {
_receiveBuffer[0] = 0x29; _receiveBuffer[0] = 0x29;
_receiveBuffer[1] = 0; _receiveBuffer[1] = 0;
#ifdef DBG_TRACE
unsigned long runTime = millis();
#endif
frameBytesReceived(_receiveBuffer, _RxByteCnt + 2); 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; _rxState = RX_WAIT_START;
#ifdef DBG_TRACE #ifdef DBG_TRACE
@ -365,7 +376,7 @@ void TpUartDataLinkLayer::loop()
else else
{ {
println("frame with invalid crc ignored"); println("frame with invalid crc ignored");
_rxState = RX_WAIT_EOP; enterRxWaitEOP();
} }
} }
else else
@ -376,20 +387,18 @@ void TpUartDataLinkLayer::loop()
case RX_WAIT_EOP: case RX_WAIT_EOP:
if (millis() - _lastByteRxTime > EOP_TIMEOUT) if (millis() - _lastByteRxTime > EOP_TIMEOUT)
{ {
_RxByteCnt = 0; // found a gap
_rxState = RX_WAIT_START; _rxState = RX_WAIT_START;
#ifdef DBG_TRACE #ifdef DBG_TRACE
println("RX_WAIT_START"); println("RX_WAIT_START");
#endif #endif
break; break;
} }
if (!_platform.uartAvailable()) if (_platform.uartAvailable())
break; {
_lastByteRxTime = millis(); _platform.readUart();
rxByte = _platform.readUart(); _lastByteRxTime = millis();
#ifdef DBG_TRACE }
print(rxByte, HEX);
#endif
break; break;
default: default:
break; break;
@ -397,8 +406,8 @@ void TpUartDataLinkLayer::loop()
} while (_rxState == RX_L_ADDR && (stayInRx || _platform.uartAvailable())); } while (_rxState == RX_L_ADDR && (stayInRx || _platform.uartAvailable()));
// Check for spurios DATA_CONN message // Check for spurios DATA_CONN message
if (dataConnMsg && _txState != TX_WAIT_CONN && _txState != TX_WAIT_ECHO) { if (dataConnMsg && _txState != TX_WAIT_CONN) {
println("got unexpected L_DATA_CON"); println("unexpected L_DATA_CON");
} }
switch (_txState) switch (_txState)
@ -419,9 +428,9 @@ void TpUartDataLinkLayer::loop()
if (sendSingleFrameByte() == false) if (sendSingleFrameByte() == false)
{ {
_waitConfirmStartTime = millis(); _waitConfirmStartTime = millis();
_txState = TX_WAIT_ECHO; _txState = TX_WAIT_CONN;
#ifdef DBG_TRACE #ifdef DBG_TRACE
println("TX_WAIT_ECHO"); println("TX_WAIT_CONN");
#endif #endif
} }
else else
@ -430,22 +439,10 @@ void TpUartDataLinkLayer::loop()
} }
} }
break; break;
case TX_WAIT_ECHO:
case TX_WAIT_CONN: case TX_WAIT_CONN:
if (isEchoComplete) if (dataConnMsg)
{ {
_txState = TX_WAIT_CONN; dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, (dataConnMsg & SUCCESS));
#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));
delete[] _sendBuffer; delete[] _sendBuffer;
_sendBuffer = 0; _sendBuffer = 0;
_sendBufferLength = 0; _sendBufferLength = 0;
@ -579,7 +576,8 @@ DptMedium TpUartDataLinkLayer::mediumType() const
bool TpUartDataLinkLayer::sendSingleFrameByte() bool TpUartDataLinkLayer::sendSingleFrameByte()
{ {
uint8_t cmd[2]; uint8_t cmd[2];
uint8_t idx = _TxByteCnt / 64;
uint8_t idx = _TxByteCnt >> 6;
if (_sendBuffer == NULL) if (_sendBuffer == NULL)
return false; return false;
@ -594,9 +592,9 @@ bool TpUartDataLinkLayer::sendSingleFrameByte()
} }
if (_TxByteCnt != _sendBufferLength - 1) if (_TxByteCnt != _sendBufferLength - 1)
cmd[0] = U_L_DATA_START_CONT_REQ | _TxByteCnt; cmd[0] = U_L_DATA_START_CONT_REQ | (_TxByteCnt & 0x3F);
else else
cmd[0] = U_L_DATA_END_REQ | _TxByteCnt; cmd[0] = U_L_DATA_END_REQ | (_TxByteCnt & 0x3F);
cmd[1] = _sendBuffer[_TxByteCnt]; cmd[1] = _sendBuffer[_TxByteCnt];
#ifdef DBG_TRACE #ifdef DBG_TRACE
@ -618,7 +616,6 @@ bool TpUartDataLinkLayer::sendSingleFrameByte()
void TpUartDataLinkLayer::addFrameTxQueue(CemiFrame& frame) void TpUartDataLinkLayer::addFrameTxQueue(CemiFrame& frame)
{ {
_tx_queue_frame_t* tx_frame = new _tx_queue_frame_t; _tx_queue_frame_t* tx_frame = new _tx_queue_frame_t;
tx_frame->length = frame.telegramLengthtTP(); tx_frame->length = frame.telegramLengthtTP();
tx_frame->data = new uint8_t[tx_frame->length]; tx_frame->data = new uint8_t[tx_frame->length];

View File

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

View File

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

View File

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

View File

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

View File

@ -48,13 +48,20 @@
#else #else
#if !defined(LED_BUILTIN) #if !defined(LED_BUILTIN)
#define LED_BUILTIN 5 // see GPIO_PinConfig gpioPinConfigs[] #define LED_BUILTIN 5 // see GPIO_PinConfig gpioPinConfigs[]
#endif #endif
#include "cc1310_platform.h" #include "cc1310_platform.h"
#ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE
extern void buttonUp(); extern void buttonUp();
#endif #endif
#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 const uint8_t* (*RestoreCallback)(const uint8_t* buffer);
typedef uint8_t* (*SaveCallback)(uint8_t* buffer); typedef uint8_t* (*SaveCallback)(uint8_t* buffer);
typedef void (*IsrFunctionPtr)(); typedef void (*IsrFunctionPtr)();
@ -73,6 +80,7 @@ template <class P, class B> class KnxFacade : private SaveRestore
KnxFacade(B& bau) : _bau(bau) KnxFacade(B& bau) : _bau(bau)
{ {
_platformPtr = static_cast<P*>(&bau.platform());
manufacturerId(0xfa); manufacturerId(0xfa);
bauNumber(platform().uniqueSerialNumber()); bauNumber(platform().uniqueSerialNumber());
_bau.addSaveRestore(this); _bau.addSaveRestore(this);
@ -176,24 +184,7 @@ template <class P, class B> class KnxFacade : private SaveRestore
_progLedOnCallback = progLedOnCallback; _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() uint32_t buttonPin()
{ {
return _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 // Workaround for https://github.com/arduino/ArduinoCore-samd/issues/587
#if (ARDUINO_API_VERSION >= 10200) #if (ARDUINO_API_VERSION >= 10200)
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, (PinStatus)_buttonPinInterruptOn); attachInterrupt(_buttonPin, _progButtonISRFuncPtr, (PinStatus)CHANGE);
#else #else
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, _buttonPinInterruptOn); attachInterrupt(_buttonPin, _progButtonISRFuncPtr, CHANGE);
#endif #endif
} }
@ -400,7 +391,18 @@ template <class P, class B> class KnxFacade : private SaveRestore
void restart(uint16_t individualAddress) 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: private:
@ -410,9 +412,8 @@ template <class P, class B> class KnxFacade : private SaveRestore
ProgLedOnCallback _progLedOnCallback = 0; ProgLedOnCallback _progLedOnCallback = 0;
ProgLedOffCallback _progLedOffCallback = 0; ProgLedOffCallback _progLedOffCallback = 0;
uint32_t _ledPinActiveOn = LOW; uint32_t _ledPinActiveOn = LOW;
uint32_t _ledPin = LED_BUILTIN; uint32_t _ledPin = KNX_LED;
uint32_t _buttonPinInterruptOn = RISING; uint32_t _buttonPin = KNX_BUTTON;
uint32_t _buttonPin = 0;
SaveCallback _saveCallback = 0; SaveCallback _saveCallback = 0;
RestoreCallback _restoreCallback = 0; RestoreCallback _restoreCallback = 0;
volatile bool _toggleProgMode = false; volatile bool _toggleProgMode = false;

View File

@ -1,16 +1,22 @@
/*----------------------------------------------------- /*-----------------------------------------------------
Plattform for Raspberry Pi Pico and other RP2040 boards 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" 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 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 RTTI must be set to enabled in the board options
A maximum of 4kB emulated EEPROM is supported. Uses direct flash reading/writing.
For more, use or own emulation (maybe with littlefs) 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 <EEPROM.h> // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support
#include <pico/unique_id.h> // from Pico SDK #include <pico/unique_id.h> // from Pico SDK
#include <hardware/watchdog.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() RP2040ArduinoPlatform::RP2040ArduinoPlatform()
#ifndef KNX_NO_DEFAULT_UART #ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial1) : ArduinoPlatform(&KNX_SERIAL)
#endif #endif
{ {
#ifndef USE_RP2040_EEPROM_EMULATION
_memoryType = Flash;
#endif
} }
RP2040ArduinoPlatform::RP2040ArduinoPlatform( HardwareSerial* s) : ArduinoPlatform(s) RP2040ArduinoPlatform::RP2040ArduinoPlatform( HardwareSerial* s) : ArduinoPlatform(s)
{ {
#ifndef USE_RP2040_EEPROM_EMULATION
_memoryType = Flash;
#endif
} }
void RP2040ArduinoPlatform::setupUart() void RP2040ArduinoPlatform::setupUart()
@ -54,7 +80,14 @@ void RP2040ArduinoPlatform::setupUart()
uint32_t RP2040ArduinoPlatform::uniqueSerialNumber() uint32_t RP2040ArduinoPlatform::uniqueSerialNumber()
{ {
pico_unique_board_id_t id; // 64Bit unique serial number from the QSPI flash 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 // 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]); 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); 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) uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint16_t size)
{ {
if(size > 4096) if(size > 4096)
@ -91,6 +165,73 @@ void RP2040ArduinoPlatform::commitToEeprom()
{ {
EEPROM.commit(); 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 #endif

View File

@ -4,6 +4,16 @@
#ifdef ARDUINO_ARCH_RP2040 #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 class RP2040ArduinoPlatform : public ArduinoPlatform
{ {
public: public:
@ -13,11 +23,36 @@ public:
void setupUart(); void setupUart();
// unique serial number // unique serial number
uint32_t uniqueSerialNumber() override; uint32_t uniqueSerialNumber() override;
void restart(); void restart();
#ifdef USE_RP2040_EEPROM_EMULATION
uint8_t* getEepromBuffer(uint16_t size); uint8_t* getEepromBuffer(uint16_t size);
void commitToEeprom(); 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 #endif

View File

@ -4,17 +4,33 @@
#include <knx/bits.h> #include <knx/bits.h>
#include <Arduino.h> #include <Arduino.h>
#ifdef USE_SAMD_EEPROM_EMULATION
#include <FlashAsEEPROM.h> #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() SamdPlatform::SamdPlatform()
#ifndef KNX_NO_DEFAULT_UART #ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial1) : ArduinoPlatform(&KNX_SERIAL)
#endif #endif
{ {
#ifndef USE_SAMD_EEPROM_EMULATION
init();
#endif
} }
SamdPlatform::SamdPlatform( HardwareSerial* s) : ArduinoPlatform(s) SamdPlatform::SamdPlatform( HardwareSerial* s) : ArduinoPlatform(s)
{ {
#ifndef USE_SAMD_EEPROM_EMULATION
init();
#endif
} }
uint32_t SamdPlatform::uniqueSerialNumber() uint32_t SamdPlatform::uniqueSerialNumber()
@ -43,7 +59,9 @@ void SamdPlatform::restart()
NVIC_SystemReset(); 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); //EEPROM.begin(size);
if(size > EEPROM_EMULATION_SIZE) if(size > EEPROM_EMULATION_SIZE)
@ -56,6 +74,156 @@ void SamdPlatform::commitToEeprom()
{ {
EEPROM.commit(); 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 #endif

View File

@ -4,6 +4,8 @@
#ifdef ARDUINO_ARCH_SAMD #ifdef ARDUINO_ARCH_SAMD
#define PAGES_PER_ROW 4
class SamdPlatform : public ArduinoPlatform class SamdPlatform : public ArduinoPlatform
{ {
public: public:
@ -14,8 +16,40 @@ public:
uint32_t uniqueSerialNumber() override; uint32_t uniqueSerialNumber() override;
void restart(); void restart();
#ifdef USE_SAMD_EEPROM_EMULATION
uint8_t* getEepromBuffer(uint16_t size); uint8_t* getEepromBuffer(uint16_t size);
void commitToEeprom(); 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 #endif

View File

@ -4,9 +4,13 @@
#include <EEPROM.h> #include <EEPROM.h>
#include "knx/bits.h" #include "knx/bits.h"
#ifndef KNX_SERIAL
#define KNX_SERIAL Serial2
#endif
Stm32Platform::Stm32Platform() Stm32Platform::Stm32Platform()
#ifndef KNX_NO_DEFAULT_UART #ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial2) : ArduinoPlatform(&KNX_SERIAL)
#endif #endif
{ {
} }
@ -32,16 +36,21 @@ void Stm32Platform::restart()
uint8_t * Stm32Platform::getEepromBuffer(uint16_t size) 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; return _eepromPtr;
} }
@ -51,6 +60,11 @@ void Stm32Platform::commitToEeprom()
return; return;
for (uint16_t i = 0; i < _eepromSize; ++i) for (uint16_t i = 0; i < _eepromSize; ++i)
eeprom_buffered_write_byte(i, _eepromPtr[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(); eeprom_buffer_flush();
} }